use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::OnceLock;
use rocketmq_error::RocketMQError;
use rocketmq_error::RocketMQResult;
use rocketmq_remoting::protocol::remoting_command::RemotingCommand;
use crate::authentication::context::default_authentication_context::DefaultAuthenticationContext;
use crate::authentication::manager::AuthenticationMetadataManagerImpl;
use crate::authentication::provider::AuthenticationMetadataProvider;
use crate::authentication::provider::AuthenticationProvider;
use crate::authentication::provider::DefaultAuthenticationProvider;
use crate::authorization::metadata_provider::AuthorizationMetadataProvider;
use crate::config::AuthConfig;
static INSTANCE_CACHE: OnceLock<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>> = OnceLock::new();
const PROVIDER_PREFIX: &str = "PROVIDER_";
#[allow(dead_code)]
const METADATA_PROVIDER_PREFIX: &str = "METADATA_PROVIDER_";
pub struct AuthenticationFactory;
impl AuthenticationFactory {
pub fn get_provider(config: &AuthConfig) -> RocketMQResult<Arc<DefaultAuthenticationProvider>> {
let key = format!("{}{}", PROVIDER_PREFIX, config.config_name);
Self::compute_if_absent(&key, || {
let provider = DefaultAuthenticationProvider::new();
Ok(Arc::new(provider) as Arc<dyn Any + Send + Sync>)
})
.and_then(|any_arc| {
any_arc
.downcast::<DefaultAuthenticationProvider>()
.map_err(|_| RocketMQError::illegal_argument("Failed to downcast provider"))
})
}
pub fn get_metadata_provider(
_config: &AuthConfig,
) -> RocketMQResult<Option<Arc<dyn AuthenticationMetadataProvider>>> {
Ok(None)
}
pub fn get_metadata_provider_with_service(
_config: &AuthConfig,
_metadata_service: Option<Arc<dyn Any + Send + Sync>>,
) -> RocketMQResult<Option<Arc<dyn AuthenticationMetadataProvider>>> {
Ok(None)
}
pub fn get_metadata_manager<A, B>(
_config: &AuthConfig,
auth_provider: Option<A>,
authz_provider: Option<B>,
) -> AuthenticationMetadataManagerImpl<A, B>
where
A: AuthenticationMetadataProvider + 'static,
B: AuthorizationMetadataProvider + 'static,
{
AuthenticationMetadataManagerImpl::new(auth_provider, authz_provider)
}
pub fn new_context_from_metadata(
config: &AuthConfig,
metadata: &HashMap<String, String>,
request: Box<dyn Any + Send>,
) -> RocketMQResult<Option<DefaultAuthenticationContext>> {
let provider = Self::get_provider(config)?;
Ok(Some(
<DefaultAuthenticationProvider as AuthenticationProvider>::new_context_from_metadata(
&provider, metadata, request,
),
))
}
pub fn new_context_from_command(
config: &AuthConfig,
command: &RemotingCommand,
) -> RocketMQResult<Option<DefaultAuthenticationContext>> {
let provider = Self::get_provider(config)?;
Ok(Some(
<DefaultAuthenticationProvider as AuthenticationProvider>::new_context_from_command(&provider, command),
))
}
fn compute_if_absent<F>(key: &str, factory_fn: F) -> RocketMQResult<Arc<dyn Any + Send + Sync>>
where
F: FnOnce() -> RocketMQResult<Arc<dyn Any + Send + Sync>>,
{
let cache = INSTANCE_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
{
let cache_guard = cache
.lock()
.map_err(|e| RocketMQError::illegal_argument(format!("Cache lock error: {}", e)))?;
if let Some(cached) = cache_guard.get(key) {
return Ok(Arc::clone(cached));
}
}
let mut cache_guard = cache
.lock()
.map_err(|e| RocketMQError::illegal_argument(format!("Cache lock error: {}", e)))?;
if let Some(cached) = cache_guard.get(key) {
return Ok(Arc::clone(cached));
}
let instance = factory_fn()?;
cache_guard.insert(key.to_string(), Arc::clone(&instance));
Ok(instance)
}
}
#[cfg(test)]
mod tests {
use cheetah_string::CheetahString;
use super::*;
fn create_test_config(config_name: &str) -> AuthConfig {
AuthConfig {
config_name: CheetahString::from_string(config_name.to_string()),
..AuthConfig::default()
}
}
#[test]
fn test_get_provider_default() {
let config = create_test_config("test_provider_default");
let provider = AuthenticationFactory::get_provider(&config);
assert!(provider.is_ok());
}
#[test]
fn test_get_metadata_provider_not_configured() {
let config = create_test_config("test_metadata_provider_none");
let provider = AuthenticationFactory::get_metadata_provider(&config);
assert!(provider.is_ok());
assert!(provider.unwrap().is_none());
}
#[test]
fn test_get_metadata_provider_local() {
let mut config = create_test_config("test_metadata_provider_local");
config.authentication_metadata_provider = CheetahString::from_static_str("LocalAuthenticationMetadataProvider");
let provider = AuthenticationFactory::get_metadata_provider(&config);
assert!(provider.is_ok());
assert!(provider.unwrap().is_none());
}
#[test]
fn test_provider_caching() {
let config = create_test_config("test_cache");
let provider1 = AuthenticationFactory::get_provider(&config).unwrap();
let provider2 = AuthenticationFactory::get_provider(&config).unwrap();
assert!(Arc::ptr_eq(&provider1, &provider2));
}
}