communitas_core/security/
auth_middleware.rs1use anyhow::Result;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::{Arc, RwLock};
13use std::time::{Duration, Instant};
14use uuid::Uuid;
15
16pub const MAX_SESSION_DURATION: Duration = Duration::from_secs(3600);
18
19pub const SESSION_CLEANUP_INTERVAL: Duration = Duration::from_secs(300);
21
22#[derive(Debug, Clone)]
24pub struct AuthSession {
25 pub session_id: String,
26 pub user_id: String,
27 pub four_words_identity: String,
28 pub permissions: Vec<Permission>,
29 pub created_at: Instant,
30 pub last_accessed: Instant,
31 pub expires_at: Instant,
32}
33
34impl AuthSession {
35 pub fn new(user_id: String, four_words_identity: String, permissions: Vec<Permission>) -> Self {
37 let now = Instant::now();
38 Self {
39 session_id: Uuid::new_v4().to_string(),
40 user_id,
41 four_words_identity,
42 permissions,
43 created_at: now,
44 last_accessed: now,
45 expires_at: now + MAX_SESSION_DURATION,
46 }
47 }
48
49 pub fn is_valid(&self) -> bool {
51 Instant::now() < self.expires_at
52 }
53
54 pub fn refresh(&mut self) {
56 self.last_accessed = Instant::now();
57 }
58
59 pub fn has_permission(&self, required: &Permission) -> bool {
61 self.permissions.iter().any(|p| p.allows(required))
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67pub struct Permission {
68 pub resource: String,
69 pub action: String,
70 pub scope: PermissionScope,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
74pub enum PermissionScope {
75 Own, Shared, All, }
79
80impl Permission {
81 pub fn new(resource: &str, action: &str, scope: PermissionScope) -> Self {
82 Self {
83 resource: resource.to_string(),
84 action: action.to_string(),
85 scope,
86 }
87 }
88
89 pub fn allows(&self, required: &Permission) -> bool {
91 let resource_match = self.resource == "*" || self.resource == required.resource;
93
94 let action_match = self.action == "*" || self.action == required.action;
96
97 let scope_match = matches!(
99 (&self.scope, &required.scope),
100 (PermissionScope::All, _)
101 | (PermissionScope::Shared, PermissionScope::Own)
102 | (PermissionScope::Shared, PermissionScope::Shared)
103 | (PermissionScope::Own, PermissionScope::Own)
104 );
105
106 resource_match && action_match && scope_match
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct AuthMiddleware {
113 sessions: Arc<RwLock<HashMap<String, AuthSession>>>,
114 last_cleanup: Arc<RwLock<Instant>>,
115}
116
117impl Default for AuthMiddleware {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123impl AuthMiddleware {
124 pub fn new() -> Self {
126 Self {
127 sessions: Arc::new(RwLock::new(HashMap::new())),
128 last_cleanup: Arc::new(RwLock::new(Instant::now())),
129 }
130 }
131
132 pub fn create_session(
134 &self,
135 user_id: String,
136 four_words_identity: String,
137 permissions: Vec<Permission>,
138 ) -> Result<String> {
139 let session = AuthSession::new(user_id, four_words_identity, permissions);
140 let session_id = session.session_id.clone();
141
142 {
143 let mut sessions = self
144 .sessions
145 .write()
146 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
147 sessions.insert(session_id.clone(), session);
148 }
149
150 self.cleanup_expired_sessions()?;
152
153 Ok(session_id)
154 }
155
156 pub fn validate_session(&self, session_id: &str) -> Result<AuthSession> {
158 let mut sessions = self
159 .sessions
160 .write()
161 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
162
163 let session = sessions
164 .get_mut(session_id)
165 .ok_or_else(|| anyhow::anyhow!("Invalid session ID"))?;
166
167 if !session.is_valid() {
168 sessions.remove(session_id);
169 return Err(anyhow::anyhow!("Session expired"));
170 }
171
172 session.refresh();
173 Ok(session.clone())
174 }
175
176 pub fn check_permission(
178 &self,
179 session_id: &str,
180 required_permission: &Permission,
181 ) -> Result<bool> {
182 let session = self.validate_session(session_id)?;
183 Ok(session.has_permission(required_permission))
184 }
185
186 pub fn require_permission(
188 &self,
189 session_id: &str,
190 required_permission: &Permission,
191 ) -> Result<AuthSession> {
192 let session = self.validate_session(session_id)?;
193
194 if !session.has_permission(required_permission) {
195 return Err(anyhow::anyhow!(
196 "Insufficient permissions. Required: {:?}",
197 required_permission
198 ));
199 }
200
201 Ok(session)
202 }
203
204 pub fn end_session(&self, session_id: &str) -> Result<()> {
206 let mut sessions = self
207 .sessions
208 .write()
209 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
210
211 sessions.remove(session_id);
212 Ok(())
213 }
214
215 pub fn get_active_sessions(&self) -> Result<Vec<AuthSession>> {
217 let sessions = self
218 .sessions
219 .read()
220 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
221
222 let active_sessions: Vec<AuthSession> = sessions
223 .values()
224 .filter(|session| session.is_valid())
225 .cloned()
226 .collect();
227
228 Ok(active_sessions)
229 }
230
231 pub fn cleanup_expired_sessions(&self) -> Result<()> {
233 let now = Instant::now();
234
235 {
237 let last_cleanup = self
238 .last_cleanup
239 .read()
240 .map_err(|_| anyhow::anyhow!("Failed to acquire cleanup lock"))?;
241
242 if now.duration_since(*last_cleanup) < SESSION_CLEANUP_INTERVAL {
243 return Ok(()); }
245 }
246
247 {
249 let mut sessions = self
250 .sessions
251 .write()
252 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
253
254 sessions.retain(|_, session| session.is_valid());
255 }
256
257 {
259 let mut last_cleanup = self
260 .last_cleanup
261 .write()
262 .map_err(|_| anyhow::anyhow!("Failed to acquire cleanup lock"))?;
263 *last_cleanup = now;
264 }
265
266 Ok(())
267 }
268
269 pub fn get_stats(&self) -> Result<SessionStats> {
271 let sessions = self
272 .sessions
273 .read()
274 .map_err(|_| anyhow::anyhow!("Failed to acquire sessions lock"))?;
275
276 let total_sessions = sessions.len();
277 let active_sessions = sessions.values().filter(|s| s.is_valid()).count();
278 let expired_sessions = total_sessions - active_sessions;
279
280 Ok(SessionStats {
281 total_sessions,
282 active_sessions,
283 expired_sessions,
284 })
285 }
286}
287
288#[derive(Debug, Serialize, Deserialize)]
290pub struct SessionStats {
291 pub total_sessions: usize,
292 pub active_sessions: usize,
293 pub expired_sessions: usize,
294}
295
296pub mod permissions {
298 use super::{Permission, PermissionScope};
299
300 pub fn read_messages() -> Permission {
301 Permission::new("messages", "read", PermissionScope::Shared)
302 }
303
304 pub fn send_messages() -> Permission {
305 Permission::new("messages", "write", PermissionScope::Shared)
306 }
307
308 pub fn manage_contacts() -> Permission {
309 Permission::new("contacts", "*", PermissionScope::Own)
310 }
311
312 pub fn dht_operations() -> Permission {
313 Permission::new("dht", "*", PermissionScope::Shared)
314 }
315
316 pub fn admin_operations() -> Permission {
317 Permission::new("*", "*", PermissionScope::All)
318 }
319
320 pub fn identity_management() -> Permission {
321 Permission::new("identity", "*", PermissionScope::Own)
322 }
323
324 pub fn file_storage() -> Permission {
325 Permission::new("storage", "*", PermissionScope::Own)
326 }
327}
328
329#[macro_export]
331macro_rules! require_auth {
332 ($auth_middleware:expr, $session_id:expr, $permission:expr) => {
333 match $auth_middleware.require_permission($session_id, &$permission) {
334 Ok(session) => session,
335 Err(e) => return Err(format!("Authentication failed: {}", e)),
336 }
337 };
338}
339
340#[macro_export]
342macro_rules! require_session {
343 ($auth_middleware:expr, $session_id:expr) => {
344 match $auth_middleware.validate_session($session_id) {
345 Ok(session) => session,
346 Err(e) => return Err(format!("Session validation failed: {}", e)),
347 }
348 };
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_permission_matching() {
357 let admin_perm = Permission::new("*", "*", PermissionScope::All);
358 let read_messages = Permission::new("messages", "read", PermissionScope::Shared);
359
360 assert!(admin_perm.allows(&read_messages));
361 assert!(!read_messages.allows(&admin_perm));
362 }
363
364 #[test]
365 fn test_session_creation_and_validation() {
366 let auth = AuthMiddleware::new();
367 let permissions = vec![permissions::read_messages(), permissions::send_messages()];
368
369 let session_id = auth
370 .create_session(
371 "test_user".to_string(),
372 "hello-world-test-net".to_string(),
373 permissions,
374 )
375 .unwrap();
376
377 let session = auth.validate_session(&session_id).unwrap();
378 assert_eq!(session.user_id, "test_user");
379 assert!(session.has_permission(&permissions::read_messages()));
380 }
381
382 #[test]
383 fn test_permission_checking() {
384 let auth = AuthMiddleware::new();
385 let permissions = vec![permissions::read_messages()];
386
387 let session_id = auth
388 .create_session(
389 "test_user".to_string(),
390 "hello-world-test-net".to_string(),
391 permissions,
392 )
393 .unwrap();
394
395 assert!(
396 auth.check_permission(&session_id, &permissions::read_messages())
397 .unwrap()
398 );
399 assert!(
400 !auth
401 .check_permission(&session_id, &permissions::admin_operations())
402 .unwrap()
403 );
404 }
405
406 #[test]
407 fn test_session_expiry() {
408 let auth = AuthMiddleware::new();
409 let permissions = vec![permissions::read_messages()];
410
411 let session_id = auth
412 .create_session(
413 "test_user".to_string(),
414 "hello-world-test-net".to_string(),
415 permissions,
416 )
417 .unwrap();
418
419 assert!(auth.validate_session(&session_id).is_ok());
421
422 {
424 let mut sessions = auth.sessions.write().unwrap();
425 if let Some(session) = sessions.get_mut(&session_id) {
426 session.expires_at = Instant::now() - Duration::from_secs(1);
427 }
428 }
429
430 assert!(auth.validate_session(&session_id).is_err());
432 }
433}