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(®istry);
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}