use std::sync::Arc;
use async_trait::async_trait;
use axum::extract::FromRequestParts;
use crate::domain::entity::StoredFile;
use crate::error::BucketError;
pub trait AuthExtractor<S = ()>: FromRequestParts<S> + Send + Sync + 'static {}
impl<T, S> AuthExtractor<S> for T where T: FromRequestParts<S> + Send + Sync + 'static {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthzDecision {
Allow,
Deny,
}
impl AuthzDecision {
pub fn is_allowed(self) -> bool {
matches!(self, AuthzDecision::Allow)
}
}
#[async_trait]
pub trait AuthzPolicy<Identity>: Send + Sync + 'static
where
Identity: Send + Sync + 'static,
{
async fn decide(
&self,
identity: &Identity,
file: &StoredFile,
) -> Result<AuthzDecision, BucketError>;
async fn ensure_can_read(
&self,
identity: &Identity,
file: &StoredFile,
) -> Result<(), BucketError> {
match self.decide(identity, file).await? {
AuthzDecision::Allow => Ok(()),
AuthzDecision::Deny => Err(BucketError::Forbidden),
}
}
}
pub struct DefaultOwnerOnlyPolicy;
#[async_trait]
impl<I> AuthzPolicy<I> for DefaultOwnerOnlyPolicy
where
I: HasOwnerId + Send + Sync + 'static,
{
async fn decide(
&self,
identity: &I,
file: &StoredFile,
) -> Result<AuthzDecision, BucketError> {
if identity.owner_id() == file.owner_id {
Ok(AuthzDecision::Allow)
} else {
Ok(AuthzDecision::Deny)
}
}
}
pub trait HasOwnerId {
fn owner_id(&self) -> uuid::Uuid;
}
pub type ArcAuthzPolicy<I> = Arc<dyn AuthzPolicy<I>>;