Skip to main content

auth_framework/
permissions.rs

1//! Permission and role-based access control (RBAC / ABAC).
2//!
3//! Core types for the authorization subsystem:
4//!
5//! - [`Permission`] — An `(action, resource)` pair representing a capability.
6//! - [`Role`] — A named set of permissions with optional inheritance.
7//! - [`UserPermissions`] — Resolved permissions for a single user.
8//! - [`PermissionChecker`] — Evaluates access decisions, including ABAC
9//!   policies and delegation chains.
10//!
11//! For the async, storage-backed authorization API see
12//! [`AuthorizationOperations`](crate::auth::AuthorizationOperations).
13
14/// Attribute-Based Access Control (ABAC) policy.
15///
16/// Associates a set of attribute rules with the permissions they grant.
17/// Evaluated by [`PermissionChecker::check_advanced_permission`].
18#[derive(Debug, Clone)]
19pub struct AbacPolicy {
20    pub attributes: HashMap<String, serde_json::Value>,
21    pub rules: Vec<AbacRule>,
22}
23
24#[derive(Debug, Clone)]
25pub struct AbacRule {
26    pub attribute: String,
27    pub expected_value: serde_json::Value,
28    pub permission: Permission,
29}
30
31impl AbacPolicy {
32    /// Create a new empty ABAC policy.
33    pub fn new() -> Self {
34        Self {
35            attributes: HashMap::new(),
36            rules: Vec::new(),
37        }
38    }
39
40    /// Add an attribute to the policy.
41    pub fn with_attribute(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
42        self.attributes.insert(key.into(), value);
43        self
44    }
45
46    /// Add multiple attributes to the policy.
47    pub fn with_attributes<I, K>(mut self, attributes: I) -> Self
48    where
49        I: IntoIterator<Item = (K, serde_json::Value)>,
50        K: Into<String>,
51    {
52        for (key, value) in attributes {
53            self.attributes.insert(key.into(), value);
54        }
55        self
56    }
57
58    /// Add a rule to the policy.
59    pub fn with_rule(
60        mut self,
61        attribute: impl Into<String>,
62        expected_value: serde_json::Value,
63        permission: Permission,
64    ) -> Self {
65        self.rules.push(AbacRule {
66            attribute: attribute.into(),
67            expected_value,
68            permission,
69        });
70        self
71    }
72
73    /// Add multiple rules to the policy.
74    pub fn with_rules<I>(mut self, rules: I) -> Self
75    where
76        I: IntoIterator<Item = AbacRule>,
77    {
78        self.rules.extend(rules);
79        self
80    }
81}
82
83/// Permission delegation record.
84///
85/// Represents a delegator granting a subset of their permissions to a delegatee,
86/// optionally for a limited time window.
87#[derive(Debug, Clone)]
88pub struct Delegation {
89    pub delegator: String,
90    pub delegatee: String,
91    pub permissions: HashSet<Permission>,
92    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
93}
94
95impl Delegation {
96    /// Create a new delegation with required fields.
97    pub fn new(delegator: impl Into<String>, delegatee: impl Into<String>) -> Self {
98        Self {
99            delegator: delegator.into(),
100            delegatee: delegatee.into(),
101            permissions: HashSet::new(),
102            expires_at: None,
103        }
104    }
105
106    /// Add permissions to the delegation.
107    pub fn with_permissions<I>(mut self, permissions: I) -> Self
108    where
109        I: IntoIterator<Item = Permission>,
110    {
111        self.permissions.extend(permissions);
112        self
113    }
114
115    /// Add a single permission to the delegation.
116    pub fn with_permission(mut self, permission: Permission) -> Self {
117        self.permissions.insert(permission);
118        self
119    }
120
121    /// Set the expiration time for the delegation.
122    pub fn with_expiry(mut self, expires_at: chrono::DateTime<chrono::Utc>) -> Self {
123        self.expires_at = Some(expires_at);
124        self
125    }
126
127    /// Set the delegation to never expire.
128    pub fn permanent(mut self) -> Self {
129        self.expires_at = None;
130        self
131    }
132}
133
134impl PermissionChecker {
135    /// Check permission for a user with ABAC and delegation support.
136    ///
137    /// This is the low-level API with positional parameters.  For a more
138    /// readable alternative, use [`check_advanced`](Self::check_advanced)
139    /// with an [`AdvancedPermissionCheck`] builder.
140    ///
141    /// # Arguments
142    ///
143    /// * `user_id` — the user to check
144    /// * `permission` — the required permission
145    /// * `user_attributes` — runtime attributes for ABAC evaluation
146    /// * `abac_policy` — optional ABAC policy
147    /// * `delegations` — optional active delegations
148    /// * `role_resolver` — closure that resolves role names to definitions
149    pub fn check_advanced_permission(
150        &self,
151        user_id: &str,
152        permission: &Permission,
153        user_attributes: &HashMap<String, serde_json::Value>,
154        abac_policy: Option<&AbacPolicy>,
155        delegations: Option<&[Delegation]>,
156        role_resolver: &dyn Fn(&str) -> Option<Role>,
157    ) -> bool {
158        // Check direct and role permissions
159        let has_basic = self.user_permissions.get(user_id).is_some_and(|up| {
160            let mut up = up.clone();
161            up.has_permission(permission, role_resolver)
162        });
163        if has_basic {
164            return true;
165        }
166        // Check ABAC policy
167        if let Some(policy) = abac_policy
168            && self.check_abac(user_attributes, permission, policy)
169        {
170            return true;
171        }
172        // Check delegation
173        if let Some(delegations) = delegations
174            && self.check_delegation(user_id, permission, delegations)
175        {
176            return true;
177        }
178        false
179    }
180    /// Check permission with ABAC policy
181    pub fn check_abac(
182        &self,
183        user_attributes: &HashMap<String, serde_json::Value>,
184        permission: &Permission,
185        abac_policy: &AbacPolicy,
186    ) -> bool {
187        for rule in &abac_policy.rules {
188            if let Some(attr_value) = user_attributes.get(&rule.attribute)
189                && attr_value == &rule.expected_value
190                && rule.permission.implies(permission)
191            {
192                return true;
193            }
194        }
195        false
196    }
197
198    /// Check permission with delegation
199    pub fn check_delegation(
200        &self,
201        user_id: &str,
202        permission: &Permission,
203        delegations: &[Delegation],
204    ) -> bool {
205        for delegation in delegations {
206            if delegation.delegatee == user_id
207                && delegation.permissions.iter().any(|p| p.implies(permission))
208            {
209                if let Some(expiry) = delegation.expires_at
210                    && expiry < chrono::Utc::now()
211                {
212                    continue;
213                }
214                return true;
215            }
216        }
217        false
218    }
219}
220
221/// Builder for constructing an advanced permission check with ABAC policies,
222/// delegations, and role resolution.
223///
224/// Wraps the six parameters of
225/// [`PermissionChecker::check_advanced_permission`] into a self-documenting
226/// builder so callers don't need to remember parameter order.
227///
228/// # Example
229///
230/// ```rust,ignore
231/// use auth_framework::permissions::*;
232///
233/// let granted = checker.check_advanced(
234///     AdvancedPermissionCheck::new("user_123", &permission, &role_resolver)
235///         .user_attributes(&attrs)
236///         .abac_policy(&policy)
237///         .delegations(&delegations),
238/// );
239/// ```
240pub struct AdvancedPermissionCheck<'a> {
241    user_id: &'a str,
242    permission: &'a Permission,
243    user_attributes: Option<&'a HashMap<String, serde_json::Value>>,
244    abac_policy: Option<&'a AbacPolicy>,
245    delegations: Option<&'a [Delegation]>,
246    role_resolver: &'a dyn Fn(&str) -> Option<Role>,
247}
248
249impl<'a> AdvancedPermissionCheck<'a> {
250    /// Start building an advanced permission check with the required fields.
251    pub fn new(
252        user_id: &'a str,
253        permission: &'a Permission,
254        role_resolver: &'a dyn Fn(&str) -> Option<Role>,
255    ) -> Self {
256        Self {
257            user_id,
258            permission,
259            user_attributes: None,
260            abac_policy: None,
261            delegations: None,
262            role_resolver,
263        }
264    }
265
266    /// Provide user attributes for ABAC evaluation.
267    pub fn user_attributes(mut self, attrs: &'a HashMap<String, serde_json::Value>) -> Self {
268        self.user_attributes = Some(attrs);
269        self
270    }
271
272    /// Provide an ABAC policy to evaluate against.
273    pub fn abac_policy(mut self, policy: &'a AbacPolicy) -> Self {
274        self.abac_policy = Some(policy);
275        self
276    }
277
278    /// Provide active delegations to consider.
279    pub fn delegations(mut self, delegations: &'a [Delegation]) -> Self {
280        self.delegations = Some(delegations);
281        self
282    }
283}
284
285impl PermissionChecker {
286    /// Check permission using an [`AdvancedPermissionCheck`] builder.
287    ///
288    /// This is the recommended entry point when combining RBAC, ABAC, and
289    /// delegation checks — it avoids the six positional parameters of the
290    /// legacy [`check_advanced_permission`](Self::check_advanced_permission).
291    ///
292    /// # Example
293    ///
294    /// ```rust,ignore
295    /// let allowed = checker.check_advanced(
296    ///     AdvancedPermissionCheck::new("user_1", &perm, &resolver)
297    ///         .user_attributes(&attrs)
298    ///         .abac_policy(&policy),
299    /// );
300    /// ```
301    pub fn check_advanced(&self, check: AdvancedPermissionCheck<'_>) -> bool {
302        static EMPTY: std::sync::LazyLock<HashMap<String, serde_json::Value>> =
303            std::sync::LazyLock::new(HashMap::new);
304        self.check_advanced_permission(
305            check.user_id,
306            check.permission,
307            check.user_attributes.unwrap_or(&EMPTY),
308            check.abac_policy,
309            check.delegations,
310            check.role_resolver,
311        )
312    }
313}
314/// Permission and role-based access control system.
315use crate::errors::{PermissionError, Result};
316use crate::tokens::AuthToken;
317use chrono;
318use serde::{Deserialize, Serialize};
319use serde_json;
320use std::collections::{HashMap, HashSet};
321
322/// Represents a permission with action and resource.
323#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
324pub struct Permission {
325    /// The action being performed (e.g., "read", "write", "delete")
326    pub action: String,
327
328    /// The resource being accessed (e.g., "documents", "users", "settings")
329    pub resource: String,
330
331    /// Optional resource instance (e.g., specific document ID)
332    pub instance: Option<String>,
333}
334
335/// Represents a role with associated permissions.
336#[derive(Debug, Clone, Serialize, Deserialize)]
337pub struct Role {
338    /// Optional unique identifier (populated when persisted via `AuthorizationEngine`)
339    pub id: Option<String>,
340
341    /// Role name
342    pub name: String,
343
344    /// Role description
345    pub description: Option<String>,
346
347    /// Permissions granted to this role
348    pub permissions: HashSet<Permission>,
349
350    /// Parent roles this role inherits from
351    pub parent_roles: HashSet<String>,
352
353    /// Whether this role is active
354    pub active: bool,
355
356    /// Arbitrary metadata key-value pairs
357    pub metadata: HashMap<String, String>,
358
359    /// When the role was created (populated when loaded from persistent storage)
360    pub created_at: Option<std::time::SystemTime>,
361
362    /// When the role was last modified
363    pub updated_at: Option<std::time::SystemTime>,
364}
365
366/// User permissions and roles.
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct UserPermissions {
369    /// User ID
370    pub user_id: String,
371
372    /// Direct permissions granted to the user
373    pub direct_permissions: HashSet<Permission>,
374
375    /// Roles assigned to the user
376    pub roles: HashSet<String>,
377
378    /// Cached computed permissions (includes role permissions)
379    pub computed_permissions: Option<HashSet<Permission>>,
380
381    /// When the permissions were last updated
382    pub last_updated: chrono::DateTime<chrono::Utc>,
383}
384
385/// Permission checker for validating access rights.
386#[derive(Debug, Clone)]
387pub struct PermissionChecker {
388    /// All defined roles
389    roles: HashMap<String, Role>,
390
391    /// User permissions cache
392    user_permissions: HashMap<String, UserPermissions>,
393
394    /// Permission hierarchy (for resource hierarchies)
395    resource_hierarchy: HashMap<String, Vec<String>>,
396}
397
398impl Permission {
399    /// Create a new permission.
400    pub fn new(action: impl Into<String>, resource: impl Into<String>) -> Self {
401        Self {
402            action: action.into(),
403            resource: resource.into(),
404            instance: None,
405        }
406    }
407
408    /// Create a new permission with just an action (resource defaults to "*").
409    pub fn from_action(action: impl Into<String>) -> Self {
410        Self {
411            action: action.into(),
412            resource: "*".to_string(),
413            instance: None,
414        }
415    }
416
417    /// Create a new permission with a specific instance.
418    pub fn with_instance(
419        action: impl Into<String>,
420        resource: impl Into<String>,
421        instance: impl Into<String>,
422    ) -> Self {
423        Self {
424            action: action.into(),
425            resource: resource.into(),
426            instance: Some(instance.into()),
427        }
428    }
429
430    /// Parse a permission from a string format "action:resource" or "action:resource:instance".
431    pub fn parse(permission_str: &str) -> Result<Self> {
432        let parts: Vec<&str> = permission_str.split(':').collect();
433
434        match parts.len() {
435            2 => Ok(Self::new(parts[0], parts[1])),
436            3 => Ok(Self::with_instance(parts[0], parts[1], parts[2])),
437            _ => Err(PermissionError::invalid_format(format!(
438                "Invalid permission format: {permission_str}"
439            ))
440            .into()),
441        }
442    }
443}
444
445impl std::fmt::Display for Permission {
446    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
447        match &self.instance {
448            Some(instance) => write!(f, "{}:{}:{}", self.action, self.resource, instance),
449            None => write!(f, "{}:{}", self.action, self.resource),
450        }
451    }
452}
453
454impl Permission {
455    /// Check if this permission matches another permission (considering wildcards).
456    pub fn matches(&self, other: &Permission) -> bool {
457        // Check action
458        if self.action != "*" && other.action != "*" && self.action != other.action {
459            return false;
460        }
461
462        // Check resource
463        if self.resource != "*" && other.resource != "*" && self.resource != other.resource {
464            return false;
465        }
466
467        // Check instance
468        match (&self.instance, &other.instance) {
469            (Some(self_instance), Some(other_instance)) => {
470                self_instance == "*" || other_instance == "*" || self_instance == other_instance
471            }
472            (None, None) => true,
473            (Some(_), None) => false, // Specific instance doesn't match general permission
474            (None, Some(_)) => true,  // General permission matches specific instance
475        }
476    }
477
478    /// Check if this permission implies another permission.
479    pub fn implies(&self, other: &Permission) -> bool {
480        // A permission implies another if it's more general or equal
481        let action_implies = self.action == "*" || self.action == other.action;
482        let resource_implies = self.resource == "*" || self.resource == other.resource;
483        let instance_implies = match (&self.instance, &other.instance) {
484            (None, _) => true, // General permission implies specific
485            (Some(self_instance), Some(other_instance)) => {
486                self_instance == "*" || self_instance == other_instance
487            }
488            (Some(_), None) => false, // Specific doesn't imply general
489        };
490
491        action_implies && resource_implies && instance_implies
492    }
493}
494
495impl Role {
496    /// Create a new role.
497    pub fn new(name: impl Into<String>) -> Self {
498        Self {
499            id: None,
500            name: name.into(),
501            description: None,
502            permissions: HashSet::new(),
503            parent_roles: HashSet::new(),
504            active: true,
505            metadata: HashMap::new(),
506            created_at: None,
507            updated_at: None,
508        }
509    }
510
511    /// Set the role description.
512    pub fn with_description(mut self, description: impl Into<String>) -> Self {
513        self.description = Some(description.into());
514        self
515    }
516
517    /// Add a permission to the role.
518    pub fn add_permission(&mut self, permission: Permission) {
519        self.permissions.insert(permission);
520    }
521
522    /// Add multiple permissions to the role.
523    pub fn with_permissions(mut self, permissions: Vec<Permission>) -> Self {
524        for permission in permissions {
525            self.permissions.insert(permission);
526        }
527        self
528    }
529
530    /// Add parent roles to the role.
531    pub fn with_parent_roles<I, S>(mut self, parent_roles: I) -> Self
532    where
533        I: IntoIterator<Item = S>,
534        S: Into<String>,
535    {
536        for parent_role in parent_roles {
537            self.parent_roles.insert(parent_role.into());
538        }
539        self
540    }
541
542    /// Add metadata to the role.
543    pub fn with_metadata<I, K, V>(mut self, metadata: I) -> Self
544    where
545        I: IntoIterator<Item = (K, V)>,
546        K: Into<String>,
547        V: Into<String>,
548    {
549        for (key, value) in metadata {
550            self.metadata.insert(key.into(), value.into());
551        }
552        self
553    }
554
555    /// Set the active status of the role.
556    pub fn with_active(mut self, active: bool) -> Self {
557        self.active = active;
558        self
559    }
560
561    /// Remove a permission from the role.
562    pub fn remove_permission(&mut self, permission: &Permission) {
563        self.permissions.remove(permission);
564    }
565
566    /// Add a parent role.
567    pub fn add_parent_role(&mut self, parent_role: impl Into<String>) {
568        self.parent_roles.insert(parent_role.into());
569    }
570
571    /// Check if the role has a specific permission.
572    pub fn has_permission(&self, permission: &Permission) -> bool {
573        self.permissions.iter().any(|p| p.implies(permission))
574    }
575
576    /// Get all permissions including inherited ones.
577    pub fn get_all_permissions(
578        &self,
579        role_resolver: &dyn Fn(&str) -> Option<Role>,
580    ) -> HashSet<Permission> {
581        let mut all_permissions = self.permissions.clone();
582
583        // Add permissions from parent roles
584        for parent_role_name in &self.parent_roles {
585            if let Some(parent_role) = role_resolver(parent_role_name) {
586                all_permissions.extend(parent_role.get_all_permissions(role_resolver));
587            }
588        }
589
590        all_permissions
591    }
592
593    /// Activate or deactivate the role.
594    pub fn set_active(&mut self, active: bool) {
595        self.active = active;
596    }
597}
598
599impl PermissionChecker {
600    /// Remove all runtime roles, assignments, and cached permission state.
601    pub fn clear(&mut self) {
602        self.roles.clear();
603        self.user_permissions.clear();
604        self.resource_hierarchy.clear();
605    }
606
607    /// Return the currently assigned role names for a user.
608    pub fn get_user_roles(&self, user_id: &str) -> Vec<String> {
609        self.user_permissions
610            .get(user_id)
611            .map(|permissions| permissions.roles.iter().cloned().collect())
612            .unwrap_or_default()
613    }
614}
615
616impl UserPermissions {
617    /// Create new user permissions.
618    pub fn new(user_id: impl Into<String>) -> Self {
619        Self {
620            user_id: user_id.into(),
621            direct_permissions: HashSet::new(),
622            roles: HashSet::new(),
623            computed_permissions: None,
624            last_updated: chrono::Utc::now(),
625        }
626    }
627
628    /// Add a direct permission to the user.
629    pub fn add_permission(&mut self, permission: Permission) {
630        self.direct_permissions.insert(permission);
631        self.computed_permissions = None; // Invalidate cache
632        self.last_updated = chrono::Utc::now();
633    }
634
635    /// Remove a direct permission from the user.
636    pub fn remove_permission(&mut self, permission: &Permission) {
637        self.direct_permissions.remove(permission);
638        self.computed_permissions = None; // Invalidate cache
639        self.last_updated = chrono::Utc::now();
640    }
641
642    /// Add a role to the user.
643    pub fn add_role(&mut self, role: impl Into<String>) {
644        self.roles.insert(role.into());
645        self.computed_permissions = None; // Invalidate cache
646        self.last_updated = chrono::Utc::now();
647    }
648
649    /// Remove a role from the user.
650    pub fn remove_role(&mut self, role: &str) {
651        self.roles.remove(role);
652        self.computed_permissions = None; // Invalidate cache
653        self.last_updated = chrono::Utc::now();
654    }
655
656    /// Compute all permissions for the user (including role permissions).
657    pub fn compute_permissions(
658        &mut self,
659        role_resolver: &dyn Fn(&str) -> Option<Role>,
660    ) -> &HashSet<Permission> {
661        if self.computed_permissions.is_none() {
662            let mut all_permissions = self.direct_permissions.clone();
663
664            // Add permissions from roles
665            for role_name in &self.roles {
666                if let Some(role) = role_resolver(role_name)
667                    && role.active
668                {
669                    all_permissions.extend(role.get_all_permissions(role_resolver));
670                }
671            }
672
673            self.computed_permissions = Some(all_permissions);
674        }
675
676        // SAFETY: guaranteed Some — either it was Some on entry or we just assigned it above.
677        self.computed_permissions
678            .as_ref()
679            .expect("computed_permissions is always Some after this block")
680    }
681
682    /// Check if the user has a specific permission.
683    pub fn has_permission(
684        &mut self,
685        permission: &Permission,
686        role_resolver: &dyn Fn(&str) -> Option<Role>,
687    ) -> bool {
688        let all_permissions = self.compute_permissions(role_resolver);
689        all_permissions.iter().any(|p| p.implies(permission))
690    }
691}
692
693/// Returns `true` if `broader` is a higher-privilege action that implies `required`.
694///
695/// Action hierarchy:
696/// - `"manage"` / `"admin"` imply all common CRUD + management actions
697/// - `"write"` / `"update"` imply `"read"`
698fn broader_action_implies(broader: &str, required: &str) -> bool {
699    // Wildcard is handled in Permission::implies; exact match is handled before calling this.
700    if broader == required {
701        return true;
702    }
703    match broader {
704        "manage" | "admin" => matches!(
705            required,
706            "read" | "write" | "delete" | "create" | "list" | "update" | "get"
707        ),
708        "write" | "update" => matches!(required, "read" | "get" | "list"),
709        _ => false,
710    }
711}
712
713impl PermissionChecker {
714    /// Create a new permission checker.
715    pub fn new() -> Self {
716        Self {
717            roles: HashMap::new(),
718            user_permissions: HashMap::new(),
719            resource_hierarchy: HashMap::new(),
720        }
721    }
722
723    /// Add a role definition.
724    pub fn add_role(&mut self, role: Role) {
725        self.roles.insert(role.name.clone(), role);
726    }
727
728    /// Remove a role definition.
729    pub fn remove_role(&mut self, role_name: &str) {
730        self.roles.remove(role_name);
731    }
732
733    /// Get a role by name.
734    pub fn get_role(&self, role_name: &str) -> Option<&Role> {
735        self.roles.get(role_name)
736    }
737
738    /// Return all defined roles.
739    pub fn list_roles(&self) -> Vec<Role> {
740        self.roles.values().cloned().collect()
741    }
742
743    /// Set user permissions.
744    pub fn set_user_permissions(&mut self, user_permissions: UserPermissions) {
745        self.user_permissions
746            .insert(user_permissions.user_id.clone(), user_permissions);
747    }
748
749    /// Get user permissions.
750    pub fn get_user_permissions(&self, user_id: &str) -> Option<&UserPermissions> {
751        self.user_permissions.get(user_id)
752    }
753
754    /// Get mutable user permissions.
755    pub fn get_user_permissions_mut(&mut self, user_id: &str) -> Option<&mut UserPermissions> {
756        self.user_permissions.get_mut(user_id)
757    }
758
759    /// Add a permission to a user.
760    pub fn add_user_permission(&mut self, user_id: &str, permission: Permission) {
761        let user_perms = self
762            .user_permissions
763            .entry(user_id.to_string())
764            .or_insert_with(|| UserPermissions::new(user_id));
765
766        user_perms.add_permission(permission);
767    }
768
769    /// Add a role to a user.
770    pub fn add_user_role(&mut self, user_id: &str, role: impl Into<String>) {
771        let user_perms = self
772            .user_permissions
773            .entry(user_id.to_string())
774            .or_insert_with(|| UserPermissions::new(user_id));
775
776        user_perms.add_role(role);
777    }
778
779    /// Remove a role from a user.
780    pub fn remove_user_role(&mut self, user_id: &str, role_name: &str) {
781        if let Some(user_perms) = self.user_permissions.get_mut(user_id) {
782            user_perms.remove_role(role_name);
783        }
784    }
785
786    /// Count the number of defined roles.
787    pub fn role_count(&self) -> usize {
788        self.roles.len()
789    }
790
791    /// Count the number of users that have explicit permissions recorded.
792    pub fn user_count(&self) -> usize {
793        self.user_permissions.len()
794    }
795
796    /// Count the total number of direct permissions across all users.
797    pub fn total_direct_permission_count(&self) -> usize {
798        self.user_permissions
799            .values()
800            .map(|up| up.direct_permissions.len())
801            .sum()
802    }
803
804    /// Check if a user has a specific permission.
805    pub fn check_permission(&mut self, user_id: &str, permission: &Permission) -> Result<bool> {
806        let user_perms = self.user_permissions.get_mut(user_id).ok_or_else(|| {
807            PermissionError::access_denied(permission.to_string(), "unknown user".to_string())
808        })?;
809
810        let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
811
812        Ok(user_perms.has_permission(permission, &role_resolver))
813    }
814
815    /// Check if a user has permission for a specific action on a resource.
816    pub fn check_access(&mut self, user_id: &str, action: &str, resource: &str) -> Result<bool> {
817        let permission = Permission::new(action, resource);
818
819        // First check direct permission
820        if self.check_permission(user_id, &permission)? {
821            return Ok(true);
822        }
823
824        // Check hierarchical permissions if direct permission not found
825        self.check_hierarchical_permission(user_id, action, resource)
826    }
827
828    /// Check if a user has permission for a specific action on a resource instance.
829    pub fn check_instance_access(
830        &mut self,
831        user_id: &str,
832        action: &str,
833        resource: &str,
834        instance: &str,
835    ) -> Result<bool> {
836        let permission = Permission::with_instance(action, resource, instance);
837        self.check_permission(user_id, &permission)
838    }
839
840    /// Check permission from an auth token.
841    pub fn check_token_permission(
842        &mut self,
843        token: &AuthToken,
844        permission: &Permission,
845    ) -> Result<bool> {
846        if !token.is_valid() {
847            return Ok(false);
848        }
849
850        // Check if the token has the required scope
851        let required_scope = permission.to_string();
852        if !token.has_scope(&required_scope) {
853            // Also check for wildcard scopes
854            let wildcard_action = format!("*:{}", permission.resource);
855            let wildcard_resource = format!("{}:*", permission.action);
856            let wildcard_all = "*:*".to_string();
857
858            if !token.has_scope(&wildcard_action)
859                && !token.has_scope(&wildcard_resource)
860                && !token.has_scope(&wildcard_all)
861            {
862                return Ok(false);
863            }
864        }
865
866        // Check user permissions
867        self.check_permission(&token.user_id, permission)
868    }
869
870    /// Add resource hierarchy relationship
871    pub fn add_resource_hierarchy(&mut self, parent: String, children: Vec<String>) {
872        self.resource_hierarchy.insert(parent, children);
873    }
874
875    /// Get child resources for a parent resource
876    pub fn get_child_resources(&self, parent: &str) -> Option<&Vec<String>> {
877        self.resource_hierarchy.get(parent)
878    }
879
880    /// Check hierarchical permission - if user has permission on parent, they have it on children
881    pub fn check_hierarchical_permission(
882        &mut self,
883        user_id: &str,
884        action: &str,
885        resource: &str,
886    ) -> Result<bool> {
887        // Clone the hierarchy to avoid borrow checker issues
888        let hierarchy = self.resource_hierarchy.clone();
889
890        // Check all ancestor resources recursively
891        if self.has_ancestor_permission(&hierarchy, user_id, action, resource)? {
892            return Ok(true);
893        }
894
895        // Check for wildcard permissions that might apply
896        if self.check_wildcard_permissions(&hierarchy, user_id, action, resource)? {
897            return Ok(true);
898        }
899
900        Ok(false)
901    }
902
903    /// Check wildcard permissions that might apply to this resource
904    fn check_wildcard_permissions(
905        &mut self,
906        hierarchy: &HashMap<String, Vec<String>>,
907        user_id: &str,
908        action: &str,
909        resource: &str,
910    ) -> Result<bool> {
911        // Check if any parent resource has a wildcard permission that applies
912        for (parent_resource, children) in hierarchy {
913            if children.contains(&resource.to_string()) {
914                // Check for wildcard permission on parent (e.g., "projects.*" covering "documents")
915                let wildcard_permission = Permission::new(action, format!("{}.*", parent_resource));
916                if self
917                    .check_permission(user_id, &wildcard_permission)
918                    .unwrap_or(false)
919                {
920                    return Ok(true);
921                }
922            }
923        }
924
925        // Also check direct wildcard on resource itself
926        if let Some(_children) = hierarchy.get(resource) {
927            let wildcard_permission = Permission::new(action, format!("{}.*", resource));
928            if self
929                .check_permission(user_id, &wildcard_permission)
930                .unwrap_or(false)
931            {
932                return Ok(true);
933            }
934        }
935
936        Ok(false)
937    }
938
939    /// Recursively check if user has permission on any ancestor resource
940    fn has_ancestor_permission(
941        &mut self,
942        hierarchy: &HashMap<String, Vec<String>>,
943        user_id: &str,
944        action: &str,
945        resource: &str,
946    ) -> Result<bool> {
947        // Find direct parent resources
948        for (parent_resource, children) in hierarchy {
949            if children.contains(&resource.to_string()) {
950                // Check if user has the exact permission on the parent resource, OR any
951                // higher-privilege action that implies the required action (e.g. "manage"
952                // implies "read" and "write").
953                if self.check_permission_with_action_hierarchy(user_id, action, parent_resource)? {
954                    return Ok(true);
955                }
956
957                // Recursively check if user has permission on ancestor of this parent
958                if self.has_ancestor_permission(hierarchy, user_id, action, parent_resource)? {
959                    return Ok(true);
960                }
961            }
962        }
963
964        Ok(false)
965    }
966
967    /// Check if a user has permission for `action` on `resource`, considering action hierarchy.
968    ///
969    /// In addition to an exact match, higher-privilege actions imply less-privileged ones:
970    /// - `"manage"` / `"admin"` imply `"read"`, `"write"`, `"delete"`, `"create"`, `"list"`, `"update"`
971    /// - `"write"` / `"update"` imply `"read"`
972    fn check_permission_with_action_hierarchy(
973        &mut self,
974        user_id: &str,
975        required_action: &str,
976        resource: &str,
977    ) -> Result<bool> {
978        // First try exact match via the standard path
979        let exact = Permission::new(required_action, resource);
980        if self.check_permission(user_id, &exact).unwrap_or(false) {
981            return Ok(true);
982        }
983
984        // Walk user's permissions and check action hierarchy
985        let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
986        let user_perms = match self.user_permissions.get_mut(user_id) {
987            Some(p) => p,
988            None => return Ok(false),
989        };
990        let all_perms = user_perms.compute_permissions(&role_resolver);
991
992        Ok(all_perms.iter().any(|p| {
993            let resource_match = p.resource == "*" || p.resource == resource;
994            let action_match =
995                p.action == "*" || broader_action_implies(&p.action, required_action);
996            resource_match && action_match
997        }))
998    }
999
1000    /// Create some default roles for common use cases.
1001    pub fn create_default_roles(&mut self) {
1002        // Admin role with all permissions
1003        let mut admin_role = Role::new("admin").with_description("Administrator with full access");
1004        admin_role.add_permission(Permission::new("*", "*"));
1005        self.add_role(admin_role);
1006
1007        // User role with basic permissions
1008        let mut user_role = Role::new("user").with_description("Regular user with basic access");
1009        user_role.add_permission(Permission::new("read", "profile"));
1010        user_role.add_permission(Permission::new("write", "profile"));
1011        user_role.add_permission(Permission::new("read", "public"));
1012        self.add_role(user_role);
1013
1014        // Guest role with read-only access
1015        let mut guest_role =
1016            Role::new("guest").with_description("Guest user with read-only access");
1017        guest_role.add_permission(Permission::new("read", "public"));
1018        self.add_role(guest_role);
1019    }
1020
1021    /// Load permissions from a configuration or database.
1022    pub fn load_permissions(&mut self, _config: &str) -> Result<()> {
1023        // This would typically load from a configuration file or database
1024        // For now, we'll create some default permissions
1025        self.create_default_roles();
1026        Ok(())
1027    }
1028
1029    /// Assign a role to a user.
1030    pub fn assign_role_to_user(&mut self, user_id: &str, role_name: &str) -> Result<()> {
1031        // Validate that role exists
1032        if !self.roles.contains_key(role_name) {
1033            return Err(PermissionError::access_denied(
1034                role_name.to_string(),
1035                "Role does not exist".to_string(),
1036            )
1037            .into());
1038        }
1039
1040        // Add role to user
1041        self.add_user_role(user_id, role_name);
1042        Ok(())
1043    }
1044
1045    /// Set role inheritance relationship.
1046    pub fn set_role_inheritance(&mut self, child_role: &str, parent_role: &str) -> Result<()> {
1047        // Validate that both roles exist
1048        if !self.roles.contains_key(child_role) {
1049            return Err(PermissionError::access_denied(
1050                child_role.to_string(),
1051                "Child role does not exist".to_string(),
1052            )
1053            .into());
1054        }
1055        if !self.roles.contains_key(parent_role) {
1056            return Err(PermissionError::access_denied(
1057                parent_role.to_string(),
1058                "Parent role does not exist".to_string(),
1059            )
1060            .into());
1061        }
1062
1063        // Update role inheritance
1064        if let Some(child) = self.roles.get_mut(child_role) {
1065            child.add_parent_role(parent_role);
1066        }
1067
1068        Ok(())
1069    }
1070
1071    /// Remove a permission from a user.
1072    pub fn remove_user_permission(&mut self, user_id: &str, permission: &Permission) {
1073        if let Some(user_perms) = self.user_permissions.get_mut(user_id) {
1074            user_perms.remove_permission(permission);
1075        }
1076    }
1077
1078    /// Check if a user has a specific role.
1079    pub fn user_has_role(&self, user_id: &str, role_name: &str) -> bool {
1080        if let Some(user_perms) = self.user_permissions.get(user_id) {
1081            user_perms.roles.contains(role_name)
1082        } else {
1083            false
1084        }
1085    }
1086
1087    /// Get effective permissions for a user (including role-based permissions).
1088    pub fn get_effective_permissions(&self, user_id: &str) -> Vec<String> {
1089        if let Some(user_perms) = self.user_permissions.get(user_id) {
1090            let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
1091
1092            // Create a mutable clone to compute permissions
1093            let mut user_perms_clone = user_perms.clone();
1094            let all_permissions = user_perms_clone.compute_permissions(&role_resolver);
1095
1096            all_permissions.iter().map(|p| p.to_string()).collect()
1097        } else {
1098            Vec::new()
1099        }
1100    }
1101}
1102
1103impl Default for PermissionChecker {
1104    fn default() -> Self {
1105        Self::new()
1106    }
1107}
1108
1109#[cfg(test)]
1110mod tests {
1111    use super::*;
1112
1113    #[test]
1114    fn test_permission_parsing() {
1115        let perm = Permission::parse("read:documents").unwrap();
1116        assert_eq!(perm.action, "read");
1117        assert_eq!(perm.resource, "documents");
1118        assert_eq!(perm.instance, None);
1119
1120        let perm = Permission::parse("write:documents:123").unwrap();
1121        assert_eq!(perm.action, "write");
1122        assert_eq!(perm.resource, "documents");
1123        assert_eq!(perm.instance, Some("123".to_string()));
1124    }
1125
1126    #[test]
1127    fn test_permission_matching() {
1128        let perm1 = Permission::new("read", "documents");
1129        let perm2 = Permission::new("read", "documents");
1130        let perm3 = Permission::new("write", "documents");
1131        let wildcard = Permission::new("*", "documents");
1132
1133        assert!(perm1.matches(&perm2));
1134        assert!(!perm1.matches(&perm3));
1135        assert!(wildcard.matches(&perm1));
1136        assert!(wildcard.matches(&perm3));
1137    }
1138
1139    #[test]
1140    fn test_permission_implies() {
1141        let general = Permission::new("read", "documents");
1142        let specific = Permission::with_instance("read", "documents", "123");
1143        let wildcard = Permission::new("*", "*");
1144
1145        assert!(general.implies(&specific));
1146        assert!(!specific.implies(&general));
1147        assert!(wildcard.implies(&general));
1148        assert!(wildcard.implies(&specific));
1149    }
1150
1151    #[test]
1152    fn test_role_permissions() {
1153        let mut role = Role::new("editor");
1154        role.add_permission(Permission::new("read", "documents"));
1155        role.add_permission(Permission::new("write", "documents"));
1156
1157        let read_perm = Permission::new("read", "documents");
1158        let delete_perm = Permission::new("delete", "documents");
1159
1160        assert!(role.has_permission(&read_perm));
1161        assert!(!role.has_permission(&delete_perm));
1162    }
1163
1164    #[test]
1165    fn test_user_permissions() {
1166        let mut user_perms = UserPermissions::new("user123");
1167        user_perms.add_permission(Permission::new("read", "profile"));
1168        user_perms.add_role("user");
1169
1170        let role_resolver = |_: &str| Some(Role::new("user"));
1171
1172        let read_perm = Permission::new("read", "profile");
1173        assert!(user_perms.has_permission(&read_perm, &role_resolver));
1174    }
1175
1176    #[test]
1177    fn test_permission_checker() {
1178        let mut checker = PermissionChecker::new();
1179        checker.create_default_roles();
1180
1181        checker.add_user_role("user123", "admin");
1182
1183        let result = checker
1184            .check_access("user123", "read", "documents")
1185            .unwrap();
1186        assert!(result);
1187
1188        let result = checker.check_access("user123", "delete", "system").unwrap();
1189        assert!(result); // Admin has all permissions
1190    }
1191}
1192
1193#[cfg(test)]
1194pub mod abac_delegation_tests;