1use crate::Permission;
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Role {
12 pub name: String,
14 allowed: HashSet<Permission>,
16 denied: HashSet<Permission>,
18}
19
20impl Role {
21 pub fn new(name: impl Into<String>) -> Self {
23 Self { name: name.into(), allowed: HashSet::new(), denied: HashSet::new() }
24 }
25
26 pub fn allow(mut self, permission: Permission) -> Self {
28 self.allowed.insert(permission);
29 self
30 }
31
32 pub fn deny(mut self, permission: Permission) -> Self {
36 self.denied.insert(permission);
37 self
38 }
39
40 pub fn can_access(&self, permission: &Permission) -> bool {
45 for denied in &self.denied {
47 if denied.covers(permission) {
48 return false;
49 }
50 }
51
52 for allowed in &self.allowed {
54 if allowed.covers(permission) {
55 return true;
56 }
57 }
58
59 false
61 }
62
63 pub fn allowed_permissions(&self) -> &HashSet<Permission> {
65 &self.allowed
66 }
67
68 pub fn denied_permissions(&self) -> &HashSet<Permission> {
70 &self.denied
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_role_allow() {
80 let role = Role::new("user")
81 .allow(Permission::Tool("search".into()))
82 .allow(Permission::Tool("summarize".into()));
83
84 assert!(role.can_access(&Permission::Tool("search".into())));
85 assert!(role.can_access(&Permission::Tool("summarize".into())));
86 assert!(!role.can_access(&Permission::Tool("other".into())));
87 }
88
89 #[test]
90 fn test_role_deny_precedence() {
91 let role = Role::new("restricted")
92 .allow(Permission::AllTools)
93 .deny(Permission::Tool("code_exec".into()));
94
95 assert!(role.can_access(&Permission::Tool("search".into())));
97 assert!(!role.can_access(&Permission::Tool("code_exec".into())));
99 }
100
101 #[test]
102 fn test_admin_role() {
103 let admin = Role::new("admin").allow(Permission::AllTools).allow(Permission::AllAgents);
104
105 assert!(admin.can_access(&Permission::Tool("anything".into())));
106 assert!(admin.can_access(&Permission::Agent("any_agent".into())));
107 assert!(admin.can_access(&Permission::AllTools));
108 assert!(admin.can_access(&Permission::AllAgents));
109 }
110
111 #[test]
112 fn test_empty_role_denies_all() {
113 let empty = Role::new("empty");
114
115 assert!(!empty.can_access(&Permission::Tool("search".into())));
116 assert!(!empty.can_access(&Permission::AllTools));
117 }
118}