auth_framework/
permissions.rs

1//! Permission and role-based access control system.
2
3use crate::errors::{PermissionError, Result};
4use crate::tokens::AuthToken;
5use serde::{Deserialize, Serialize};
6use std::collections::{HashMap, HashSet};
7
8/// Represents a permission with action and resource.
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct Permission {
11    /// The action being performed (e.g., "read", "write", "delete")
12    pub action: String,
13    
14    /// The resource being accessed (e.g., "documents", "users", "settings")
15    pub resource: String,
16    
17    /// Optional resource instance (e.g., specific document ID)
18    pub instance: Option<String>,
19}
20
21/// Represents a role with associated permissions.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Role {
24    /// Role name
25    pub name: String,
26    
27    /// Role description
28    pub description: Option<String>,
29    
30    /// Permissions granted to this role
31    pub permissions: HashSet<Permission>,
32    
33    /// Parent roles this role inherits from
34    pub parent_roles: HashSet<String>,
35    
36    /// Whether this role is active
37    pub active: bool,
38}
39
40/// User permissions and roles.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct UserPermissions {
43    /// User ID
44    pub user_id: String,
45    
46    /// Direct permissions granted to the user
47    pub direct_permissions: HashSet<Permission>,
48    
49    /// Roles assigned to the user
50    pub roles: HashSet<String>,
51    
52    /// Cached computed permissions (includes role permissions)
53    pub computed_permissions: Option<HashSet<Permission>>,
54    
55    /// When the permissions were last updated
56    pub last_updated: chrono::DateTime<chrono::Utc>,
57}
58
59/// Permission checker for validating access rights.
60#[derive(Debug, Clone)]
61pub struct PermissionChecker {
62    /// All defined roles
63    roles: HashMap<String, Role>,
64    
65    /// User permissions cache
66    user_permissions: HashMap<String, UserPermissions>,
67    
68    /// Permission hierarchy (for resource hierarchies)
69    #[allow(dead_code)]
70    resource_hierarchy: HashMap<String, Vec<String>>,
71}
72
73impl Permission {
74    /// Create a new permission.
75    pub fn new(action: impl Into<String>, resource: impl Into<String>) -> Self {
76        Self {
77            action: action.into(),
78            resource: resource.into(),
79            instance: None,
80        }
81    }
82
83    /// Create a new permission with just an action (resource defaults to "*").
84    pub fn from_action(action: impl Into<String>) -> Self {
85        Self {
86            action: action.into(),
87            resource: "*".to_string(),
88            instance: None,
89        }
90    }
91
92    /// Create a new permission with a specific instance.
93    pub fn with_instance(
94        action: impl Into<String>,
95        resource: impl Into<String>,
96        instance: impl Into<String>,
97    ) -> Self {
98        Self {
99            action: action.into(),
100            resource: resource.into(),
101            instance: Some(instance.into()),
102        }
103    }
104
105    /// Parse a permission from a string format "action:resource" or "action:resource:instance".
106    pub fn parse(permission_str: &str) -> Result<Self> {
107        let parts: Vec<&str> = permission_str.split(':').collect();
108        
109        match parts.len() {
110            2 => Ok(Self::new(parts[0], parts[1])),
111            3 => Ok(Self::with_instance(parts[0], parts[1], parts[2])),
112            _ => Err(PermissionError::invalid_format(
113                format!("Invalid permission format: {permission_str}")
114            ).into()),
115        }
116    }
117
118}
119
120impl std::fmt::Display for Permission {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        match &self.instance {
123            Some(instance) => write!(f, "{}:{}:{}", self.action, self.resource, instance),
124            None => write!(f, "{}:{}", self.action, self.resource),
125        }
126    }
127}
128
129impl Permission {
130    /// Check if this permission matches another permission (considering wildcards).
131    pub fn matches(&self, other: &Permission) -> bool {
132        // Check action
133        if self.action != "*" && other.action != "*" && self.action != other.action {
134            return false;
135        }
136
137        // Check resource
138        if self.resource != "*" && other.resource != "*" && self.resource != other.resource {
139            return false;
140        }
141
142        // Check instance
143        match (&self.instance, &other.instance) {
144            (Some(self_instance), Some(other_instance)) => {
145                self_instance == "*" || other_instance == "*" || self_instance == other_instance
146            }
147            (None, None) => true,
148            (Some(_), None) => false, // Specific instance doesn't match general permission
149            (None, Some(_)) => true,  // General permission matches specific instance
150        }
151    }
152
153    /// Check if this permission implies another permission.
154    pub fn implies(&self, other: &Permission) -> bool {
155        // A permission implies another if it's more general or equal
156        let action_implies = self.action == "*" || self.action == other.action;
157        let resource_implies = self.resource == "*" || self.resource == other.resource;
158        let instance_implies = match (&self.instance, &other.instance) {
159            (None, _) => true,          // General permission implies specific
160            (Some(self_instance), Some(other_instance)) => {
161                self_instance == "*" || self_instance == other_instance
162            }
163            (Some(_), None) => false,   // Specific doesn't imply general
164        };
165
166        action_implies && resource_implies && instance_implies
167    }
168}
169
170impl Role {
171    /// Create a new role.
172    pub fn new(name: impl Into<String>) -> Self {
173        Self {
174            name: name.into(),
175            description: None,
176            permissions: HashSet::new(),
177            parent_roles: HashSet::new(),
178            active: true,
179        }
180    }
181
182    /// Set the role description.
183    pub fn with_description(mut self, description: impl Into<String>) -> Self {
184        self.description = Some(description.into());
185        self
186    }
187
188    /// Add a permission to the role.
189    pub fn add_permission(&mut self, permission: Permission) {
190        self.permissions.insert(permission);
191    }
192
193    /// Add multiple permissions to the role.
194    pub fn with_permissions(mut self, permissions: Vec<Permission>) -> Self {
195        for permission in permissions {
196            self.permissions.insert(permission);
197        }
198        self
199    }
200
201    /// Remove a permission from the role.
202    pub fn remove_permission(&mut self, permission: &Permission) {
203        self.permissions.remove(permission);
204    }
205
206    /// Add a parent role.
207    pub fn add_parent_role(&mut self, parent_role: impl Into<String>) {
208        self.parent_roles.insert(parent_role.into());
209    }
210
211    /// Check if the role has a specific permission.
212    pub fn has_permission(&self, permission: &Permission) -> bool {
213        self.permissions.iter().any(|p| p.implies(permission))
214    }
215
216    /// Get all permissions including inherited ones.
217    pub fn get_all_permissions(&self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> HashSet<Permission> {
218        let mut all_permissions = self.permissions.clone();
219        
220        // Add permissions from parent roles
221        for parent_role_name in &self.parent_roles {
222            if let Some(parent_role) = role_resolver(parent_role_name) {
223                all_permissions.extend(parent_role.get_all_permissions(role_resolver));
224            }
225        }
226        
227        all_permissions
228    }
229
230    /// Activate or deactivate the role.
231    pub fn set_active(&mut self, active: bool) {
232        self.active = active;
233    }
234}
235
236impl UserPermissions {
237    /// Create new user permissions.
238    pub fn new(user_id: impl Into<String>) -> Self {
239        Self {
240            user_id: user_id.into(),
241            direct_permissions: HashSet::new(),
242            roles: HashSet::new(),
243            computed_permissions: None,
244            last_updated: chrono::Utc::now(),
245        }
246    }
247
248    /// Add a direct permission to the user.
249    pub fn add_permission(&mut self, permission: Permission) {
250        self.direct_permissions.insert(permission);
251        self.computed_permissions = None; // Invalidate cache
252        self.last_updated = chrono::Utc::now();
253    }
254
255    /// Remove a direct permission from the user.
256    pub fn remove_permission(&mut self, permission: &Permission) {
257        self.direct_permissions.remove(permission);
258        self.computed_permissions = None; // Invalidate cache
259        self.last_updated = chrono::Utc::now();
260    }
261
262    /// Add a role to the user.
263    pub fn add_role(&mut self, role: impl Into<String>) {
264        self.roles.insert(role.into());
265        self.computed_permissions = None; // Invalidate cache
266        self.last_updated = chrono::Utc::now();
267    }
268
269    /// Remove a role from the user.
270    pub fn remove_role(&mut self, role: &str) {
271        self.roles.remove(role);
272        self.computed_permissions = None; // Invalidate cache
273        self.last_updated = chrono::Utc::now();
274    }
275
276    /// Compute all permissions for the user (including role permissions).
277    pub fn compute_permissions(&mut self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> &HashSet<Permission> {
278        if self.computed_permissions.is_none() {
279            let mut all_permissions = self.direct_permissions.clone();
280            
281            // Add permissions from roles
282            for role_name in &self.roles {
283                if let Some(role) = role_resolver(role_name) {
284                    if role.active {
285                        all_permissions.extend(role.get_all_permissions(role_resolver));
286                    }
287                }
288            }
289            
290            self.computed_permissions = Some(all_permissions);
291        }
292        
293        self.computed_permissions.as_ref().unwrap()
294    }
295
296    /// Check if the user has a specific permission.
297    pub fn has_permission(&mut self, permission: &Permission, role_resolver: &dyn Fn(&str) -> Option<Role>) -> bool {
298        let all_permissions = self.compute_permissions(role_resolver);
299        all_permissions.iter().any(|p| p.implies(permission))
300    }
301}
302
303impl PermissionChecker {
304    /// Create a new permission checker.
305    pub fn new() -> Self {
306        Self {
307            roles: HashMap::new(),
308            user_permissions: HashMap::new(),
309            resource_hierarchy: HashMap::new(),
310        }
311    }
312
313    /// Add a role definition.
314    pub fn add_role(&mut self, role: Role) {
315        self.roles.insert(role.name.clone(), role);
316    }
317
318    /// Remove a role definition.
319    pub fn remove_role(&mut self, role_name: &str) {
320        self.roles.remove(role_name);
321    }
322
323    /// Get a role by name.
324    pub fn get_role(&self, role_name: &str) -> Option<&Role> {
325        self.roles.get(role_name)
326    }
327
328    /// Set user permissions.
329    pub fn set_user_permissions(&mut self, user_permissions: UserPermissions) {
330        self.user_permissions.insert(user_permissions.user_id.clone(), user_permissions);
331    }
332
333    /// Get user permissions.
334    pub fn get_user_permissions(&self, user_id: &str) -> Option<&UserPermissions> {
335        self.user_permissions.get(user_id)
336    }
337
338    /// Get mutable user permissions.
339    pub fn get_user_permissions_mut(&mut self, user_id: &str) -> Option<&mut UserPermissions> {
340        self.user_permissions.get_mut(user_id)
341    }
342
343    /// Add a permission to a user.
344    pub fn add_user_permission(&mut self, user_id: &str, permission: Permission) {
345        let user_perms = self.user_permissions
346            .entry(user_id.to_string())
347            .or_insert_with(|| UserPermissions::new(user_id));
348        
349        user_perms.add_permission(permission);
350    }
351
352    /// Add a role to a user.
353    pub fn add_user_role(&mut self, user_id: &str, role: impl Into<String>) {
354        let user_perms = self.user_permissions
355            .entry(user_id.to_string())
356            .or_insert_with(|| UserPermissions::new(user_id));
357        
358        user_perms.add_role(role);
359    }
360
361    /// Check if a user has a specific permission.
362    pub fn check_permission(&mut self, user_id: &str, permission: &Permission) -> Result<bool> {
363        let user_perms = self.user_permissions
364            .get_mut(user_id)
365            .ok_or_else(|| PermissionError::access_denied(
366                permission.to_string(),
367                "unknown user".to_string(),
368            ))?;
369
370        let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
371        
372        Ok(user_perms.has_permission(permission, &role_resolver))
373    }
374
375    /// Check if a user has permission for a specific action on a resource.
376    pub fn check_access(&mut self, user_id: &str, action: &str, resource: &str) -> Result<bool> {
377        let permission = Permission::new(action, resource);
378        self.check_permission(user_id, &permission)
379    }
380
381    /// Check if a user has permission for a specific action on a resource instance.
382    pub fn check_instance_access(
383        &mut self,
384        user_id: &str,
385        action: &str,
386        resource: &str,
387        instance: &str,
388    ) -> Result<bool> {
389        let permission = Permission::with_instance(action, resource, instance);
390        self.check_permission(user_id, &permission)
391    }
392
393    /// Check permission from an auth token.
394    pub fn check_token_permission(&mut self, token: &AuthToken, permission: &Permission) -> Result<bool> {
395        if !token.is_valid() {
396            return Ok(false);
397        }
398
399        // Check if the token has the required scope
400        let required_scope = permission.to_string();
401        if !token.has_scope(&required_scope) {
402            // Also check for wildcard scopes
403            let wildcard_action = format!("*:{}", permission.resource);
404            let wildcard_resource = format!("{}:*", permission.action);
405            let wildcard_all = "*:*".to_string();
406            
407            if !token.has_scope(&wildcard_action) 
408                && !token.has_scope(&wildcard_resource) 
409                && !token.has_scope(&wildcard_all) {
410                return Ok(false);
411            }
412        }
413
414        // Check user permissions
415        self.check_permission(&token.user_id, permission)
416    }
417
418    /// Create some default roles for common use cases.
419    pub fn create_default_roles(&mut self) {
420        // Admin role with all permissions
421        let mut admin_role = Role::new("admin")
422            .with_description("Administrator with full access");
423        admin_role.add_permission(Permission::new("*", "*"));
424        self.add_role(admin_role);
425
426        // User role with basic permissions
427        let mut user_role = Role::new("user")
428            .with_description("Regular user with basic access");
429        user_role.add_permission(Permission::new("read", "profile"));
430        user_role.add_permission(Permission::new("write", "profile"));
431        user_role.add_permission(Permission::new("read", "public"));
432        self.add_role(user_role);
433
434        // Guest role with read-only access
435        let mut guest_role = Role::new("guest")
436            .with_description("Guest user with read-only access");
437        guest_role.add_permission(Permission::new("read", "public"));
438        self.add_role(guest_role);
439    }
440
441    /// Load permissions from a configuration or database.
442    pub fn load_permissions(&mut self, _config: &str) -> Result<()> {
443        // This would typically load from a configuration file or database
444        // For now, we'll create some default permissions
445        self.create_default_roles();
446        Ok(())
447    }
448}
449
450impl Default for PermissionChecker {
451    fn default() -> Self {
452        Self::new()
453    }
454}
455
456#[cfg(test)]
457mod tests {
458    use super::*;
459
460    #[test]
461    fn test_permission_parsing() {
462        let perm = Permission::parse("read:documents").unwrap();
463        assert_eq!(perm.action, "read");
464        assert_eq!(perm.resource, "documents");
465        assert_eq!(perm.instance, None);
466
467        let perm = Permission::parse("write:documents:123").unwrap();
468        assert_eq!(perm.action, "write");
469        assert_eq!(perm.resource, "documents");
470        assert_eq!(perm.instance, Some("123".to_string()));
471    }
472
473    #[test]
474    fn test_permission_matching() {
475        let perm1 = Permission::new("read", "documents");
476        let perm2 = Permission::new("read", "documents");
477        let perm3 = Permission::new("write", "documents");
478        let wildcard = Permission::new("*", "documents");
479
480        assert!(perm1.matches(&perm2));
481        assert!(!perm1.matches(&perm3));
482        assert!(wildcard.matches(&perm1));
483        assert!(wildcard.matches(&perm3));
484    }
485
486    #[test]
487    fn test_permission_implies() {
488        let general = Permission::new("read", "documents");
489        let specific = Permission::with_instance("read", "documents", "123");
490        let wildcard = Permission::new("*", "*");
491
492        assert!(general.implies(&specific));
493        assert!(!specific.implies(&general));
494        assert!(wildcard.implies(&general));
495        assert!(wildcard.implies(&specific));
496    }
497
498    #[test]
499    fn test_role_permissions() {
500        let mut role = Role::new("editor");
501        role.add_permission(Permission::new("read", "documents"));
502        role.add_permission(Permission::new("write", "documents"));
503
504        let read_perm = Permission::new("read", "documents");
505        let delete_perm = Permission::new("delete", "documents");
506
507        assert!(role.has_permission(&read_perm));
508        assert!(!role.has_permission(&delete_perm));
509    }
510
511    #[test]
512    fn test_user_permissions() {
513        let mut user_perms = UserPermissions::new("user123");
514        user_perms.add_permission(Permission::new("read", "profile"));
515        user_perms.add_role("user");
516
517        let role_resolver = |_: &str| Some(Role::new("user"));
518        
519        let read_perm = Permission::new("read", "profile");
520        assert!(user_perms.has_permission(&read_perm, &role_resolver));
521    }
522
523    #[test]
524    fn test_permission_checker() {
525        let mut checker = PermissionChecker::new();
526        checker.create_default_roles();
527
528        checker.add_user_role("user123", "admin");
529        
530        let result = checker.check_access("user123", "read", "documents").unwrap();
531        assert!(result);
532
533        let result = checker.check_access("user123", "delete", "system").unwrap();
534        assert!(result); // Admin has all permissions
535    }
536}