Skip to main content

rocketmq_auth/authorization/manager/
metadata_manager.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 metadata manager implementation.
16//!
17//! This module provides the `AuthorizationMetadataManager`, which coordinates
18//! authorization and authentication metadata providers to manage ACL (Access Control List)
19//! operations in RocketMQ's authorization system.
20//!
21//! # Architecture
22//!
23//! The manager acts as a facade that:
24//! - Validates subjects exist before ACL operations (via `AuthenticationMetadataProvider`)
25//! - Performs ACL validation and initialization
26//! - Delegates storage operations to `AuthorizationMetadataProvider`
27//! - Provides a unified, type-safe API for ACL management
28//!
29//! # Design Principles
30//!
31//! 1. **Two-Provider Coordination**: Authentication provider validates subject existence,
32//!    authorization provider handles ACL persistence
33//! 2. **Validation-First**: All ACL mutations are validated before storage
34//! 3. **Async-First**: All I/O operations are async
35//! 4. **Error Transparency**: Errors from underlying providers are propagated clearly
36//!
37//! # Thread Safety
38//!
39//! The manager can be safely shared across threads. Provider instances must implement
40//! `Send + Sync`.
41
42use std::sync::Arc;
43
44use tracing::debug;
45
46use crate::authentication::enums::subject_type::SubjectType;
47use crate::authentication::model::subject::Subject;
48use crate::authentication::provider::AuthenticationMetadataProvider;
49use crate::authorization::enums::policy_type::PolicyType;
50use crate::authorization::metadata_provider::local::LocalAuthorizationMetadataProvider;
51use crate::authorization::model::acl::Acl;
52use crate::authorization::model::policy::Policy;
53use crate::authorization::model::policy_entry::PolicyEntry;
54use crate::authorization::model::resource::Resource;
55use crate::config::AuthConfig;
56use crate::AuthFailureKind;
57use crate::AuthServiceError;
58use crate::AuthServiceResult;
59use crate::ProviderRegistry;
60
61/// Authorization metadata manager.
62///
63/// This struct manages ACL (Access Control List) operations by coordinating
64/// two metadata providers:
65/// - `AuthorizationMetadataProvider`: Persists ACL data
66/// - `AuthenticationMetadataProvider`: Validates USER subject existence
67///
68/// # Examples
69///
70/// ```rust,ignore
71/// use rocketmq_auth::AuthorizationMetadataManager;
72/// use rocketmq_auth::AuthConfig;
73///
74/// // Create manager with configuration
75/// let config = AuthConfig::default();
76/// let manager = AuthorizationMetadataManager::new(config)?;
77///
78/// // Create ACL for a user
79/// let acl = Acl::of("user:alice", SubjectType::User, policy);
80/// manager.create_acl(acl).await?;
81///
82/// // Retrieve ACL
83/// let user = User::of("alice");
84/// if let Some(acl) = manager.get_acl(&user).await? {
85///     println!("ACL found: {:?}", acl);
86/// }
87///
88/// // Shutdown
89/// manager.shutdown().await;
90/// ```
91pub struct AuthorizationMetadataManager {
92    /// Authorization metadata provider for ACL persistence
93    authorization_provider: Arc<crate::AclMetadataHandle>,
94
95    /// Authentication metadata provider for USER subject validation
96    authentication_provider: Option<Arc<dyn crate::UserMetadataRead>>,
97    owned_registry: Option<ProviderRegistry>,
98}
99
100impl AuthorizationMetadataManager {
101    /// Create a new manager with the given providers.
102    ///
103    /// # Arguments
104    ///
105    /// * `authorization_provider` - Provider for ACL storage operations
106    /// * `authentication_provider` - Optional provider for subject validation
107    ///
108    /// # Examples
109    ///
110    /// ```rust,ignore
111    /// let auth_provider = Arc::new(LocalAuthorizationMetadataProvider::new(...));
112    /// let manager = AuthorizationMetadataManager::with_providers(
113    ///     auth_provider,
114    ///     None
115    /// );
116    /// ```
117    pub fn with_providers(
118        authorization_provider: Arc<LocalAuthorizationMetadataProvider>,
119        authentication_provider: Option<Arc<dyn AuthenticationMetadataProvider>>,
120    ) -> Self {
121        Self {
122            authorization_provider: crate::AclMetadataHandle::legacy(authorization_provider),
123            authentication_provider: authentication_provider.map(|port| {
124                Arc::new(crate::provider_ports::LegacyUserReader(port)) as Arc<dyn crate::UserMetadataRead>
125            }),
126            owned_registry: None,
127        }
128    }
129
130    pub fn with_registry(registry: &ProviderRegistry) -> Self {
131        Self {
132            authorization_provider: registry.authorization_metadata_provider(),
133            authentication_provider: Some(registry.authentication_metadata_provider()),
134            owned_registry: None,
135        }
136    }
137
138    /// Create a manager from configuration.
139    ///
140    /// This will instantiate providers based on the configuration.
141    ///
142    /// # Arguments
143    ///
144    /// * `config` - Authorization configuration
145    ///
146    /// # Errors
147    ///
148    /// Returns `AuthFailureKind::InvalidConfiguration` if provider initialization fails
149    pub fn new(config: AuthConfig) -> AuthServiceResult<Self> {
150        let registry = ProviderRegistry::local(&config).map_err(|error| {
151            AuthServiceError::with_source(
152                crate::AuthOperation::Initialize,
153                crate::AuthFailureKind::InvalidConfiguration,
154                error,
155            )
156        })?;
157        let mut manager = Self::with_registry(&registry);
158        manager.owned_registry = Some(registry);
159        Ok(manager)
160    }
161
162    /// Shutdown the manager and release resources.
163    ///
164    /// Managers created from configuration close their providers. Managers
165    /// borrowing a registry leave provider finalization to its runtime owner.
166    pub async fn shutdown(&mut self) {
167        if let Err(error) = self
168            .shutdown_until(rocketmq_runtime::ShutdownDeadline::after(
169                std::time::Duration::from_secs(5),
170            ))
171            .await
172        {
173            tracing::warn!(?error, "authorization metadata manager shutdown incomplete");
174        }
175    }
176
177    pub async fn shutdown_until(&self, deadline: rocketmq_runtime::ShutdownDeadline) -> AuthServiceResult<()> {
178        match &self.owned_registry {
179            Some(registry) => registry.shutdown_until(deadline).await,
180            None => Ok(()),
181        }
182    }
183
184    /// Create a new ACL.
185    ///
186    /// This method:
187    /// 1. Validates the ACL structure
188    /// 2. Initializes default policy types
189    /// 3. Verifies the subject exists (if authentication provider is available)
190    /// 4. Checks if ACL already exists
191    /// 5. Creates or updates the ACL
192    ///
193    /// # Arguments
194    ///
195    /// * `acl` - The ACL to create
196    ///
197    /// # Errors
198    ///
199    /// - `AuthFailureKind::NotFound` if subject doesn't exist
200    /// - `AuthFailureKind::InvalidInput` if ACL validation fails
201    /// - `AuthFailureKind::InvalidInput` if ACL already exists (and merge fails)
202    /// - `AuthFailureKind::Unavailable` if
203    ///   storage operation fails
204    ///
205    /// # Examples
206    ///
207    /// ```rust,ignore
208    /// let acl = Acl::of("user:alice", SubjectType::User, policy);
209    /// manager.create_acl(acl).await?;
210    /// ```
211    pub async fn create_acl(&self, mut acl: Acl) -> AuthServiceResult<()> {
212        // 1. Validate ACL
213        self.validate_acl(&acl)?;
214
215        // 2. Initialize default policy types
216        Self::init_acl(&mut acl);
217
218        // 3. Verify subject exists (for USER type)
219        self.verify_subject_exists(&acl).await?;
220
221        // 4. Check if ACL already exists and merge if needed
222        let subject_key = acl.subject_key();
223        let subject_type = acl.subject_type();
224
225        match self
226            .authorization_provider
227            .get_acl(&UserSubjectWrapper {
228                key: subject_key.to_string(),
229                subject_type,
230            })
231            .await?
232        {
233            None => {
234                // No existing ACL, create new one
235                debug!("Creating new ACL for subject: {}", subject_key);
236                self.authorization_provider.create_acl(acl).await
237            }
238            Some(mut old_acl) => {
239                // ACL exists, update policies
240                debug!("ACL already exists for subject {}, updating policies", subject_key);
241                old_acl.update_policies(acl.policies().clone());
242                self.authorization_provider.update_acl(old_acl).await
243            }
244        }
245    }
246
247    /// Update an existing ACL.
248    ///
249    /// This method:
250    /// 1. Validates the ACL structure
251    /// 2. Initializes default policy types
252    /// 3. Verifies the subject exists
253    /// 4. Creates the ACL if it doesn't exist, or merges with existing ACL
254    ///
255    /// # Arguments
256    ///
257    /// * `acl` - The ACL with updated policies
258    ///
259    /// # Errors
260    ///
261    /// - `AuthFailureKind::NotFound` if subject doesn't exist
262    /// - `AuthFailureKind::InvalidInput` if ACL validation fails
263    /// - `AuthFailureKind::Unavailable` if
264    ///   storage operation fails
265    ///
266    /// # Examples
267    ///
268    /// ```rust,ignore
269    /// let mut acl = manager.get_acl(&user).await?.unwrap();
270    /// acl.add_policy(new_policy);
271    /// manager.update_acl(acl).await?;
272    /// ```
273    pub async fn update_acl(&self, mut acl: Acl) -> AuthServiceResult<()> {
274        // 1. Validate ACL
275        self.validate_acl(&acl)?;
276
277        // 2. Initialize default policy types
278        Self::init_acl(&mut acl);
279
280        // 3. Verify subject exists
281        self.verify_subject_exists(&acl).await?;
282
283        // 4. Get existing ACL and merge or create
284        let subject_key = acl.subject_key();
285        let subject_type = acl.subject_type();
286
287        match self
288            .authorization_provider
289            .get_acl(&UserSubjectWrapper {
290                key: subject_key.to_string(),
291                subject_type,
292            })
293            .await?
294        {
295            None => {
296                // No existing ACL, create new one
297                debug!("No existing ACL for subject {}, creating new", subject_key);
298                self.authorization_provider.create_acl(acl).await
299            }
300            Some(mut old_acl) => {
301                // Merge policies
302                debug!("Updating existing ACL for subject: {}", subject_key);
303                old_acl.update_policies(acl.policies().clone());
304                self.authorization_provider.update_acl(old_acl).await
305            }
306        }
307    }
308
309    /// Delete an ACL for a subject.
310    ///
311    /// This is a convenience method that calls `delete_acl_with_filter` with no filters.
312    ///
313    /// # Arguments
314    ///
315    /// * `subject` - The subject whose ACL should be deleted
316    ///
317    /// # Errors
318    ///
319    /// - `AuthFailureKind::NotFound` if subject doesn't exist
320    /// - `AuthFailureKind::Unavailable` if
321    ///   storage operation fails
322    pub async fn delete_acl<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<()> {
323        self.delete_acl_with_filter(subject, None, None).await
324    }
325
326    /// Delete an ACL or specific policy entries.
327    ///
328    /// This method provides fine-grained deletion:
329    /// - If `resource` is specified: deletes specific policy entry for that resource
330    /// - If only `policy_type` is specified: deletes entire policy of that type
331    /// - If neither specified: deletes entire ACL
332    ///
333    /// # Arguments
334    ///
335    /// * `subject` - The subject whose ACL to modify
336    /// * `policy_type` - Optional policy type filter (defaults to CUSTOM if resource specified)
337    /// * `resource` - Optional resource to delete from the policy
338    ///
339    /// # Errors
340    ///
341    /// - `AuthFailureKind::NotFound` if subject doesn't exist
342    /// - `AuthFailureKind::InvalidInput` if ACL doesn't exist
343    /// - `AuthFailureKind::Unavailable` if
344    ///   storage operation fails
345    ///
346    /// # Examples
347    ///
348    /// ```rust,ignore
349    /// // Delete entire ACL
350    /// manager.delete_acl_with_filter(&user, None, None).await?;
351    ///
352    /// // Delete a specific policy type
353    /// manager.delete_acl_with_filter(&user, Some(PolicyType::CUSTOM), None).await?;
354    ///
355    /// // Delete specific resource from policy
356    /// let resource = Resource::of_topic("test-topic");
357    /// manager.delete_acl_with_filter(&user, Some(PolicyType::CUSTOM), Some(&resource)).await?;
358    /// ```
359    pub async fn delete_acl_with_filter<S: Subject + Send + Sync>(
360        &self,
361        subject: &S,
362        policy_type: Option<PolicyType>,
363        resource: Option<&Resource>,
364    ) -> AuthServiceResult<()> {
365        // 1. Verify subject exists
366        self.verify_subject_by_ref(subject).await?;
367
368        // 2. Default policy type to CUSTOM if not specified
369        let policy_type = policy_type.unwrap_or(PolicyType::Custom);
370
371        // 3. Get existing ACL
372        let mut acl = self.authorization_provider.get_acl(subject).await?.ok_or_else(|| {
373            AuthServiceError::invalid_context(format!(
374                "The ACL for subject '{}' does not exist",
375                subject.subject_key()
376            ))
377        })?;
378
379        // 4. Delete policy entry or entire ACL
380        if let Some(resource) = resource {
381            // Delete specific resource from policy
382            debug!(
383                "Deleting resource {:?} from {:?} policy for subject {}",
384                resource,
385                policy_type,
386                subject.subject_key()
387            );
388            acl.delete_policy(policy_type, resource);
389
390            if acl.policies().is_empty() {
391                // No policies left, delete entire ACL
392                self.authorization_provider.delete_acl(subject).await
393            } else {
394                // Update ACL with remaining policies
395                self.authorization_provider.update_acl(acl).await
396            }
397        } else {
398            // Delete entire ACL
399            debug!("Deleting entire ACL for subject: {}", subject.subject_key());
400            self.authorization_provider.delete_acl(subject).await
401        }
402    }
403
404    /// Get the ACL for a subject.
405    ///
406    /// # Arguments
407    ///
408    /// * `subject` - The subject to query
409    ///
410    /// # Returns
411    ///
412    /// - `Ok(Some(Acl))` if ACL exists
413    /// - `Ok(None)` if no ACL found
414    /// - `Err(...)` if operation fails
415    ///
416    /// # Errors
417    ///
418    /// - `AuthFailureKind::NotFound` if subject doesn't exist
419    /// - `AuthFailureKind::Unavailable` if
420    ///   storage operation fails
421    ///
422    /// # Examples
423    ///
424    /// ```rust,ignore
425    /// let user = User::of("alice");
426    /// if let Some(acl) = manager.get_acl(&user).await? {
427    ///     println!("Found ACL with {} policies", acl.policies().len());
428    /// }
429    /// ```
430    pub async fn get_acl<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<Option<Acl>> {
431        // 1. Verify subject exists
432        self.verify_subject_by_ref(subject).await?;
433
434        // 2. Get ACL from provider
435        self.authorization_provider.get_acl(subject).await
436    }
437
438    /// List ACLs with optional filtering.
439    ///
440    /// # Arguments
441    ///
442    /// * `subject_filter` - Optional subject name filter (substring match)
443    /// * `resource_filter` - Optional resource name filter (substring match)
444    ///
445    /// # Returns
446    ///
447    /// A vector of matching ACLs. Empty vector if no matches found.
448    ///
449    /// # Errors
450    ///
451    /// - `AuthFailureKind::Unavailable` if
452    ///   storage operation fails
453    ///
454    /// # Examples
455    ///
456    /// ```rust,ignore
457    /// // List all ACLs
458    /// let all_acls = manager.list_acl(None, None).await?;
459    ///
460    /// // Filter by subject
461    /// let user_acls = manager.list_acl(Some("user:"), None).await?;
462    ///
463    /// // Filter by resource
464    /// let topic_acls = manager.list_acl(None, Some("Topic:test")).await?;
465    /// ```
466    pub async fn list_acl(
467        &self,
468        subject_filter: Option<&str>,
469        resource_filter: Option<&str>,
470    ) -> AuthServiceResult<Vec<Acl>> {
471        self.authorization_provider
472            .list_acl(subject_filter, resource_filter)
473            .await
474    }
475
476    // ========== Private Helper Methods ==========
477
478    /// Initialize ACL defaults.
479    ///
480    /// Rust policies always carry an explicit `PolicyType`; no mutation is needed here.
481    fn init_acl(_acl: &mut Acl) {}
482
483    /// Validate ACL structure.
484    ///
485    /// Checks:
486    /// - Subject type is not None/Unknown
487    /// - Policies list is not empty
488    /// - Each policy is valid
489    fn validate_acl(&self, acl: &Acl) -> AuthServiceResult<()> {
490        // Validate subject type (currently only User is valid)
491        // SubjectType doesn't have Unknown variant, so validation is simplified
492
493        // Validate policies exist
494        if acl.policies().is_empty() {
495            return Err(AuthServiceError::invalid_context(
496                "The policies list is empty".to_string(),
497            ));
498        }
499
500        // Validate each policy
501        for policy in acl.policies() {
502            self.validate_policy(policy)?;
503        }
504
505        Ok(())
506    }
507
508    /// Validate policy structure.
509    ///
510    /// Checks:
511    /// - Policy entries list is not empty
512    /// - Each entry is valid
513    fn validate_policy(&self, policy: &Policy) -> AuthServiceResult<()> {
514        // Validate entries exist
515        if policy.entries().is_empty() {
516            return Err(AuthServiceError::invalid_context(
517                "Policy entries list is empty".to_string(),
518            ));
519        }
520
521        // Validate each entry
522        for entry in policy.entries() {
523            self.validate_policy_entry(entry)?;
524        }
525
526        Ok(())
527    }
528
529    /// Validate policy entry structure.
530    ///
531    /// Checks:
532    /// - Resources list is not empty
533    /// - Actions list is not empty
534    fn validate_policy_entry(&self, entry: &PolicyEntry) -> AuthServiceResult<()> {
535        // PolicyEntry has a single resource (not a list)
536        // Validate actions list is not empty
537        if entry.actions().is_empty() {
538            return Err(AuthServiceError::invalid_context(
539                "PolicyEntry actions list is empty".to_string(),
540            ));
541        }
542
543        Ok(())
544    }
545
546    /// Verify that the subject exists in the authentication system.
547    ///
548    /// For USER subjects, checks with authentication provider (when available).
549    /// For other subject types, assumes existence.
550    async fn verify_subject_exists(&self, acl: &Acl) -> AuthServiceResult<()> {
551        if acl.subject_type() == SubjectType::User {
552            self.verify_user_subject_key(acl.subject_key()).await?;
553        }
554        Ok(())
555    }
556
557    /// Verify that a subject exists by reference.
558    ///
559    /// Similar to verify_subject_exists but works with Subject trait references.
560    async fn verify_subject_by_ref<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<()> {
561        if subject.subject_type() == SubjectType::User {
562            self.verify_user_subject_key(subject.subject_key()).await?;
563        }
564        Ok(())
565    }
566
567    async fn verify_user_subject_key(&self, subject_key: &str) -> AuthServiceResult<()> {
568        let provider = self.authentication_provider.as_ref().ok_or_else(|| {
569            AuthServiceError::not_initialized("The authenticationMetadataProvider is not configured.")
570        })?;
571        let username = subject_key
572            .split_once(':')
573            .map(|(_, username)| username)
574            .unwrap_or(subject_key)
575            .trim();
576        if username.is_empty() {
577            return Err(AuthServiceError::subject_not_found(subject_key));
578        }
579
580        match provider.lookup_user(username).await {
581            Ok(_) => Ok(()),
582            Err(error) if error.kind() == AuthFailureKind::NotFound => Err(AuthServiceError::subject_not_found(
583                format!("The subject of {subject_key} is not exist."),
584            )),
585            Err(error) => Err(AuthServiceError::storage_read_failed(error)),
586        }
587    }
588}
589
590/// Wrapper struct to implement Subject trait for get_acl operations.
591///
592/// This is needed because we need to pass subject information to the provider's
593/// get_acl method which expects a type implementing the Subject trait.
594struct UserSubjectWrapper {
595    key: String,
596    subject_type: SubjectType,
597}
598
599impl Subject for UserSubjectWrapper {
600    fn subject_key(&self) -> &str {
601        &self.key
602    }
603
604    fn subject_type(&self) -> SubjectType {
605        self.subject_type
606    }
607}