axum_gate/authz/
access_policy.rs1use super::{AccessHierarchy, AccessScope};
8use crate::permissions::{PermissionId, Permissions};
9
10#[derive(Debug, Clone)]
16pub struct AccessPolicy<R, G>
17where
18 R: AccessHierarchy + Eq + std::fmt::Display,
19 G: Eq,
20{
21 role_requirements: Vec<AccessScope<R>>,
22 group_requirements: Vec<G>,
23 permission_requirements: Permissions,
24}
25
26impl<R, G> AccessPolicy<R, G>
27where
28 R: AccessHierarchy + Eq + std::fmt::Display,
29 G: Eq,
30{
31 pub fn deny_all() -> Self {
36 Self {
37 role_requirements: vec![],
38 group_requirements: vec![],
39 permission_requirements: Permissions::new(),
40 }
41 }
42
43 pub fn require_role(role: R) -> Self {
56 Self {
57 role_requirements: vec![AccessScope::new(role)],
58 group_requirements: vec![],
59 permission_requirements: Permissions::new(),
60 }
61 }
62
63 pub fn require_role_or_supervisor(role: R) -> Self {
80 Self {
81 role_requirements: vec![AccessScope::new(role).allow_supervisor()],
82 group_requirements: vec![],
83 permission_requirements: Permissions::new(),
84 }
85 }
86
87 pub fn require_group(group: G) -> Self {
101 Self {
102 role_requirements: vec![],
103 group_requirements: vec![group],
104 permission_requirements: Permissions::new(),
105 }
106 }
107
108 pub fn require_permission<P: Into<PermissionId>>(permission: P) -> Self {
125 let mut permissions = Permissions::new();
126 let id: PermissionId = permission.into();
127 permissions.bitmap_mut().insert(id.as_u64());
128 Self {
129 role_requirements: vec![],
130 group_requirements: vec![],
131 permission_requirements: permissions,
132 }
133 }
134
135 pub fn or_require_role(mut self, role: R) -> Self {
139 self.role_requirements.push(AccessScope::new(role));
140 self
141 }
142
143 pub fn or_require_role_or_supervisor(mut self, role: R) -> Self {
147 self.role_requirements
148 .push(AccessScope::new(role).allow_supervisor());
149 self
150 }
151
152 pub fn or_require_group(mut self, group: G) -> Self {
156 self.group_requirements.push(group);
157 self
158 }
159
160 pub fn or_require_permission<P: Into<PermissionId>>(mut self, permission: P) -> Self {
164 let id: PermissionId = permission.into();
165 self.permission_requirements
166 .bitmap_mut()
167 .insert(id.as_u64());
168 self
169 }
170
171 pub fn or_require_permissions<P: Into<PermissionId>>(mut self, permissions: Vec<P>) -> Self {
175 permissions.into_iter().for_each(|p| {
176 let id: PermissionId = p.into();
177 self.permission_requirements
178 .bitmap_mut()
179 .insert(id.as_u64());
180 });
181 self
182 }
183
184 pub fn role_requirements(&self) -> &[AccessScope<R>] {
186 &self.role_requirements
187 }
188
189 pub fn group_requirements(&self) -> &[G] {
191 &self.group_requirements
192 }
193
194 pub fn permission_requirements(&self) -> &Permissions {
196 &self.permission_requirements
197 }
198
199 pub fn denies_all(&self) -> bool {
204 self.role_requirements.is_empty()
205 && self.group_requirements.is_empty()
206 && self.permission_requirements.is_empty()
207 }
208
209 pub fn has_requirements(&self) -> bool {
214 !self.denies_all()
215 }
216
217 pub fn into_components(self) -> (Vec<AccessScope<R>>, Vec<G>, Permissions) {
221 (
222 self.role_requirements,
223 self.group_requirements,
224 self.permission_requirements,
225 )
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use crate::groups::Group;
233 use crate::permissions::PermissionId;
234 use crate::roles::Role;
235
236 #[test]
237 fn deny_all_creates_empty_policy() {
238 let policy: AccessPolicy<Role, Group> = AccessPolicy::deny_all();
239 assert!(policy.denies_all());
240 assert!(!policy.has_requirements());
241 assert!(policy.role_requirements().is_empty());
242 assert!(policy.group_requirements().is_empty());
243 assert!(policy.permission_requirements().is_empty());
244 }
245
246 #[test]
247 fn require_role_creates_role_policy() {
248 let policy: AccessPolicy<Role, Group> = AccessPolicy::require_role(Role::Admin);
249 assert!(!policy.denies_all());
250 assert!(policy.has_requirements());
251 assert_eq!(policy.role_requirements().len(), 1);
252 assert!(policy.group_requirements().is_empty());
253 assert!(policy.permission_requirements().is_empty());
254 }
255
256 #[test]
257 fn require_role_or_supervisor_creates_supervisor_policy() {
258 let policy: AccessPolicy<Role, Group> =
259 AccessPolicy::require_role_or_supervisor(Role::Moderator);
260 assert!(!policy.denies_all());
261 assert!(policy.has_requirements());
262 assert_eq!(policy.role_requirements().len(), 1);
263 assert!(policy.role_requirements()[0].allow_supervisor_access);
264 }
265
266 #[test]
267 fn require_group_creates_group_policy() {
268 let policy: AccessPolicy<Role, Group> =
269 AccessPolicy::require_group(Group::new("engineering"));
270 assert!(!policy.denies_all());
271 assert!(policy.has_requirements());
272 assert!(policy.role_requirements().is_empty());
273 assert_eq!(policy.group_requirements().len(), 1);
274 assert!(policy.permission_requirements().is_empty());
275 }
276
277 #[test]
278 fn require_permission_creates_permission_policy() {
279 let permission_name = "read:api";
280 let expected_id = PermissionId::from(permission_name).as_u64();
281 let policy: AccessPolicy<Role, Group> = AccessPolicy::require_permission(permission_name);
282 assert!(!policy.denies_all());
283 assert!(policy.has_requirements());
284 assert!(policy.role_requirements().is_empty());
285 assert!(policy.group_requirements().is_empty());
286 assert!(
287 policy
288 .permission_requirements()
289 .iter()
290 .any(|id| id == expected_id)
291 );
292 }
293
294 #[test]
295 fn builder_methods_add_requirements() {
296 let base_perms = vec!["read:api", "write:api", "admin:panel"];
297 let policy: AccessPolicy<Role, Group> = AccessPolicy::require_role(Role::Admin)
298 .or_require_role_or_supervisor(Role::Moderator)
299 .or_require_group(Group::new("engineering"))
300 .or_require_permission(base_perms[0])
301 .or_require_permissions(vec![base_perms[1], base_perms[2]]);
302
303 assert!(!policy.denies_all());
304 assert!(policy.has_requirements());
305 assert_eq!(policy.role_requirements().len(), 2);
306 assert_eq!(policy.group_requirements().len(), 1);
307
308 let collected: Vec<u64> = policy.permission_requirements().iter().collect();
309 for name in &base_perms {
310 let id = PermissionId::from(*name).as_u64();
311 assert!(collected.contains(&id), "missing permission {}", name);
312 }
313 }
314
315 #[test]
316 fn into_components_returns_all_requirements() {
317 let permission_name = "system:health";
318 let expected = PermissionId::from(permission_name).as_u64();
319 let policy: AccessPolicy<Role, Group> = AccessPolicy::require_role(Role::Admin)
320 .or_require_group(Group::new("test"))
321 .or_require_permission(permission_name);
322
323 let (roles, groups, permissions) = policy.into_components();
324 assert_eq!(roles.len(), 1);
325 assert_eq!(groups.len(), 1);
326 assert!(permissions.iter().any(|id| id == expected));
327 }
328}