Skip to main content

auth_framework/auth_modular/
authorization_manager.rs

1//! Authorization manager: wraps `PermissionChecker` and exposes all role/permission operations.
2
3use crate::errors::{AuthError, Result};
4use crate::permissions::{Permission, PermissionChecker, Role};
5use crate::storage::AuthStorage;
6use crate::tokens::AuthToken;
7use std::sync::Arc;
8use tokio::sync::RwLock;
9use tracing::{debug, info};
10
11/// Authorization manager that owns the `PermissionChecker` and exposes
12/// all role and permission operations for delegation from `AuthFramework`.
13///
14/// # Example
15/// ```rust,ignore
16/// use auth_framework::auth_modular::AuthorizationManager;
17/// let am = AuthorizationManager::new(checker.clone(), storage.clone());
18/// am.create_default_roles().await;
19/// ```
20pub struct AuthorizationManager {
21    checker: Arc<RwLock<PermissionChecker>>,
22    storage: Arc<dyn AuthStorage>,
23}
24
25impl AuthorizationManager {
26    /// Create a new authorization manager.
27    ///
28    /// # Example
29    /// ```rust,ignore
30    /// let am = AuthorizationManager::new(checker.clone(), storage.clone());
31    /// ```
32    pub fn new(checker: Arc<RwLock<PermissionChecker>>, storage: Arc<dyn AuthStorage>) -> Self {
33        Self { checker, storage }
34    }
35
36    /// Initialize the default roles in the permission checker (called during framework init).
37    ///
38    /// # Example
39    /// ```rust,ignore
40    /// am.create_default_roles().await;
41    /// ```
42    pub async fn create_default_roles(&self) {
43        let mut c = self.checker.write().await;
44        c.create_default_roles();
45    }
46
47    /// Load persisted roles and user→role assignments from KV storage into
48    /// the in-memory permission checker. Called during framework initialization
49    /// after `create_default_roles` so that previously persisted state survives
50    /// process restarts.
51    ///
52    /// # Example
53    /// ```rust,ignore
54    /// am.load_persisted_roles().await?;
55    /// ```
56    pub async fn load_persisted_roles(&self) -> Result<()> {
57        // Load roles
58        let role_keys = self.storage.list_kv_keys("rbac:role:").await?;
59        if !role_keys.is_empty() {
60            let mut c = self.checker.write().await;
61            for key in &role_keys {
62                if let Some(bytes) = self.storage.get_kv(key).await? {
63                    if let Ok(role) = serde_json::from_slice::<Role>(&bytes) {
64                        c.add_role(role);
65                    }
66                }
67            }
68        }
69
70        // Load user→role assignments
71        let assignment_keys = self.storage.list_kv_keys("rbac:user_roles:").await?;
72        if !assignment_keys.is_empty() {
73            let mut c = self.checker.write().await;
74            for key in &assignment_keys {
75                let user_id = key.strip_prefix("rbac:user_roles:").unwrap_or(key);
76                if let Some(bytes) = self.storage.get_kv(key).await? {
77                    if let Ok(roles) = serde_json::from_slice::<Vec<String>>(&bytes) {
78                        for role_name in roles {
79                            if let Err(e) = c.assign_role_to_user(user_id, &role_name) {
80                                tracing::warn!("Failed to assign role '{}' to user '{}': {}", role_name, user_id, e);
81                            }
82                        }
83                    }
84                }
85            }
86        }
87
88        Ok(())
89    }
90
91    /// Reset runtime authorization state back to the default role set.
92    ///
93    /// # Example
94    /// ```rust,ignore
95    /// am.reset_runtime_state().await;
96    /// ```
97    pub async fn reset_runtime_state(&self) {
98        let mut c = self.checker.write().await;
99        c.clear();
100        c.create_default_roles();
101
102        // Clean up persisted role data
103        if let Ok(keys) = self.storage.list_kv_keys("rbac:role:").await {
104            for key in keys {
105                let _ = self.storage.delete_kv(&key).await;
106            }
107        }
108        if let Ok(keys) = self.storage.list_kv_keys("rbac:user_roles:").await {
109            for key in keys {
110                let _ = self.storage.delete_kv(&key).await;
111            }
112        }
113    }
114
115    /// Grant a direct permission to a user.
116    ///
117    /// # Example
118    /// ```rust,ignore
119    /// am.grant_permission("user-1", "read", "documents").await?;
120    /// ```
121    pub async fn grant_permission(
122        &self,
123        user_id: &str,
124        action: &str,
125        resource: &str,
126    ) -> Result<()> {
127        debug!(
128            "Granting permission '{}:{}' to user '{}'",
129            action, resource, user_id
130        );
131        let mut c = self.checker.write().await;
132        let permission = Permission::new(action, resource);
133        c.add_user_permission(user_id, permission);
134        info!(
135            "Permission '{}:{}' granted to user '{}'",
136            action, resource, user_id
137        );
138        Ok(())
139    }
140
141    /// Revoke a direct permission from a user.
142    ///
143    /// # Example
144    /// ```rust,ignore
145    /// am.revoke_permission("user-1", "read", "documents").await?;
146    /// ```
147    pub async fn revoke_permission(
148        &self,
149        user_id: &str,
150        action: &str,
151        resource: &str,
152    ) -> Result<()> {
153        debug!(
154            "Revoking permission '{}:{}' from user '{}'",
155            action, resource, user_id
156        );
157        if user_id.is_empty() || action.is_empty() || resource.is_empty() {
158            return Err(AuthError::validation(
159                "User ID, action, and resource cannot be empty",
160            ));
161        }
162        let mut c = self.checker.write().await;
163        let permission = Permission::new(action, resource);
164        c.remove_user_permission(user_id, &permission);
165        info!(
166            "Permission '{}:{}' revoked from user '{}'",
167            action, resource, user_id
168        );
169        Ok(())
170    }
171
172    /// Create (register) a new role.
173    ///
174    /// # Example
175    /// ```rust,ignore
176    /// use auth_framework::permissions::Role;
177    /// am.create_role(Role::new("editor")).await?;
178    /// ```
179    pub async fn create_role(&self, role: Role) -> Result<()> {
180        debug!("Creating role '{}'", role.name);
181        if role.name.is_empty() {
182            return Err(AuthError::validation("Role name cannot be empty"));
183        }
184        let mut c = self.checker.write().await;
185        c.add_role(role.clone());
186        drop(c);
187
188        // Persist to storage
189        let key = format!("rbac:role:{}", role.name);
190        let role_json = serde_json::to_vec(&role)
191            .map_err(|e| AuthError::internal(format!("Failed to serialize role: {e}")))?;
192        self.storage.store_kv(&key, &role_json, None).await?;
193
194        info!("Role '{}' created", role.name);
195        Ok(())
196    }
197
198    /// Return all known roles.
199    ///
200    /// # Example
201    /// ```rust,ignore
202    /// let roles = am.list_roles().await;
203    /// for r in &roles { println!("{}", r.name); }
204    /// ```
205    pub async fn list_roles(&self) -> Vec<Role> {
206        let c = self.checker.read().await;
207        c.list_roles()
208    }
209
210    /// Fetch a role definition by name.
211    ///
212    /// # Example
213    /// ```rust,ignore
214    /// let role = am.get_role("admin").await?;
215    /// println!("permissions: {:?}", role.permissions());
216    /// ```
217    pub async fn get_role(&self, role_name: &str) -> Result<Role> {
218        if role_name.is_empty() {
219            return Err(AuthError::validation("Role name cannot be empty"));
220        }
221
222        let c = self.checker.read().await;
223        c.get_role(role_name)
224            .cloned()
225            .ok_or_else(|| AuthError::validation(format!("Role '{role_name}' not found")))
226    }
227
228    /// Add a permission to an existing role.
229    ///
230    /// # Example
231    /// ```rust,ignore
232    /// use auth_framework::permissions::Permission;
233    /// am.add_role_permission("editor", Permission::new("write", "posts")).await?;
234    /// ```
235    pub async fn add_role_permission(&self, role_name: &str, permission: Permission) -> Result<()> {
236        if role_name.is_empty() {
237            return Err(AuthError::validation("Role name cannot be empty"));
238        }
239
240        let mut c = self.checker.write().await;
241        let role = c
242            .get_role(role_name)
243            .cloned()
244            .ok_or_else(|| AuthError::validation(format!("Role '{role_name}' not found")))?;
245        let mut updated_role = role;
246        updated_role.add_permission(permission);
247        c.add_role(updated_role.clone());
248        drop(c);
249
250        // Re-persist the updated role
251        let key = format!("rbac:role:{}", role_name);
252        let role_json = serde_json::to_vec(&updated_role)
253            .map_err(|e| AuthError::internal(format!("Failed to serialize role: {e}")))?;
254        self.storage.store_kv(&key, &role_json, None).await?;
255
256        Ok(())
257    }
258
259    /// Assign a role to a user.
260    ///
261    /// # Example
262    /// ```rust,ignore
263    /// am.assign_role("user-1", "editor").await?;
264    /// ```
265    pub async fn assign_role(&self, user_id: &str, role_name: &str) -> Result<()> {
266        debug!("Assigning role '{}' to user '{}'", role_name, user_id);
267        if user_id.is_empty() {
268            return Err(AuthError::validation("User ID cannot be empty"));
269        }
270        if role_name.is_empty() {
271            return Err(AuthError::validation("Role name cannot be empty"));
272        }
273        let mut c = self.checker.write().await;
274        c.assign_role_to_user(user_id, role_name)?;
275        let roles = c.get_user_roles(user_id);
276        drop(c);
277
278        // Persist user→roles mapping to storage
279        let key = format!("rbac:user_roles:{}", user_id);
280        let roles_json = serde_json::to_vec(&roles)
281            .map_err(|e| AuthError::internal(format!("Failed to serialize user roles: {e}")))?;
282        self.storage.store_kv(&key, &roles_json, None).await?;
283
284        info!("Role '{}' assigned to user '{}'", role_name, user_id);
285        Ok(())
286    }
287
288    /// Remove a role from a user.
289    ///
290    /// # Example
291    /// ```rust,ignore
292    /// am.remove_role("user-1", "editor").await?;
293    /// ```
294    pub async fn remove_role(&self, user_id: &str, role_name: &str) -> Result<()> {
295        debug!("Removing role '{}' from user '{}'", role_name, user_id);
296        if user_id.is_empty() || role_name.is_empty() {
297            return Err(AuthError::validation(
298                "User ID and role name cannot be empty",
299            ));
300        }
301        let mut c = self.checker.write().await;
302        c.remove_user_role(user_id, role_name);
303        let roles = c.get_user_roles(user_id);
304        drop(c);
305
306        // Update persisted user→roles mapping
307        let key = format!("rbac:user_roles:{}", user_id);
308        if roles.is_empty() {
309            let _ = self.storage.delete_kv(&key).await;
310        } else {
311            let roles_json = serde_json::to_vec(&roles)
312                .map_err(|e| AuthError::internal(format!("Failed to serialize user roles: {e}")))?;
313            self.storage.store_kv(&key, &roles_json, None).await?;
314        }
315
316        info!("Role '{}' removed from user '{}'", role_name, user_id);
317        Ok(())
318    }
319
320    /// Set role inheritance (`child_role` inherits all permissions from `parent_role`).
321    ///
322    /// # Example
323    /// ```rust,ignore
324    /// am.set_role_inheritance("moderator", "user").await?;
325    /// ```
326    pub async fn set_role_inheritance(&self, child_role: &str, parent_role: &str) -> Result<()> {
327        debug!(
328            "Setting inheritance: '{}' inherits from '{}'",
329            child_role, parent_role
330        );
331        if child_role.is_empty() || parent_role.is_empty() {
332            return Err(AuthError::validation("Role names cannot be empty"));
333        }
334        let mut c = self.checker.write().await;
335        c.set_role_inheritance(child_role, parent_role)?;
336        info!(
337            "Role inheritance set: '{}' inherits from '{}'",
338            child_role, parent_role
339        );
340        Ok(())
341    }
342
343    /// Check if a token has a specific direct permission.
344    ///
345    /// Does **not** validate the token itself β€” the caller must validate
346    /// the token's signature and expiry before calling this method.
347    ///
348    /// # Example
349    /// ```rust,ignore
350    /// let allowed = am.check_token_permission(&token, "read", "users").await?;
351    /// ```
352    pub async fn check_token_permission(
353        &self,
354        token: &AuthToken,
355        action: &str,
356        resource: &str,
357    ) -> Result<bool> {
358        let permission = Permission::new(action, resource);
359        let mut c = self.checker.write().await;
360        c.check_token_permission(token, &permission)
361    }
362
363    /// Check if a user (by ID) has a specific permission (ABAC/RBAC evaluation).
364    ///
365    /// # Example
366    /// ```rust,ignore
367    /// if am.check_user_permission("user-1", "write", "posts").await {
368    ///     println!("allowed");
369    /// }
370    /// ```
371    pub async fn check_user_permission(&self, user_id: &str, action: &str, resource: &str) -> bool {
372        let permission = Permission::new(action, resource);
373        let mut c = self.checker.write().await;
374        c.check_permission(user_id, &permission).unwrap_or(false)
375    }
376
377    /// Check whether a user currently holds a named role.
378    ///
379    /// # Example
380    /// ```rust,ignore
381    /// let is_admin = am.user_has_role("user-1", "admin").await?;
382    /// ```
383    pub async fn user_has_role(&self, user_id: &str, role_name: &str) -> Result<bool> {
384        debug!("Checking if user '{}' has role '{}'", user_id, role_name);
385        if user_id.is_empty() || role_name.is_empty() {
386            return Err(AuthError::validation(
387                "User ID and role name cannot be empty",
388            ));
389        }
390        let c = self.checker.read().await;
391        let has_role = c.user_has_role(user_id, role_name);
392        debug!("User '{}' has role '{}': {}", user_id, role_name, has_role);
393        Ok(has_role)
394    }
395
396    /// Get all effective permissions for a user (direct + role-inherited).
397    ///
398    /// # Example
399    /// ```rust,ignore
400    /// let perms = am.get_effective_permissions("user-1").await?;
401    /// for p in &perms { println!("{}", p); }
402    /// ```
403    pub async fn get_effective_permissions(&self, user_id: &str) -> Result<Vec<String>> {
404        debug!("Getting effective permissions for user '{}'", user_id);
405        if user_id.is_empty() {
406            return Err(AuthError::validation("User ID cannot be empty"));
407        }
408        let c = self.checker.read().await;
409        let permissions = c.get_effective_permissions(user_id);
410        debug!(
411            "User '{}' has {} effective permissions",
412            user_id,
413            permissions.len()
414        );
415        Ok(permissions)
416    }
417
418    /// List the currently assigned runtime roles for a user.
419    ///
420    /// # Example
421    /// ```rust,ignore
422    /// let roles = am.list_user_roles("user-1").await?;
423    /// ```
424    pub async fn list_user_roles(&self, user_id: &str) -> Result<Vec<String>> {
425        if user_id.is_empty() {
426            return Err(AuthError::validation("User ID cannot be empty"));
427        }
428
429        let c = self.checker.read().await;
430        Ok(c.get_user_roles(user_id))
431    }
432
433    /// Get raw permission metrics: `(role_count, user_count, total_direct_permission_count)`.
434    ///
435    /// # Example
436    /// ```rust,ignore
437    /// let (roles, users, perms) = am.get_metrics().await;
438    /// ```
439    pub async fn get_metrics(&self) -> (usize, usize, usize) {
440        let c = self.checker.read().await;
441        (
442            c.role_count(),
443            c.user_count(),
444            c.total_direct_permission_count(),
445        )
446    }
447
448    // ── ABAC / Storage-backed operations ────────────────────────────────────
449
450    /// Create or overwrite an ABAC policy record in storage.
451    ///
452    /// # Example
453    /// ```rust,ignore
454    /// am.create_abac_policy("time-restricted", "Business hours only").await?;
455    /// ```
456    pub async fn create_abac_policy(&self, name: &str, description: &str) -> Result<()> {
457        debug!("Creating ABAC policy '{}'", name);
458        if name.is_empty() {
459            return Err(AuthError::validation("Policy name cannot be empty"));
460        }
461        if description.is_empty() {
462            return Err(AuthError::validation("Policy description cannot be empty"));
463        }
464        let policy_data = serde_json::json!({
465            "name": name,
466            "description": description,
467            "created_at": chrono::Utc::now(),
468            "rules": [],
469            "active": true
470        });
471        let key = format!("abac:policy:{}", name);
472        let policy_json = serde_json::to_vec(&policy_data)
473            .map_err(|e| AuthError::validation(format!("Failed to serialize policy: {}", e)))?;
474        self.storage.store_kv(&key, &policy_json, None).await?;
475        info!("ABAC policy '{}' created", name);
476        Ok(())
477    }
478
479    /// Store a user attribute used in ABAC policy evaluation.
480    ///
481    /// # Example
482    /// ```rust,ignore
483    /// am.map_user_attribute("user-1", "department", "engineering").await?;
484    /// ```
485    pub async fn map_user_attribute(
486        &self,
487        user_id: &str,
488        attribute: &str,
489        value: &str,
490    ) -> Result<()> {
491        debug!(
492            "Mapping attribute '{}' = '{}' for user '{}'",
493            attribute, value, user_id
494        );
495        if user_id.is_empty() || attribute.is_empty() {
496            return Err(AuthError::validation(
497                "User ID and attribute name cannot be empty",
498            ));
499        }
500        let attrs_key = format!("user:{}:attributes", user_id);
501        let mut user_attrs = if let Some(bytes) = self.storage.get_kv(&attrs_key).await? {
502            serde_json::from_slice::<std::collections::HashMap<String, String>>(&bytes)
503                .unwrap_or_default()
504        } else {
505            std::collections::HashMap::new()
506        };
507        user_attrs.insert(attribute.to_string(), value.to_string());
508        let attrs_json = serde_json::to_vec(&user_attrs)
509            .map_err(|e| AuthError::validation(format!("Failed to serialize attributes: {}", e)))?;
510        self.storage.store_kv(&attrs_key, &attrs_json, None).await?;
511        info!("Attribute '{}' mapped for user '{}'", attribute, user_id);
512        Ok(())
513    }
514
515    /// Retrieve a single user attribute.
516    ///
517    /// # Example
518    /// ```rust,ignore
519    /// let dept = am.get_user_attribute("user-1", "department").await?;
520    /// ```
521    pub async fn get_user_attribute(
522        &self,
523        user_id: &str,
524        attribute: &str,
525    ) -> Result<Option<String>> {
526        debug!("Getting attribute '{}' for user '{}'", attribute, user_id);
527        if user_id.is_empty() || attribute.is_empty() {
528            return Err(AuthError::validation(
529                "User ID and attribute name cannot be empty",
530            ));
531        }
532        let attrs_key = format!("user:{}:attributes", user_id);
533        if let Some(bytes) = self.storage.get_kv(&attrs_key).await? {
534            let user_attrs: std::collections::HashMap<String, String> =
535                serde_json::from_slice(&bytes).unwrap_or_default();
536            Ok(user_attrs.get(attribute).cloned())
537        } else {
538            Ok(None)
539        }
540    }
541
542    /// Evaluate a permission request with full ABAC context.
543    ///
544    /// # Example
545    /// ```rust,ignore
546    /// use std::collections::HashMap;
547    /// let mut ctx = HashMap::new();
548    /// ctx.insert("time_restriction".into(), "business_hours".into());
549    /// let ok = am.check_dynamic_permission("user-1", "read", "reports", ctx).await?;
550    /// ```
551    pub async fn check_dynamic_permission(
552        &self,
553        user_id: &str,
554        action: &str,
555        resource: &str,
556        context: std::collections::HashMap<String, String>,
557    ) -> Result<bool> {
558        debug!(
559            "Checking dynamic permission for user '{}': {}:{} with context: {:?}",
560            user_id, action, resource, context
561        );
562        if user_id.is_empty() || action.is_empty() || resource.is_empty() {
563            return Err(AuthError::validation(
564                "User ID, action, and resource cannot be empty",
565            ));
566        }
567        // Load user attributes for ABAC evaluation.
568        let user_attrs_key = format!("user:{}:attributes", user_id);
569        let user_attrs = if let Some(bytes) = self.storage.get_kv(&user_attrs_key).await? {
570            serde_json::from_slice::<std::collections::HashMap<String, String>>(&bytes)
571                .unwrap_or_default()
572        } else {
573            std::collections::HashMap::new()
574        };
575        // Start with RBAC check.
576        let mut permission_granted = self.check_user_permission(user_id, action, resource).await;
577        // Apply context-based rules only when the base permission is granted.
578        if permission_granted {
579            // Time-based access control.
580            if let Some(time_restriction) = context.get("time_restriction") {
581                let current_hour = chrono::Utc::now()
582                    .format("%H")
583                    .to_string()
584                    .parse::<u32>()
585                    .unwrap_or(0);
586                if time_restriction == "business_hours" && !(9..=17).contains(&current_hour) {
587                    permission_granted = false;
588                    debug!("Access denied: outside business hours");
589                }
590            }
591            // Location-based access control.
592            if let Some(required_location) = context.get("required_location")
593                && let Some(user_location) = user_attrs.get("location")
594                && user_location != required_location
595            {
596                permission_granted = false;
597                debug!(
598                    "Access denied: user location {} != required {}",
599                    user_location, required_location
600                );
601            }
602            // Clearance-level access control.
603            if let Some(required_clearance) = context.get("required_clearance")
604                && let Some(user_clearance) = user_attrs.get("clearance_level")
605            {
606                let required_level = required_clearance.parse::<u32>().unwrap_or(0);
607                let user_level = user_clearance.parse::<u32>().unwrap_or(0);
608                if user_level < required_level {
609                    permission_granted = false;
610                    debug!(
611                        "Access denied: user clearance {} < required {}",
612                        user_level, required_level
613                    );
614                }
615            }
616        }
617        debug!(
618            "Dynamic permission check result for user '{}': {}",
619            user_id, permission_granted
620        );
621        Ok(permission_granted)
622    }
623
624    /// Register a resource in the permission system.
625    ///
626    /// # Example
627    /// ```rust,ignore
628    /// am.create_resource("documents").await?;
629    /// ```
630    pub async fn create_resource(&self, resource: &str) -> Result<()> {
631        debug!("Creating resource '{}'", resource);
632        if resource.is_empty() {
633            return Err(AuthError::validation("Resource name cannot be empty"));
634        }
635        let resource_data = serde_json::json!({
636            "name": resource,
637            "created_at": chrono::Utc::now(),
638            "active": true
639        });
640        let key = format!("resource:{}", resource);
641        let resource_json = serde_json::to_vec(&resource_data)
642            .map_err(|e| AuthError::validation(format!("Failed to serialize resource: {}", e)))?;
643        self.storage.store_kv(&key, &resource_json, None).await?;
644        info!("Resource '{}' created", resource);
645        Ok(())
646    }
647
648    /// Delegate a permission from one user to another for a limited duration.
649    ///
650    /// # Example
651    /// ```rust,ignore
652    /// am.delegate_permission(
653    ///     "admin-1", "user-2", "read", "reports",
654    ///     std::time::Duration::from_secs(3600),
655    /// ).await?;
656    /// ```
657    pub async fn delegate_permission(
658        &self,
659        delegator_id: &str,
660        delegatee_id: &str,
661        action: &str,
662        resource: &str,
663        duration: std::time::Duration,
664    ) -> Result<()> {
665        debug!(
666            "Delegating permission '{}:{}' from '{}' to '{}' for {:?}",
667            action, resource, delegator_id, delegatee_id, duration
668        );
669        if delegator_id.is_empty()
670            || delegatee_id.is_empty()
671            || action.is_empty()
672            || resource.is_empty()
673        {
674            return Err(AuthError::validation(
675                "All delegation parameters cannot be empty",
676            ));
677        }
678        if !self
679            .check_user_permission(delegator_id, action, resource)
680            .await
681        {
682            return Err(AuthError::authorization(
683                "Delegator does not have the permission to delegate",
684            ));
685        }
686        let delegation_id = uuid::Uuid::new_v4().to_string();
687        let expires_secs = std::time::SystemTime::now()
688            .checked_add(duration)
689            .and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
690            .map(|d| d.as_secs())
691            .unwrap_or(0);
692        let delegation_data = serde_json::json!({
693            "id": delegation_id,
694            "delegator_id": delegator_id,
695            "delegatee_id": delegatee_id,
696            "action": action,
697            "resource": resource,
698            "created_at": chrono::Utc::now(),
699            "expires_at": expires_secs
700        });
701        let key = format!("delegation:{}", delegation_id);
702        let delegation_json = serde_json::to_vec(&delegation_data)
703            .map_err(|e| AuthError::validation(format!("Failed to serialize delegation: {}", e)))?;
704        self.storage
705            .store_kv(&key, &delegation_json, Some(duration))
706            .await?;
707        // Maintain a per-delegatee index.
708        let index_key = format!("delegations_index:{}", delegatee_id);
709        let mut ids: Vec<String> = match self.storage.get_kv(&index_key).await? {
710            Some(bytes) => serde_json::from_slice(&bytes).unwrap_or_default(),
711            None => vec![],
712        };
713        ids.push(delegation_id.clone());
714        let ids_json = serde_json::to_vec(&ids).map_err(|e| {
715            AuthError::validation(format!("Failed to serialize delegation index: {}", e))
716        })?;
717        self.storage.store_kv(&index_key, &ids_json, None).await?;
718        info!(
719            "Permission '{}:{}' delegated from '{}' to '{}' for {:?}",
720            action, resource, delegator_id, delegatee_id, duration
721        );
722        Ok(())
723    }
724
725    /// List currently active permission delegations for a user (as delegatee).
726    ///
727    /// # Example
728    /// ```rust,ignore
729    /// let delegations = am.get_active_delegations("user-2").await?;
730    /// ```
731    pub async fn get_active_delegations(&self, user_id: &str) -> Result<Vec<String>> {
732        debug!("Getting active delegations for user '{}'", user_id);
733        if user_id.is_empty() {
734            return Err(AuthError::validation("User ID cannot be empty"));
735        }
736        let index_key = format!("delegations_index:{}", user_id);
737        let delegation_ids: Vec<String> = match self.storage.get_kv(&index_key).await? {
738            Some(bytes) => serde_json::from_slice(&bytes).unwrap_or_default(),
739            None => return Ok(vec![]),
740        };
741        let now_secs = std::time::SystemTime::now()
742            .duration_since(std::time::UNIX_EPOCH)
743            .unwrap_or_default()
744            .as_secs();
745        let mut result = Vec::new();
746        let mut active_ids = Vec::new();
747        for id in &delegation_ids {
748            let key = format!("delegation:{}", id);
749            if let Some(bytes) = self.storage.get_kv(&key).await?
750                && let Ok(data) = serde_json::from_slice::<serde_json::Value>(&bytes)
751            {
752                let expires_at = data["expires_at"].as_u64().unwrap_or(0);
753                if expires_at > now_secs {
754                    let action = data["action"].as_str().unwrap_or("unknown");
755                    let resource = data["resource"].as_str().unwrap_or("unknown");
756                    let delegator = data["delegator_id"].as_str().unwrap_or("unknown");
757                    result.push(format!(
758                        "{}:{}:delegated_from:{}",
759                        action, resource, delegator
760                    ));
761                    active_ids.push(id.clone());
762                }
763            }
764        }
765        // Prune stale entries from the index.
766        if active_ids.len() != delegation_ids.len()
767            && let Ok(pruned) = serde_json::to_vec(&active_ids)
768        {
769            let _ = self.storage.store_kv(&index_key, &pruned, None).await;
770        }
771        debug!(
772            "Found {} active delegations for user '{}'",
773            result.len(),
774            user_id
775        );
776        Ok(result)
777    }
778
779    /// Assemble aggregated permission metrics.
780    ///
781    /// `active_sessions` and `permission_checks_last_hour` are provided by the
782    /// caller so that the manager stays independent from the session and audit
783    /// subsystems.
784    ///
785    /// # Example
786    /// ```rust,ignore
787    /// let metrics = am.get_permission_metrics(42, 1000).await?;
788    /// println!("total_roles: {}", metrics["total_roles"]);
789    /// ```
790    pub async fn get_permission_metrics(
791        &self,
792        active_sessions: u64,
793        permission_checks_last_hour: u64,
794    ) -> Result<std::collections::HashMap<String, u64>> {
795        let (total_roles, total_users, total_permissions) = self.get_metrics().await;
796        let mut metrics = std::collections::HashMap::new();
797        metrics.insert(
798            "total_users_with_permissions".to_string(),
799            total_users as u64,
800        );
801        metrics.insert("total_roles".to_string(), total_roles as u64);
802        metrics.insert("total_permissions".to_string(), total_permissions as u64);
803        metrics.insert("active_sessions".to_string(), active_sessions);
804        metrics.insert(
805            "permission_checks_last_hour".to_string(),
806            permission_checks_last_hour,
807        );
808        debug!("Retrieved {} permission metrics", metrics.len());
809        Ok(metrics)
810    }
811}
812
813#[cfg(test)]
814mod tests {
815    use super::*;
816    use crate::storage::MemoryStorage;
817
818    fn make_manager() -> AuthorizationManager {
819        let checker = Arc::new(RwLock::new(PermissionChecker::new()));
820        let storage: Arc<dyn AuthStorage> = Arc::new(MemoryStorage::new());
821        AuthorizationManager::new(checker, storage)
822    }
823
824    // ── create_role ─────────────────────────────────────────────────────
825
826    #[tokio::test]
827    async fn test_create_role_success() {
828        let mgr = make_manager();
829        let role = Role::new("editor");
830        assert!(mgr.create_role(role).await.is_ok());
831        let roles = mgr.list_roles().await;
832        assert!(roles.iter().any(|r| r.name == "editor"));
833    }
834
835    #[tokio::test]
836    async fn test_create_role_persists_to_storage() {
837        let mgr = make_manager();
838        mgr.create_role(Role::new("persisted_role")).await.unwrap();
839        let data = mgr
840            .storage
841            .get_kv("rbac:role:persisted_role")
842            .await
843            .unwrap();
844        assert!(data.is_some());
845        let role: Role = serde_json::from_slice(&data.unwrap()).unwrap();
846        assert_eq!(role.name, "persisted_role");
847    }
848
849    #[tokio::test]
850    async fn test_create_role_empty_name_rejected() {
851        let mgr = make_manager();
852        let role = Role::new("");
853        let err = mgr.create_role(role).await;
854        assert!(err.is_err());
855    }
856
857    #[tokio::test]
858    async fn test_create_role_duplicate_overwrites() {
859        let mgr = make_manager();
860        let mut role1 = Role::new("dup");
861        role1.description = Some("first".into());
862        mgr.create_role(role1).await.unwrap();
863
864        let mut role2 = Role::new("dup");
865        role2.description = Some("second".into());
866        mgr.create_role(role2).await.unwrap();
867
868        let fetched = mgr.get_role("dup").await.unwrap();
869        assert_eq!(fetched.description.as_deref(), Some("second"));
870    }
871
872    // ── get_role ────────────────────────────────────────────────────────
873
874    #[tokio::test]
875    async fn test_get_role_not_found() {
876        let mgr = make_manager();
877        assert!(mgr.get_role("nonexistent").await.is_err());
878    }
879
880    #[tokio::test]
881    async fn test_get_role_empty_name_rejected() {
882        let mgr = make_manager();
883        assert!(mgr.get_role("").await.is_err());
884    }
885
886    // ── assign_role ─────────────────────────────────────────────────────
887
888    #[tokio::test]
889    async fn test_assign_role_success() {
890        let mgr = make_manager();
891        mgr.create_role(Role::new("viewer")).await.unwrap();
892        mgr.assign_role("user1", "viewer").await.unwrap();
893        assert!(mgr.user_has_role("user1", "viewer").await.unwrap());
894    }
895
896    #[tokio::test]
897    async fn test_assign_role_persists_to_storage() {
898        let mgr = make_manager();
899        mgr.create_role(Role::new("writer")).await.unwrap();
900        mgr.assign_role("u2", "writer").await.unwrap();
901        let data = mgr.storage.get_kv("rbac:user_roles:u2").await.unwrap();
902        assert!(data.is_some());
903        let roles: Vec<String> = serde_json::from_slice(&data.unwrap()).unwrap();
904        assert!(roles.contains(&"writer".to_string()));
905    }
906
907    #[tokio::test]
908    async fn test_assign_role_empty_user_rejected() {
909        let mgr = make_manager();
910        mgr.create_role(Role::new("r")).await.unwrap();
911        assert!(mgr.assign_role("", "r").await.is_err());
912    }
913
914    #[tokio::test]
915    async fn test_assign_role_empty_role_rejected() {
916        let mgr = make_manager();
917        assert!(mgr.assign_role("u", "").await.is_err());
918    }
919
920    // ── remove_role ─────────────────────────────────────────────────────
921
922    #[tokio::test]
923    async fn test_remove_role_success() {
924        let mgr = make_manager();
925        mgr.create_role(Role::new("temp")).await.unwrap();
926        mgr.assign_role("u3", "temp").await.unwrap();
927        assert!(mgr.user_has_role("u3", "temp").await.unwrap());
928        mgr.remove_role("u3", "temp").await.unwrap();
929        assert!(!mgr.user_has_role("u3", "temp").await.unwrap());
930    }
931
932    #[tokio::test]
933    async fn test_remove_role_cleans_storage() {
934        let mgr = make_manager();
935        mgr.create_role(Role::new("temp")).await.unwrap();
936        mgr.assign_role("u4", "temp").await.unwrap();
937        mgr.remove_role("u4", "temp").await.unwrap();
938        let data = mgr.storage.get_kv("rbac:user_roles:u4").await.unwrap();
939        // Should be deleted when empty
940        assert!(data.is_none());
941    }
942
943    #[tokio::test]
944    async fn test_remove_role_empty_params_rejected() {
945        let mgr = make_manager();
946        assert!(mgr.remove_role("", "role").await.is_err());
947        assert!(mgr.remove_role("user", "").await.is_err());
948    }
949
950    // ── add_role_permission ─────────────────────────────────────────────
951
952    #[tokio::test]
953    async fn test_add_role_permission_success() {
954        let mgr = make_manager();
955        mgr.create_role(Role::new("admin")).await.unwrap();
956        let perm = Permission::new("write", "documents");
957        mgr.add_role_permission("admin", perm).await.unwrap();
958        let role = mgr.get_role("admin").await.unwrap();
959        assert!(
960            role.permissions
961                .iter()
962                .any(|p| p.action == "write" && p.resource == "documents")
963        );
964    }
965
966    #[tokio::test]
967    async fn test_add_role_permission_persists() {
968        let mgr = make_manager();
969        mgr.create_role(Role::new("admin2")).await.unwrap();
970        mgr.add_role_permission("admin2", Permission::new("read", "reports"))
971            .await
972            .unwrap();
973        let data = mgr
974            .storage
975            .get_kv("rbac:role:admin2")
976            .await
977            .unwrap()
978            .unwrap();
979        let role: Role = serde_json::from_slice(&data).unwrap();
980        assert!(role.permissions.iter().any(|p| p.action == "read"));
981    }
982
983    #[tokio::test]
984    async fn test_add_role_permission_nonexistent_role() {
985        let mgr = make_manager();
986        let result = mgr
987            .add_role_permission("ghost", Permission::new("x", "y"))
988            .await;
989        assert!(result.is_err());
990    }
991
992    // ── set_role_inheritance ────────────────────────────────────────────
993
994    #[tokio::test]
995    async fn test_set_role_inheritance_success() {
996        let mgr = make_manager();
997        mgr.create_role(Role::new("parent")).await.unwrap();
998        mgr.create_role(Role::new("child")).await.unwrap();
999        assert!(mgr.set_role_inheritance("child", "parent").await.is_ok());
1000    }
1001
1002    #[tokio::test]
1003    async fn test_set_role_inheritance_empty_names_rejected() {
1004        let mgr = make_manager();
1005        assert!(mgr.set_role_inheritance("", "parent").await.is_err());
1006        assert!(mgr.set_role_inheritance("child", "").await.is_err());
1007    }
1008
1009    // ── grant_permission / check_user_permission ────────────────────────
1010
1011    #[tokio::test]
1012    async fn test_grant_and_check_permission() {
1013        let mgr = make_manager();
1014        mgr.grant_permission("u5", "read", "files").await.unwrap();
1015        assert!(mgr.check_user_permission("u5", "read", "files").await);
1016    }
1017
1018    #[tokio::test]
1019    async fn test_check_permission_not_granted() {
1020        let mgr = make_manager();
1021        assert!(!mgr.check_user_permission("u6", "delete", "files").await);
1022    }
1023
1024    #[tokio::test]
1025    async fn test_revoke_permission() {
1026        let mgr = make_manager();
1027        mgr.grant_permission("u7", "write", "data").await.unwrap();
1028        assert!(mgr.check_user_permission("u7", "write", "data").await);
1029        mgr.revoke_permission("u7", "write", "data").await.unwrap();
1030        assert!(!mgr.check_user_permission("u7", "write", "data").await);
1031    }
1032
1033    #[tokio::test]
1034    async fn test_revoke_permission_empty_params_rejected() {
1035        let mgr = make_manager();
1036        assert!(mgr.revoke_permission("", "a", "r").await.is_err());
1037        assert!(mgr.revoke_permission("u", "", "r").await.is_err());
1038        assert!(mgr.revoke_permission("u", "a", "").await.is_err());
1039    }
1040
1041    // ── user_has_role ───────────────────────────────────────────────────
1042
1043    #[tokio::test]
1044    async fn test_user_has_role_false_when_not_assigned() {
1045        let mgr = make_manager();
1046        mgr.create_role(Role::new("r")).await.unwrap();
1047        assert!(!mgr.user_has_role("u", "r").await.unwrap());
1048    }
1049
1050    #[tokio::test]
1051    async fn test_user_has_role_empty_params_rejected() {
1052        let mgr = make_manager();
1053        assert!(mgr.user_has_role("", "r").await.is_err());
1054        assert!(mgr.user_has_role("u", "").await.is_err());
1055    }
1056
1057    // ── list_user_roles ─────────────────────────────────────────────────
1058
1059    #[tokio::test]
1060    async fn test_list_user_roles() {
1061        let mgr = make_manager();
1062        mgr.create_role(Role::new("a")).await.unwrap();
1063        mgr.create_role(Role::new("b")).await.unwrap();
1064        mgr.assign_role("u8", "a").await.unwrap();
1065        mgr.assign_role("u8", "b").await.unwrap();
1066        let roles = mgr.list_user_roles("u8").await.unwrap();
1067        assert!(roles.contains(&"a".to_string()));
1068        assert!(roles.contains(&"b".to_string()));
1069    }
1070
1071    #[tokio::test]
1072    async fn test_list_user_roles_empty_user_rejected() {
1073        let mgr = make_manager();
1074        assert!(mgr.list_user_roles("").await.is_err());
1075    }
1076
1077    // ── get_effective_permissions ────────────────────────────────────────
1078
1079    #[tokio::test]
1080    async fn test_get_effective_permissions() {
1081        let mgr = make_manager();
1082        mgr.grant_permission("u9", "read", "docs").await.unwrap();
1083        let perms = mgr.get_effective_permissions("u9").await.unwrap();
1084        assert!(!perms.is_empty());
1085    }
1086
1087    #[tokio::test]
1088    async fn test_get_effective_permissions_empty_user_rejected() {
1089        let mgr = make_manager();
1090        assert!(mgr.get_effective_permissions("").await.is_err());
1091    }
1092
1093    // ── load_persisted_roles ────────────────────────────────────────────
1094
1095    #[tokio::test]
1096    async fn test_load_persisted_roles_restores_roles() {
1097        let storage: Arc<dyn AuthStorage> = Arc::new(MemoryStorage::new());
1098
1099        // Create manager, add roles and assignments, then drop it
1100        {
1101            let checker = Arc::new(RwLock::new(PermissionChecker::new()));
1102            let mgr = AuthorizationManager::new(checker, storage.clone());
1103            mgr.create_role(Role::new("restored_role")).await.unwrap();
1104            mgr.assign_role("restored_user", "restored_role")
1105                .await
1106                .unwrap();
1107        }
1108
1109        // Create fresh manager with the SAME storage, load persisted data
1110        let checker2 = Arc::new(RwLock::new(PermissionChecker::new()));
1111        let mgr2 = AuthorizationManager::new(checker2, storage);
1112        mgr2.load_persisted_roles().await.unwrap();
1113
1114        assert!(mgr2.get_role("restored_role").await.is_ok());
1115        assert!(
1116            mgr2.user_has_role("restored_user", "restored_role")
1117                .await
1118                .unwrap()
1119        );
1120    }
1121
1122    #[tokio::test]
1123    async fn test_load_persisted_roles_empty_storage_ok() {
1124        let mgr = make_manager();
1125        assert!(mgr.load_persisted_roles().await.is_ok());
1126    }
1127
1128    // ── reset_runtime_state ─────────────────────────────────────────────
1129
1130    #[tokio::test]
1131    async fn test_reset_runtime_state_clears_roles() {
1132        let mgr = make_manager();
1133        mgr.create_role(Role::new("custom")).await.unwrap();
1134        mgr.assign_role("u10", "custom").await.unwrap();
1135        mgr.reset_runtime_state().await;
1136        assert!(!mgr.user_has_role("u10", "custom").await.unwrap_or(false));
1137        // Storage should also be cleaned
1138        assert!(
1139            mgr.storage
1140                .get_kv("rbac:role:custom")
1141                .await
1142                .unwrap()
1143                .is_none()
1144        );
1145        assert!(
1146            mgr.storage
1147                .get_kv("rbac:user_roles:u10")
1148                .await
1149                .unwrap()
1150                .is_none()
1151        );
1152    }
1153
1154    // ── ABAC operations ─────────────────────────────────────────────────
1155
1156    #[tokio::test]
1157    async fn test_create_abac_policy() {
1158        let mgr = make_manager();
1159        mgr.create_abac_policy("location_gate", "Restricts by location")
1160            .await
1161            .unwrap();
1162        let data = mgr
1163            .storage
1164            .get_kv("abac:policy:location_gate")
1165            .await
1166            .unwrap();
1167        assert!(data.is_some());
1168    }
1169
1170    #[tokio::test]
1171    async fn test_create_abac_policy_empty_name_rejected() {
1172        let mgr = make_manager();
1173        assert!(mgr.create_abac_policy("", "desc").await.is_err());
1174    }
1175
1176    #[tokio::test]
1177    async fn test_create_abac_policy_empty_description_rejected() {
1178        let mgr = make_manager();
1179        assert!(mgr.create_abac_policy("name", "").await.is_err());
1180    }
1181
1182    #[tokio::test]
1183    async fn test_map_and_get_user_attribute() {
1184        let mgr = make_manager();
1185        mgr.map_user_attribute("u11", "department", "engineering")
1186            .await
1187            .unwrap();
1188        let val = mgr.get_user_attribute("u11", "department").await.unwrap();
1189        assert_eq!(val.as_deref(), Some("engineering"));
1190    }
1191
1192    #[tokio::test]
1193    async fn test_get_user_attribute_nonexistent() {
1194        let mgr = make_manager();
1195        let val = mgr.get_user_attribute("nobody", "x").await.unwrap();
1196        assert!(val.is_none());
1197    }
1198
1199    #[tokio::test]
1200    async fn test_map_user_attribute_empty_params_rejected() {
1201        let mgr = make_manager();
1202        assert!(mgr.map_user_attribute("", "a", "v").await.is_err());
1203        assert!(mgr.map_user_attribute("u", "", "v").await.is_err());
1204    }
1205
1206    #[tokio::test]
1207    async fn test_get_user_attribute_empty_params_rejected() {
1208        let mgr = make_manager();
1209        assert!(mgr.get_user_attribute("", "x").await.is_err());
1210        assert!(mgr.get_user_attribute("u", "").await.is_err());
1211    }
1212
1213    // ── check_dynamic_permission ────────────────────────────────────────
1214
1215    #[tokio::test]
1216    async fn test_check_dynamic_permission_granted() {
1217        let mgr = make_manager();
1218        mgr.grant_permission("u12", "read", "reports")
1219            .await
1220            .unwrap();
1221        let ctx = std::collections::HashMap::new();
1222        assert!(
1223            mgr.check_dynamic_permission("u12", "read", "reports", ctx)
1224                .await
1225                .unwrap()
1226        );
1227    }
1228
1229    #[tokio::test]
1230    async fn test_check_dynamic_permission_denied_no_permission() {
1231        let mgr = make_manager();
1232        let ctx = std::collections::HashMap::new();
1233        assert!(
1234            !mgr.check_dynamic_permission("u13", "read", "reports", ctx)
1235                .await
1236                .unwrap()
1237        );
1238    }
1239
1240    #[tokio::test]
1241    async fn test_check_dynamic_permission_location_mismatch() {
1242        let mgr = make_manager();
1243        mgr.grant_permission("u14", "read", "files").await.unwrap();
1244        mgr.map_user_attribute("u14", "location", "US")
1245            .await
1246            .unwrap();
1247        let mut ctx = std::collections::HashMap::new();
1248        ctx.insert("required_location".to_string(), "EU".to_string());
1249        assert!(
1250            !mgr.check_dynamic_permission("u14", "read", "files", ctx)
1251                .await
1252                .unwrap()
1253        );
1254    }
1255
1256    #[tokio::test]
1257    async fn test_check_dynamic_permission_clearance_insufficient() {
1258        let mgr = make_manager();
1259        mgr.grant_permission("u15", "read", "secret").await.unwrap();
1260        mgr.map_user_attribute("u15", "clearance_level", "2")
1261            .await
1262            .unwrap();
1263        let mut ctx = std::collections::HashMap::new();
1264        ctx.insert("required_clearance".to_string(), "5".to_string());
1265        assert!(
1266            !mgr.check_dynamic_permission("u15", "read", "secret", ctx)
1267                .await
1268                .unwrap()
1269        );
1270    }
1271
1272    #[tokio::test]
1273    async fn test_check_dynamic_permission_empty_params_rejected() {
1274        let mgr = make_manager();
1275        let ctx = std::collections::HashMap::new();
1276        assert!(
1277            mgr.check_dynamic_permission("", "r", "x", ctx.clone())
1278                .await
1279                .is_err()
1280        );
1281        assert!(
1282            mgr.check_dynamic_permission("u", "", "x", ctx.clone())
1283                .await
1284                .is_err()
1285        );
1286        assert!(
1287            mgr.check_dynamic_permission("u", "r", "", ctx)
1288                .await
1289                .is_err()
1290        );
1291    }
1292
1293    // ── create_resource ─────────────────────────────────────────────────
1294
1295    #[tokio::test]
1296    async fn test_create_resource() {
1297        let mgr = make_manager();
1298        mgr.create_resource("documents").await.unwrap();
1299        let data = mgr.storage.get_kv("resource:documents").await.unwrap();
1300        assert!(data.is_some());
1301    }
1302
1303    #[tokio::test]
1304    async fn test_create_resource_empty_name_rejected() {
1305        let mgr = make_manager();
1306        assert!(mgr.create_resource("").await.is_err());
1307    }
1308
1309    // ── delegate_permission ─────────────────────────────────────────────
1310
1311    #[tokio::test]
1312    async fn test_delegate_permission_success() {
1313        let mgr = make_manager();
1314        mgr.grant_permission("delegator", "read", "files")
1315            .await
1316            .unwrap();
1317        mgr.delegate_permission(
1318            "delegator",
1319            "delegatee",
1320            "read",
1321            "files",
1322            std::time::Duration::from_secs(3600),
1323        )
1324        .await
1325        .unwrap();
1326        let delegations = mgr.get_active_delegations("delegatee").await.unwrap();
1327        assert!(!delegations.is_empty());
1328    }
1329
1330    #[tokio::test]
1331    async fn test_delegate_permission_without_holding_it() {
1332        let mgr = make_manager();
1333        let result = mgr
1334            .delegate_permission(
1335                "delegator",
1336                "delegatee",
1337                "write",
1338                "files",
1339                std::time::Duration::from_secs(3600),
1340            )
1341            .await;
1342        assert!(result.is_err());
1343    }
1344
1345    #[tokio::test]
1346    async fn test_delegate_permission_empty_params_rejected() {
1347        let mgr = make_manager();
1348        let dur = std::time::Duration::from_secs(60);
1349        assert!(
1350            mgr.delegate_permission("", "d", "a", "r", dur)
1351                .await
1352                .is_err()
1353        );
1354        assert!(
1355            mgr.delegate_permission("d", "", "a", "r", dur)
1356                .await
1357                .is_err()
1358        );
1359        assert!(
1360            mgr.delegate_permission("d", "d2", "", "r", dur)
1361                .await
1362                .is_err()
1363        );
1364        assert!(
1365            mgr.delegate_permission("d", "d2", "a", "", dur)
1366                .await
1367                .is_err()
1368        );
1369    }
1370
1371    // ── get_active_delegations ──────────────────────────────────────────
1372
1373    #[tokio::test]
1374    async fn test_get_active_delegations_empty() {
1375        let mgr = make_manager();
1376        let delegations = mgr.get_active_delegations("nobody").await.unwrap();
1377        assert!(delegations.is_empty());
1378    }
1379
1380    #[tokio::test]
1381    async fn test_get_active_delegations_empty_user_rejected() {
1382        let mgr = make_manager();
1383        assert!(mgr.get_active_delegations("").await.is_err());
1384    }
1385
1386    // ── get_permission_metrics ──────────────────────────────────────────
1387
1388    #[tokio::test]
1389    async fn test_get_permission_metrics() {
1390        let mgr = make_manager();
1391        mgr.create_default_roles().await;
1392        mgr.create_role(Role::new("custom")).await.unwrap();
1393        let metrics = mgr.get_permission_metrics(10, 100).await.unwrap();
1394        assert!(metrics.get("total_roles").copied().unwrap_or(0) > 0);
1395        assert_eq!(metrics["active_sessions"], 10);
1396        assert_eq!(metrics["permission_checks_last_hour"], 100);
1397    }
1398
1399    // ── get_metrics ─────────────────────────────────────────────────────
1400
1401    #[tokio::test]
1402    async fn test_get_metrics() {
1403        let mgr = make_manager();
1404        mgr.create_role(Role::new("m")).await.unwrap();
1405        mgr.grant_permission("u16", "a", "r").await.unwrap();
1406        let (roles, users, perms) = mgr.get_metrics().await;
1407        assert!(roles >= 1);
1408        assert!(users >= 1);
1409        assert!(perms >= 1);
1410    }
1411}