use crate::auth::scope::{authorize_with_matcher, FlexibleMatcher};
use crate::error::AuthError;
use crate::model::{AuthContext, AuthStrategy, Identifiable, Resource};
pub struct Authorization {
strategy: AuthStrategy,
}
impl Authorization {
pub fn new(strategy: &str) -> Result<Self, AuthError> {
let strategy = AuthStrategy::from_str(strategy)?;
Ok(Self { strategy })
}
pub fn authorize(
&mut self,
context: &AuthContext,
service: &str,
permission: &str,
delimiter: Option<&str>,
) -> Result<(), AuthError> {
match self.strategy {
AuthStrategy::ABAC => {
let user = context.user.clone().ok_or(AuthError::MissingUser)?;
let resource = context.resource.clone().ok_or(AuthError::MissingResource)?;
gen_authorize(&user, service, permission, |u, _, _| {
u.department == resource.department && u.clearance_level >= resource.required_level
})
}
AuthStrategy::RBAC => {
let user = context.user.clone().ok_or(AuthError::MissingUser)?;
gen_authorize(&user, service, permission, |u, _, p| {
u.role.permissions.iter().any(|perm| format!("{:?}", perm).eq_ignore_ascii_case(p))
})
}
AuthStrategy::SBA => {
let claims = context.claims.clone().ok_or(AuthError::MissingClaims)?;
let resource = context.resource.clone().unwrap_or_else(|| Resource {
department: "*".to_string(),
required_level: 0,
});
let delim = delimiter.unwrap_or(".");
let candidates = vec![
format!("{}{}{}{}{}", service, delim, resource.department, delim, permission),
format!("{}{}{}", service, delim, permission),
format!("{}", permission),
];
let scopes = claims.scopes.join(" ");
gen_authorize(&claims, service, permission, |_, _, _| {
candidates.iter().any(|candidate| {
authorize_with_matcher::<FlexibleMatcher>(&scopes, candidate)
})
})
}
}
}
}
pub fn gen_authorize<U, S, P, F>(
user: &U,
service: S,
permission: P,
check_permission: F,
) -> Result<(), AuthError>
where
F: Fn(&U, &S, &P) -> bool,
U: Identifiable,
S: ToString,
P: ToString,
{
if check_permission(user, &service, &permission) {
Ok(())
} else {
Err(AuthError::AccessDenied {
user: user.identity(),
service: service.to_string(),
permission: permission.to_string(),
})
}
}