role_system/
auth_context.rs

1//! Generic authentication context integration.
2//! 
3//! This module provides a trait for integrating with various authentication
4//! systems, making it easy to adapt the role system to work with any auth
5//! framework like OAuth, JWT, or custom token solutions.
6
7use std::fmt::Debug;
8
9/// A generic trait that any authentication system can implement.
10/// 
11/// This allows the role system to be integrated with any authentication
12/// mechanism, such as JWT tokens, OAuth tokens, session data, etc.
13pub trait AuthenticationContext: Send + Sync + Debug {
14    /// The type used to identify users
15    type UserId: AsRef<str> + Debug;
16    
17    /// Additional context data type
18    type ContextData: Debug;
19    
20    /// Get the user ID from the authentication context
21    fn get_user_id(&self) -> &Self::UserId;
22    
23    /// Get any granted scopes from the authentication context
24    /// These are typically permissions granted directly via tokens
25    fn get_granted_scopes(&self) -> Vec<String>;
26    
27    /// Get additional context data
28    fn get_context(&self) -> &Self::ContextData;
29    
30    /// Check if this authentication context is valid
31    /// (e.g., token not expired, signature valid, etc.)
32    fn is_valid(&self) -> bool;
33}
34
35/// Simple JWT-based authentication context example
36#[derive(Debug)]
37pub struct JwtContext {
38    /// The user ID extracted from the token
39    user_id: String,
40    /// Scopes/permissions granted by the token
41    scopes: Vec<String>,
42    /// Whether the token is valid
43    valid: bool,
44    /// Additional data from the token
45    payload: std::collections::HashMap<String, String>,
46}
47
48impl JwtContext {
49    /// Create a new JWT context
50    pub fn new(
51        user_id: String, 
52        scopes: Vec<String>, 
53        valid: bool, 
54        payload: std::collections::HashMap<String, String>
55    ) -> Self {
56        Self {
57            user_id,
58            scopes,
59            valid,
60            payload,
61        }
62    }
63}
64
65impl AuthenticationContext for JwtContext {
66    type UserId = String;
67    type ContextData = std::collections::HashMap<String, String>;
68    
69    fn get_user_id(&self) -> &Self::UserId {
70        &self.user_id
71    }
72    
73    fn get_granted_scopes(&self) -> Vec<String> {
74        self.scopes.clone()
75    }
76    
77    fn get_context(&self) -> &Self::ContextData {
78        &self.payload
79    }
80    
81    fn is_valid(&self) -> bool {
82        self.valid
83    }
84}
85
86/// Example session-based authentication context
87#[derive(Debug)]
88pub struct SessionContext {
89    /// The user ID from the session
90    user_id: String,
91    /// Roles assigned in the session
92    roles: Vec<String>,
93    /// Whether the session is active
94    active: bool,
95    /// Additional session data
96    data: std::collections::HashMap<String, String>,
97}
98
99impl SessionContext {
100    /// Create a new session context
101    pub fn new(
102        user_id: String,
103        roles: Vec<String>,
104        active: bool,
105        data: std::collections::HashMap<String, String>,
106    ) -> Self {
107        Self {
108            user_id,
109            roles,
110            active,
111            data,
112        }
113    }
114    
115    /// Get roles assigned in the session
116    pub fn get_roles(&self) -> &[String] {
117        &self.roles
118    }
119}
120
121impl AuthenticationContext for SessionContext {
122    type UserId = String;
123    type ContextData = std::collections::HashMap<String, String>;
124    
125    fn get_user_id(&self) -> &Self::UserId {
126        &self.user_id
127    }
128    
129    fn get_granted_scopes(&self) -> Vec<String> {
130        // Convert roles to scopes format (just an example approach)
131        self.roles.iter()
132            .map(|role| format!("role:{}", role))
133            .collect()
134    }
135    
136    fn get_context(&self) -> &Self::ContextData {
137        &self.data
138    }
139    
140    fn is_valid(&self) -> bool {
141        self.active
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    
149    #[test]
150    fn test_jwt_context() {
151        let mut payload = std::collections::HashMap::new();
152        payload.insert("exp".to_string(), "1625097600".to_string());
153        payload.insert("iat".to_string(), "1625011200".to_string());
154        payload.insert("iss".to_string(), "auth-system".to_string());
155        
156        let context = JwtContext::new(
157            "user123".to_string(),
158            vec!["read:users".to_string(), "write:posts".to_string()],
159            true,
160            payload
161        );
162        
163        assert_eq!(context.get_user_id(), "user123");
164        assert!(context.get_granted_scopes().contains(&"read:users".to_string()));
165        assert!(context.is_valid());
166    }
167    
168    #[test]
169    fn test_session_context() {
170        let mut data = std::collections::HashMap::new();
171        data.insert("last_login".to_string(), "2023-01-01T00:00:00Z".to_string());
172        
173        let context = SessionContext::new(
174            "user456".to_string(),
175            vec!["admin".to_string(), "editor".to_string()],
176            true,
177            data
178        );
179        
180        assert_eq!(context.get_user_id(), "user456");
181        assert!(context.get_granted_scopes().contains(&"role:admin".to_string()));
182        assert!(context.is_valid());
183        assert_eq!(context.get_roles().len(), 2);
184    }
185}