rocketmq_auth/authorization/
provider.rs1use std::sync::Arc;
21
22use rocketmq_protocol::protocol::remoting_command::RemotingCommand;
23use rocketmq_security_api::AuthorizationDecision;
24
25use crate::authentication::provider::LocalAuthenticationMetadataProvider;
26use crate::authorization::builder::default_authorization_context_builder::DefaultAuthorizationContextBuilder;
27use crate::authorization::builder::AuthorizationContextBuilder;
28use crate::authorization::chain::AclAuthorizationHandler;
29use crate::authorization::chain::AuthorizationHandler;
30use crate::authorization::chain::UserAuthorizationHandler;
31use crate::authorization::context::default_authorization_context::DefaultAuthorizationContext;
32use crate::authorization::metadata_provider::AuthorizationMetadataProvider;
33use crate::authorization::metadata_provider::LocalAuthorizationMetadataProvider;
34use crate::config::AuthConfig;
35use crate::runtime::ProviderRegistry;
36use crate::AuthFailureKind;
37use crate::AuthMetrics;
38use crate::AuthOperation;
39use crate::AuthServiceError;
40use crate::AuthServiceResult;
41use crate::RemotingAuthContext;
42
43#[allow(async_fn_in_trait)]
83pub trait AuthorizationProvider: Send + Sync {
84 fn initialize(&mut self, config: AuthConfig) -> AuthServiceResult<()>;
99
100 fn initialize_with_metadata(
114 &mut self,
115 config: AuthConfig,
116 #[allow(unused_variables)] metadata_service: Option<Box<dyn std::any::Any + Send + Sync>>,
117 ) -> AuthServiceResult<()> {
118 self.initialize(config)
120 }
121
122 async fn authorize(&self, context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision>;
153
154 #[allow(unused_variables)]
170 fn new_contexts_from_grpc_metadata(
171 &self,
172 metadata: &dyn std::any::Any,
173 message: &dyn std::any::Any,
174 ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
175 Ok(Vec::new())
177 }
178
179 #[allow(unused_variables)]
194 fn new_contexts_from_remoting_command(
195 &self,
196 auth_context: &RemotingAuthContext,
197 command: &RemotingCommand,
198 ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
199 Ok(Vec::new())
201 }
202}
203
204#[cfg(test)]
205struct NoopAuthorizationProvider;
206
207#[cfg(test)]
208impl NoopAuthorizationProvider {
209 fn new() -> Self {
210 Self
211 }
212}
213
214#[cfg(test)]
215impl Default for NoopAuthorizationProvider {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221#[cfg(test)]
222impl AuthorizationProvider for NoopAuthorizationProvider {
223 fn initialize(&mut self, _config: AuthConfig) -> AuthServiceResult<()> {
224 Ok(())
226 }
227
228 async fn authorize(&self, _context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision> {
229 Ok(AuthorizationDecision::Allow)
231 }
232
233 fn new_contexts_from_grpc_metadata(
234 &self,
235 _metadata: &dyn std::any::Any,
236 _message: &dyn std::any::Any,
237 ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
238 Ok(Vec::new())
240 }
241
242 fn new_contexts_from_remoting_command(
243 &self,
244 _auth_context: &RemotingAuthContext,
245 _command: &RemotingCommand,
246 ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
247 Ok(Vec::new())
249 }
250}
251
252pub struct DefaultAuthorizationProvider {
300 config: Option<AuthConfig>,
302
303 metadata_service: Option<Box<dyn std::any::Any + Send + Sync>>,
305
306 authentication_metadata_provider: Option<Arc<crate::UserMetadataHandle>>,
307 authorization_metadata_provider: Option<Arc<crate::AclMetadataHandle>>,
308 context_builder: Option<DefaultAuthorizationContextBuilder>,
309 metrics: AuthMetrics,
310}
311
312impl DefaultAuthorizationProvider {
313 pub fn new() -> Self {
315 Self {
316 config: None,
317 metadata_service: None,
318 authentication_metadata_provider: None,
319 authorization_metadata_provider: None,
320 context_builder: None,
321 metrics: AuthMetrics::default(),
322 }
323 }
324
325 pub fn authentication_metadata_provider(&self) -> Option<Arc<crate::UserMetadataHandle>> {
326 self.authentication_metadata_provider.clone()
327 }
328
329 pub fn authorization_metadata_provider(&self) -> Option<Arc<crate::AclMetadataHandle>> {
330 self.authorization_metadata_provider.clone()
331 }
332
333 pub fn initialize_with_registry(
334 &mut self,
335 config: AuthConfig,
336 provider_registry: ProviderRegistry,
337 ) -> AuthServiceResult<()> {
338 self.config = Some(config.clone());
339 self.metadata_service = None;
340 self.context_builder = Some(DefaultAuthorizationContextBuilder::new(config));
341 self.authentication_metadata_provider = Some(provider_registry.authentication_metadata_provider());
342 self.authorization_metadata_provider = Some(provider_registry.authorization_metadata_provider());
343 self.metrics = provider_registry.metrics();
344 Ok(())
345 }
346
347 fn audit_log(&self, context: &DefaultAuthorizationContext, result: &AuthServiceResult<AuthorizationDecision>) {
353 use tracing::debug;
354 use tracing::info;
355 use tracing::warn;
356
357 let action_count = context.actions().len();
358
359 match result {
360 Ok(AuthorizationDecision::Allow) => {
361 debug!(outcome = "allow", action_count, "authorization decision")
362 }
363 Ok(AuthorizationDecision::Deny(denial)) => info!(
364 outcome = "deny",
365 denial = ?denial,
366 action_count,
367 "authorization decision"
368 ),
369 Err(_) => warn!(outcome = "error", action_count, "authorization evaluation failed"),
370 }
371 }
372}
373
374impl Default for DefaultAuthorizationProvider {
375 fn default() -> Self {
376 Self::new()
377 }
378}
379
380#[allow(async_fn_in_trait)]
381impl AuthorizationProvider for DefaultAuthorizationProvider {
382 fn initialize(&mut self, config: AuthConfig) -> AuthServiceResult<()> {
383 self.initialize_with_metadata(config, None)
384 }
385
386 fn initialize_with_metadata(
387 &mut self,
388 config: AuthConfig,
389 metadata_service: Option<Box<dyn std::any::Any + Send + Sync>>,
390 ) -> AuthServiceResult<()> {
391 use tracing::debug;
392
393 if let Some(service) = metadata_service {
394 let registry = service.downcast_ref::<ProviderRegistry>().ok_or_else(|| {
395 AuthServiceError::new(AuthOperation::InitializeProvider, AuthFailureKind::Unsupported)
396 })?;
397 return self.initialize_with_registry(config, registry.clone());
398 }
399 debug!("Initializing DefaultAuthorizationProvider");
400 self.config = Some(config.clone());
401 self.metadata_service = None;
402 self.context_builder = Some(DefaultAuthorizationContextBuilder::new(config.clone()));
403
404 let authentication_metadata_provider =
405 LocalAuthenticationMetadataProvider::with_config(&config).map_err(|source| {
406 AuthServiceError::with_source(AuthOperation::InitializeProvider, AuthFailureKind::Unavailable, source)
407 })?;
408 self.authentication_metadata_provider = Some(crate::UserMetadataHandle::legacy(Arc::new(
409 authentication_metadata_provider,
410 )));
411
412 let mut authorization_metadata_provider = LocalAuthorizationMetadataProvider::new();
413 authorization_metadata_provider.initialize(config, None)?;
414 self.authorization_metadata_provider = Some(crate::AclMetadataHandle::legacy(Arc::new(
415 authorization_metadata_provider,
416 )));
417 self.metrics = AuthMetrics::default();
418
419 Ok(())
420 }
421
422 async fn authorize(&self, context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision> {
423 use tracing::debug;
424 use tracing::warn;
425
426 if context.subject_key().is_none() {
428 warn!("Authorization context missing subject");
429 self.metrics.record_authorization_result(false);
430 return Err(AuthServiceError::invalid_context(
431 "Missing subject in authorization context".to_string(),
432 ));
433 }
434
435 if context.resource().is_none() {
436 warn!("Authorization context missing resource");
437 self.metrics.record_authorization_result(false);
438 return Err(AuthServiceError::invalid_context(
439 "Missing resource in authorization context".to_string(),
440 ));
441 }
442
443 if context.actions().is_empty() {
444 warn!("Authorization context has no actions");
445 self.metrics.record_authorization_result(false);
446 return Err(AuthServiceError::invalid_context(
447 "No actions specified in authorization context".to_string(),
448 ));
449 }
450
451 debug!(
452 action_count = context.actions().len(),
453 "authorization evaluation started"
454 );
455
456 let authentication_metadata_provider = self
457 .authentication_metadata_provider
458 .as_ref()
459 .ok_or_else(|| AuthServiceError::not_initialized("Authentication metadata provider is not configured"))?;
460 let authorization_metadata_provider = self
461 .authorization_metadata_provider
462 .as_ref()
463 .ok_or_else(|| AuthServiceError::not_initialized("Authorization metadata provider is not configured"))?;
464
465 let result: AuthServiceResult<AuthorizationDecision> = async {
466 let user_handler = UserAuthorizationHandler::new(authentication_metadata_provider.clone());
467 match user_handler.authorize_subject(context).await? {
468 Some(decision) => Ok(decision),
469 None => {
470 let acl_handler = AclAuthorizationHandler::new(authorization_metadata_provider.clone());
471 acl_handler.handle(context).await
472 }
473 }
474 }
475 .await;
476
477 self.audit_log(context, &result);
478 self.metrics
479 .record_authorization_result(matches!(&result, Ok(AuthorizationDecision::Allow)));
480 result
481 }
482
483 fn new_contexts_from_remoting_command(
484 &self,
485 auth_context: &RemotingAuthContext,
486 command: &RemotingCommand,
487 ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
488 let builder = self
489 .context_builder
490 .as_ref()
491 .ok_or_else(|| AuthServiceError::not_initialized("Authorization context builder is not configured"))?;
492 builder.build_from_remoting(auth_context, command)
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use cheetah_string::CheetahString;
499 use tempfile::TempDir;
500
501 use super::*;
502 use crate::authentication::enums::subject_type::SubjectType;
503 use crate::authentication::provider::authentication_metadata_provider::AuthenticationMetadataProvider;
504
505 #[tokio::test]
506 async fn test_noop_provider_always_allows() {
507 let mut provider = NoopAuthorizationProvider::new();
508 let config = AuthConfig::default();
509
510 assert!(provider.initialize(config).is_ok());
512
513 assert_eq!(
515 provider
516 .authorize(&DefaultAuthorizationContext::default())
517 .await
518 .unwrap(),
519 AuthorizationDecision::Allow
520 );
521 }
522
523 #[tokio::test]
524 async fn test_default_provider_initialization() {
525 let mut provider = DefaultAuthorizationProvider::new();
526 let config = AuthConfig::default();
527
528 let result = provider.initialize(config);
529 assert!(result.is_ok());
530 }
531
532 #[tokio::test]
533 async fn test_default_provider_authorize_missing_subject() {
534 let mut provider = DefaultAuthorizationProvider::new();
535 provider.initialize(AuthConfig::default()).unwrap();
536
537 let context = DefaultAuthorizationContext::default();
538 let result = provider.authorize(&context).await;
539
540 let error = result.unwrap_err();
541 assert_eq!(error.operation(), AuthOperation::BuildContext);
542 assert_eq!(error.kind(), AuthFailureKind::InvalidInput);
543 }
544
545 #[tokio::test]
546 async fn test_default_provider_authorize_missing_resource() {
547 use crate::authentication::enums::subject_type::SubjectType;
548
549 let mut provider = DefaultAuthorizationProvider::new();
550 provider.initialize(AuthConfig::default()).unwrap();
551
552 let mut context = DefaultAuthorizationContext::default();
553 context.set_subject("user:test", SubjectType::User);
554
555 let result = provider.authorize(&context).await;
556
557 let error = result.unwrap_err();
558 assert_eq!(error.operation(), AuthOperation::BuildContext);
559 assert_eq!(error.kind(), AuthFailureKind::InvalidInput);
560 }
561
562 #[tokio::test]
563 async fn test_default_provider_authorize_missing_actions() {
564 use crate::authentication::enums::subject_type::SubjectType;
565 use crate::authorization::model::resource::Resource;
566
567 let mut provider = DefaultAuthorizationProvider::new();
568 provider.initialize(AuthConfig::default()).unwrap();
569
570 let mut context = DefaultAuthorizationContext::default();
571 context.set_subject("user:test", SubjectType::User);
572 context.set_resource(Resource::of_topic("test-topic"));
573
574 let result = provider.authorize(&context).await;
575
576 let error = result.unwrap_err();
577 assert_eq!(error.operation(), AuthOperation::BuildContext);
578 assert_eq!(error.kind(), AuthFailureKind::InvalidInput);
579 }
580
581 #[test]
582 fn test_default_provider_default_construction() {
583 let provider1 = DefaultAuthorizationProvider::new();
584 let provider2 = DefaultAuthorizationProvider::default();
585
586 assert!(provider1.config.is_none());
588 assert!(provider2.config.is_none());
589 }
590
591 #[tokio::test]
592 async fn test_default_provider_super_user_bypass() {
593 use crate::authentication::enums::user_status::UserStatus;
594 use crate::authentication::enums::user_type::UserType;
595 use crate::authorization::model::resource::Resource;
596
597 let mut provider = DefaultAuthorizationProvider::new();
598 provider.initialize(AuthConfig::default()).unwrap();
599
600 let auth_provider = provider.authentication_metadata_provider().unwrap();
601 let mut user = crate::authentication::model::user::User::of_with_type("alice", "secret", UserType::Super);
602 user.set_user_status(UserStatus::Enable);
603 auth_provider.create_user(user).await.unwrap();
604
605 let mut context = DefaultAuthorizationContext::default();
606 context.set_subject("alice", SubjectType::User);
607 context.set_resource(Resource::of_topic("test-topic"));
608 context.set_actions(vec![rocketmq_security_api::Action::Pub]);
609 context.set_source_ip("127.0.0.1");
610
611 assert_eq!(
612 provider.authorize(&context).await.unwrap(),
613 AuthorizationDecision::Allow
614 );
615 }
616
617 #[tokio::test]
618 async fn default_provider_loads_persisted_authentication_users() {
619 use crate::authentication::enums::user_status::UserStatus;
620 use crate::authentication::enums::user_type::UserType;
621 use crate::authorization::model::resource::Resource;
622
623 let temp = TempDir::new().unwrap();
624 let config = AuthConfig {
625 auth_config_path: CheetahString::from_string(temp.path().join("auth.yml").to_string_lossy().into_owned()),
626 ..AuthConfig::default()
627 };
628
629 let mut seed_provider = LocalAuthenticationMetadataProvider::new();
630 seed_provider.initialize(config.clone(), None).await.unwrap();
631 let mut user = crate::authentication::model::user::User::of_with_type("persisted", "secret", UserType::Super);
632 user.set_user_status(UserStatus::Enable);
633 seed_provider.create_user(user).await.unwrap();
634
635 let mut provider = DefaultAuthorizationProvider::new();
636 provider.initialize(config).unwrap();
637
638 let mut context = DefaultAuthorizationContext::default();
639 context.set_subject("persisted", SubjectType::User);
640 context.set_resource(Resource::of_topic("test-topic"));
641 context.set_actions(vec![rocketmq_security_api::Action::Pub]);
642 context.set_source_ip("127.0.0.1");
643
644 assert_eq!(
645 provider.authorize(&context).await.unwrap(),
646 AuthorizationDecision::Allow
647 );
648 }
649
650 #[tokio::test]
651 async fn test_default_provider_acl_authorization() {
652 use crate::authentication::enums::user_status::UserStatus;
653 use crate::authentication::enums::user_type::UserType;
654 use crate::authorization::enums::decision::Decision;
655 use crate::authorization::model::acl::Acl;
656 use crate::authorization::model::policy::Policy;
657 use crate::authorization::model::resource::Resource;
658
659 let mut provider = DefaultAuthorizationProvider::new();
660 provider.initialize(AuthConfig::default()).unwrap();
661
662 let auth_provider = provider.authentication_metadata_provider().unwrap();
663 let mut user = crate::authentication::model::user::User::of_with_type("alice", "secret", UserType::Normal);
664 user.set_user_status(UserStatus::Enable);
665 auth_provider.create_user(user).await.unwrap();
666
667 let acl_provider = provider.authorization_metadata_provider().unwrap();
668 let resource = Resource::of_topic("test-topic");
669 let acl = Acl::of(
670 "alice",
671 SubjectType::User,
672 Policy::of(
673 vec![resource.clone()],
674 vec![rocketmq_security_api::Action::Pub],
675 None,
676 Decision::Allow,
677 ),
678 );
679 acl_provider.create_acl(acl).await.unwrap();
680
681 let mut context = DefaultAuthorizationContext::default();
682 context.set_subject("alice", SubjectType::User);
683 context.set_resource(resource);
684 context.set_actions(vec![rocketmq_security_api::Action::Pub]);
685 context.set_source_ip("127.0.0.1");
686
687 assert_eq!(
688 provider.authorize(&context).await.unwrap(),
689 AuthorizationDecision::Allow
690 );
691 }
692
693 #[tokio::test]
694 async fn test_default_provider_unknown_user_is_subject_denial() {
695 use crate::authorization::model::resource::Resource;
696 use rocketmq_security_api::AuthorizationDenial;
697
698 let mut provider = DefaultAuthorizationProvider::new();
699 provider.initialize(AuthConfig::default()).unwrap();
700
701 let mut context = DefaultAuthorizationContext::default();
702 context.set_subject("missing-user", SubjectType::User);
703 context.set_resource(Resource::of_topic("test-topic"));
704 context.set_actions(vec![rocketmq_security_api::Action::Pub]);
705 context.set_source_ip("127.0.0.1");
706
707 assert_eq!(
708 provider.authorize(&context).await.unwrap(),
709 AuthorizationDecision::Deny(AuthorizationDenial::SubjectUnknown)
710 );
711 }
712}