Skip to main content

backbone_authorization/
types.rs

1//! Authorization types
2//!
3//! Core types for authorization system.
4
5use serde::{Deserialize, Serialize};
6use std::fmt;
7
8/// Authorization error
9#[derive(Debug, Clone, thiserror::Error)]
10pub enum AuthorizationError {
11    /// No user matches the given identifier.
12    #[error("User not found: {0}")]
13    UserNotFound(String),
14
15    /// The user lacks the permission the request needs.
16    #[error("Permission denied: {0}")]
17    PermissionDenied(String),
18
19    /// The presented token is malformed, expired or not trusted.
20    #[error("Invalid token: {0}")]
21    InvalidToken(String),
22
23    /// The authorization setup itself is wrong.
24    #[error("Configuration error: {0}")]
25    Configuration(String),
26
27    /// Reading roles or permissions from storage failed.
28    #[error("Database error: {0}")]
29    Database(String),
30}
31
32/// User information with roles and permissions
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct AuthUser {
35    /// The user's identifier.
36    pub user_id: String,
37    /// The user's login name.
38    pub username: String,
39    /// Role names the user holds.
40    pub roles: Vec<String>,
41    /// Permission names granted directly or through roles.
42    pub permissions: Vec<String>,
43    /// When the authorization expires, as a Unix timestamp in seconds.
44    pub expires_at: Option<i64>,
45}
46
47/// Action types for permissions
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum Action {
51    /// Create a record.
52    Create,
53    /// Read one record.
54    Read,
55    /// Change a record.
56    Update,
57    /// Remove a record.
58    Delete,
59    /// List records.
60    List,
61    /// Bring back a removed record.
62    Restore,
63}
64
65impl Action {
66    /// Every action.
67    pub fn all() -> Vec<Self> {
68        vec![
69            Self::Create,
70            Self::Read,
71            Self::Update,
72            Self::Delete,
73            Self::List,
74            Self::Restore,
75        ]
76    }
77}
78
79impl fmt::Display for Action {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::Create => write!(f, "create"),
83            Self::Read => write!(f, "read"),
84            Self::Update => write!(f, "update"),
85            Self::Delete => write!(f, "delete"),
86            Self::List => write!(f, "list"),
87            Self::Restore => write!(f, "restore"),
88        }
89    }
90}
91
92/// Resource types for permissions
93#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
94#[serde(rename_all = "snake_case")]
95pub enum Resource {
96    /// User accounts.
97    User,
98    /// Roles.
99    Role,
100    /// Permission definitions.
101    Permission,
102    /// System settings.
103    Settings,
104}
105
106impl Resource {
107    /// Every resource.
108    pub fn all() -> Vec<Self> {
109        vec![
110            Self::User,
111            Self::Role,
112            Self::Permission,
113            Self::Settings,
114        ]
115    }
116}
117
118impl fmt::Display for Resource {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::User => write!(f, "user"),
122            Self::Role => write!(f, "role"),
123            Self::Permission => write!(f, "permission"),
124            Self::Settings => write!(f, "settings"),
125        }
126    }
127}
128
129/// Standard roles
130#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
131#[serde(rename_all = "snake_case")]
132pub enum Role {
133    /// Every permission, without checks.
134    SuperAdmin,
135    /// Administers the system.
136    Admin,
137    /// A signed-in user.
138    User,
139    /// An anonymous or unprivileged visitor.
140    Guest,
141}
142
143impl Role {
144    /// The names of every standard role.
145    pub fn all() -> Vec<&'static str> {
146        vec![
147            "super_admin",
148            "admin",
149            "user",
150            "guest",
151        ]
152    }
153
154    /// The role's name.
155    pub fn as_str(&self) -> &'static str {
156        match self {
157            Self::SuperAdmin => "super_admin",
158            Self::Admin => "admin",
159            Self::User => "user",
160            Self::Guest => "guest",
161        }
162    }
163
164    /// The standard role with this name, if any.
165    pub fn from_str(s: &str) -> Option<Self> {
166        match s {
167            "super_admin" => Some(Self::SuperAdmin),
168            "admin" => Some(Self::Admin),
169            "user" => Some(Self::User),
170            "guest" => Some(Self::Guest),
171            _ => None,
172        }
173    }
174}
175
176/// Permission check result
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct PermissionCheck {
179    /// Whether the permission was granted.
180    pub allowed: bool,
181    /// The permission checked.
182    pub permission: Permission,
183    /// Why it was granted or refused.
184    pub reason: String,
185}
186
187/// Authorization request
188#[derive(Debug, Clone)]
189pub struct AuthorizationRequest {
190    /// Who is asking.
191    pub user: AuthUser,
192    /// What kind of record the action touches.
193    pub resource: Resource,
194    /// What the user wants to do.
195    pub action: Action,
196    /// The one record, when the action targets one.
197    pub resource_id: Option<String>,
198}
199
200/// Authorization response
201#[derive(Debug, Clone)]
202pub struct AuthorizationResponse {
203    /// Whether the request is allowed.
204    pub allowed: bool,
205    /// Each permission checked on the way to the answer.
206    pub checks: Vec<PermissionCheck>,
207}
208
209/// Authorization configuration
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct AuthorizationConfig {
212    /// How long a cached permission answer stays valid, in seconds.
213    pub cache_ttl_seconds: u64,
214    /// The role a user without any role is treated as.
215    pub default_role: String,
216    /// Whether permission answers are cached.
217    pub enable_permission_caching: bool,
218}
219
220impl Default for AuthorizationConfig {
221    fn default() -> Self {
222        Self {
223            cache_ttl_seconds: 300, // 5 minutes
224            default_role: "guest".to_string(),
225            enable_permission_caching: true,
226        }
227    }
228}
229
230/// User actions
231#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
232#[serde(rename_all = "snake_case")]
233pub enum UserAction {
234    /// Create one.
235    Create,
236    /// Read one.
237    Read,
238    /// Change one.
239    Update,
240    /// Remove one.
241    Delete,
242    /// List them.
243    List,
244    /// Reset the user's password.
245    ResetPassword,
246    /// Change the user's roles.
247    ChangeRole,
248}
249
250/// Role actions
251#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
252#[serde(rename_all = "snake_case")]
253pub enum RoleAction {
254    /// Create one.
255    Create,
256    /// Read one.
257    Read,
258    /// Change one.
259    Update,
260    /// Remove one.
261    Delete,
262    /// List them.
263    List,
264    /// Grant a permission to the role.
265    AssignPermission,
266    /// Take a permission from the role.
267    RevokePermission,
268}
269
270/// Permission actions
271#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
272#[serde(rename_all = "snake_case")]
273pub enum PermissionAction {
274    /// Create one.
275    Create,
276    /// Read one.
277    Read,
278    /// Change one.
279    Update,
280    /// Remove one.
281    Delete,
282    /// List them.
283    List,
284}
285
286/// Settings actions
287#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
288#[serde(rename_all = "snake_case")]
289pub enum SettingsAction {
290    /// Create one.
291    Create,
292    /// Read one.
293    Read,
294    /// Change one.
295    Update,
296    /// Remove one.
297    Delete,
298    /// List them.
299    List,
300}
301
302/// Permission definition
303#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
304pub enum Permission {
305    /// An action on user accounts.
306    User(UserAction),
307    /// An action on roles.
308    Role(RoleAction),
309    /// An action on permission definitions.
310    Permission(PermissionAction),
311    /// An action on system settings.
312    Settings(SettingsAction),
313}
314
315impl Permission {
316    /// Get all possible permissions
317    pub fn all_permissions() -> HashSet<Permission> {
318        let mut perms = HashSet::new();
319
320        // User permissions
321        for action in UserAction::all() {
322            perms.insert(Permission::User(action));
323        }
324
325        // Role permissions
326        for action in RoleAction::all() {
327            perms.insert(Permission::Role(action));
328        }
329
330        // Permission permissions
331        for action in PermissionAction::all() {
332            perms.insert(Permission::Permission(action));
333        }
334
335        // Settings permissions
336        for action in SettingsAction::all() {
337            perms.insert(Permission::Settings(action));
338        }
339
340        perms
341    }
342
343    /// Create permission from action and resource.
344    ///
345    /// The action enums carry no restore of their own, so restoring a record
346    /// asks for the permission to delete it: whoever may remove a record may
347    /// bring it back, and no unrelated permission (a password reset, a
348    /// permission grant) opens the restore path.
349    pub fn from_action_resource(action: Action, resource: Resource) -> Self {
350        match resource {
351            Resource::User => match action {
352                Action::Create => Permission::User(UserAction::Create),
353                Action::Read => Permission::User(UserAction::Read),
354                Action::Update => Permission::User(UserAction::Update),
355                Action::Delete => Permission::User(UserAction::Delete),
356                Action::List => Permission::User(UserAction::List),
357                Action::Restore => Permission::User(UserAction::Delete),
358            },
359            Resource::Role => match action {
360                Action::Create => Permission::Role(RoleAction::Create),
361                Action::Read => Permission::Role(RoleAction::Read),
362                Action::Update => Permission::Role(RoleAction::Update),
363                Action::Delete => Permission::Role(RoleAction::Delete),
364                Action::List => Permission::Role(RoleAction::List),
365                Action::Restore => Permission::Role(RoleAction::Delete),
366            },
367            Resource::Permission => match action {
368                Action::Create => Permission::Permission(PermissionAction::Create),
369                Action::Read => Permission::Permission(PermissionAction::Read),
370                Action::Update => Permission::Permission(PermissionAction::Update),
371                Action::Delete => Permission::Permission(PermissionAction::Delete),
372                Action::List => Permission::Permission(PermissionAction::List),
373                Action::Restore => Permission::Permission(PermissionAction::Delete),
374            },
375            Resource::Settings => match action {
376                Action::Create => Permission::Settings(SettingsAction::Create),
377                Action::Read => Permission::Settings(SettingsAction::Read),
378                Action::Update => Permission::Settings(SettingsAction::Update),
379                Action::Delete => Permission::Settings(SettingsAction::Delete),
380                Action::List => Permission::Settings(SettingsAction::List),
381                Action::Restore => Permission::Settings(SettingsAction::Delete),
382            },
383        }
384    }
385}
386
387impl UserAction {
388    /// Every user action.
389    pub fn all() -> Vec<Self> {
390        vec![
391            Self::Create,
392            Self::Read,
393            Self::Update,
394            Self::Delete,
395            Self::List,
396            Self::ResetPassword,
397            Self::ChangeRole,
398        ]
399    }
400}
401
402impl RoleAction {
403    /// Every role action.
404    pub fn all() -> Vec<Self> {
405        vec![
406            Self::Create,
407            Self::Read,
408            Self::Update,
409            Self::Delete,
410            Self::List,
411            Self::AssignPermission,
412            Self::RevokePermission,
413        ]
414    }
415}
416
417impl PermissionAction {
418    /// Every permission action.
419    pub fn all() -> Vec<Self> {
420        vec![
421            Self::Create,
422            Self::Read,
423            Self::Update,
424            Self::Delete,
425            Self::List,
426        ]
427    }
428}
429
430impl SettingsAction {
431    /// Every settings action.
432    pub fn all() -> Vec<Self> {
433        vec![
434            Self::Create,
435            Self::Read,
436            Self::Update,
437            Self::Delete,
438            Self::List,
439        ]
440    }
441}
442
443use std::collections::HashSet;
444
445#[cfg(test)]
446mod restore_permission_tests {
447    use super::*;
448
449    #[test]
450    fn restoring_a_record_requires_the_permission_to_delete_it() {
451        assert_eq!(
452            Permission::from_action_resource(Action::Restore, Resource::User),
453            Permission::User(UserAction::Delete)
454        );
455        assert_eq!(
456            Permission::from_action_resource(Action::Restore, Resource::Role),
457            Permission::Role(RoleAction::Delete)
458        );
459        assert_eq!(
460            Permission::from_action_resource(Action::Restore, Resource::Permission),
461            Permission::Permission(PermissionAction::Delete)
462        );
463        assert_eq!(
464            Permission::from_action_resource(Action::Restore, Resource::Settings),
465            Permission::Settings(SettingsAction::Delete)
466        );
467    }
468}