1use crate::errors::{PermissionError, Result};
4use crate::tokens::AuthToken;
5use serde::{Deserialize, Serialize};
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct Permission {
11 pub action: String,
13
14 pub resource: String,
16
17 pub instance: Option<String>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Role {
24 pub name: String,
26
27 pub description: Option<String>,
29
30 pub permissions: HashSet<Permission>,
32
33 pub parent_roles: HashSet<String>,
35
36 pub active: bool,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct UserPermissions {
43 pub user_id: String,
45
46 pub direct_permissions: HashSet<Permission>,
48
49 pub roles: HashSet<String>,
51
52 pub computed_permissions: Option<HashSet<Permission>>,
54
55 pub last_updated: chrono::DateTime<chrono::Utc>,
57}
58
59#[derive(Debug, Clone)]
61pub struct PermissionChecker {
62 roles: HashMap<String, Role>,
64
65 user_permissions: HashMap<String, UserPermissions>,
67
68 #[allow(dead_code)]
70 resource_hierarchy: HashMap<String, Vec<String>>,
71}
72
73impl Permission {
74 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 pub fn with_instance(
85 action: impl Into<String>,
86 resource: impl Into<String>,
87 instance: impl Into<String>,
88 ) -> Self {
89 Self {
90 action: action.into(),
91 resource: resource.into(),
92 instance: Some(instance.into()),
93 }
94 }
95
96 pub fn parse(permission_str: &str) -> Result<Self> {
98 let parts: Vec<&str> = permission_str.split(':').collect();
99
100 match parts.len() {
101 2 => Ok(Self::new(parts[0], parts[1])),
102 3 => Ok(Self::with_instance(parts[0], parts[1], parts[2])),
103 _ => Err(PermissionError::invalid_format(
104 format!("Invalid permission format: {permission_str}")
105 ).into()),
106 }
107 }
108
109}
110
111impl std::fmt::Display for Permission {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match &self.instance {
114 Some(instance) => write!(f, "{}:{}:{}", self.action, self.resource, instance),
115 None => write!(f, "{}:{}", self.action, self.resource),
116 }
117 }
118}
119
120impl Permission {
121 pub fn matches(&self, other: &Permission) -> bool {
123 if self.action != "*" && other.action != "*" && self.action != other.action {
125 return false;
126 }
127
128 if self.resource != "*" && other.resource != "*" && self.resource != other.resource {
130 return false;
131 }
132
133 match (&self.instance, &other.instance) {
135 (Some(self_instance), Some(other_instance)) => {
136 self_instance == "*" || other_instance == "*" || self_instance == other_instance
137 }
138 (None, None) => true,
139 (Some(_), None) => false, (None, Some(_)) => true, }
142 }
143
144 pub fn implies(&self, other: &Permission) -> bool {
146 let action_implies = self.action == "*" || self.action == other.action;
148 let resource_implies = self.resource == "*" || self.resource == other.resource;
149 let instance_implies = match (&self.instance, &other.instance) {
150 (None, _) => true, (Some(self_instance), Some(other_instance)) => {
152 self_instance == "*" || self_instance == other_instance
153 }
154 (Some(_), None) => false, };
156
157 action_implies && resource_implies && instance_implies
158 }
159}
160
161impl Role {
162 pub fn new(name: impl Into<String>) -> Self {
164 Self {
165 name: name.into(),
166 description: None,
167 permissions: HashSet::new(),
168 parent_roles: HashSet::new(),
169 active: true,
170 }
171 }
172
173 pub fn with_description(mut self, description: impl Into<String>) -> Self {
175 self.description = Some(description.into());
176 self
177 }
178
179 pub fn add_permission(&mut self, permission: Permission) {
181 self.permissions.insert(permission);
182 }
183
184 pub fn remove_permission(&mut self, permission: &Permission) {
186 self.permissions.remove(permission);
187 }
188
189 pub fn add_parent_role(&mut self, parent_role: impl Into<String>) {
191 self.parent_roles.insert(parent_role.into());
192 }
193
194 pub fn has_permission(&self, permission: &Permission) -> bool {
196 self.permissions.iter().any(|p| p.implies(permission))
197 }
198
199 pub fn get_all_permissions(&self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> HashSet<Permission> {
201 let mut all_permissions = self.permissions.clone();
202
203 for parent_role_name in &self.parent_roles {
205 if let Some(parent_role) = role_resolver(parent_role_name) {
206 all_permissions.extend(parent_role.get_all_permissions(role_resolver));
207 }
208 }
209
210 all_permissions
211 }
212
213 pub fn set_active(&mut self, active: bool) {
215 self.active = active;
216 }
217}
218
219impl UserPermissions {
220 pub fn new(user_id: impl Into<String>) -> Self {
222 Self {
223 user_id: user_id.into(),
224 direct_permissions: HashSet::new(),
225 roles: HashSet::new(),
226 computed_permissions: None,
227 last_updated: chrono::Utc::now(),
228 }
229 }
230
231 pub fn add_permission(&mut self, permission: Permission) {
233 self.direct_permissions.insert(permission);
234 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
236 }
237
238 pub fn remove_permission(&mut self, permission: &Permission) {
240 self.direct_permissions.remove(permission);
241 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
243 }
244
245 pub fn add_role(&mut self, role: impl Into<String>) {
247 self.roles.insert(role.into());
248 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
250 }
251
252 pub fn remove_role(&mut self, role: &str) {
254 self.roles.remove(role);
255 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
257 }
258
259 pub fn compute_permissions(&mut self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> &HashSet<Permission> {
261 if self.computed_permissions.is_none() {
262 let mut all_permissions = self.direct_permissions.clone();
263
264 for role_name in &self.roles {
266 if let Some(role) = role_resolver(role_name) {
267 if role.active {
268 all_permissions.extend(role.get_all_permissions(role_resolver));
269 }
270 }
271 }
272
273 self.computed_permissions = Some(all_permissions);
274 }
275
276 self.computed_permissions.as_ref().unwrap()
277 }
278
279 pub fn has_permission(&mut self, permission: &Permission, role_resolver: &dyn Fn(&str) -> Option<Role>) -> bool {
281 let all_permissions = self.compute_permissions(role_resolver);
282 all_permissions.iter().any(|p| p.implies(permission))
283 }
284}
285
286impl PermissionChecker {
287 pub fn new() -> Self {
289 Self {
290 roles: HashMap::new(),
291 user_permissions: HashMap::new(),
292 resource_hierarchy: HashMap::new(),
293 }
294 }
295
296 pub fn add_role(&mut self, role: Role) {
298 self.roles.insert(role.name.clone(), role);
299 }
300
301 pub fn remove_role(&mut self, role_name: &str) {
303 self.roles.remove(role_name);
304 }
305
306 pub fn get_role(&self, role_name: &str) -> Option<&Role> {
308 self.roles.get(role_name)
309 }
310
311 pub fn set_user_permissions(&mut self, user_permissions: UserPermissions) {
313 self.user_permissions.insert(user_permissions.user_id.clone(), user_permissions);
314 }
315
316 pub fn get_user_permissions(&self, user_id: &str) -> Option<&UserPermissions> {
318 self.user_permissions.get(user_id)
319 }
320
321 pub fn get_user_permissions_mut(&mut self, user_id: &str) -> Option<&mut UserPermissions> {
323 self.user_permissions.get_mut(user_id)
324 }
325
326 pub fn add_user_permission(&mut self, user_id: &str, permission: Permission) {
328 let user_perms = self.user_permissions
329 .entry(user_id.to_string())
330 .or_insert_with(|| UserPermissions::new(user_id));
331
332 user_perms.add_permission(permission);
333 }
334
335 pub fn add_user_role(&mut self, user_id: &str, role: impl Into<String>) {
337 let user_perms = self.user_permissions
338 .entry(user_id.to_string())
339 .or_insert_with(|| UserPermissions::new(user_id));
340
341 user_perms.add_role(role);
342 }
343
344 pub fn check_permission(&mut self, user_id: &str, permission: &Permission) -> Result<bool> {
346 let user_perms = self.user_permissions
347 .get_mut(user_id)
348 .ok_or_else(|| PermissionError::access_denied(
349 permission.to_string(),
350 "unknown user".to_string(),
351 ))?;
352
353 let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
354
355 Ok(user_perms.has_permission(permission, &role_resolver))
356 }
357
358 pub fn check_access(&mut self, user_id: &str, action: &str, resource: &str) -> Result<bool> {
360 let permission = Permission::new(action, resource);
361 self.check_permission(user_id, &permission)
362 }
363
364 pub fn check_instance_access(
366 &mut self,
367 user_id: &str,
368 action: &str,
369 resource: &str,
370 instance: &str,
371 ) -> Result<bool> {
372 let permission = Permission::with_instance(action, resource, instance);
373 self.check_permission(user_id, &permission)
374 }
375
376 pub fn check_token_permission(&mut self, token: &AuthToken, permission: &Permission) -> Result<bool> {
378 if !token.is_valid() {
379 return Ok(false);
380 }
381
382 let required_scope = permission.to_string();
384 if !token.has_scope(&required_scope) {
385 let wildcard_action = format!("*:{}", permission.resource);
387 let wildcard_resource = format!("{}:*", permission.action);
388 let wildcard_all = "*:*".to_string();
389
390 if !token.has_scope(&wildcard_action)
391 && !token.has_scope(&wildcard_resource)
392 && !token.has_scope(&wildcard_all) {
393 return Ok(false);
394 }
395 }
396
397 self.check_permission(&token.user_id, permission)
399 }
400
401 pub fn create_default_roles(&mut self) {
403 let mut admin_role = Role::new("admin")
405 .with_description("Administrator with full access");
406 admin_role.add_permission(Permission::new("*", "*"));
407 self.add_role(admin_role);
408
409 let mut user_role = Role::new("user")
411 .with_description("Regular user with basic access");
412 user_role.add_permission(Permission::new("read", "profile"));
413 user_role.add_permission(Permission::new("write", "profile"));
414 user_role.add_permission(Permission::new("read", "public"));
415 self.add_role(user_role);
416
417 let mut guest_role = Role::new("guest")
419 .with_description("Guest user with read-only access");
420 guest_role.add_permission(Permission::new("read", "public"));
421 self.add_role(guest_role);
422 }
423
424 pub fn load_permissions(&mut self, _config: &str) -> Result<()> {
426 self.create_default_roles();
429 Ok(())
430 }
431}
432
433impl Default for PermissionChecker {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442
443 #[test]
444 fn test_permission_parsing() {
445 let perm = Permission::parse("read:documents").unwrap();
446 assert_eq!(perm.action, "read");
447 assert_eq!(perm.resource, "documents");
448 assert_eq!(perm.instance, None);
449
450 let perm = Permission::parse("write:documents:123").unwrap();
451 assert_eq!(perm.action, "write");
452 assert_eq!(perm.resource, "documents");
453 assert_eq!(perm.instance, Some("123".to_string()));
454 }
455
456 #[test]
457 fn test_permission_matching() {
458 let perm1 = Permission::new("read", "documents");
459 let perm2 = Permission::new("read", "documents");
460 let perm3 = Permission::new("write", "documents");
461 let wildcard = Permission::new("*", "documents");
462
463 assert!(perm1.matches(&perm2));
464 assert!(!perm1.matches(&perm3));
465 assert!(wildcard.matches(&perm1));
466 assert!(wildcard.matches(&perm3));
467 }
468
469 #[test]
470 fn test_permission_implies() {
471 let general = Permission::new("read", "documents");
472 let specific = Permission::with_instance("read", "documents", "123");
473 let wildcard = Permission::new("*", "*");
474
475 assert!(general.implies(&specific));
476 assert!(!specific.implies(&general));
477 assert!(wildcard.implies(&general));
478 assert!(wildcard.implies(&specific));
479 }
480
481 #[test]
482 fn test_role_permissions() {
483 let mut role = Role::new("editor");
484 role.add_permission(Permission::new("read", "documents"));
485 role.add_permission(Permission::new("write", "documents"));
486
487 let read_perm = Permission::new("read", "documents");
488 let delete_perm = Permission::new("delete", "documents");
489
490 assert!(role.has_permission(&read_perm));
491 assert!(!role.has_permission(&delete_perm));
492 }
493
494 #[test]
495 fn test_user_permissions() {
496 let mut user_perms = UserPermissions::new("user123");
497 user_perms.add_permission(Permission::new("read", "profile"));
498 user_perms.add_role("user");
499
500 let role_resolver = |_: &str| Some(Role::new("user"));
501
502 let read_perm = Permission::new("read", "profile");
503 assert!(user_perms.has_permission(&read_perm, &role_resolver));
504 }
505
506 #[test]
507 fn test_permission_checker() {
508 let mut checker = PermissionChecker::new();
509 checker.create_default_roles();
510
511 checker.add_user_role("user123", "admin");
512
513 let result = checker.check_access("user123", "read", "documents").unwrap();
514 assert!(result);
515
516 let result = checker.check_access("user123", "delete", "system").unwrap();
517 assert!(result); }
519}