1#[derive(Debug, Clone)]
3pub struct AbacPolicy {
4 pub attributes: HashMap<String, serde_json::Value>,
5 pub rules: Vec<AbacRule>,
6}
7
8#[derive(Debug, Clone)]
9pub struct AbacRule {
10 pub attribute: String,
11 pub expected_value: serde_json::Value,
12 pub permission: Permission,
13}
14
15#[derive(Debug, Clone)]
17pub struct Delegation {
18 pub delegator: String,
19 pub delegatee: String,
20 pub permissions: HashSet<Permission>,
21 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
22}
23
24impl PermissionChecker {
25 pub fn check_advanced_permission(
27 &self,
28 user_id: &str,
29 permission: &Permission,
30 user_attributes: &HashMap<String, serde_json::Value>,
31 abac_policy: Option<&AbacPolicy>,
32 delegations: Option<&[Delegation]>,
33 role_resolver: &dyn Fn(&str) -> Option<Role>,
34 ) -> bool {
35 let has_basic = self.user_permissions.get(user_id).is_some_and(|up| {
37 let mut up = up.clone();
38 up.has_permission(permission, role_resolver)
39 });
40 if has_basic {
41 return true;
42 }
43 if let Some(policy) = abac_policy
45 && self.check_abac(user_attributes, permission, policy)
46 {
47 return true;
48 }
49 if let Some(delegations) = delegations
51 && self.check_delegation(user_id, permission, delegations)
52 {
53 return true;
54 }
55 false
56 }
57 pub fn check_abac(
59 &self,
60 user_attributes: &HashMap<String, serde_json::Value>,
61 permission: &Permission,
62 abac_policy: &AbacPolicy,
63 ) -> bool {
64 for rule in &abac_policy.rules {
65 if let Some(attr_value) = user_attributes.get(&rule.attribute)
66 && attr_value == &rule.expected_value
67 && rule.permission.implies(permission)
68 {
69 return true;
70 }
71 }
72 false
73 }
74
75 pub fn check_delegation(
77 &self,
78 user_id: &str,
79 permission: &Permission,
80 delegations: &[Delegation],
81 ) -> bool {
82 for delegation in delegations {
83 if delegation.delegatee == user_id
84 && delegation.permissions.iter().any(|p| p.implies(permission))
85 {
86 if let Some(expiry) = delegation.expires_at
87 && expiry < chrono::Utc::now()
88 {
89 continue;
90 }
91 return true;
92 }
93 }
94 false
95 }
96}
97use crate::errors::{PermissionError, Result};
99use crate::tokens::AuthToken;
100use serde::{Deserialize, Serialize};
101use std::collections::{HashMap, HashSet};
102
103#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
105pub struct Permission {
106 pub action: String,
108
109 pub resource: String,
111
112 pub instance: Option<String>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Role {
119 pub name: String,
121
122 pub description: Option<String>,
124
125 pub permissions: HashSet<Permission>,
127
128 pub parent_roles: HashSet<String>,
130
131 pub active: bool,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct UserPermissions {
138 pub user_id: String,
140
141 pub direct_permissions: HashSet<Permission>,
143
144 pub roles: HashSet<String>,
146
147 pub computed_permissions: Option<HashSet<Permission>>,
149
150 pub last_updated: chrono::DateTime<chrono::Utc>,
152}
153
154#[derive(Debug, Clone)]
156pub struct PermissionChecker {
157 roles: HashMap<String, Role>,
159
160 user_permissions: HashMap<String, UserPermissions>,
162
163 resource_hierarchy: HashMap<String, Vec<String>>,
165}
166
167impl Permission {
168 pub fn new(action: impl Into<String>, resource: impl Into<String>) -> Self {
170 Self {
171 action: action.into(),
172 resource: resource.into(),
173 instance: None,
174 }
175 }
176
177 pub fn from_action(action: impl Into<String>) -> Self {
179 Self {
180 action: action.into(),
181 resource: "*".to_string(),
182 instance: None,
183 }
184 }
185
186 pub fn with_instance(
188 action: impl Into<String>,
189 resource: impl Into<String>,
190 instance: impl Into<String>,
191 ) -> Self {
192 Self {
193 action: action.into(),
194 resource: resource.into(),
195 instance: Some(instance.into()),
196 }
197 }
198
199 pub fn parse(permission_str: &str) -> Result<Self> {
201 let parts: Vec<&str> = permission_str.split(':').collect();
202
203 match parts.len() {
204 2 => Ok(Self::new(parts[0], parts[1])),
205 3 => Ok(Self::with_instance(parts[0], parts[1], parts[2])),
206 _ => Err(PermissionError::invalid_format(format!(
207 "Invalid permission format: {permission_str}"
208 ))
209 .into()),
210 }
211 }
212}
213
214impl std::fmt::Display for Permission {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 match &self.instance {
217 Some(instance) => write!(f, "{}:{}:{}", self.action, self.resource, instance),
218 None => write!(f, "{}:{}", self.action, self.resource),
219 }
220 }
221}
222
223impl Permission {
224 pub fn matches(&self, other: &Permission) -> bool {
226 if self.action != "*" && other.action != "*" && self.action != other.action {
228 return false;
229 }
230
231 if self.resource != "*" && other.resource != "*" && self.resource != other.resource {
233 return false;
234 }
235
236 match (&self.instance, &other.instance) {
238 (Some(self_instance), Some(other_instance)) => {
239 self_instance == "*" || other_instance == "*" || self_instance == other_instance
240 }
241 (None, None) => true,
242 (Some(_), None) => false, (None, Some(_)) => true, }
245 }
246
247 pub fn implies(&self, other: &Permission) -> bool {
249 let action_implies = self.action == "*" || self.action == other.action;
251 let resource_implies = self.resource == "*" || self.resource == other.resource;
252 let instance_implies = match (&self.instance, &other.instance) {
253 (None, _) => true, (Some(self_instance), Some(other_instance)) => {
255 self_instance == "*" || self_instance == other_instance
256 }
257 (Some(_), None) => false, };
259
260 action_implies && resource_implies && instance_implies
261 }
262}
263
264impl Role {
265 pub fn new(name: impl Into<String>) -> Self {
267 Self {
268 name: name.into(),
269 description: None,
270 permissions: HashSet::new(),
271 parent_roles: HashSet::new(),
272 active: true,
273 }
274 }
275
276 pub fn with_description(mut self, description: impl Into<String>) -> Self {
278 self.description = Some(description.into());
279 self
280 }
281
282 pub fn add_permission(&mut self, permission: Permission) {
284 self.permissions.insert(permission);
285 }
286
287 pub fn with_permissions(mut self, permissions: Vec<Permission>) -> Self {
289 for permission in permissions {
290 self.permissions.insert(permission);
291 }
292 self
293 }
294
295 pub fn remove_permission(&mut self, permission: &Permission) {
297 self.permissions.remove(permission);
298 }
299
300 pub fn add_parent_role(&mut self, parent_role: impl Into<String>) {
302 self.parent_roles.insert(parent_role.into());
303 }
304
305 pub fn has_permission(&self, permission: &Permission) -> bool {
307 self.permissions.iter().any(|p| p.implies(permission))
308 }
309
310 pub fn get_all_permissions(
312 &self,
313 role_resolver: &dyn Fn(&str) -> Option<Role>,
314 ) -> HashSet<Permission> {
315 let mut all_permissions = self.permissions.clone();
316
317 for parent_role_name in &self.parent_roles {
319 if let Some(parent_role) = role_resolver(parent_role_name) {
320 all_permissions.extend(parent_role.get_all_permissions(role_resolver));
321 }
322 }
323
324 all_permissions
325 }
326
327 pub fn set_active(&mut self, active: bool) {
329 self.active = active;
330 }
331}
332
333impl UserPermissions {
334 pub fn new(user_id: impl Into<String>) -> Self {
336 Self {
337 user_id: user_id.into(),
338 direct_permissions: HashSet::new(),
339 roles: HashSet::new(),
340 computed_permissions: None,
341 last_updated: chrono::Utc::now(),
342 }
343 }
344
345 pub fn add_permission(&mut self, permission: Permission) {
347 self.direct_permissions.insert(permission);
348 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
350 }
351
352 pub fn remove_permission(&mut self, permission: &Permission) {
354 self.direct_permissions.remove(permission);
355 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
357 }
358
359 pub fn add_role(&mut self, role: impl Into<String>) {
361 self.roles.insert(role.into());
362 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
364 }
365
366 pub fn remove_role(&mut self, role: &str) {
368 self.roles.remove(role);
369 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
371 }
372
373 pub fn compute_permissions(
375 &mut self,
376 role_resolver: &dyn Fn(&str) -> Option<Role>,
377 ) -> &HashSet<Permission> {
378 if self.computed_permissions.is_none() {
379 let mut all_permissions = self.direct_permissions.clone();
380
381 for role_name in &self.roles {
383 if let Some(role) = role_resolver(role_name)
384 && role.active
385 {
386 all_permissions.extend(role.get_all_permissions(role_resolver));
387 }
388 }
389
390 self.computed_permissions = Some(all_permissions);
391 }
392
393 self.computed_permissions.as_ref().unwrap()
394 }
395
396 pub fn has_permission(
398 &mut self,
399 permission: &Permission,
400 role_resolver: &dyn Fn(&str) -> Option<Role>,
401 ) -> bool {
402 let all_permissions = self.compute_permissions(role_resolver);
403 all_permissions.iter().any(|p| p.implies(permission))
404 }
405}
406
407impl PermissionChecker {
408 pub fn new() -> Self {
410 Self {
411 roles: HashMap::new(),
412 user_permissions: HashMap::new(),
413 resource_hierarchy: HashMap::new(),
414 }
415 }
416
417 pub fn add_role(&mut self, role: Role) {
419 self.roles.insert(role.name.clone(), role);
420 }
421
422 pub fn remove_role(&mut self, role_name: &str) {
424 self.roles.remove(role_name);
425 }
426
427 pub fn get_role(&self, role_name: &str) -> Option<&Role> {
429 self.roles.get(role_name)
430 }
431
432 pub fn set_user_permissions(&mut self, user_permissions: UserPermissions) {
434 self.user_permissions
435 .insert(user_permissions.user_id.clone(), user_permissions);
436 }
437
438 pub fn get_user_permissions(&self, user_id: &str) -> Option<&UserPermissions> {
440 self.user_permissions.get(user_id)
441 }
442
443 pub fn get_user_permissions_mut(&mut self, user_id: &str) -> Option<&mut UserPermissions> {
445 self.user_permissions.get_mut(user_id)
446 }
447
448 pub fn add_user_permission(&mut self, user_id: &str, permission: Permission) {
450 let user_perms = self
451 .user_permissions
452 .entry(user_id.to_string())
453 .or_insert_with(|| UserPermissions::new(user_id));
454
455 user_perms.add_permission(permission);
456 }
457
458 pub fn add_user_role(&mut self, user_id: &str, role: impl Into<String>) {
460 let user_perms = self
461 .user_permissions
462 .entry(user_id.to_string())
463 .or_insert_with(|| UserPermissions::new(user_id));
464
465 user_perms.add_role(role);
466 }
467
468 pub fn check_permission(&mut self, user_id: &str, permission: &Permission) -> Result<bool> {
470 let user_perms = self.user_permissions.get_mut(user_id).ok_or_else(|| {
471 PermissionError::access_denied(permission.to_string(), "unknown user".to_string())
472 })?;
473
474 let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
475
476 Ok(user_perms.has_permission(permission, &role_resolver))
477 }
478
479 pub fn check_access(&mut self, user_id: &str, action: &str, resource: &str) -> Result<bool> {
481 let permission = Permission::new(action, resource);
482
483 if self.check_permission(user_id, &permission)? {
485 return Ok(true);
486 }
487
488 self.check_hierarchical_permission(user_id, action, resource)
490 }
491
492 pub fn check_instance_access(
494 &mut self,
495 user_id: &str,
496 action: &str,
497 resource: &str,
498 instance: &str,
499 ) -> Result<bool> {
500 let permission = Permission::with_instance(action, resource, instance);
501 self.check_permission(user_id, &permission)
502 }
503
504 pub fn check_token_permission(
506 &mut self,
507 token: &AuthToken,
508 permission: &Permission,
509 ) -> Result<bool> {
510 if !token.is_valid() {
511 return Ok(false);
512 }
513
514 let required_scope = permission.to_string();
516 if !token.has_scope(&required_scope) {
517 let wildcard_action = format!("*:{}", permission.resource);
519 let wildcard_resource = format!("{}:*", permission.action);
520 let wildcard_all = "*:*".to_string();
521
522 if !token.has_scope(&wildcard_action)
523 && !token.has_scope(&wildcard_resource)
524 && !token.has_scope(&wildcard_all)
525 {
526 return Ok(false);
527 }
528 }
529
530 self.check_permission(&token.user_id, permission)
532 }
533
534 pub fn add_resource_hierarchy(&mut self, parent: String, children: Vec<String>) {
536 self.resource_hierarchy.insert(parent, children);
537 }
538
539 pub fn get_child_resources(&self, parent: &str) -> Option<&Vec<String>> {
541 self.resource_hierarchy.get(parent)
542 }
543
544 pub fn check_hierarchical_permission(
546 &mut self,
547 user_id: &str,
548 action: &str,
549 resource: &str,
550 ) -> Result<bool> {
551 let hierarchy = self.resource_hierarchy.clone();
553
554 if self.has_ancestor_permission(&hierarchy, user_id, action, resource)? {
556 return Ok(true);
557 }
558
559 if self.check_wildcard_permissions(&hierarchy, user_id, action, resource)? {
561 return Ok(true);
562 }
563
564 Ok(false)
565 }
566
567 fn check_wildcard_permissions(
569 &mut self,
570 hierarchy: &HashMap<String, Vec<String>>,
571 user_id: &str,
572 action: &str,
573 resource: &str,
574 ) -> Result<bool> {
575 for (parent_resource, children) in hierarchy {
577 if children.contains(&resource.to_string()) {
578 let wildcard_permission = Permission::new(action, format!("{}.*", parent_resource));
580 if self
581 .check_permission(user_id, &wildcard_permission)
582 .unwrap_or(false)
583 {
584 return Ok(true);
585 }
586 }
587 }
588
589 if let Some(_children) = hierarchy.get(resource) {
591 let wildcard_permission = Permission::new(action, format!("{}.*", resource));
592 if self
593 .check_permission(user_id, &wildcard_permission)
594 .unwrap_or(false)
595 {
596 return Ok(true);
597 }
598 }
599
600 Ok(false)
601 }
602
603 fn has_ancestor_permission(
605 &mut self,
606 hierarchy: &HashMap<String, Vec<String>>,
607 user_id: &str,
608 action: &str,
609 resource: &str,
610 ) -> Result<bool> {
611 for (parent_resource, children) in hierarchy {
613 if children.contains(&resource.to_string()) {
614 let parent_permission = Permission::new(action, parent_resource);
616 if self.check_permission(user_id, &parent_permission)? {
617 return Ok(true);
618 }
619
620 if self.has_ancestor_permission(hierarchy, user_id, action, parent_resource)? {
622 return Ok(true);
623 }
624 }
625 }
626
627 Ok(false)
628 }
629
630 pub fn create_default_roles(&mut self) {
632 let mut admin_role = Role::new("admin").with_description("Administrator with full access");
634 admin_role.add_permission(Permission::new("*", "*"));
635 self.add_role(admin_role);
636
637 let mut user_role = Role::new("user").with_description("Regular user with basic access");
639 user_role.add_permission(Permission::new("read", "profile"));
640 user_role.add_permission(Permission::new("write", "profile"));
641 user_role.add_permission(Permission::new("read", "public"));
642 self.add_role(user_role);
643
644 let mut guest_role =
646 Role::new("guest").with_description("Guest user with read-only access");
647 guest_role.add_permission(Permission::new("read", "public"));
648 self.add_role(guest_role);
649 }
650
651 pub fn load_permissions(&mut self, _config: &str) -> Result<()> {
653 self.create_default_roles();
656 Ok(())
657 }
658
659 pub fn assign_role_to_user(&mut self, user_id: &str, role_name: &str) -> Result<()> {
661 if !self.roles.contains_key(role_name) {
663 return Err(PermissionError::access_denied(
664 role_name.to_string(),
665 "Role does not exist".to_string(),
666 )
667 .into());
668 }
669
670 self.add_user_role(user_id, role_name);
672 Ok(())
673 }
674
675 pub fn set_role_inheritance(&mut self, child_role: &str, parent_role: &str) -> Result<()> {
677 if !self.roles.contains_key(child_role) {
679 return Err(PermissionError::access_denied(
680 child_role.to_string(),
681 "Child role does not exist".to_string(),
682 )
683 .into());
684 }
685 if !self.roles.contains_key(parent_role) {
686 return Err(PermissionError::access_denied(
687 parent_role.to_string(),
688 "Parent role does not exist".to_string(),
689 )
690 .into());
691 }
692
693 if let Some(child) = self.roles.get_mut(child_role) {
695 child.add_parent_role(parent_role);
696 }
697
698 Ok(())
699 }
700
701 pub fn remove_user_permission(&mut self, user_id: &str, permission: &Permission) {
703 if let Some(user_perms) = self.user_permissions.get_mut(user_id) {
704 user_perms.remove_permission(permission);
705 }
706 }
707
708 pub fn user_has_role(&self, user_id: &str, role_name: &str) -> bool {
710 if let Some(user_perms) = self.user_permissions.get(user_id) {
711 user_perms.roles.contains(role_name)
712 } else {
713 false
714 }
715 }
716
717 pub fn get_effective_permissions(&self, user_id: &str) -> Vec<String> {
719 if let Some(user_perms) = self.user_permissions.get(user_id) {
720 let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
721
722 let mut user_perms_clone = user_perms.clone();
724 let all_permissions = user_perms_clone.compute_permissions(&role_resolver);
725
726 all_permissions.iter().map(|p| p.to_string()).collect()
727 } else {
728 Vec::new()
729 }
730 }
731}
732
733impl Default for PermissionChecker {
734 fn default() -> Self {
735 Self::new()
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use super::*;
742
743 #[test]
744 fn test_permission_parsing() {
745 let perm = Permission::parse("read:documents").unwrap();
746 assert_eq!(perm.action, "read");
747 assert_eq!(perm.resource, "documents");
748 assert_eq!(perm.instance, None);
749
750 let perm = Permission::parse("write:documents:123").unwrap();
751 assert_eq!(perm.action, "write");
752 assert_eq!(perm.resource, "documents");
753 assert_eq!(perm.instance, Some("123".to_string()));
754 }
755
756 #[test]
757 fn test_permission_matching() {
758 let perm1 = Permission::new("read", "documents");
759 let perm2 = Permission::new("read", "documents");
760 let perm3 = Permission::new("write", "documents");
761 let wildcard = Permission::new("*", "documents");
762
763 assert!(perm1.matches(&perm2));
764 assert!(!perm1.matches(&perm3));
765 assert!(wildcard.matches(&perm1));
766 assert!(wildcard.matches(&perm3));
767 }
768
769 #[test]
770 fn test_permission_implies() {
771 let general = Permission::new("read", "documents");
772 let specific = Permission::with_instance("read", "documents", "123");
773 let wildcard = Permission::new("*", "*");
774
775 assert!(general.implies(&specific));
776 assert!(!specific.implies(&general));
777 assert!(wildcard.implies(&general));
778 assert!(wildcard.implies(&specific));
779 }
780
781 #[test]
782 fn test_role_permissions() {
783 let mut role = Role::new("editor");
784 role.add_permission(Permission::new("read", "documents"));
785 role.add_permission(Permission::new("write", "documents"));
786
787 let read_perm = Permission::new("read", "documents");
788 let delete_perm = Permission::new("delete", "documents");
789
790 assert!(role.has_permission(&read_perm));
791 assert!(!role.has_permission(&delete_perm));
792 }
793
794 #[test]
795 fn test_user_permissions() {
796 let mut user_perms = UserPermissions::new("user123");
797 user_perms.add_permission(Permission::new("read", "profile"));
798 user_perms.add_role("user");
799
800 let role_resolver = |_: &str| Some(Role::new("user"));
801
802 let read_perm = Permission::new("read", "profile");
803 assert!(user_perms.has_permission(&read_perm, &role_resolver));
804 }
805
806 #[test]
807 fn test_permission_checker() {
808 let mut checker = PermissionChecker::new();
809 checker.create_default_roles();
810
811 checker.add_user_role("user123", "admin");
812
813 let result = checker
814 .check_access("user123", "read", "documents")
815 .unwrap();
816 assert!(result);
817
818 let result = checker.check_access("user123", "delete", "system").unwrap();
819 assert!(result); }
821}
822
823#[cfg(test)]
824pub mod abac_delegation_tests;
825
826