Skip to main content

rocketmq_auth/authorization/
provider.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Authorization provider abstraction for RocketMQ authentication and authorization.
16//!
17//! This module defines the core `AuthorizationProvider` trait, which serves as the
18//! unified interface for all authorization implementations (ACL, RBAC, OPA, etc.).
19
20use 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/// Authorization provider trait.
44///
45/// This trait defines the core abstraction for authorization in RocketMQ. Implementors
46/// can provide different authorization strategies such as:
47/// - ACL (Access Control Lists)
48/// - RBAC (Role-Based Access Control)
49/// - ABAC (Attribute-Based Access Control)
50/// - Integration with external policy engines (e.g., Open Policy Agent)
51///
52/// # Design Considerations
53/// - **Async-first**: All methods are async to support asynchronous implementations
54/// - **Context-based**: Authorization decisions are made based on `DefaultAuthorizationContext`
55/// - **Extensible**: Implementations can maintain internal state via `initialize`
56/// - **Error handling**: All failures are expressed through `AuthServiceError`
57///
58/// # Examples
59///
60/// ```rust,ignore
61/// use rocketmq_auth::{AuthorizationProvider, AuthServiceResult};
62/// use rocketmq_auth::DefaultAuthorizationContext;
63/// use rocketmq_auth::AuthConfig;
64///
65/// struct MyAuthProvider;
66///
67/// impl AuthorizationProvider for MyAuthProvider {
68///     fn initialize(&mut self, config: AuthConfig) -> AuthServiceResult<()> {
69///         // Initialize provider with configuration
70///         Ok(())
71///     }
72///
73///     async fn authorize(
74///         &self,
75///         context: &DefaultAuthorizationContext,
76///     ) -> AuthServiceResult<AuthorizationDecision> {
77///         // Implement authorization logic
78///         Ok(AuthorizationDecision::Allow)
79///     }
80/// }
81/// ```
82#[allow(async_fn_in_trait)]
83pub trait AuthorizationProvider: Send + Sync {
84    /// Initialize the authorization provider with configuration.
85    ///
86    /// This method is called once during provider setup. Implementations should:
87    /// - Load configuration
88    /// - Initialize metadata services (database, cache, etc.)
89    /// - Establish connections to external services if needed
90    /// - Validate configuration parameters
91    ///
92    /// # Arguments
93    /// * `config` - Authorization configuration including provider-specific settings
94    ///
95    /// # Errors
96    /// Returns `AuthFailureKind::InvalidConfiguration` if configuration is invalid
97    /// or initialization fails.
98    fn initialize(&mut self, config: AuthConfig) -> AuthServiceResult<()>;
99
100    /// Initialize with both configuration and optional metadata service.
101    ///
102    /// This is an extended version of `initialize` that accepts a metadata service
103    /// supplier for advanced scenarios (e.g., shared metadata across components).
104    ///
105    /// Default implementation delegates to `initialize(config)`.
106    ///
107    /// # Arguments
108    /// * `config` - Authorization configuration
109    /// * `metadata_service` - Optional metadata service supplier
110    ///
111    /// # Errors
112    /// Returns `AuthFailureKind::InvalidConfiguration` if initialization fails.
113    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        // Default implementation ignores metadata_service
119        self.initialize(config)
120    }
121
122    /// Authorize an operation based on the given context.
123    ///
124    /// This is the core authorization method. Implementations should:
125    /// - Extract subject, resource, and action from context
126    /// - Evaluate authorization policies
127    /// - Check permissions and constraints (IP whitelist, time-based rules, etc.)
128    /// - Audit log the authorization decision
129    ///
130    /// # Arguments
131    /// * `context` - Authorization context containing subject, resource, actions, and metadata
132    ///
133    /// # Returns
134    /// - `Ok(AuthorizationDecision::Allow)` if authorization succeeds
135    /// - `Ok(AuthorizationDecision::Deny(_))` if policy denies the request
136    /// - `Err(AuthServiceError)` if no decision can be made
137    ///
138    /// # Examples
139    ///
140    /// ```rust,ignore
141    /// let context = DefaultAuthorizationContext::new(
142    ///     subject,
143    ///     resource,
144    ///     vec![Action::Pub],
145    ///     source_ip
146    /// );
147    /// match provider.authorize(&context).await? {
148    ///     AuthorizationDecision::Allow => proceed(),
149    ///     AuthorizationDecision::Deny(reason) => reject(reason),
150    /// }
151    /// ```
152    async fn authorize(&self, context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision>;
153
154    /// Create authorization contexts from gRPC metadata and request message.
155    ///
156    /// Parses gRPC request metadata (headers) and the protocol buffer message to
157    /// construct authorization contexts. Multiple contexts may be returned if the
158    /// request involves multiple resources (e.g., batch operations).
159    ///
160    /// # Arguments
161    /// * `metadata` - gRPC metadata containing authentication tokens, source IP, etc.
162    /// * `message` - Protocol buffer message (e.g., SendMessageRequest)
163    ///
164    /// # Returns
165    /// List of authorization contexts to be evaluated. Empty list if no authorization needed.
166    ///
167    /// # Errors
168    /// Returns `AuthFailureKind::InvalidInput` if context cannot be constructed.
169    #[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        // Default implementation returns empty list (no-op)
176        Ok(Vec::new())
177    }
178
179    /// Create authorization contexts from trusted remoting ingress facts and command.
180    ///
181    /// Parses typed ingress facts and RocketMQ remoting command
182    /// to construct authorization contexts for TCP-based protocols.
183    ///
184    /// # Arguments
185    /// * `auth_context` - Trusted, read-only source and session facts
186    /// * `command` - RocketMQ remoting command containing request code and data
187    ///
188    /// # Returns
189    /// List of authorization contexts to be evaluated.
190    ///
191    /// # Errors
192    /// Returns `AuthFailureKind::InvalidInput` if context cannot be constructed.
193    #[allow(unused_variables)]
194    fn new_contexts_from_remoting_command(
195        &self,
196        auth_context: &RemotingAuthContext,
197        command: &RemotingCommand,
198    ) -> AuthServiceResult<Vec<DefaultAuthorizationContext>> {
199        // Default implementation returns empty list (no-op)
200        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        // No initialization needed
225        Ok(())
226    }
227
228    async fn authorize(&self, _context: &DefaultAuthorizationContext) -> AuthServiceResult<AuthorizationDecision> {
229        // Always allow
230        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        // Return empty contexts (no authorization needed)
239        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        // Return empty contexts (no authorization needed)
248        Ok(Vec::new())
249    }
250}
251
252/// Default authorization provider implementation.
253///
254/// This provider implements a chain-of-responsibility pattern for authorization,
255/// delegating to specialized handlers:
256/// 1. **UserAuthorizationHandler**: Handles super-user bypass logic
257/// 2. **AclAuthorizationHandler**: Performs ACL-based permission checks
258///
259/// # Architecture
260///
261/// The provider follows RocketMQ's authorization model:
262/// - Subject-based access control (users, roles, service accounts)
263/// - Resource-level permissions (topics, groups, clusters)
264/// - Action-based authorization (PUB, SUB, CREATE, UPDATE, DELETE, GET, LIST)
265/// - Policy evaluation with ALLOW/DENY decisions
266/// - IP whitelist support
267/// - Default-deny security policy
268///
269/// # Authorization Flow
270///
271/// 1. Extract subject, resource, and actions from context
272/// 2. Check if user is a super-user (bypass authorization)
273/// 3. Query ACL metadata for the subject
274/// 4. Evaluate policies against the requested resource and actions
275/// 5. Check environment constraints (IP whitelist, time-based rules)
276/// 6. Apply decision (ALLOW/DENY) with default-deny policy
277/// 7. Audit log the authorization decision
278///
279/// # Thread Safety
280///
281/// This implementation is thread-safe and can be shared across multiple async tasks.
282/// Internal state is protected using `Arc` and atomic operations.
283///
284/// # Examples
285///
286/// ```rust,ignore
287/// use rocketmq_auth::DefaultAuthorizationProvider;
288/// use std::sync::Arc;
289///
290/// let mut provider = DefaultAuthorizationProvider::new();
291/// provider.initialize(config)?;
292///
293/// // Authorize a request
294/// match provider.authorize(&context).await? {
295///     AuthorizationDecision::Allow => proceed(),
296///     AuthorizationDecision::Deny(reason) => reject(reason),
297/// }
298/// ```
299pub struct DefaultAuthorizationProvider {
300    /// Authorization configuration
301    config: Option<AuthConfig>,
302
303    /// Metadata service supplier (reserved for future external providers)
304    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    /// Create a new default authorization provider.
314    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    /// Audit an authorization outcome without recording request identities or ACL data.
348    ///
349    /// Only closed decision categories and the action count are emitted. Subject,
350    /// resource, source address, credentials, and operational error details remain
351    /// available to typed diagnostics rather than tracing fields.
352    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        // Validate context
427        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        // Initialize should succeed
511        assert!(provider.initialize(config).is_ok());
512
513        // Authorize should always succeed (even with empty context)
514        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        // Both should be properly initialized
587        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}