1use super::entity::{
2 permission::{Model as Permission, PermissionId},
3 resource::{Model as Resource, ResourceId},
4 role::{Model as Role, RoleId},
5 role_hierarchy::Model as RoleHierarchy,
6 role_permission::Model as RolePermission,
7 user::UserId,
8 user_override::Model as UserOverride,
9 user_role::Model as UserRole,
10};
11use super::{Error, WILDCARD};
12
13mod loader;
14mod permission_request;
15mod resource_request;
16mod role_hierarchy_impl;
17mod snapshot;
18
19pub use permission_request::*;
20pub use resource_request::*;
21use role_hierarchy_impl::*;
22pub use snapshot::*;
23
24use std::collections::{HashMap, HashSet};
25
26pub struct RbacEngine {
27 resources: HashMap<ResourceRequest, Resource>,
28 permissions: HashMap<PermissionRequest, Permission>,
29 wildcard_resources: HashMap<ResourceId, Resource>,
30 wildcard_permissions: HashMap<PermissionId, Permission>,
31 roles: HashMap<RoleId, Role>,
32 user_roles: HashMap<UserId, RoleId>,
33 role_permissions: HashMap<RoleId, HashSet<(PermissionId, ResourceId)>>,
34 user_overrides: HashMap<UserId, Vec<UserOverride>>,
35 role_hierarchy: HashMap<RoleId, Vec<RoleId>>, }
37
38impl std::fmt::Debug for RbacEngine {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 write!(f, "RbacEngine")
41 }
42}
43
44#[derive(Debug, PartialEq, Eq)]
45pub struct RbacUserRolePermissions {
46 pub role: Role,
47 pub resource_permissions: RbacPermissionsByResources,
48}
49
50pub type RbacRolesAndRanks = Vec<(Role, u32)>;
51
52pub type RbacRoleHierarchyList = Vec<RoleHierarchy>;
53
54pub type RbacResourcesAndPermissions = (Vec<Resource>, Vec<Permission>);
55
56pub type RbacPermissionsByResources = Vec<(Resource, Vec<Permission>)>;
57
58impl RbacEngine {
59 pub fn from_snapshot(
60 RbacSnapshot {
61 resources: resources_rows,
62 permissions: permissions_rows,
63 roles: roles_rows,
64 user_roles: user_roles_rows,
65 role_permissions: role_permissions_rows,
66 user_overrides: user_overrides_rows,
67 role_hierarchy: role_hierarchy_rows,
68 }: RbacSnapshot,
69 ) -> Self {
70 let mut resources: HashMap<ResourceRequest, Resource> = Default::default();
71 let mut wildcard_resources = HashMap::new();
72 for resource in resources_rows {
73 if resource.schema.as_deref() == Some(WILDCARD) || resource.table == WILDCARD {
74 wildcard_resources.insert(resource.id, resource);
75 } else {
76 resources.insert(resource.clone().into(), resource);
77 }
78 }
79
80 let mut permissions: HashMap<PermissionRequest, Permission> = Default::default();
81 let mut wildcard_permissions = HashMap::new();
82 for permission in permissions_rows {
83 if permission.action == WILDCARD {
84 wildcard_permissions.insert(permission.id, permission);
85 } else {
86 permissions.insert(permission.clone().into(), permission);
87 }
88 }
89
90 let roles: HashMap<RoleId, Role> = roles_rows.into_iter().map(|r| (r.id, r)).collect();
91
92 let mut user_roles: HashMap<UserId, RoleId> = Default::default();
93 for user_role in user_roles_rows {
94 user_roles.insert(user_role.user_id, user_role.role_id);
95 }
96
97 let mut role_permissions: HashMap<RoleId, HashSet<(PermissionId, ResourceId)>> =
98 Default::default();
99 for rp in role_permissions_rows {
100 let set = role_permissions.entry(rp.role_id).or_default();
101 set.insert((rp.permission_id, rp.resource_id));
102 }
103
104 let mut user_overrides: HashMap<UserId, Vec<UserOverride>> = Default::default();
105 for user_override in user_overrides_rows {
106 user_overrides
107 .entry(user_override.user_id)
108 .or_default()
109 .push(user_override);
110 }
111
112 let mut role_hierarchy: HashMap<RoleId, Vec<RoleId>> = Default::default();
113 for rh in role_hierarchy_rows {
114 role_hierarchy
115 .entry(rh.super_role_id)
116 .or_default()
117 .push(rh.role_id);
118 }
119
120 RbacEngine {
121 resources,
122 permissions,
123 wildcard_resources,
124 wildcard_permissions,
125 roles,
126 user_roles,
127 role_permissions,
128 user_overrides,
129 role_hierarchy,
130 }
131 }
132
133 fn get_user_role_ids(&self, user_id: &UserId) -> Result<HashSet<RoleId>, Error> {
135 if let Some(role) = self.user_roles.get(&user_id) {
136 let mut user_roles = HashSet::new();
137 for role in enumerate_role(*role, &self.role_hierarchy) {
138 if !self.roles.contains_key(&role) {
139 return Err(Error::RoleNotFound(format!("{role:?}")));
140 }
141 user_roles.insert(role);
142 }
143 Ok(user_roles)
144 } else {
145 Err(Error::UserNotFound(format!("{user_id:?}")))
146 }
147 }
148
149 pub fn get_roles_and_ranks(&self) -> Result<RbacRolesAndRanks, Error> {
150 let mut all_roles = Vec::new();
151 for role_id in self.roles.keys() {
152 all_roles.push((
153 self.roles
154 .get(role_id)
155 .cloned()
156 .ok_or_else(|| Error::RoleNotFound(format!("{role_id:?}")))?,
157 enumerate_role(*role_id, &self.role_hierarchy).len() as u32,
158 ));
159 }
160 all_roles.sort_by_key(|r| (-(r.1 as i64), r.0.id));
162 Ok(all_roles)
163 }
164
165 pub fn get_user_role_permissions(
166 &self,
167 user_id: UserId,
168 ) -> Result<RbacUserRolePermissions, Error> {
169 let mut user_roles: Vec<RoleId> = self.get_user_role_ids(&user_id)?.into_iter().collect();
170 user_roles.sort();
171
172 let mut role_permissions: HashSet<(PermissionId, ResourceId)> = Default::default();
173
174 for role_id in user_roles {
175 if let Some(items) = self.role_permissions.get(&role_id) {
176 role_permissions.extend(items.into_iter());
177 }
178 }
179
180 if let Some(user_overrides) = self.user_overrides.get(&user_id) {
181 for over in user_overrides {
182 let role_permission = (over.permission_id, over.resource_id);
183 if role_permissions.contains(&role_permission) {
184 if !over.grant {
185 role_permissions.remove(&role_permission);
186 }
187 } else if over.grant {
188 role_permissions.insert(role_permission);
189 }
190 }
191 }
192
193 Ok(RbacUserRolePermissions {
194 role: self
195 .roles
196 .get(&self.user_roles.get(&user_id).expect("Checked above"))
197 .expect("Checked above")
198 .to_owned(),
199 resource_permissions: self.group_permissions_by_resources(
200 role_permissions.into_iter().map(|(p, r)| (r, p)),
201 )?,
202 })
203 }
204
205 pub fn list_resources_and_permissions(&self) -> RbacResourcesAndPermissions {
206 (
207 self.resources
208 .values()
209 .chain(self.wildcard_resources.values())
210 .cloned()
211 .collect(),
212 self.permissions
213 .values()
214 .chain(self.wildcard_permissions.values())
215 .cloned()
216 .collect(),
217 )
218 }
219
220 pub fn list_role_hierarchy_edges(&self, role_id: RoleId) -> Vec<RoleHierarchy> {
221 list_role_hierarchy_edges(role_id, &self.role_hierarchy)
222 }
223
224 fn group_permissions_by_resources(
225 &self,
226 items: impl Iterator<Item = (ResourceId, PermissionId)>,
227 ) -> Result<RbacPermissionsByResources, Error> {
228 let mut map: HashMap<ResourceId, (Resource, Vec<Permission>)> = Default::default();
229
230 for item in items {
231 let permission = if let Some(p) = self.wildcard_permissions.get(&item.1) {
232 p
233 } else {
234 self.permissions
235 .values()
236 .find(|p| p.id == item.1)
237 .ok_or_else(|| Error::PermissionNotFound(format!("{:?}", item.1)))?
238 };
239
240 let resource = if let Some(r) = self.wildcard_resources.get(&item.0) {
241 r
242 } else {
243 self.resources
244 .values()
245 .find(|r| r.id == item.0)
246 .ok_or_else(|| Error::ResourceNotFound(format!("{:?}", item.0)))?
247 };
248
249 map.entry(item.0)
250 .or_insert_with(|| (resource.to_owned(), Default::default()))
251 .1
252 .push(permission.to_owned());
253 }
254
255 let mut vec: Vec<_> = map.into_values().collect();
256 vec.sort_by_key(|r| r.0.id);
257 vec.iter_mut().for_each(|r| r.1.sort_by_key(|p| p.id));
258 Ok(vec)
259 }
260
261 pub fn list_role_permissions_by_resources(
262 &self,
263 role_id: RoleId,
264 ) -> Result<RbacPermissionsByResources, Error> {
265 self.group_permissions_by_resources(
266 self.role_permissions
267 .get(&role_id)
268 .ok_or_else(|| Error::RoleNotFound(format!("{role_id:?}")))?
269 .iter()
270 .map(|(p, r)| (*r, *p)),
271 )
272 }
273
274 pub fn user_can<P, R>(&self, user_id: UserId, permission: P, resource: R) -> Result<bool, Error>
275 where
276 P: Into<PermissionRequest>,
277 R: Into<ResourceRequest>,
278 {
279 let resource = resource.into();
280 let permission = permission.into();
281 let resource = self.resources.get(&resource);
282 let permission = self.permissions.get(&permission);
283
284 let user_roles = self.get_user_role_ids(&user_id)?;
286
287 if let (Some(permission), Some(resource)) = (permission, resource) {
288 if let Some(user_overrides) = self.user_overrides.get(&user_id) {
289 for user_override in user_overrides {
290 if user_override.permission_id == permission.id
291 && user_override.resource_id == resource.id
292 {
293 return Ok(user_override.grant);
294 }
295 }
296 }
297 }
298
299 for role_id in user_roles {
300 if let Some(role_permissions) = self.role_permissions.get(&role_id) {
301 if let (Some(permission), Some(resource)) = (permission, resource) {
302 if role_permissions.contains(&(permission.id, resource.id)) {
303 return Ok(true);
304 }
305 }
306 for (permission_id, resource_id) in role_permissions {
307 let is_wildcard_permission =
308 self.is_wildcard_permission(*permission_id, permission);
309 let is_wildcard_resource = self.is_wildcard_resource(*resource_id, resource);
310 if let Some(resource) = &resource {
311 if resource_id == &resource.id && is_wildcard_permission {
312 return Ok(true);
313 }
314 }
315 if let Some(permission) = &permission {
316 if permission_id == &permission.id && is_wildcard_resource {
317 return Ok(true);
318 }
319 }
320 if is_wildcard_permission && is_wildcard_resource {
321 return Ok(true);
322 }
323 }
324 }
325 }
326
327 if resource.is_none() {
328 return Err(Error::ResourceNotFound(format!("{resource:?}")));
329 }
330
331 if permission.is_none() {
332 return Err(Error::PermissionNotFound(format!("{permission:?}")));
333 }
334
335 Ok(false)
336 }
337
338 fn is_wildcard_resource(&self, id: ResourceId, target: Option<&Resource>) -> bool {
339 if let Some(resource) = self.wildcard_resources.get(&id) {
340 if let Some(target) = target {
341 let schema_match = resource.schema.is_none()
342 || resource.schema.as_ref().unwrap() == WILDCARD
343 || resource.schema == target.schema;
344 let table_match = resource.table == WILDCARD || resource.table == target.table;
345 schema_match && table_match
346 } else {
347 (resource.schema.is_none() || resource.schema.as_ref().unwrap() == WILDCARD)
348 && resource.table == WILDCARD
349 }
350 } else {
351 false
352 }
353 }
354
355 fn is_wildcard_permission(&self, id: PermissionId, _: Option<&Permission>) -> bool {
356 if let Some(permission) = self.wildcard_permissions.get(&id) {
357 return permission.action == WILDCARD;
358 }
359 false
360 }
361}
362
363#[cfg(test)]
364mod test {
365 use super::*;
366
367 #[allow(non_snake_case)]
368 fn Object(r: &str) -> Table<'_> {
369 Table(r)
370 }
371
372 fn resource(table: &str) -> Resource {
373 Resource {
374 id: ResourceId(0),
375 schema: None,
376 table: table.to_owned(),
377 }
378 }
379
380 fn permission(action: &str) -> Permission {
381 Permission {
382 id: PermissionId(0),
383 action: action.to_owned(),
384 }
385 }
386
387 fn role(role: &str) -> Role {
388 Role {
389 id: RoleId(0),
390 role: role.to_owned(),
391 }
392 }
393
394 fn seed_1() -> RbacSnapshot {
395 let mut snapshot = RbacSnapshot::default();
396 snapshot.set_resources(vec![
397 resource("book"),
398 resource("paper"),
399 resource("pen"),
400 resource("*"),
401 ]);
402 snapshot.set_permissions(vec![
403 permission("browse"), permission("buy"), permission("replace"), permission("dispose"), permission("*"), ]);
409 snapshot.set_roles(vec![
410 role("admin"),
411 role("manager"),
412 role("clerk"),
413 role("auditor"),
414 ]);
415 snapshot.set_user_role(UserId(1), "admin");
416 snapshot.set_user_role(UserId(2), "manager");
417 snapshot.set_user_role(UserId(3), "clerk");
418 snapshot.set_user_role(UserId(4), "auditor");
419 snapshot.set_user_role(UserId(5), "clerk");
420
421 snapshot.add_role_hierarchy("manager", "admin");
422 snapshot.add_role_hierarchy("clerk", "manager");
423 snapshot.add_role_hierarchy("auditor", "admin");
424
425 snapshot.add_role_permission("clerk", Action("browse"), Object("pen"));
426 snapshot.add_role_permission("clerk", Action("browse"), Object("paper"));
427 snapshot.add_role_permission("clerk", Action("dispose"), Object("paper"));
428
429 snapshot.add_role_permission("manager", Action("browse"), Object("book"));
430 snapshot.add_role_permission("manager", Action("buy"), Object("book"));
431 snapshot.add_role_permission("manager", Action("dispose"), Object("book"));
432 snapshot.add_role_permission("manager", Action("replace"), Object("paper"));
433
434 snapshot.add_role_permission("auditor", Action("browse"), Object("*"));
435
436 snapshot.add_user_override(UserId(5), Action("buy"), Object("pen"), true);
437 snapshot.add_user_override(UserId(5), Action("dispose"), Object("paper"), false);
438
439 snapshot.add_role_permission("admin", Action("*"), Object("*"));
440
441 snapshot
442 }
443
444 #[test]
445 #[rustfmt::skip]
446 fn test_rbac_engine_basic() {
447 let admin = UserId(1);
448 let manager = UserId(2);
449 let clerk = UserId(3);
450 let auditor = UserId(4);
451 let designer = UserId(5);
452
453 let engine = RbacEngine::from_snapshot(seed_1());
454
455 for item in ["pen", "paper"] {
457 assert!(engine.user_can(clerk, Action("browse"), Object(item)).unwrap());
458 assert!(engine.user_can(manager, Action("browse"), Object(item)).unwrap());
459 assert!(engine.user_can(admin, Action("browse"), Object(item)).unwrap());
460 assert!(engine.user_can(auditor, Action("browse"), Object(item)).unwrap());
462 }
463
464 for user in [clerk, manager, admin] {
466 assert!(engine.user_can(user, Action("dispose"), Object("paper")).unwrap());
467 }
468 for user in [designer, auditor] {
469 assert!(!engine.user_can(user, Action("dispose"), Object("paper")).unwrap());
470 }
471
472 for user in [clerk, designer] {
474 assert!(!engine.user_can(user, Action("browse"), Object("book")).unwrap());
475 }
476
477 for user in [admin, manager] {
478 assert!(engine.user_can(user, Action("browse"), Object("book")).unwrap());
479 assert!(engine.user_can(user, Action("buy"), Object("book")).unwrap());
480 assert!(engine.user_can(user, Action("dispose"), Object("book")).unwrap());
481 }
482
483 for action in ["buy", "replace", "dispose"] {
485 for item in ["book", "paper", "pen"] {
486 assert!(!engine.user_can(auditor, Action(action), Object(item)).unwrap());
487 }
488 }
489
490 assert!(!engine.user_can(manager, Action("replace"), Object("book")).unwrap());
492 assert!(engine.user_can(admin, Action("replace"), Object("book")).unwrap());
493
494 assert!(!engine.user_can(clerk, Action("replace"), Object("paper")).unwrap());
496 assert!(!engine.user_can(designer, Action("replace"), Object("paper")).unwrap());
497 assert!(engine.user_can(manager, Action("replace"), Object("paper")).unwrap());
498 assert!(engine.user_can(admin, Action("replace"), Object("paper")).unwrap());
499
500 for user in [clerk, manager, designer] {
502 assert!(!engine.user_can(user, Action("buy"), Object("paper")).unwrap());
503 }
504 assert!(engine.user_can(admin, Action("buy"), Object("paper")).unwrap());
505
506 for user in [designer, admin] {
508 assert!(engine.user_can(user, Action("buy"), Object("pen")).unwrap());
509 }
510 for user in [clerk, manager] {
511 assert!(!engine.user_can(user, Action("buy"), Object("pen")).unwrap());
512 }
513
514 for action in ["replace", "dispose"] {
516 assert!(engine.user_can(admin, Action(action), Object("pen")).unwrap());
517 }
518
519 assert!(engine.user_can(admin, Action("?"), Object("?")).is_ok());
521 assert!(engine.user_can(manager, Action("?"), Object("?")).is_err());
522 assert!(engine.user_can(clerk, Action("?"), Object("?")).is_err());
523
524 assert_eq!(engine.get_user_role_permissions(clerk).unwrap(), RbacUserRolePermissions {
525 role: Role {
526 id: RoleId(3),
527 role: "clerk".to_owned(),
528 },
529 resource_permissions: vec![
530 (
531 Resource { id: ResourceId(2), schema: None, table: "paper".to_owned() },
532 vec![
533 Permission { id: PermissionId(1), action: "browse".to_owned() },
534 Permission { id: PermissionId(4), action: "dispose".to_owned() },
535 ]
536 ),
537 (
538 Resource { id: ResourceId(3), schema: None, table: "pen".to_owned() },
539 vec![Permission { id: PermissionId(1), action: "browse".to_owned() }]
540 ),
541 ],
542 });
543
544 assert_eq!(engine.get_user_role_permissions(designer).unwrap(), RbacUserRolePermissions {
545 role: Role {
546 id: RoleId(3),
547 role: "clerk".to_owned(),
548 },
549 resource_permissions: vec![
550 (
551 Resource { id: ResourceId(2), schema: None, table: "paper".to_owned() },
552 vec![Permission { id: PermissionId(1), action: "browse".to_owned() }]
553 ),
554 (
555 Resource { id: ResourceId(3), schema: None, table: "pen".to_owned() },
556 vec![
557 Permission { id: PermissionId(1), action: "browse".to_owned() },
558 Permission { id: PermissionId(2), action: "buy".to_owned() },
559 ]
560 ),
561 ],
562 });
563
564 assert_eq!(engine.get_roles_and_ranks().unwrap(), vec![
565 (Role { id: RoleId(1), role: "admin".to_owned() }, 4), (Role { id: RoleId(2), role: "manager".to_owned() }, 2), (Role { id: RoleId(3), role: "clerk".to_owned() }, 1), (Role { id: RoleId(4), role: "auditor".to_owned() }, 1), ]);
570
571 assert_eq!(engine.list_role_hierarchy_edges(RoleId(1)), vec![
572 RoleHierarchy { super_role_id: RoleId(1), role_id: RoleId(2) },
573 RoleHierarchy { super_role_id: RoleId(1), role_id: RoleId(4) },
574 RoleHierarchy { super_role_id: RoleId(2), role_id: RoleId(3) },
575 ]);
576
577 assert_eq!(engine.list_role_hierarchy_edges(RoleId(2)), vec![
578 RoleHierarchy { super_role_id: RoleId(2), role_id: RoleId(3) },
579 ]);
580
581 assert_eq!(engine.list_role_hierarchy_edges(RoleId(3)), vec![]);
582
583 assert_eq!(engine.list_role_permissions_by_resources(RoleId(2)).unwrap(), vec![
584 (Resource { id: ResourceId(1), schema: None, table: "book".into() }, vec![
585 Permission { id: PermissionId(1), action: "browse".into() },
586 Permission { id: PermissionId(2), action: "buy".into() },
587 Permission { id: PermissionId(4), action: "dispose".into() },
588 ]),
589 (Resource { id: ResourceId(2), schema: None, table: "paper".into() }, vec![
590 Permission { id: PermissionId(3), action: "replace".into() },
591 ]),
592 ]);
593
594 assert_eq!(engine.list_role_permissions_by_resources(RoleId(4)).unwrap(), vec![
595 (Resource { id: ResourceId(4), schema: None, table: "*".into() }, vec![
596 Permission { id: PermissionId(1), action: "browse".into() },
597 ]),
598 ]);
599 }
600
601 #[rustfmt::skip]
602 fn seed_2() -> RbacSnapshot {
603 fn resource(schema: &str, table: &str) -> Resource {
604 Resource {
605 id: ResourceId(0),
606 schema: Some(schema.to_owned()),
607 table: table.to_owned(),
608 }
609 }
610
611 let mut snapshot = RbacSnapshot::default();
612 snapshot.set_resources(vec![
613 resource("departmentA", "book"),
614 resource("departmentB", "book"),
615 resource("departmentB", "CD"),
616 resource("*", "book"),
617 resource("departmentB", "*"),
618 resource("*", "*"),
619 ]);
620 snapshot.set_permissions(vec![
621 permission("browse"),
622 ]);
623 snapshot.set_roles(vec![
624 role("silver"),
625 role("gold"),
626 role("platinum"),
627 role("reader"),
628 role("admin"),
629 ]);
630 snapshot.set_user_role(UserId(1), "silver");
631 snapshot.set_user_role(UserId(2), "gold");
632 snapshot.set_user_role(UserId(3), "platinum");
633 snapshot.set_user_role(UserId(4), "reader");
634 snapshot.set_user_role(UserId(5), "admin");
635
636 snapshot.add_role_permission("silver", Action("browse"), SchemaTable("departmentA", "book"));
637 snapshot.add_role_permission("gold", Action("browse"), SchemaTable("departmentB", "book"));
638 snapshot.add_role_permission("platinum", Action("browse"), SchemaTable("departmentA", "book"));
639 snapshot.add_role_permission("platinum", Action("browse"), SchemaTable("departmentB", "*"));
640
641 snapshot.add_role_permission("reader", Action("browse"), SchemaTable("*", "book"));
642
643 snapshot.add_role_permission("admin", Action("browse"), SchemaTable("*", "*"));
644
645 snapshot
646 }
647
648 #[test]
649 #[rustfmt::skip]
650 fn test_rbac_engine_wildcard() {
651 let silver = UserId(1);
652 let gold = UserId(2);
653 let platinum = UserId(3);
654 let reader = UserId(4);
655 let admin = UserId(5);
656
657 let engine = RbacEngine::from_snapshot(seed_2());
658
659 assert!(engine.user_can(silver, Action("browse"), SchemaTable("departmentA", "book")).unwrap());
660 assert!(!engine.user_can(silver, Action("browse"), SchemaTable("departmentB", "book")).unwrap());
661 assert!(!engine.user_can(silver, Action("browse"), SchemaTable("departmentB", "CD")).unwrap());
662
663 assert!(!engine.user_can(gold, Action("browse"), SchemaTable("departmentA", "book")).unwrap());
664 assert!(engine.user_can(gold, Action("browse"), SchemaTable("departmentB", "book")).unwrap());
665 assert!(!engine.user_can(gold, Action("browse"), SchemaTable("departmentB", "CD")).unwrap());
666
667 assert!(engine.user_can(platinum, Action("browse"), SchemaTable("departmentA", "book")).unwrap());
668 assert!(engine.user_can(platinum, Action("browse"), SchemaTable("departmentB", "book")).unwrap());
669 assert!(engine.user_can(platinum, Action("browse"), SchemaTable("departmentB", "CD")).unwrap());
670
671 assert!(engine.user_can(reader, Action("browse"), SchemaTable("departmentA", "book")).unwrap());
672 assert!(engine.user_can(reader, Action("browse"), SchemaTable("departmentB", "book")).unwrap());
673 assert!(!engine.user_can(reader, Action("browse"), SchemaTable("departmentB", "CD")).unwrap());
674
675 assert!(engine.user_can(admin, Action("browse"), SchemaTable("departmentA", "book")).unwrap());
676 assert!(engine.user_can(admin, Action("browse"), SchemaTable("departmentB", "book")).unwrap());
677 assert!(engine.user_can(admin, Action("browse"), SchemaTable("departmentB", "CD")).unwrap());
678 }
679
680 #[rustfmt::skip]
681 fn seed_3() -> RbacSnapshot {
682 let mut snapshot = RbacSnapshot::default();
683 snapshot.set_resources(vec![
684 resource("book"),
685 resource("CD"),
686 resource("magazine"),
687 ]);
688 snapshot.set_permissions(vec![
689 permission("browse"),
690 ]);
691 snapshot.set_roles(vec![
692 role("A"),
693 role("B"),
694 role("C"),
695 role("A+B"),
696 role("A+C"),
697 role("A+B+C"),
698 role("(A+B)+C"),
699 ]);
700 snapshot.set_user_role(UserId(1), "A");
701 snapshot.set_user_role(UserId(2), "B");
702 snapshot.set_user_role(UserId(3), "C");
703 snapshot.set_user_role(UserId(4), "A+B");
704 snapshot.set_user_role(UserId(5), "A+C");
705 snapshot.set_user_role(UserId(6), "A+B+C");
706 snapshot.set_user_role(UserId(7), "(A+B)+C");
707
708 snapshot.add_role_permission("A", Action("browse"), Object("book"));
709 snapshot.add_role_permission("B", Action("browse"), Object("CD"));
710 snapshot.add_role_permission("C", Action("browse"), Object("magazine"));
711
712 snapshot.add_role_hierarchy("A", "A+B");
713 snapshot.add_role_hierarchy("B", "A+B");
714
715 snapshot.add_role_hierarchy("A", "A+C");
716 snapshot.add_role_hierarchy("C", "A+C");
717
718 snapshot.add_role_hierarchy("A", "A+B+C");
719 snapshot.add_role_hierarchy("B", "A+B+C");
720 snapshot.add_role_hierarchy("C", "A+B+C");
721
722 snapshot.add_role_hierarchy("A+B", "(A+B)+C");
723 snapshot.add_role_hierarchy("C", "(A+B)+C");
724
725 snapshot
726 }
727
728 #[test]
729 #[rustfmt::skip]
730 #[allow(non_snake_case)]
731 fn test_rbac_engine_hierarchy() {
732 let A = UserId(1);
733 let B = UserId(2);
734 let C = UserId(3);
735 let A_B = UserId(4);
736 let A_C = UserId(5);
737 let A_B_C = UserId(6);
738 let A_B_C_ = UserId(7);
739
740 let engine = RbacEngine::from_snapshot(seed_3());
741
742 assert!(engine.user_can(A, Action("browse"), Object("book")).unwrap());
743 assert!(!engine.user_can(A, Action("browse"), Object("CD")).unwrap());
744 assert!(!engine.user_can(A, Action("browse"), Object("magazine")).unwrap());
745
746 assert!(!engine.user_can(B, Action("browse"), Object("book")).unwrap());
747 assert!(engine.user_can(B, Action("browse"), Object("CD")).unwrap());
748 assert!(!engine.user_can(B, Action("browse"), Object("magazine")).unwrap());
749
750 assert!(!engine.user_can(C, Action("browse"), Object("book")).unwrap());
751 assert!(!engine.user_can(C, Action("browse"), Object("CD")).unwrap());
752 assert!(engine.user_can(C, Action("browse"), Object("magazine")).unwrap());
753
754 assert!(engine.user_can(A_B, Action("browse"), Object("book")).unwrap());
755 assert!(engine.user_can(A_B, Action("browse"), Object("CD")).unwrap());
756 assert!(!engine.user_can(A_B, Action("browse"), Object("magazine")).unwrap());
757
758 assert!(engine.user_can(A_C, Action("browse"), Object("book")).unwrap());
759 assert!(!engine.user_can(A_C, Action("browse"), Object("CD")).unwrap());
760 assert!(engine.user_can(A_C, Action("browse"), Object("magazine")).unwrap());
761
762 assert!(engine.user_can(A_B_C, Action("browse"), Object("book")).unwrap());
763 assert!(engine.user_can(A_B_C, Action("browse"), Object("CD")).unwrap());
764 assert!(engine.user_can(A_B_C, Action("browse"), Object("magazine")).unwrap());
765
766 assert!(engine.user_can(A_B_C_, Action("browse"), Object("book")).unwrap());
767 assert!(engine.user_can(A_B_C_, Action("browse"), Object("CD")).unwrap());
768 assert!(engine.user_can(A_B_C_, Action("browse"), Object("magazine")).unwrap());
769
770 assert_eq!(engine.get_roles_and_ranks().unwrap(), vec![
771 (Role { id: RoleId(7), role: "(A+B)+C".into() }, 5),
772 (Role { id: RoleId(6), role: "A+B+C".into() }, 4),
773 (Role { id: RoleId(4), role: "A+B".into() }, 3),
774 (Role { id: RoleId(5), role: "A+C".into() }, 3),
775 (Role { id: RoleId(1), role: "A".into() }, 1),
776 (Role { id: RoleId(2), role: "B".into() }, 1),
777 (Role { id: RoleId(3), role: "C".into() }, 1),
778 ]);
779
780 assert_eq!(engine.list_role_hierarchy_edges(RoleId(1)), vec![]);
781 assert_eq!(engine.list_role_hierarchy_edges(RoleId(2)), vec![]);
782 assert_eq!(engine.list_role_hierarchy_edges(RoleId(3)), vec![]);
783
784 assert_eq!(engine.list_role_hierarchy_edges(RoleId(4)), vec![
785 RoleHierarchy { super_role_id: RoleId(4), role_id: RoleId(1) },
786 RoleHierarchy { super_role_id: RoleId(4), role_id: RoleId(2) },
787 ]);
788
789 assert_eq!(engine.list_role_hierarchy_edges(RoleId(5)), vec![
790 RoleHierarchy { super_role_id: RoleId(5), role_id: RoleId(1) },
791 RoleHierarchy { super_role_id: RoleId(5), role_id: RoleId(3) },
792 ]);
793
794 assert_eq!(engine.list_role_hierarchy_edges(RoleId(6)), vec![
795 RoleHierarchy { super_role_id: RoleId(6), role_id: RoleId(1) },
796 RoleHierarchy { super_role_id: RoleId(6), role_id: RoleId(2) },
797 RoleHierarchy { super_role_id: RoleId(6), role_id: RoleId(3) },
798 ]);
799
800 assert_eq!(engine.list_role_hierarchy_edges(RoleId(7)), vec![
801 RoleHierarchy { super_role_id: RoleId(7), role_id: RoleId(4) },
802 RoleHierarchy { super_role_id: RoleId(7), role_id: RoleId(3) },
803 RoleHierarchy { super_role_id: RoleId(4), role_id: RoleId(1) },
804 RoleHierarchy { super_role_id: RoleId(4), role_id: RoleId(2) },
805 ]);
806 }
807
808 #[test]
809 fn test_unrestricted() {
810 let engine = RbacEngine::from_snapshot(RbacSnapshot::danger_unrestricted());
811 assert!(
812 engine
813 .user_can(UserId(0), Action("browse"), Object("book"))
814 .unwrap()
815 );
816 assert_eq!(
817 engine.get_user_role_permissions(UserId(0)).unwrap(),
818 RbacUserRolePermissions {
819 role: Role {
820 id: RoleId(1),
821 role: "unrestricted".to_owned(),
822 },
823 resource_permissions: vec![(
824 Resource {
825 id: ResourceId(1),
826 schema: None,
827 table: "*".to_owned(),
828 },
829 vec![Permission {
830 id: PermissionId(1),
831 action: "*".to_owned(),
832 }]
833 ),],
834 }
835 );
836 }
837}