#[cfg(test)]
mod tests;
use crate::{
config::{BucketAccess, BucketConfig},
metadata::StorageMetadataRow,
};
const ADMIN_ROLE: &str = "admin";
#[derive(Debug, Clone, Copy)]
pub struct StorageRlsEvaluator;
impl StorageRlsEvaluator {
#[must_use]
pub const fn new() -> Self {
Self
}
#[must_use]
pub fn can_read(
&self,
user_id: Option<&str>,
roles: &[String],
bucket: &BucketConfig,
object: &StorageMetadataRow,
) -> bool {
match bucket.access {
BucketAccess::PublicRead => true,
BucketAccess::Private => is_admin(roles) || is_owner(user_id, object),
}
}
#[must_use]
pub fn can_write(
&self,
user_id: Option<&str>,
roles: &[String],
_bucket: &BucketConfig,
) -> bool {
if is_admin(roles) {
return true;
}
user_id.is_some()
}
#[must_use]
pub fn can_delete(
&self,
user_id: Option<&str>,
roles: &[String],
_bucket: &BucketConfig,
object: &StorageMetadataRow,
) -> bool {
is_admin(roles) || is_owner(user_id, object)
}
#[must_use]
pub fn filter_visible(
&self,
user_id: Option<&str>,
roles: &[String],
bucket: &BucketConfig,
objects: Vec<StorageMetadataRow>,
) -> Vec<StorageMetadataRow> {
match bucket.access {
BucketAccess::PublicRead => objects,
BucketAccess::Private => {
if is_admin(roles) {
return objects;
}
objects.into_iter().filter(|obj| is_owner(user_id, obj)).collect()
},
}
}
}
impl Default for StorageRlsEvaluator {
fn default() -> Self {
Self::new()
}
}
fn is_admin(roles: &[String]) -> bool {
roles.iter().any(|r| r == ADMIN_ROLE)
}
fn is_owner(user_id: Option<&str>, object: &StorageMetadataRow) -> bool {
match (user_id, &object.owner_id) {
(Some(uid), Some(owner)) => uid == owner,
_ => false,
}
}