use std::any::Any;
use tracing::debug;
use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
use crate::authorization::strategy::abstract_authorization_strategy::AbstractAuthorizationStrategy;
use crate::authorization::strategy::abstract_authorization_strategy::AuthorizationStrategy;
use crate::authorization::strategy::abstract_authorization_strategy::StrategyResult;
use crate::authorization::strategy::block_on_base_authorization;
use crate::config::AuthConfig;
pub struct StatelessAuthorizationStrategy {
base: AbstractAuthorizationStrategy,
}
impl StatelessAuthorizationStrategy {
pub fn new(auth_config: AuthConfig, metadata_service: Option<Box<dyn Any + Send + Sync>>) -> StrategyResult<Self> {
let base = AbstractAuthorizationStrategy::new(auth_config, metadata_service)?;
debug!("StatelessAuthorizationStrategy initialized");
Ok(Self { base })
}
pub fn base(&self) -> &AbstractAuthorizationStrategy {
&self.base
}
}
impl AuthorizationStrategy for StatelessAuthorizationStrategy {
fn evaluate(&self, context: &DefaultAuthorizationContext) -> StrategyResult<()> {
debug!(
"Stateless authorization evaluation for subject: {:?}",
context.subject().map(|s| s.subject_key())
);
block_on_base_authorization(&self.base, context)
}
}
#[cfg(test)]
mod tests {
use cheetah_string::CheetahString;
use super::*;
fn create_test_config() -> AuthConfig {
AuthConfig {
config_name: CheetahString::from("test"),
cluster_name: CheetahString::from("test-cluster"),
auth_config_path: CheetahString::from("/tmp/auth"),
acl_file: CheetahString::new(),
acl_file_watch_enabled: false,
acl_file_watch_interval_millis: 5_000,
authentication_enabled: false,
authentication_provider: CheetahString::new(),
authentication_metadata_provider: CheetahString::new(),
authentication_strategy: CheetahString::new(),
authentication_whitelist: CheetahString::new(),
init_authentication_user: CheetahString::new(),
inner_client_authentication_credentials: CheetahString::new(),
signature_algorithm: Default::default(),
request_timestamp_expired_millis: 0,
authorization_enabled: true,
authorization_provider: CheetahString::new(),
authorization_metadata_provider: CheetahString::new(),
authorization_strategy: CheetahString::new(),
authorization_whitelist: CheetahString::from("10,11"),
migrate_auth_from_v1_enabled: false,
user_cache_max_num: 1000,
user_cache_expired_second: 300,
user_cache_refresh_second: 60,
acl_cache_max_num: 1000,
acl_cache_expired_second: 300,
acl_cache_refresh_second: 60,
stateful_authentication_cache_max_num: 1000,
stateful_authentication_cache_expired_second: 300,
stateful_authorization_cache_max_num: 1000,
stateful_authorization_cache_expired_second: 300,
}
}
#[test]
fn test_stateless_strategy_creation() {
let config = create_test_config();
let strategy = StatelessAuthorizationStrategy::new(config, None);
assert!(strategy.is_ok());
}
#[test]
fn test_stateless_strategy_whitelist() {
let config = create_test_config();
let strategy = StatelessAuthorizationStrategy::new(config, None).unwrap();
assert!(strategy.base().is_whitelisted("10"));
assert!(strategy.base().is_whitelisted("11"));
assert!(!strategy.base().is_whitelisted("12"));
}
#[test]
fn test_stateless_evaluate_disabled() {
let rt = tokio::runtime::Runtime::new().unwrap();
let _guard = rt.enter();
let mut config = create_test_config();
config.authorization_enabled = false;
let strategy = StatelessAuthorizationStrategy::new(config, None).unwrap();
let context = DefaultAuthorizationContext::default();
let result = strategy.evaluate(&context);
assert!(result.is_ok());
}
#[test]
fn evaluate_inside_current_thread_runtime_without_nested_block_on_panic() {
let config = create_test_config();
let strategy = StatelessAuthorizationStrategy::new(config, None).unwrap();
let context = DefaultAuthorizationContext::default();
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
runtime.block_on(async {
assert!(strategy.evaluate(&context).is_ok());
});
}
}