backbone_auth/middleware.rs
1//! Authentication middleware
2
3use anyhow::Result;
4use async_trait::async_trait;
5
6/// Authentication middleware trait
7#[async_trait]
8pub trait AuthMiddleware {
9 async fn authenticate(&self, token: &str) -> Result<AuthContext>;
10}
11
12/// Trait for extracting authentication information from requests
13#[async_trait]
14pub trait AuthExtractor {
15 async fn extract_token(&self) -> Result<Option<String>>;
16 async fn extract_user_context(&self) -> Result<Option<AuthContext>>;
17}
18
19/// Authentication context
20#[derive(Debug, Clone)]
21pub struct AuthContext {
22 pub user_id: String,
23 pub roles: Vec<String>,
24 pub permissions: Vec<String>,
25}
26
27impl AuthContext {
28 pub fn new(user_id: String) -> Self {
29 Self {
30 user_id,
31 roles: Vec::new(),
32 permissions: Vec::new(),
33 }
34 }
35
36 /// Whether `permission` (a canonical `<resource>:<action>`) is among what the caller holds.
37 ///
38 /// Reads `self.permissions` and nothing else: with no grant resolver installed that is whatever
39 /// the host's authentication put there, with one installed it is what the resolver loaded. An
40 /// empty list holds nothing. Wildcards (`ticket:*`, `*:read`, `*:*`) and the legacy spellings
41 /// are matched as [`crate::grants`] describes.
42 pub fn has_permission(&self, permission: &str) -> bool {
43 crate::grants::check_held(self.permissions.iter(), permission, &[])
44 }
45
46 /// Refuse unless the caller holds `permission`.
47 ///
48 /// The generated routes, transitions and verbs all call this. The error carries the permission,
49 /// and answers `403` with the violation `permission_required` and `params.permission`.
50 pub fn require(&self, permission: &str) -> Result<(), crate::grants::PermissionRequired> {
51 self.require_with(permission, &[])
52 }
53
54 /// [`require`](Self::require), also accepting any of `alternates` (legacy spellings a route
55 /// still honours for one release, such as the module-wide `<module>:<action>`). The refusal
56 /// always names `permission`.
57 pub fn require_with(
58 &self,
59 permission: &str,
60 alternates: &[&str],
61 ) -> Result<(), crate::grants::PermissionRequired> {
62 if crate::grants::check_held(self.permissions.iter(), permission, alternates) {
63 Ok(())
64 } else {
65 Err(crate::grants::PermissionRequired { permission: permission.to_string() })
66 }
67 }
68}