rbac_policy/
rbac_policy.rs1use gatehouse::*;
18use std::collections::HashSet;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21enum Role {
22 Admin,
23 Editor,
24 Viewer,
25}
26
27#[derive(Debug, Clone)]
28struct User {
29 name: &'static str,
30 roles: HashSet<Role>,
31}
32
33#[derive(Debug, Clone)]
34struct Document {
35 name: &'static str,
36 required_roles: HashSet<Role>,
38}
39
40#[derive(Debug, Clone)]
41struct ReadAction;
42
43struct DocumentDomain;
44
45impl PolicyDomain for DocumentDomain {
46 type Subject = User;
47 type Action = ReadAction;
48 type Resource = Document;
49 type Context = ();
50}
51
52fn user<const N: usize>(name: &'static str, roles: [Role; N]) -> User {
53 User {
54 name,
55 roles: roles.into_iter().collect(),
56 }
57}
58
59fn document<const N: usize>(name: &'static str, required_roles: [Role; N]) -> Document {
60 Document {
61 name,
62 required_roles: required_roles.into_iter().collect(),
63 }
64}
65
66#[tokio::main]
67async fn main() {
68 let rbac_policy = RbacPolicy::<DocumentDomain, _, _>::new(
72 |_action: &ReadAction, doc: &Document| doc.required_roles.iter().copied().collect(),
73 |user: &User| user.roles.iter().copied().collect(),
74 );
75
76 let mut checker = PermissionChecker::<DocumentDomain>::new();
77 checker.add_policy(rbac_policy);
78
79 let admin = user("admin", [Role::Admin]);
80 let editor = user("editor", [Role::Editor]);
81 let multi_role = user("editor+viewer", [Role::Editor, Role::Viewer]);
82 let no_roles = user("no-roles", []);
83
84 let admin_doc = document("admin handbook", [Role::Admin]);
85 let editor_doc = document("style guide", [Role::Editor]);
86 let shared_doc = document("team wiki", [Role::Editor, Role::Viewer]);
87
88 let cases = [
90 (&admin, &admin_doc, true),
91 (&admin, &editor_doc, false), (&editor, &admin_doc, false),
93 (&editor, &editor_doc, true),
94 (&multi_role, &editor_doc, true),
95 (&multi_role, &shared_doc, true),
96 (&no_roles, &admin_doc, false),
97 (&no_roles, &shared_doc, false),
98 ];
99
100 let session = EvaluationSession::empty();
101 let action = ReadAction;
102 let context = ();
103
104 println!("{:<16} {:<16} verdict", "user", "document");
105 println!("{}", "-".repeat(42));
106 for (user, document, expected_granted) in cases {
107 let decision = checker
108 .bind(&session, user, &action, &context)
109 .check(document)
110 .await;
111 println!(
112 "{:<16} {:<16} {}",
113 user.name,
114 document.name,
115 if decision.is_granted() {
116 "GRANTED"
117 } else {
118 "DENIED"
119 }
120 );
121 assert_eq!(decision.is_granted(), expected_granted);
122 }
123
124 println!("\nWhy the editor is denied the admin handbook:");
130 let decision = checker
131 .bind(&session, &editor, &action, &context)
132 .check(&admin_doc)
133 .await;
134 println!("{}", decision.display_trace());
135}