Skip to main content

engram/auth/
permissions.rs

1//! Permission system for access control
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashSet;
5
6/// Permission types
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum Permission {
10    /// Read access
11    Read,
12    /// Write/create access
13    Write,
14    /// Update existing resources
15    Update,
16    /// Delete access
17    Delete,
18    /// Share with others
19    Share,
20    /// Administrative access
21    Admin,
22}
23
24/// Resource types that can be protected
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
26#[serde(rename_all = "snake_case")]
27pub enum ResourceType {
28    /// Memory records
29    Memory,
30    /// Cross-references
31    CrossRef,
32    /// Tags
33    Tag,
34    /// Namespaces
35    Namespace,
36    /// Users
37    User,
38    /// API keys
39    ApiKey,
40    /// System-level operations
41    System,
42}
43
44/// A set of permissions for various resources
45#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct PermissionSet {
47    /// Set of (permission, resource) tuples
48    permissions: HashSet<(Permission, ResourceType)>,
49    /// Whether this is an admin set (all permissions)
50    is_admin: bool,
51}
52
53impl PermissionSet {
54    /// Create an empty permission set
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    /// Create a permission set from a list of permissions
60    pub fn from_permissions(perms: Vec<(Permission, ResourceType)>) -> Self {
61        Self {
62            permissions: perms.into_iter().collect(),
63            is_admin: false,
64        }
65    }
66
67    /// Create an admin permission set (all permissions)
68    pub fn admin() -> Self {
69        Self {
70            permissions: HashSet::new(),
71            is_admin: true,
72        }
73    }
74
75    /// Create a read-only permission set
76    pub fn read_only() -> Self {
77        let mut permissions = HashSet::new();
78        permissions.insert((Permission::Read, ResourceType::Memory));
79        permissions.insert((Permission::Read, ResourceType::CrossRef));
80        permissions.insert((Permission::Read, ResourceType::Tag));
81        Self {
82            permissions,
83            is_admin: false,
84        }
85    }
86
87    /// Create a standard user permission set
88    pub fn standard_user() -> Self {
89        let resources = [
90            ResourceType::Memory,
91            ResourceType::CrossRef,
92            ResourceType::Tag,
93        ];
94        let permissions = [
95            Permission::Read,
96            Permission::Write,
97            Permission::Update,
98            Permission::Delete,
99        ];
100
101        let mut set = HashSet::new();
102        for resource in resources {
103            for permission in permissions {
104                set.insert((permission, resource));
105            }
106        }
107
108        // Users can read their own API keys
109        set.insert((Permission::Read, ResourceType::ApiKey));
110        set.insert((Permission::Write, ResourceType::ApiKey));
111        set.insert((Permission::Delete, ResourceType::ApiKey));
112
113        Self {
114            permissions: set,
115            is_admin: false,
116        }
117    }
118
119    /// Add a permission
120    pub fn add(&mut self, permission: Permission, resource: ResourceType) {
121        self.permissions.insert((permission, resource));
122    }
123
124    /// Remove a permission
125    pub fn remove(&mut self, permission: Permission, resource: ResourceType) {
126        self.permissions.remove(&(permission, resource));
127    }
128
129    /// Check if a permission exists
130    pub fn has_permission(&self, permission: Permission, resource: ResourceType) -> bool {
131        if self.is_admin {
132            return true;
133        }
134        self.permissions.contains(&(permission, resource))
135    }
136
137    /// Check if this is an admin set
138    pub fn is_admin(&self) -> bool {
139        self.is_admin
140    }
141
142    /// Merge another permission set into this one
143    pub fn merge(&mut self, other: &PermissionSet) {
144        if other.is_admin {
145            self.is_admin = true;
146        }
147        self.permissions.extend(other.permissions.iter().cloned());
148    }
149
150    /// Get all permissions as a vector
151    pub fn to_vec(&self) -> Vec<(Permission, ResourceType)> {
152        self.permissions.iter().cloned().collect()
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159
160    #[test]
161    fn test_permission_set_basic() {
162        let mut set = PermissionSet::new();
163        set.add(Permission::Read, ResourceType::Memory);
164
165        assert!(set.has_permission(Permission::Read, ResourceType::Memory));
166        assert!(!set.has_permission(Permission::Write, ResourceType::Memory));
167        assert!(!set.has_permission(Permission::Read, ResourceType::User));
168    }
169
170    #[test]
171    fn test_admin_set() {
172        let set = PermissionSet::admin();
173        assert!(set.has_permission(Permission::Admin, ResourceType::System));
174        assert!(set.has_permission(Permission::Delete, ResourceType::User));
175        assert!(set.has_permission(Permission::Read, ResourceType::Memory));
176    }
177
178    #[test]
179    fn test_standard_user() {
180        let set = PermissionSet::standard_user();
181        assert!(set.has_permission(Permission::Read, ResourceType::Memory));
182        assert!(set.has_permission(Permission::Write, ResourceType::Memory));
183        assert!(set.has_permission(Permission::Delete, ResourceType::Memory));
184        assert!(!set.has_permission(Permission::Admin, ResourceType::System));
185        assert!(!set.has_permission(Permission::Delete, ResourceType::User));
186    }
187
188    #[test]
189    fn test_merge() {
190        let mut set1 = PermissionSet::new();
191        set1.add(Permission::Read, ResourceType::Memory);
192
193        let mut set2 = PermissionSet::new();
194        set2.add(Permission::Write, ResourceType::Memory);
195
196        set1.merge(&set2);
197
198        assert!(set1.has_permission(Permission::Read, ResourceType::Memory));
199        assert!(set1.has_permission(Permission::Write, ResourceType::Memory));
200    }
201
202    #[test]
203    fn test_serialization() {
204        let set = PermissionSet::standard_user();
205        let json = serde_json::to_string(&set).unwrap();
206        let restored: PermissionSet = serde_json::from_str(&json).unwrap();
207
208        assert!(restored.has_permission(Permission::Read, ResourceType::Memory));
209        assert!(restored.has_permission(Permission::Write, ResourceType::Memory));
210    }
211}