use async_trait::async_trait;
use crate::error::LimiteronError;
#[async_trait]
pub trait AuthorizationProvider: Send + Sync {
async fn check_authorization(
&self,
operation: &str,
operator: &str,
target: &str,
) -> Result<(), LimiteronError>;
}
pub struct SimpleAuthorizationProvider {
authorized_roles: Vec<String>,
}
impl SimpleAuthorizationProvider {
pub fn new(authorized_roles: Vec<String>) -> Self {
Self { authorized_roles }
}
pub fn from_roles<I, S>(roles: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
Self {
authorized_roles: roles.into_iter().map(|s| s.into()).collect(),
}
}
pub fn is_authorized(&self, role: &str) -> bool {
self.authorized_roles.iter().any(|r| r == role)
}
pub fn authorized_roles(&self) -> &[String] {
&self.authorized_roles
}
pub fn add_role(&mut self, role: String) {
if !self.authorized_roles.contains(&role) {
self.authorized_roles.push(role);
}
}
pub fn remove_role(&mut self, role: &str) -> bool {
if let Some(pos) = self.authorized_roles.iter().position(|r| r == role) {
self.authorized_roles.remove(pos);
true
} else {
false
}
}
}
#[async_trait]
impl AuthorizationProvider for SimpleAuthorizationProvider {
async fn check_authorization(
&self,
_operation: &str,
operator: &str,
_target: &str,
) -> Result<(), LimiteronError> {
if self.is_authorized(operator) {
Ok(())
} else {
Err(LimiteronError::AuthorizationError(format!(
"操作者 '{}' 未被授权执行此操作",
operator
)))
}
}
}
#[cfg(test)]
pub struct AllowAllAuthorizationProvider;
#[cfg(test)]
#[async_trait]
impl AuthorizationProvider for AllowAllAuthorizationProvider {
async fn check_authorization(
&self,
_operation: &str,
_operator: &str,
_target: &str,
) -> Result<(), LimiteronError> {
Ok(())
}
}
#[cfg(test)]
pub struct DenyAllAuthorizationProvider;
#[cfg(test)]
#[async_trait]
impl AuthorizationProvider for DenyAllAuthorizationProvider {
async fn check_authorization(
&self,
_operation: &str,
_operator: &str,
_target: &str,
) -> Result<(), LimiteronError> {
Err(LimiteronError::AuthorizationError(
"所有操作都被拒绝".to_string(),
))
}
}
#[cfg(feature = "ban-manager")]
pub struct OperationAuthorizationProvider {
operation_roles: ahash::AHashMap<String, Vec<String>>,
}
#[cfg(feature = "ban-manager")]
impl OperationAuthorizationProvider {
pub fn new(operation_roles: ahash::AHashMap<String, Vec<String>>) -> Self {
Self { operation_roles }
}
pub fn builder() -> OperationAuthorizationProviderBuilder {
OperationAuthorizationProviderBuilder::default()
}
pub fn has_operation(&self, operation: &str) -> bool {
self.operation_roles.contains_key(operation)
}
pub fn get_roles(&self, operation: &str) -> Option<&[String]> {
self.operation_roles.get(operation).map(|v| v.as_slice())
}
}
#[cfg(feature = "ban-manager")]
#[async_trait]
impl AuthorizationProvider for OperationAuthorizationProvider {
async fn check_authorization(
&self,
operation: &str,
operator: &str,
_target: &str,
) -> Result<(), LimiteronError> {
match self.operation_roles.get(operation) {
Some(roles) => {
if roles.iter().any(|r| r == operator) {
Ok(())
} else {
Err(LimiteronError::AuthorizationError(format!(
"操作者 '{}' 未被授权执行操作 '{}'",
operator, operation
)))
}
}
None => Err(LimiteronError::AuthorizationError(format!(
"未知的操作类型: '{}'",
operation
))),
}
}
}
#[cfg(feature = "ban-manager")]
#[derive(Default)]
pub struct OperationAuthorizationProviderBuilder {
operation_roles: ahash::AHashMap<String, Vec<String>>,
}
#[cfg(feature = "ban-manager")]
impl OperationAuthorizationProviderBuilder {
pub fn with_operation(mut self, operation: impl Into<String>, roles: Vec<String>) -> Self {
self.operation_roles.insert(operation.into(), roles);
self
}
pub fn build(self) -> OperationAuthorizationProvider {
OperationAuthorizationProvider::new(self.operation_roles)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_simple_authorization_provider_authorized() {
let provider =
SimpleAuthorizationProvider::new(vec!["admin".to_string(), "moderator".to_string()]);
let result = provider
.check_authorization("create_ban", "admin", "user123")
.await;
assert!(result.is_ok());
let result = provider
.check_authorization("create_ban", "moderator", "user456")
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_simple_authorization_provider_unauthorized() {
let provider = SimpleAuthorizationProvider::new(vec!["admin".to_string()]);
let result = provider
.check_authorization("create_ban", "user", "target123")
.await;
assert!(result.is_err());
match result {
Err(LimiteronError::AuthorizationError(msg)) => {
assert!(msg.contains("user"));
}
_ => panic!("期望 AuthorizationError"),
}
}
#[tokio::test]
async fn test_simple_authorization_provider_empty_roles() {
let provider = SimpleAuthorizationProvider::new(vec![]);
let result = provider
.check_authorization("create_ban", "admin", "target123")
.await;
assert!(result.is_err());
}
#[test]
fn test_simple_authorization_provider_from_roles() {
let provider = SimpleAuthorizationProvider::from_roles(["admin", "moderator"]);
assert_eq!(provider.authorized_roles().len(), 2);
assert!(provider.is_authorized("admin"));
assert!(provider.is_authorized("moderator"));
}
#[test]
fn test_simple_authorization_provider_add_remove_role() {
let mut provider = SimpleAuthorizationProvider::new(vec!["admin".to_string()]);
provider.add_role("moderator".to_string());
assert!(provider.is_authorized("moderator"));
provider.add_role("moderator".to_string());
assert_eq!(provider.authorized_roles().len(), 2);
assert!(provider.remove_role("moderator"));
assert!(!provider.is_authorized("moderator"));
assert!(!provider.remove_role("nonexistent"));
}
#[tokio::test]
async fn test_allow_all_authorization_provider() {
let provider = AllowAllAuthorizationProvider;
assert!(
provider
.check_authorization("create_ban", "anyone", "anything")
.await
.is_ok()
);
assert!(
provider
.check_authorization("remove_ban", "anyone", "anything")
.await
.is_ok()
);
}
#[tokio::test]
async fn test_deny_all_authorization_provider() {
let provider = DenyAllAuthorizationProvider;
assert!(
provider
.check_authorization("create_ban", "admin", "anything")
.await
.is_err()
);
assert!(
provider
.check_authorization("remove_ban", "admin", "anything")
.await
.is_err()
);
}
#[cfg(feature = "ban-manager")]
#[tokio::test]
async fn test_operation_authorization_provider() {
let mut operation_roles = ahash::AHashMap::new();
operation_roles.insert(
"create_ban".to_string(),
vec!["admin".to_string(), "moderator".to_string()],
);
operation_roles.insert("remove_ban".to_string(), vec!["admin".to_string()]);
let provider = OperationAuthorizationProvider::new(operation_roles);
assert!(
provider
.check_authorization("create_ban", "moderator", "user123")
.await
.is_ok()
);
assert!(
provider
.check_authorization("remove_ban", "moderator", "user123")
.await
.is_err()
);
assert!(
provider
.check_authorization("remove_ban", "admin", "user123")
.await
.is_ok()
);
assert!(
provider
.check_authorization("unknown_op", "admin", "user123")
.await
.is_err()
);
}
#[cfg(feature = "ban-manager")]
#[test]
fn test_operation_authorization_provider_builder() {
let provider = OperationAuthorizationProvider::builder()
.with_operation(
"create_ban",
vec!["admin".to_string(), "moderator".to_string()],
)
.with_operation("remove_ban", vec!["admin".to_string()])
.build();
assert!(provider.has_operation("create_ban"));
assert!(provider.has_operation("remove_ban"));
assert!(!provider.has_operation("unknown"));
assert_eq!(provider.get_roles("create_ban").unwrap().len(), 2);
assert_eq!(provider.get_roles("remove_ban").unwrap().len(), 1);
}
#[test]
fn test_authorization_error_message() {
let error = LimiteronError::AuthorizationError("测试授权错误".to_string());
assert_eq!(error.to_string(), "授权错误: 测试授权错误");
}
}