adk_auth/
role.rs

1//! Role type with allow/deny permissions.
2
3use crate::Permission;
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6
7/// A role with a set of allowed and denied permissions.
8///
9/// Deny rules take precedence over allow rules.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Role {
12    /// Role name (e.g., "admin", "user", "analyst").
13    pub name: String,
14    /// Permissions explicitly allowed for this role.
15    allowed: HashSet<Permission>,
16    /// Permissions explicitly denied for this role.
17    denied: HashSet<Permission>,
18}
19
20impl Role {
21    /// Create a new role with the given name.
22    pub fn new(name: impl Into<String>) -> Self {
23        Self { name: name.into(), allowed: HashSet::new(), denied: HashSet::new() }
24    }
25
26    /// Allow a permission for this role.
27    pub fn allow(mut self, permission: Permission) -> Self {
28        self.allowed.insert(permission);
29        self
30    }
31
32    /// Deny a permission for this role.
33    ///
34    /// Deny rules take precedence over allow rules.
35    pub fn deny(mut self, permission: Permission) -> Self {
36        self.denied.insert(permission);
37        self
38    }
39
40    /// Check if this role can access the given permission.
41    ///
42    /// Returns `true` if the permission is allowed and not denied.
43    /// Deny rules take precedence over allow rules.
44    pub fn can_access(&self, permission: &Permission) -> bool {
45        // Check if explicitly denied (or covered by a deny rule)
46        for denied in &self.denied {
47            if denied.covers(permission) {
48                return false;
49            }
50        }
51
52        // Check if explicitly allowed (or covered by an allow rule)
53        for allowed in &self.allowed {
54            if allowed.covers(permission) {
55                return true;
56            }
57        }
58
59        // Default: deny
60        false
61    }
62
63    /// Get all allowed permissions.
64    pub fn allowed_permissions(&self) -> &HashSet<Permission> {
65        &self.allowed
66    }
67
68    /// Get all denied permissions.
69    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        // AllTools allows everything...
96        assert!(role.can_access(&Permission::Tool("search".into())));
97        // ...except explicitly denied
98        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}