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 from_action(action: impl Into<String>) -> Self {
85 Self {
86 action: action.into(),
87 resource: "*".to_string(),
88 instance: None,
89 }
90 }
91
92 pub fn with_instance(
94 action: impl Into<String>,
95 resource: impl Into<String>,
96 instance: impl Into<String>,
97 ) -> Self {
98 Self {
99 action: action.into(),
100 resource: resource.into(),
101 instance: Some(instance.into()),
102 }
103 }
104
105 pub fn parse(permission_str: &str) -> Result<Self> {
107 let parts: Vec<&str> = permission_str.split(':').collect();
108
109 match parts.len() {
110 2 => Ok(Self::new(parts[0], parts[1])),
111 3 => Ok(Self::with_instance(parts[0], parts[1], parts[2])),
112 _ => Err(PermissionError::invalid_format(
113 format!("Invalid permission format: {permission_str}")
114 ).into()),
115 }
116 }
117
118}
119
120impl std::fmt::Display for Permission {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match &self.instance {
123 Some(instance) => write!(f, "{}:{}:{}", self.action, self.resource, instance),
124 None => write!(f, "{}:{}", self.action, self.resource),
125 }
126 }
127}
128
129impl Permission {
130 pub fn matches(&self, other: &Permission) -> bool {
132 if self.action != "*" && other.action != "*" && self.action != other.action {
134 return false;
135 }
136
137 if self.resource != "*" && other.resource != "*" && self.resource != other.resource {
139 return false;
140 }
141
142 match (&self.instance, &other.instance) {
144 (Some(self_instance), Some(other_instance)) => {
145 self_instance == "*" || other_instance == "*" || self_instance == other_instance
146 }
147 (None, None) => true,
148 (Some(_), None) => false, (None, Some(_)) => true, }
151 }
152
153 pub fn implies(&self, other: &Permission) -> bool {
155 let action_implies = self.action == "*" || self.action == other.action;
157 let resource_implies = self.resource == "*" || self.resource == other.resource;
158 let instance_implies = match (&self.instance, &other.instance) {
159 (None, _) => true, (Some(self_instance), Some(other_instance)) => {
161 self_instance == "*" || self_instance == other_instance
162 }
163 (Some(_), None) => false, };
165
166 action_implies && resource_implies && instance_implies
167 }
168}
169
170impl Role {
171 pub fn new(name: impl Into<String>) -> Self {
173 Self {
174 name: name.into(),
175 description: None,
176 permissions: HashSet::new(),
177 parent_roles: HashSet::new(),
178 active: true,
179 }
180 }
181
182 pub fn with_description(mut self, description: impl Into<String>) -> Self {
184 self.description = Some(description.into());
185 self
186 }
187
188 pub fn add_permission(&mut self, permission: Permission) {
190 self.permissions.insert(permission);
191 }
192
193 pub fn with_permissions(mut self, permissions: Vec<Permission>) -> Self {
195 for permission in permissions {
196 self.permissions.insert(permission);
197 }
198 self
199 }
200
201 pub fn remove_permission(&mut self, permission: &Permission) {
203 self.permissions.remove(permission);
204 }
205
206 pub fn add_parent_role(&mut self, parent_role: impl Into<String>) {
208 self.parent_roles.insert(parent_role.into());
209 }
210
211 pub fn has_permission(&self, permission: &Permission) -> bool {
213 self.permissions.iter().any(|p| p.implies(permission))
214 }
215
216 pub fn get_all_permissions(&self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> HashSet<Permission> {
218 let mut all_permissions = self.permissions.clone();
219
220 for parent_role_name in &self.parent_roles {
222 if let Some(parent_role) = role_resolver(parent_role_name) {
223 all_permissions.extend(parent_role.get_all_permissions(role_resolver));
224 }
225 }
226
227 all_permissions
228 }
229
230 pub fn set_active(&mut self, active: bool) {
232 self.active = active;
233 }
234}
235
236impl UserPermissions {
237 pub fn new(user_id: impl Into<String>) -> Self {
239 Self {
240 user_id: user_id.into(),
241 direct_permissions: HashSet::new(),
242 roles: HashSet::new(),
243 computed_permissions: None,
244 last_updated: chrono::Utc::now(),
245 }
246 }
247
248 pub fn add_permission(&mut self, permission: Permission) {
250 self.direct_permissions.insert(permission);
251 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
253 }
254
255 pub fn remove_permission(&mut self, permission: &Permission) {
257 self.direct_permissions.remove(permission);
258 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
260 }
261
262 pub fn add_role(&mut self, role: impl Into<String>) {
264 self.roles.insert(role.into());
265 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
267 }
268
269 pub fn remove_role(&mut self, role: &str) {
271 self.roles.remove(role);
272 self.computed_permissions = None; self.last_updated = chrono::Utc::now();
274 }
275
276 pub fn compute_permissions(&mut self, role_resolver: &dyn Fn(&str) -> Option<Role>) -> &HashSet<Permission> {
278 if self.computed_permissions.is_none() {
279 let mut all_permissions = self.direct_permissions.clone();
280
281 for role_name in &self.roles {
283 if let Some(role) = role_resolver(role_name) {
284 if role.active {
285 all_permissions.extend(role.get_all_permissions(role_resolver));
286 }
287 }
288 }
289
290 self.computed_permissions = Some(all_permissions);
291 }
292
293 self.computed_permissions.as_ref().unwrap()
294 }
295
296 pub fn has_permission(&mut self, permission: &Permission, role_resolver: &dyn Fn(&str) -> Option<Role>) -> bool {
298 let all_permissions = self.compute_permissions(role_resolver);
299 all_permissions.iter().any(|p| p.implies(permission))
300 }
301}
302
303impl PermissionChecker {
304 pub fn new() -> Self {
306 Self {
307 roles: HashMap::new(),
308 user_permissions: HashMap::new(),
309 resource_hierarchy: HashMap::new(),
310 }
311 }
312
313 pub fn add_role(&mut self, role: Role) {
315 self.roles.insert(role.name.clone(), role);
316 }
317
318 pub fn remove_role(&mut self, role_name: &str) {
320 self.roles.remove(role_name);
321 }
322
323 pub fn get_role(&self, role_name: &str) -> Option<&Role> {
325 self.roles.get(role_name)
326 }
327
328 pub fn set_user_permissions(&mut self, user_permissions: UserPermissions) {
330 self.user_permissions.insert(user_permissions.user_id.clone(), user_permissions);
331 }
332
333 pub fn get_user_permissions(&self, user_id: &str) -> Option<&UserPermissions> {
335 self.user_permissions.get(user_id)
336 }
337
338 pub fn get_user_permissions_mut(&mut self, user_id: &str) -> Option<&mut UserPermissions> {
340 self.user_permissions.get_mut(user_id)
341 }
342
343 pub fn add_user_permission(&mut self, user_id: &str, permission: Permission) {
345 let user_perms = self.user_permissions
346 .entry(user_id.to_string())
347 .or_insert_with(|| UserPermissions::new(user_id));
348
349 user_perms.add_permission(permission);
350 }
351
352 pub fn add_user_role(&mut self, user_id: &str, role: impl Into<String>) {
354 let user_perms = self.user_permissions
355 .entry(user_id.to_string())
356 .or_insert_with(|| UserPermissions::new(user_id));
357
358 user_perms.add_role(role);
359 }
360
361 pub fn check_permission(&mut self, user_id: &str, permission: &Permission) -> Result<bool> {
363 let user_perms = self.user_permissions
364 .get_mut(user_id)
365 .ok_or_else(|| PermissionError::access_denied(
366 permission.to_string(),
367 "unknown user".to_string(),
368 ))?;
369
370 let role_resolver = |role_name: &str| self.roles.get(role_name).cloned();
371
372 Ok(user_perms.has_permission(permission, &role_resolver))
373 }
374
375 pub fn check_access(&mut self, user_id: &str, action: &str, resource: &str) -> Result<bool> {
377 let permission = Permission::new(action, resource);
378 self.check_permission(user_id, &permission)
379 }
380
381 pub fn check_instance_access(
383 &mut self,
384 user_id: &str,
385 action: &str,
386 resource: &str,
387 instance: &str,
388 ) -> Result<bool> {
389 let permission = Permission::with_instance(action, resource, instance);
390 self.check_permission(user_id, &permission)
391 }
392
393 pub fn check_token_permission(&mut self, token: &AuthToken, permission: &Permission) -> Result<bool> {
395 if !token.is_valid() {
396 return Ok(false);
397 }
398
399 let required_scope = permission.to_string();
401 if !token.has_scope(&required_scope) {
402 let wildcard_action = format!("*:{}", permission.resource);
404 let wildcard_resource = format!("{}:*", permission.action);
405 let wildcard_all = "*:*".to_string();
406
407 if !token.has_scope(&wildcard_action)
408 && !token.has_scope(&wildcard_resource)
409 && !token.has_scope(&wildcard_all) {
410 return Ok(false);
411 }
412 }
413
414 self.check_permission(&token.user_id, permission)
416 }
417
418 pub fn create_default_roles(&mut self) {
420 let mut admin_role = Role::new("admin")
422 .with_description("Administrator with full access");
423 admin_role.add_permission(Permission::new("*", "*"));
424 self.add_role(admin_role);
425
426 let mut user_role = Role::new("user")
428 .with_description("Regular user with basic access");
429 user_role.add_permission(Permission::new("read", "profile"));
430 user_role.add_permission(Permission::new("write", "profile"));
431 user_role.add_permission(Permission::new("read", "public"));
432 self.add_role(user_role);
433
434 let mut guest_role = Role::new("guest")
436 .with_description("Guest user with read-only access");
437 guest_role.add_permission(Permission::new("read", "public"));
438 self.add_role(guest_role);
439 }
440
441 pub fn load_permissions(&mut self, _config: &str) -> Result<()> {
443 self.create_default_roles();
446 Ok(())
447 }
448}
449
450impl Default for PermissionChecker {
451 fn default() -> Self {
452 Self::new()
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459
460 #[test]
461 fn test_permission_parsing() {
462 let perm = Permission::parse("read:documents").unwrap();
463 assert_eq!(perm.action, "read");
464 assert_eq!(perm.resource, "documents");
465 assert_eq!(perm.instance, None);
466
467 let perm = Permission::parse("write:documents:123").unwrap();
468 assert_eq!(perm.action, "write");
469 assert_eq!(perm.resource, "documents");
470 assert_eq!(perm.instance, Some("123".to_string()));
471 }
472
473 #[test]
474 fn test_permission_matching() {
475 let perm1 = Permission::new("read", "documents");
476 let perm2 = Permission::new("read", "documents");
477 let perm3 = Permission::new("write", "documents");
478 let wildcard = Permission::new("*", "documents");
479
480 assert!(perm1.matches(&perm2));
481 assert!(!perm1.matches(&perm3));
482 assert!(wildcard.matches(&perm1));
483 assert!(wildcard.matches(&perm3));
484 }
485
486 #[test]
487 fn test_permission_implies() {
488 let general = Permission::new("read", "documents");
489 let specific = Permission::with_instance("read", "documents", "123");
490 let wildcard = Permission::new("*", "*");
491
492 assert!(general.implies(&specific));
493 assert!(!specific.implies(&general));
494 assert!(wildcard.implies(&general));
495 assert!(wildcard.implies(&specific));
496 }
497
498 #[test]
499 fn test_role_permissions() {
500 let mut role = Role::new("editor");
501 role.add_permission(Permission::new("read", "documents"));
502 role.add_permission(Permission::new("write", "documents"));
503
504 let read_perm = Permission::new("read", "documents");
505 let delete_perm = Permission::new("delete", "documents");
506
507 assert!(role.has_permission(&read_perm));
508 assert!(!role.has_permission(&delete_perm));
509 }
510
511 #[test]
512 fn test_user_permissions() {
513 let mut user_perms = UserPermissions::new("user123");
514 user_perms.add_permission(Permission::new("read", "profile"));
515 user_perms.add_role("user");
516
517 let role_resolver = |_: &str| Some(Role::new("user"));
518
519 let read_perm = Permission::new("read", "profile");
520 assert!(user_perms.has_permission(&read_perm, &role_resolver));
521 }
522
523 #[test]
524 fn test_permission_checker() {
525 let mut checker = PermissionChecker::new();
526 checker.create_default_roles();
527
528 checker.add_user_role("user123", "admin");
529
530 let result = checker.check_access("user123", "read", "documents").unwrap();
531 assert!(result);
532
533 let result = checker.check_access("user123", "delete", "system").unwrap();
534 assert!(result); }
536}