use crate::{EvalCtx, Policy, PolicyEvalResult};
use async_trait::async_trait;
pub struct RbacPolicy<S, F1, F2> {
required_roles_resolver: F1,
user_roles_resolver: F2,
_marker: std::marker::PhantomData<S>,
}
impl<S, F1, F2> RbacPolicy<S, F1, F2> {
pub fn new(required_roles_resolver: F1, user_roles_resolver: F2) -> Self {
Self {
required_roles_resolver,
user_roles_resolver,
_marker: std::marker::PhantomData,
}
}
}
#[async_trait]
impl<S, R, A, C, F1, F2> Policy<S, R, A, C> for RbacPolicy<S, F1, F2>
where
S: Sync + Send,
R: Sync + Send,
A: Sync + Send,
C: Sync + Send,
F1: Fn(&R, &A) -> Vec<uuid::Uuid> + Sync + Send,
F2: Fn(&S) -> Vec<uuid::Uuid> + Sync + Send,
{
async fn evaluate(&self, ctx: &EvalCtx<'_, S, R, A, C>) -> PolicyEvalResult {
let required_roles = (self.required_roles_resolver)(ctx.resource, ctx.action);
let user_roles = (self.user_roles_resolver)(ctx.subject);
let has_role = required_roles.iter().any(|role| user_roles.contains(role));
if has_role {
PolicyEvalResult::Granted {
policy_type: Policy::<S, R, A, C>::policy_type(self).to_string(),
reason: Some("User has required role".to_string()),
}
} else {
PolicyEvalResult::Denied {
policy_type: Policy::<S, R, A, C>::policy_type(self).to_string(),
reason: "User doesn't have required role".to_string(),
}
}
}
fn policy_type(&self) -> &str {
"RbacPolicy"
}
}