use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthzResult {
Allowed,
Denied,
Error(String),
}
#[async_trait::async_trait]
pub trait Authz: Send + Sync + 'static {
async fn check(
&self,
namespace: &str,
object_id: &str,
relation: &str,
subject_id: &str,
) -> AuthzResult;
}
pub struct NoneAuthz;
#[async_trait::async_trait]
impl Authz for NoneAuthz {
async fn check(&self, _ns: &str, _obj: &str, _rel: &str, _sub: &str) -> AuthzResult {
AuthzResult::Allowed
}
}
#[cfg(feature = "auth-zanzibar")]
pub struct AssayZanzibarAuthz {
store: Arc<dyn crate::zanzibar::ZanzibarStore>,
}
#[cfg(feature = "auth-zanzibar")]
impl AssayZanzibarAuthz {
pub fn new(store: Arc<dyn crate::zanzibar::ZanzibarStore>) -> Self {
Self { store }
}
}
#[cfg(feature = "auth-zanzibar")]
#[async_trait::async_trait]
impl Authz for AssayZanzibarAuthz {
async fn check(
&self,
namespace: &str,
object_id: &str,
relation: &str,
subject_id: &str,
) -> AuthzResult {
use crate::zanzibar::{CheckResult, Consistency, ObjectRef, SubjectRef};
let resource = ObjectRef {
object_type: namespace.to_string(),
object_id: object_id.to_string(),
};
let subject = SubjectRef {
subject_type: "user".to_string(),
subject_id: subject_id.to_string(),
subject_rel: String::new(),
};
match self
.store
.check(&resource, relation, &subject, Consistency::Minimum)
.await
{
Ok(CheckResult::Allowed { .. }) => AuthzResult::Allowed,
Ok(_) => AuthzResult::Denied,
Err(e) => AuthzResult::Error(format!("zanzibar check: {e}")),
}
}
}
pub type DynAuthz = Arc<dyn Authz>;
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn none_authz_always_allows() {
let a = NoneAuthz;
assert_eq!(
a.check("vault", "main", "access", "usr_x").await,
AuthzResult::Allowed
);
assert_eq!(
a.check("vault_path", "secret/prod", "reader", "usr_y")
.await,
AuthzResult::Allowed
);
}
}