Skip to main content

rocketmq_auth/authorization/manager/
metadata_manager_impl.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//! Production implementation of Authorization Metadata Manager.
16//!
17//! This module provides `AuthorizationMetadataManagerImpl`, a concrete implementation
18//! that coordinates authorization and authentication metadata providers to manage
19//! ACL (Access Control List) operations in RocketMQ's authorization system.
20//!
21//! # Architecture
22//!
23//! ```text
24//! AuthorizationMetadataManagerImpl
25//! ├── AuthorizationMetadataProvider (ACL storage)
26//! │   ├── create_acl()
27//! │   ├── update_acl()
28//! │   ├── delete_acl()
29//! │   ├── get_acl()
30//! │   └── list_acl()
31//! └── AuthenticationMetadataProvider (Subject validation)
32//!     └── get_user() / validate_subject()
33//! ```
34//!
35//! # Validation Strategy
36//!
37//! All ACL mutations follow a strict validation pipeline:
38//! 1. **Structural validation**: Ensure ACL/Policy/Entry data is well-formed
39//! 2. **Subject validation**: Verify subject exists in authentication system (for USER type)
40//! 3. **Business validation**: Check IP addresses, resource patterns, actions
41//! 4. **Storage operation**: Delegate to provider after all validations pass
42//!
43//! # Error Handling
44//!
45//! - Validation errors return `AuthFailureKind::InvalidInput`
46//! - Missing subjects return `AuthFailureKind::NotFound`
47//! - Storage failures return `AuthFailureKind::Unavailable`
48//! - All errors are propagated without panic, enabling graceful degradation
49//!
50//! # Thread Safety
51//!
52//! This implementation is thread-safe through immutable references (`&self`) and
53//! `Arc`-wrapped providers that implement `Send + Sync`.
54
55use std::sync::Arc;
56
57use tracing::debug;
58
59use crate::authentication::enums::subject_type::SubjectType;
60use crate::authentication::model::subject::Subject;
61use crate::authentication::provider::AuthenticationMetadataProvider;
62use crate::authorization::enums::policy_type::PolicyType;
63use crate::authorization::metadata_provider::local::LocalAuthorizationMetadataProvider;
64#[cfg(test)]
65use crate::authorization::metadata_provider::AuthorizationMetadataProvider;
66use crate::authorization::model::acl::Acl;
67use crate::authorization::model::environment::Environment;
68use crate::authorization::model::policy::Policy;
69use crate::authorization::model::policy_entry::PolicyEntry;
70use crate::authorization::model::resource::Resource;
71use crate::config::AuthConfig;
72use crate::AuthFailureKind;
73use crate::AuthServiceError;
74use crate::AuthServiceResult;
75use crate::ProviderRegistry;
76
77/// Production implementation of Authorization Metadata Manager.
78///
79/// This struct manages ACL (Access Control List) operations by coordinating
80/// authorization and authentication metadata providers. It provides a unified,
81/// type-safe API with comprehensive validation for all ACL operations.
82///
83/// # Provider Coordination
84///
85/// - **AuthorizationMetadataProvider**: Handles ACL persistence (create, update, delete, query)
86/// - **AuthenticationMetadataProvider**: Validates subject existence for USER subjects
87///
88/// # Examples
89///
90/// ```rust,ignore
91/// use rocketmq_auth::AuthorizationMetadataManagerImpl;
92/// use rocketmq_auth::AuthConfig;
93/// use rocketmq_auth::LocalAuthorizationMetadataProvider;
94/// use std::sync::Arc;
95///
96/// // Create manager with local provider
97/// let mut provider = LocalAuthorizationMetadataProvider::new();
98/// provider.initialize(AuthConfig::default(), None)?;
99///
100/// let manager = AuthorizationMetadataManagerImpl::new(Arc::new(provider), None);
101///
102/// // Create ACL for a user
103/// let acl = Acl::of("user:alice", SubjectType::User, policy);
104/// manager.create_acl(acl).await?;
105///
106/// // Query ACL
107/// let user = UserSubject::new("alice");
108/// if let Some(acl) = manager.get_acl(&user).await? {
109///     println!("ACL found: {:?}", acl);
110/// }
111///
112/// // Cleanup
113/// manager.shutdown().await;
114/// ```
115pub struct AuthorizationMetadataManagerImpl {
116    /// Authorization metadata provider for ACL persistence.
117    ///
118    /// This provider handles all storage operations: create, read, update, delete.
119    /// Must be initialized before the manager is used.
120    ///
121    /// The handle borrows the runtime's dynamic ACL port and admission owner.
122    authorization_provider: Arc<crate::AclMetadataHandle>,
123
124    /// Authentication metadata provider used to validate USER subjects before ACL mutations.
125    authentication_provider: Option<Arc<dyn crate::UserMetadataRead>>,
126    owned_registry: Option<ProviderRegistry>,
127}
128
129impl AuthorizationMetadataManagerImpl {
130    /// Create a new Authorization Metadata Manager with providers.
131    ///
132    /// # Arguments
133    ///
134    /// * `authorization_provider` - Provider for ACL storage (required, must be initialized)
135    /// * `authentication_provider` - Optional provider for subject validation
136    ///
137    /// # Examples
138    ///
139    /// ```rust,ignore
140    /// let auth_provider = Arc::new(LocalAuthorizationMetadataProvider::new());
141    /// let manager = AuthorizationMetadataManagerImpl::new(auth_provider, None);
142    /// ```
143    pub fn new(
144        authorization_provider: Arc<LocalAuthorizationMetadataProvider>,
145        authentication_provider: Option<Arc<dyn AuthenticationMetadataProvider>>,
146    ) -> Self {
147        Self {
148            authorization_provider: crate::AclMetadataHandle::legacy(authorization_provider),
149            authentication_provider: authentication_provider.map(|port| {
150                Arc::new(crate::provider_ports::LegacyUserReader(port)) as Arc<dyn crate::UserMetadataRead>
151            }),
152            owned_registry: None,
153        }
154    }
155
156    pub fn with_registry(registry: &ProviderRegistry) -> Self {
157        Self {
158            authorization_provider: registry.authorization_metadata_provider(),
159            authentication_provider: Some(registry.authentication_metadata_provider()),
160            owned_registry: None,
161        }
162    }
163
164    /// Create a manager from configuration.
165    ///
166    /// This factory method instantiates providers based on configuration.
167    ///
168    /// # Arguments
169    ///
170    /// * `config` - Authorization configuration
171    ///
172    /// # Errors
173    ///
174    /// Returns `AuthFailureKind::InvalidConfiguration` if:
175    /// - Provider initialization fails
176    /// - Required configuration is missing
177    /// - Configuration contains invalid values
178    pub fn from_config(config: &AuthConfig) -> AuthServiceResult<Self> {
179        let registry = ProviderRegistry::local(config).map_err(|error| {
180            AuthServiceError::with_source(
181                crate::AuthOperation::Initialize,
182                crate::AuthFailureKind::InvalidConfiguration,
183                error,
184            )
185        })?;
186        let mut manager = Self::with_registry(&registry);
187        manager.owned_registry = Some(registry);
188        Ok(manager)
189    }
190
191    /// Shutdown the manager and release resources.
192    ///
193    /// This method:
194    /// - Calls shutdown on authentication provider (if present)
195    /// - Calls shutdown on authorization provider
196    /// - Flushes pending operations
197    /// - Releases system resources
198    ///
199    /// This method is idempotent (safe to call multiple times).
200    ///
201    /// # Examples
202    ///
203    /// ```rust,ignore
204    /// manager.shutdown().await;
205    /// ```
206    pub async fn shutdown(&mut self) {
207        if let Err(error) = self
208            .shutdown_until(rocketmq_runtime::ShutdownDeadline::after(
209                std::time::Duration::from_secs(5),
210            ))
211            .await
212        {
213            tracing::warn!(?error, "authorization metadata manager shutdown incomplete");
214        }
215    }
216
217    pub async fn shutdown_until(&self, deadline: rocketmq_runtime::ShutdownDeadline) -> AuthServiceResult<()> {
218        match &self.owned_registry {
219            Some(registry) => registry.shutdown_until(deadline).await,
220            None => Ok(()),
221        }
222    }
223
224    /// Create a new ACL.
225    ///
226    /// This method performs the following steps:
227    /// 1. **Validate** ACL structure (subject, policies, entries, resources, actions)
228    /// 2. **Initialize** default policy types (sets CUSTOM if not specified)
229    /// 3. **Verify subject exists** (for USER type, via authentication provider)
230    /// 4. **Check for existing ACL**:
231    ///    - If exists: merge policies and update
232    ///    - If not: create new ACL
233    ///
234    /// # Arguments
235    ///
236    /// * `acl` - The ACL to create
237    ///
238    /// # Errors
239    ///
240    /// - `AuthFailureKind::InvalidInput` if ACL validation fails
241    /// - `AuthFailureKind::NotFound` if subject doesn't exist
242    /// - `AuthFailureKind::Unavailable` if
243    ///   storage operation fails
244    ///
245    /// # Examples
246    ///
247    /// ```rust,ignore
248    /// let acl = Acl::of("user:alice", SubjectType::User, policy);
249    /// manager.create_acl(acl).await?;
250    /// ```
251    pub async fn create_acl(&self, mut acl: Acl) -> AuthServiceResult<()> {
252        // Step 1: Validate ACL structure
253        self.validate_acl(&acl)?;
254
255        // Step 2: Initialize default policy types
256        Self::init_acl(&mut acl);
257
258        // Step 3: Verify subject exists (for USER type)
259        self.verify_subject_exists(&acl).await?;
260
261        // Step 4: Check if ACL already exists
262        let subject_key = acl.subject_key();
263        let subject_type = acl.subject_type();
264
265        let existing_acl = self
266            .authorization_provider
267            .get_acl(&SubjectWrapper {
268                key: subject_key.to_string(),
269                subject_type,
270            })
271            .await?;
272
273        match existing_acl {
274            None => {
275                // No existing ACL, create new one
276                debug!("Creating new ACL for subject: {}", subject_key);
277                self.authorization_provider.create_acl(acl).await
278            }
279            Some(mut old_acl) => {
280                // ACL exists, merge policies and update
281                debug!("ACL already exists for subject {}, merging policies", subject_key);
282                old_acl.update_policies(acl.policies().clone());
283                self.authorization_provider.update_acl(old_acl).await
284            }
285        }
286    }
287
288    /// Update an existing ACL.
289    ///
290    /// This method:
291    /// 1. Validates the ACL structure
292    /// 2. Initializes default policy types
293    /// 3. Verifies the subject exists
294    /// 4. Merges with existing ACL or creates new if not found
295    ///
296    /// # Arguments
297    ///
298    /// * `acl` - The ACL with updated policies
299    ///
300    /// # Errors
301    ///
302    /// - `AuthFailureKind::InvalidInput` if ACL validation fails
303    /// - `AuthFailureKind::NotFound` if subject doesn't exist
304    /// - `AuthFailureKind::Unavailable` if
305    ///   storage operation fails
306    ///
307    /// # Examples
308    ///
309    /// ```rust,ignore
310    /// let mut acl = manager.get_acl(&user).await?.unwrap();
311    /// acl.add_policy(new_policy);
312    /// manager.update_acl(acl).await?;
313    /// ```
314    pub async fn update_acl(&self, mut acl: Acl) -> AuthServiceResult<()> {
315        // Step 1: Validate ACL
316        self.validate_acl(&acl)?;
317
318        // Step 2: Initialize default policy types
319        Self::init_acl(&mut acl);
320
321        // Step 3: Verify subject exists
322        self.verify_subject_exists(&acl).await?;
323
324        // Step 4: Get existing ACL and merge or create
325        let subject_key = acl.subject_key();
326        let subject_type = acl.subject_type();
327
328        let existing_acl = self
329            .authorization_provider
330            .get_acl(&SubjectWrapper {
331                key: subject_key.to_string(),
332                subject_type,
333            })
334            .await?;
335
336        match existing_acl {
337            None => {
338                // No existing ACL, create new one
339                debug!("No existing ACL for subject {}, creating new", subject_key);
340                self.authorization_provider.create_acl(acl).await
341            }
342            Some(mut old_acl) => {
343                // Merge policies
344                debug!("Updating existing ACL for subject: {}", subject_key);
345                old_acl.update_policies(acl.policies().clone());
346                self.authorization_provider.update_acl(old_acl).await
347            }
348        }
349    }
350
351    /// Delete an ACL for a subject.
352    ///
353    /// This is a convenience method that deletes the entire ACL.
354    ///
355    /// # Arguments
356    ///
357    /// * `subject` - The subject whose ACL should be deleted
358    ///
359    /// # Errors
360    ///
361    /// - `AuthFailureKind::NotFound` if subject doesn't exist
362    /// - `AuthFailureKind::InvalidInput` if ACL doesn't exist
363    /// - `AuthFailureKind::Unavailable` if
364    ///   storage operation fails
365    ///
366    /// # Examples
367    ///
368    /// ```rust,ignore
369    /// let user = UserSubject::new("alice");
370    /// manager.delete_acl(&user).await?;
371    /// ```
372    pub async fn delete_acl<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<()> {
373        self.delete_acl_with_filter(subject, None, None).await
374    }
375
376    /// Delete an ACL or specific policy entries.
377    ///
378    /// This method provides fine-grained deletion:
379    /// - If `resource` is specified: deletes specific policy entry for that resource
380    /// - If only `policy_type` is specified: deletes entire policy of that type
381    /// - If neither specified: deletes entire ACL
382    ///
383    /// # Arguments
384    ///
385    /// * `subject` - The subject whose ACL to modify
386    /// * `policy_type` - Optional policy type filter (defaults to CUSTOM)
387    /// * `resource` - Optional resource to delete from the policy
388    ///
389    /// # Errors
390    ///
391    /// - `AuthFailureKind::NotFound` if subject doesn't exist
392    /// - `AuthFailureKind::InvalidInput` if ACL doesn't exist
393    /// - `AuthFailureKind::Unavailable` if
394    ///   storage operation fails
395    ///
396    /// # Examples
397    ///
398    /// ```rust,ignore
399    /// // Delete entire ACL
400    /// manager.delete_acl_with_filter(&user, None, None).await?;
401    ///
402    /// // Delete a specific policy type
403    /// manager.delete_acl_with_filter(&user, Some(PolicyType::Custom), None).await?;
404    ///
405    /// // Delete specific resource from policy
406    /// let resource = Resource::of_topic("test-topic");
407    /// manager.delete_acl_with_filter(&user, Some(PolicyType::Custom), Some(&resource)).await?;
408    /// ```
409    pub async fn delete_acl_with_filter<S: Subject + Send + Sync>(
410        &self,
411        subject: &S,
412        policy_type: Option<PolicyType>,
413        resource: Option<&Resource>,
414    ) -> AuthServiceResult<()> {
415        // Step 1: Verify subject exists
416        self.verify_subject_by_ref(subject).await?;
417
418        // Step 2: Default policy type to CUSTOM if not specified
419        let policy_type = policy_type.unwrap_or(PolicyType::Custom);
420
421        // Step 3: Get existing ACL
422        let mut acl = self.authorization_provider.get_acl(subject).await?.ok_or_else(|| {
423            AuthServiceError::invalid_context(format!(
424                "The ACL for subject '{}' does not exist",
425                subject.subject_key()
426            ))
427        })?;
428
429        // Step 4: Delete policy entry or entire ACL
430        if let Some(resource) = resource {
431            // Delete specific resource from policy
432            debug!(
433                "Deleting resource {:?} from {:?} policy for subject {}",
434                resource,
435                policy_type,
436                subject.subject_key()
437            );
438            acl.delete_policy(policy_type, resource);
439
440            if acl.policies().is_empty() {
441                // No policies left, delete entire ACL
442                self.authorization_provider.delete_acl(subject).await
443            } else {
444                // Update ACL with remaining policies
445                self.authorization_provider.update_acl(acl).await
446            }
447        } else {
448            // Delete entire ACL
449            debug!("Deleting entire ACL for subject: {}", subject.subject_key());
450            self.authorization_provider.delete_acl(subject).await
451        }
452    }
453
454    /// Get the ACL for a subject.
455    ///
456    /// # Arguments
457    ///
458    /// * `subject` - The subject to query
459    ///
460    /// # Returns
461    ///
462    /// - `Ok(Some(Acl))` if ACL exists
463    /// - `Ok(None)` if no ACL found
464    /// - `Err(...)` if operation fails
465    ///
466    /// # Errors
467    ///
468    /// - `AuthFailureKind::NotFound` if subject doesn't exist
469    /// - `AuthFailureKind::Unavailable` if
470    ///   storage operation fails
471    ///
472    /// # Examples
473    ///
474    /// ```rust,ignore
475    /// let user = UserSubject::new("alice");
476    /// if let Some(acl) = manager.get_acl(&user).await? {
477    ///     println!("Found ACL with {} policies", acl.policies().len());
478    /// }
479    /// ```
480    pub async fn get_acl<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<Option<Acl>> {
481        // Step 1: Verify subject exists
482        self.verify_subject_by_ref(subject).await?;
483
484        // Step 2: Get ACL from provider
485        self.authorization_provider.get_acl(subject).await
486    }
487
488    /// List ACLs with optional filtering.
489    ///
490    /// # Arguments
491    ///
492    /// * `subject_filter` - Optional subject name filter (substring match)
493    /// * `resource_filter` - Optional resource name filter (substring match)
494    ///
495    /// # Returns
496    ///
497    /// A vector of matching ACLs. Empty vector if no matches found.
498    ///
499    /// # Errors
500    ///
501    /// - `AuthFailureKind::Unavailable` if
502    ///   storage operation fails
503    ///
504    /// # Examples
505    ///
506    /// ```rust,ignore
507    /// // List all ACLs
508    /// let all_acls = manager.list_acl(None, None).await?;
509    ///
510    /// // Filter by subject
511    /// let user_acls = manager.list_acl(Some("user:"), None).await?;
512    ///
513    /// // Filter by resource
514    /// let topic_acls = manager.list_acl(None, Some("Topic:test")).await?;
515    /// ```
516    pub async fn list_acl(
517        &self,
518        subject_filter: Option<&str>,
519        resource_filter: Option<&str>,
520    ) -> AuthServiceResult<Vec<Acl>> {
521        self.authorization_provider
522            .list_acl(subject_filter, resource_filter)
523            .await
524    }
525
526    // ========== Private Validation Methods ==========
527
528    /// Initialize ACL defaults.
529    ///
530    /// Rust policies always carry an explicit `PolicyType`; no mutation is needed here.
531    fn init_acl(_acl: &mut Acl) {}
532
533    /// Validate ACL structure.
534    ///
535    /// Validates:
536    /// - Subject type is valid (not None/Unknown)
537    /// - Policies list is not empty
538    /// - Each policy is structurally valid
539    fn validate_acl(&self, acl: &Acl) -> AuthServiceResult<()> {
540        // Validate subject type (currently only User is supported)
541        // SubjectType enum doesn't have Unknown variant in Rust
542
543        // Validate policies exist
544        if acl.policies().is_empty() {
545            return Err(AuthServiceError::invalid_context("The policies is empty."));
546        }
547
548        // Validate each policy
549        for policy in acl.policies() {
550            self.validate_policy(policy)?;
551        }
552
553        Ok(())
554    }
555
556    /// Validate policy structure.
557    ///
558    /// Validates:
559    /// - Policy entries list is not empty
560    /// - Each entry is structurally valid
561    fn validate_policy(&self, policy: &Policy) -> AuthServiceResult<()> {
562        // Validate entries exist
563        if policy.entries().is_empty() {
564            return Err(AuthServiceError::invalid_context(
565                "The policy entries is empty.".to_string(),
566            ));
567        }
568
569        // Validate each entry
570        for entry in policy.entries() {
571            self.validate_policy_entry(entry)?;
572        }
573
574        Ok(())
575    }
576
577    /// Validate policy entry structure.
578    ///
579    /// Validates:
580    /// - Resource is present and valid
581    /// - Resource type is set
582    /// - Resource pattern is set
583    /// - Actions list is not empty
584    /// - Actions do not include Action::ANY
585    /// - Environment (if present) has valid IP addresses
586    /// - Decision is set
587    fn validate_policy_entry(&self, entry: &PolicyEntry) -> AuthServiceResult<()> {
588        // Validate resource
589        let resource = entry.resource();
590        // ResourceType is an enum, always has a value
591        // resource_name() returns Option<&str>, check if it's None or empty
592        if let Some(name) = resource.resource_name() {
593            if name.is_empty() {
594                return Err(AuthServiceError::invalid_context(
595                    "The resource pattern is empty.".to_string(),
596                ));
597            }
598        } else {
599            return Err(AuthServiceError::invalid_context(
600                "The resource pattern is null.".to_string(),
601            ));
602        }
603
604        // Validate actions
605        if entry.actions().is_empty() {
606            return Err(AuthServiceError::invalid_context("The actions is empty."));
607        }
608
609        // Check for Action::ANY (should not be allowed in ACL entries)
610        use rocketmq_security_api::Action;
611        if entry.actions().contains(&Action::Any) {
612            return Err(AuthServiceError::invalid_context(
613                "The actions can not be Any.".to_string(),
614            ));
615        }
616
617        // Validate environment (if present)
618        if let Some(environment) = entry.environment() {
619            self.validate_environment(environment)?;
620        }
621
622        // Decision is always set in Rust (enum with no null)
623        // No validation needed
624
625        Ok(())
626    }
627
628    /// Validate environment configuration.
629    ///
630    /// Validates:
631    /// - Source IPs are not blank
632    /// - Source IPs are valid IP addresses or CIDR blocks
633    fn validate_environment(&self, environment: &Environment) -> AuthServiceResult<()> {
634        let source_ips = environment.source_ips();
635        // source_ips() returns &Vec<String>
636        if !source_ips.is_empty() {
637            for source_ip in source_ips {
638                if source_ip.trim().is_empty() {
639                    return Err(AuthServiceError::invalid_context("The source ip is empty.".to_string()));
640                }
641
642                // Validate IP address or CIDR format
643                if !Self::is_valid_ip_or_cidr(source_ip) {
644                    return Err(AuthServiceError::invalid_context(format!(
645                        "The source ip '{}' is invalid.",
646                        source_ip
647                    )));
648                }
649            }
650        }
651
652        Ok(())
653    }
654
655    /// Validate if a string is a valid IP address or CIDR block.
656    ///
657    /// This is a simplified implementation. In production, consider using
658    /// the `ipnetwork` crate or similar for robust IP validation.
659    fn is_valid_ip_or_cidr(ip: &str) -> bool {
660        use std::net::IpAddr;
661
662        // Try parsing as plain IP address
663        if ip.parse::<IpAddr>().is_ok() {
664            return true;
665        }
666
667        // Try parsing as CIDR (contains '/')
668        if let Some((addr, prefix)) = ip.split_once('/') {
669            if addr.parse::<IpAddr>().is_ok() {
670                if let Ok(prefix_len) = prefix.parse::<u8>() {
671                    // Validate prefix length based on IP version
672                    if addr.contains(':') {
673                        // IPv6
674                        return prefix_len <= 128;
675                    } else {
676                        // IPv4
677                        return prefix_len <= 32;
678                    }
679                }
680            }
681        }
682
683        false
684    }
685
686    // ========== Subject Validation Methods ==========
687
688    /// Verify that the subject exists in the authentication system.
689    ///
690    /// For USER subjects, checks with authentication provider (when available).
691    /// For other subject types, assumes existence.
692    async fn verify_subject_exists(&self, acl: &Acl) -> AuthServiceResult<()> {
693        if acl.subject_type() == SubjectType::User {
694            self.verify_user_subject_key(acl.subject_key()).await?;
695        }
696
697        Ok(())
698    }
699
700    /// Verify that a subject exists by reference.
701    ///
702    /// Similar to `verify_subject_exists` but works with Subject trait references.
703    async fn verify_subject_by_ref<S: Subject + Send + Sync>(&self, subject: &S) -> AuthServiceResult<()> {
704        if subject.subject_type() == SubjectType::User {
705            self.verify_user_subject_key(subject.subject_key()).await?;
706        }
707
708        Ok(())
709    }
710
711    async fn verify_user_subject_key(&self, subject_key: &str) -> AuthServiceResult<()> {
712        let provider = self.authentication_provider.as_ref().ok_or_else(|| {
713            AuthServiceError::not_initialized("The authenticationMetadataProvider is not configured.")
714        })?;
715        let username = subject_key
716            .split_once(':')
717            .map(|(_, username)| username)
718            .unwrap_or(subject_key)
719            .trim();
720        if username.is_empty() {
721            return Err(AuthServiceError::subject_not_found(subject_key));
722        }
723
724        match provider.lookup_user(username).await {
725            Ok(_) => Ok(()),
726            Err(error) if error.kind() == AuthFailureKind::NotFound => Err(AuthServiceError::subject_not_found(
727                format!("The subject of {subject_key} is not exist."),
728            )),
729            Err(error) => Err(AuthServiceError::storage_read_failed(error)),
730        }
731    }
732}
733
734// ========== Helper Types ==========
735
736/// Wrapper struct to implement Subject trait for provider operations.
737///
738/// This is needed because we need to pass subject information to the provider's
739/// methods which expect types implementing the Subject trait.
740struct SubjectWrapper {
741    key: String,
742    subject_type: SubjectType,
743}
744
745impl Subject for SubjectWrapper {
746    fn subject_key(&self) -> &str {
747        &self.key
748    }
749
750    fn subject_type(&self) -> SubjectType {
751        self.subject_type
752    }
753}
754
755// ========== Unit Tests ==========
756
757#[cfg(test)]
758mod tests {
759    use rocketmq_security_api::Action;
760
761    use crate::authentication::enums::user_status::UserStatus;
762    use crate::authentication::enums::user_type::UserType;
763    use crate::authentication::model::user::User;
764    use crate::authentication::provider::LocalAuthenticationMetadataProvider;
765    use crate::authorization::enums::decision::Decision;
766
767    use super::*;
768    use crate::AuthFailureKind;
769
770    #[test]
771    fn test_is_valid_ip_or_cidr() {
772        // Valid IPv4
773        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("192.168.0.1"));
774        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("10.0.0.0"));
775
776        // Valid IPv4 CIDR
777        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("192.168.0.0/24"));
778        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("10.0.0.0/8"));
779
780        // Valid IPv6
781        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("2001:db8::1"));
782        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("::1"));
783
784        // Valid IPv6 CIDR
785        assert!(AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("2001:db8::/32"));
786
787        // Invalid IPs
788        assert!(!AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("invalid"));
789        assert!(!AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr(
790            "256.256.256.256"
791        ));
792        assert!(!AuthorizationMetadataManagerImpl::is_valid_ip_or_cidr("192.168.0.0/33"));
793        // Invalid prefix
794    }
795
796    #[tokio::test]
797    async fn create_acl_validates_user_subject_with_authentication_provider() {
798        let mut authorization_provider = LocalAuthorizationMetadataProvider::new();
799        authorization_provider
800            .initialize(AuthConfig::default(), None)
801            .expect("authorization provider should initialize");
802        let authentication_provider = Arc::new(LocalAuthenticationMetadataProvider::new());
803        let manager = AuthorizationMetadataManagerImpl::new(
804            Arc::new(authorization_provider),
805            Some(authentication_provider.clone() as Arc<dyn AuthenticationMetadataProvider>),
806        );
807        let acl = Acl::of(
808            "alice",
809            SubjectType::User,
810            Policy::of(
811                vec![Resource::of_topic("topic-a")],
812                vec![Action::Pub],
813                None,
814                Decision::Allow,
815            ),
816        );
817
818        let missing_error = manager
819            .create_acl(acl.clone())
820            .await
821            .expect_err("missing user subject should be rejected");
822        assert_eq!(missing_error.kind(), AuthFailureKind::NotFound);
823
824        let mut user = User::of_with_type("alice", "secret", UserType::Normal);
825        user.set_user_status(UserStatus::Enable);
826        authentication_provider
827            .create_user(user)
828            .await
829            .expect("user should be created");
830
831        manager
832            .create_acl(acl)
833            .await
834            .expect("existing user subject should be accepted");
835    }
836}