use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
use crate::authorization::strategy::abstract_authorization_strategy::AuthorizationStrategy;
use crate::AuthServiceResult;
use rocketmq_security_api::AuthorizationDecision;
pub struct AuthorizationEvaluator<S>
where
S: AuthorizationStrategy,
{
authorization_strategy: S,
}
impl<S> AuthorizationEvaluator<S>
where
S: AuthorizationStrategy,
{
pub fn new(authorization_strategy: S) -> Self {
Self { authorization_strategy }
}
pub async fn evaluate(&self, contexts: &[DefaultAuthorizationContext]) -> AuthServiceResult<AuthorizationDecision> {
if contexts.is_empty() {
return Ok(AuthorizationDecision::Allow);
}
for context in contexts {
match self.authorization_strategy.evaluate(context).await? {
AuthorizationDecision::Allow => {}
decision @ AuthorizationDecision::Deny(_) => return Ok(decision),
}
}
Ok(AuthorizationDecision::Allow)
}
pub fn strategy(&self) -> &S {
&self.authorization_strategy
}
}
#[cfg(test)]
mod tests {
use rocketmq_security_api::AuthorizationDenial;
use super::*;
use crate::authorization::strategy::abstract_authorization_strategy::AuthorizationFuture;
struct MockStrategy {
should_fail: bool,
decision: AuthorizationDecision,
}
impl AuthorizationStrategy for MockStrategy {
fn evaluate<'a>(&'a self, _context: &'a DefaultAuthorizationContext) -> AuthorizationFuture<'a> {
Box::pin(async move {
if self.should_fail {
Err(crate::AuthServiceError::invalid_context("mock failure".to_owned()))
} else {
Ok(self.decision)
}
})
}
}
#[tokio::test]
async fn test_evaluator_empty_contexts() {
let strategy = MockStrategy {
should_fail: false,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let result = evaluator.evaluate(&[]).await;
assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
}
#[tokio::test]
async fn test_evaluator_success() {
let strategy = MockStrategy {
should_fail: false,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let context = DefaultAuthorizationContext::default();
let result = evaluator.evaluate(&[context]).await;
assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
}
#[tokio::test]
async fn test_evaluator_failure() {
let strategy = MockStrategy {
should_fail: true,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let context = DefaultAuthorizationContext::default();
let result = evaluator.evaluate(&[context]).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_evaluator_multiple_contexts_success() {
let strategy = MockStrategy {
should_fail: false,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let contexts = vec![
DefaultAuthorizationContext::default(),
DefaultAuthorizationContext::default(),
DefaultAuthorizationContext::default(),
];
let result = evaluator.evaluate(&contexts).await;
assert_eq!(result.unwrap(), AuthorizationDecision::Allow);
}
#[tokio::test]
async fn test_evaluator_multiple_contexts_one_failure() {
let strategy = MockStrategy {
should_fail: true,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let contexts = vec![
DefaultAuthorizationContext::default(),
DefaultAuthorizationContext::default(),
];
let result = evaluator.evaluate(&contexts).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_evaluator_stops_at_denial() {
let strategy = MockStrategy {
should_fail: false,
decision: AuthorizationDecision::Deny(AuthorizationDenial::PolicyNotApplicable),
};
let evaluator = AuthorizationEvaluator::new(strategy);
let result = evaluator.evaluate(&[DefaultAuthorizationContext::default()]).await;
assert_eq!(
result.unwrap(),
AuthorizationDecision::Deny(AuthorizationDenial::PolicyNotApplicable)
);
}
#[test]
fn test_evaluator_get_strategy() {
let strategy = MockStrategy {
should_fail: false,
decision: AuthorizationDecision::Allow,
};
let evaluator = AuthorizationEvaluator::new(strategy);
let _strategy_ref = evaluator.strategy();
}
}