use std::sync::Arc;
use rocketmq_security_api::AuthorizationDecision;
use rocketmq_security_api::AuthorizationDenial;
use crate::authentication::enums::subject_type::SubjectType;
use crate::authentication::enums::user_status::UserStatus;
use crate::authentication::enums::user_type::UserType;
use crate::authentication::model::user::User;
#[cfg(test)]
use crate::authentication::provider::AuthenticationMetadataProvider;
use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
use crate::AuthFailureKind;
use crate::AuthOperation;
use crate::AuthServiceError;
use crate::AuthServiceResult;
use crate::UserMetadataRead;
pub struct UserAuthorizationHandler<P: UserMetadataRead + ?Sized> {
authentication_metadata_provider: Arc<P>,
}
impl<P: UserMetadataRead + ?Sized> UserAuthorizationHandler<P> {
pub fn new(authentication_metadata_provider: Arc<P>) -> Self {
Self {
authentication_metadata_provider,
}
}
pub async fn authorize_subject(
&self,
context: &DefaultAuthorizationContext,
) -> AuthServiceResult<Option<AuthorizationDecision>> {
let subject = match context.subject() {
Some(subject) => subject,
None => return Ok(None),
};
if subject.subject_type() != SubjectType::User {
return Ok(None);
}
let username = User::username_from_subject_key(subject.subject_key());
let user = match self.authentication_metadata_provider.lookup_user(username).await {
Ok(user) => user,
Err(error) if error.kind() == AuthFailureKind::NotFound => {
return Ok(Some(AuthorizationDecision::Deny(AuthorizationDenial::SubjectUnknown)));
}
Err(source) => {
return Err(AuthServiceError::provider_failed(AuthOperation::Authorize, source));
}
};
if user.user_status() == Some(UserStatus::Disable) {
return Ok(Some(AuthorizationDecision::Deny(AuthorizationDenial::PermissionDenied)));
}
if user.user_type() == Some(UserType::Super) {
return Ok(Some(AuthorizationDecision::Allow));
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::authentication::provider::LocalAuthenticationMetadataProvider;
use crate::config::AuthConfig;
#[tokio::test]
async fn test_super_user_bypass() {
let mut metadata_provider = LocalAuthenticationMetadataProvider::new();
metadata_provider.initialize(AuthConfig::default(), None).await.unwrap();
let mut user = User::of_with_type("alice", "secret", UserType::Super);
user.set_user_status(UserStatus::Enable);
metadata_provider.create_user(user).await.unwrap();
let handler = UserAuthorizationHandler::new(Arc::new(metadata_provider));
let mut context = DefaultAuthorizationContext::default();
context.set_subject("alice", SubjectType::User);
let decision = handler.authorize_subject(&context).await.unwrap();
assert_eq!(decision, Some(AuthorizationDecision::Allow));
}
#[tokio::test]
async fn test_disabled_user_is_rejected() {
let mut metadata_provider = LocalAuthenticationMetadataProvider::new();
metadata_provider.initialize(AuthConfig::default(), None).await.unwrap();
let mut user = User::of_with_type("alice", "secret", UserType::Normal);
user.set_user_status(UserStatus::Disable);
metadata_provider.create_user(user).await.unwrap();
let handler = UserAuthorizationHandler::new(Arc::new(metadata_provider));
let mut context = DefaultAuthorizationContext::default();
context.set_subject("alice", SubjectType::User);
let result = handler.authorize_subject(&context).await;
assert_eq!(
result.unwrap(),
Some(AuthorizationDecision::Deny(AuthorizationDenial::PermissionDenied))
);
}
}