Skip to main content

auth_framework/authorization_enhanced/
storage.rs

1//! Storage adapters for role-system integration
2//!
3//! This module provides storage adapters that integrate role-system with
4//! AuthFramework's existing storage infrastructure. It defines its own
5//! serializable types (`StoredRole`, `StoredPermission`) for persistence,
6//! with conversions to/from the `role_system` crate's types.
7
8use async_trait::async_trait;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::sync::Arc;
12use tokio::sync::RwLock;
13use tracing::{debug, info};
14
15// ── Storage-layer error types ────────────────────────────────────────────────
16
17/// Error type for storage operations
18#[derive(Debug, thiserror::Error)]
19pub enum StorageError {
20    #[error("Database error: {0}")]
21    Database(String),
22    #[error("Serialization error: {0}")]
23    Serialization(String),
24    #[error("Not found: {0}")]
25    NotFound(String),
26}
27
28/// Result alias for storage operations
29pub type StorageResult<T> = Result<T, StorageError>;
30
31// ── Serializable storage types (decoupled from role_system's private fields) ─
32
33/// A serializable role representation for persistence
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct StoredRole {
36    pub id: String,
37    pub name: String,
38    pub description: Option<String>,
39    pub permissions: Vec<StoredPermission>,
40}
41
42/// A serializable permission representation for persistence
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct StoredPermission {
45    pub action: String,
46    pub resource: String,
47    pub instance: Option<String>,
48}
49
50/// A role-to-user assignment record
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct RoleAssignment {
53    pub user_id: String,
54    pub role_id: String,
55    pub assigned_by: Option<String>,
56    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
57}
58
59/// An audit log entry for authorization events
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct AuditEntry {
62    pub user_id: Option<String>,
63    pub action: String,
64    pub resource: Option<String>,
65    pub result: String,
66    pub context: HashMap<String, String>,
67}
68
69// ── Conversions between storage types and role_system types ──────────────────
70
71impl StoredRole {
72    /// Convert to a `role_system::Role` (for use with the role-system engine)
73    pub fn to_role_system(&self) -> role_system::Role {
74        let mut role = role_system::Role::with_id(&self.id, &self.name);
75        if let Some(ref desc) = self.description {
76            role = role.with_description(desc);
77        }
78        for perm in &self.permissions {
79            role = role.add_permission(perm.to_role_system());
80        }
81        role
82    }
83
84    /// Create from a `role_system::Role`
85    pub fn from_role_system(role: &role_system::Role) -> Self {
86        Self {
87            id: role.id().to_string(),
88            name: role.name().to_string(),
89            description: role.description().map(|s| s.to_string()),
90            permissions: role
91                .permissions()
92                .permissions()
93                .iter()
94                .map(StoredPermission::from_role_system)
95                .collect(),
96        }
97    }
98}
99
100impl StoredPermission {
101    /// Convert to a `role_system::Permission`
102    pub fn to_role_system(&self) -> role_system::Permission {
103        if let Some(ref instance) = self.instance {
104            role_system::Permission::with_instance(&self.action, &self.resource, instance)
105        } else {
106            role_system::Permission::new(&self.action, &self.resource)
107        }
108    }
109
110    /// Create from a `role_system::Permission`
111    pub fn from_role_system(perm: &role_system::Permission) -> Self {
112        Self {
113            action: perm.action().to_string(),
114            resource: perm.resource_type().to_string(),
115            instance: perm.instance().map(|s| s.to_string()),
116        }
117    }
118}
119
120// ── Async storage trait ─────────────────────────────────────────────────────
121
122/// Async storage trait for persisting RBAC data (roles, permissions, assignments, audit)
123#[async_trait]
124pub trait RoleStorage: Send + Sync {
125    async fn create_role(&self, role: &StoredRole) -> StorageResult<()>;
126    async fn update_role(&self, role: &StoredRole) -> StorageResult<()>;
127    async fn delete_role(&self, role_id: &str) -> StorageResult<()>;
128    async fn get_role(&self, role_id: &str) -> StorageResult<Option<StoredRole>>;
129    async fn list_roles(&self) -> StorageResult<Vec<StoredRole>>;
130
131    async fn store_permission(&self, id: &str, permission: &StoredPermission) -> StorageResult<()>;
132    async fn get_permission(&self, permission_id: &str) -> StorageResult<Option<StoredPermission>>;
133
134    async fn assign_role(&self, assignment: &RoleAssignment) -> StorageResult<()>;
135    async fn revoke_role(&self, user_id: &str, role_id: &str) -> StorageResult<()>;
136    async fn get_user_roles(&self, user_id: &str) -> StorageResult<Vec<String>>;
137    async fn get_role_permissions(&self, role_id: &str) -> StorageResult<Vec<String>>;
138
139    async fn log_audit_entry(&self, entry: &AuditEntry) -> StorageResult<()>;
140}
141
142// ── Database abstractions ───────────────────────────────────────────────────
143
144/// Database-backed storage adapter for role-system
145pub struct DatabaseStorage {
146    connection: Arc<dyn DatabaseConnection>,
147    role_cache: Arc<RwLock<HashMap<String, StoredRole>>>,
148    permission_cache: Arc<RwLock<HashMap<String, StoredPermission>>>,
149    cache_ttl: u64,
150}
151
152/// Database connection trait (abstraction over actual database)
153#[async_trait]
154pub trait DatabaseConnection: Send + Sync {
155    async fn execute_query(
156        &self,
157        query: &str,
158        params: &[&dyn DatabaseValue],
159    ) -> Result<QueryResult, DatabaseError>;
160    async fn fetch_one(
161        &self,
162        query: &str,
163        params: &[&dyn DatabaseValue],
164    ) -> Result<Row, DatabaseError>;
165    async fn fetch_all(
166        &self,
167        query: &str,
168        params: &[&dyn DatabaseValue],
169    ) -> Result<Vec<Row>, DatabaseError>;
170}
171
172/// Database value trait for query parameters
173pub trait DatabaseValue: Send + Sync {
174    fn as_str(&self) -> Option<&str>;
175    fn as_i64(&self) -> Option<i64>;
176    fn as_bool(&self) -> Option<bool>;
177}
178
179impl DatabaseValue for String {
180    fn as_str(&self) -> Option<&str> {
181        Some(self.as_ref())
182    }
183    fn as_i64(&self) -> Option<i64> {
184        None
185    }
186    fn as_bool(&self) -> Option<bool> {
187        None
188    }
189}
190
191impl DatabaseValue for &str {
192    fn as_str(&self) -> Option<&str> {
193        Some(self)
194    }
195    fn as_i64(&self) -> Option<i64> {
196        None
197    }
198    fn as_bool(&self) -> Option<bool> {
199        None
200    }
201}
202
203impl DatabaseValue for Option<&str> {
204    fn as_str(&self) -> Option<&str> {
205        *self
206    }
207    fn as_i64(&self) -> Option<i64> {
208        None
209    }
210    fn as_bool(&self) -> Option<bool> {
211        None
212    }
213}
214
215impl DatabaseValue for i64 {
216    fn as_str(&self) -> Option<&str> {
217        None
218    }
219    fn as_i64(&self) -> Option<i64> {
220        Some(*self)
221    }
222    fn as_bool(&self) -> Option<bool> {
223        None
224    }
225}
226
227impl DatabaseValue for bool {
228    fn as_str(&self) -> Option<&str> {
229        None
230    }
231    fn as_i64(&self) -> Option<i64> {
232        None
233    }
234    fn as_bool(&self) -> Option<bool> {
235        Some(*self)
236    }
237}
238
239/// Database query result
240#[derive(Debug)]
241pub struct QueryResult {
242    pub rows_affected: u64,
243}
244
245/// Database row
246#[derive(Debug)]
247pub struct Row {
248    pub columns: HashMap<String, DatabaseColumnValue>,
249}
250
251/// Database column value
252#[derive(Debug, Clone)]
253pub enum DatabaseColumnValue {
254    String(String),
255    Integer(i64),
256    Boolean(bool),
257    Null,
258}
259
260/// Database error
261#[derive(Debug, thiserror::Error)]
262pub enum DatabaseError {
263    #[error("Connection error: {0}")]
264    Connection(String),
265    #[error("Query error: {0}")]
266    Query(String),
267    #[error("Serialization error: {0}")]
268    Serialization(String),
269}
270
271impl DatabaseStorage {
272    /// Create new database storage adapter
273    pub fn new(connection: Arc<dyn DatabaseConnection>) -> Self {
274        Self {
275            connection,
276            role_cache: Arc::new(RwLock::new(HashMap::new())),
277            permission_cache: Arc::new(RwLock::new(HashMap::new())),
278            cache_ttl: 300, // 5 minutes
279        }
280    }
281
282    /// Set cache TTL
283    pub fn with_cache_ttl(mut self, ttl_seconds: u64) -> Self {
284        self.cache_ttl = ttl_seconds;
285        self
286    }
287
288    /// Initialize database schema
289    pub async fn initialize_schema(&self) -> Result<(), DatabaseError> {
290        self.connection
291            .execute_query(
292                r#"CREATE TABLE IF NOT EXISTS roles (
293                    id VARCHAR(255) PRIMARY KEY,
294                    name VARCHAR(255) NOT NULL UNIQUE,
295                    description TEXT,
296                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
297                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
298                )"#,
299                &[],
300            )
301            .await?;
302
303        self.connection
304            .execute_query(
305                r#"CREATE TABLE IF NOT EXISTS permissions (
306                    id VARCHAR(255) PRIMARY KEY,
307                    action VARCHAR(255) NOT NULL,
308                    resource VARCHAR(255) NOT NULL,
309                    instance VARCHAR(255),
310                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
311                    UNIQUE(action, resource)
312                )"#,
313                &[],
314            )
315            .await?;
316
317        self.connection
318            .execute_query(
319                r#"CREATE TABLE IF NOT EXISTS role_permissions (
320                    role_id VARCHAR(255),
321                    permission_id VARCHAR(255),
322                    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
323                    PRIMARY KEY (role_id, permission_id),
324                    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE,
325                    FOREIGN KEY (permission_id) REFERENCES permissions(id) ON DELETE CASCADE
326                )"#,
327                &[],
328            )
329            .await?;
330
331        self.connection
332            .execute_query(
333                r#"CREATE TABLE IF NOT EXISTS user_roles (
334                    user_id VARCHAR(255),
335                    role_id VARCHAR(255),
336                    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
337                    assigned_by VARCHAR(255),
338                    expires_at TIMESTAMP NULL,
339                    PRIMARY KEY (user_id, role_id),
340                    FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE
341                )"#,
342                &[],
343            )
344            .await?;
345
346        self.connection
347            .execute_query(
348                r#"CREATE TABLE IF NOT EXISTS audit_log (
349                    id BIGINT PRIMARY KEY AUTO_INCREMENT,
350                    user_id VARCHAR(255),
351                    action VARCHAR(255) NOT NULL,
352                    resource VARCHAR(255),
353                    result VARCHAR(50) NOT NULL,
354                    context TEXT,
355                    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
356                )"#,
357                &[],
358            )
359            .await?;
360
361        info!("Database schema initialized successfully");
362        Ok(())
363    }
364
365    /// Clear caches
366    async fn clear_caches(&self) {
367        self.role_cache.write().await.clear();
368        self.permission_cache.write().await.clear();
369        debug!("Cleared authorization caches");
370    }
371
372    fn row_to_stored_role(&self, row: &Row) -> StorageResult<StoredRole> {
373        let id = self.get_string_column(row, "id")?;
374        let name = self.get_string_column(row, "name")?;
375        let description = self.get_optional_string_column(row, "description");
376        Ok(StoredRole {
377            id,
378            name,
379            description,
380            permissions: Vec::new(), // loaded via role_permissions join
381        })
382    }
383
384    fn row_to_stored_permission(&self, row: &Row) -> StorageResult<StoredPermission> {
385        let action = self.get_string_column(row, "action")?;
386        let resource = self.get_string_column(row, "resource")?;
387        let instance = self.get_optional_string_column(row, "instance");
388        Ok(StoredPermission {
389            action,
390            resource,
391            instance,
392        })
393    }
394
395    fn get_string_column(&self, row: &Row, column: &str) -> StorageResult<String> {
396        match row.columns.get(column) {
397            Some(DatabaseColumnValue::String(value)) => Ok(value.clone()),
398            Some(DatabaseColumnValue::Null) => {
399                Err(StorageError::Database(format!("Column {column} is null")))
400            }
401            Some(_) => Err(StorageError::Database(format!(
402                "Column {column} is not a string"
403            ))),
404            None => Err(StorageError::Database(format!("Column {column} not found"))),
405        }
406    }
407
408    fn get_optional_string_column(&self, row: &Row, column: &str) -> Option<String> {
409        match row.columns.get(column) {
410            Some(DatabaseColumnValue::String(value)) => Some(value.clone()),
411            _ => None,
412        }
413    }
414}
415
416#[async_trait]
417impl RoleStorage for DatabaseStorage {
418    async fn create_role(&self, role: &StoredRole) -> StorageResult<()> {
419        self.connection
420            .execute_query(
421                "INSERT INTO roles (id, name, description) VALUES (?, ?, ?)",
422                &[
423                    &role.id as &dyn DatabaseValue,
424                    &role.name as &dyn DatabaseValue,
425                    &role.description.as_deref().unwrap_or("") as &dyn DatabaseValue,
426                ],
427            )
428            .await
429            .map_err(|e| StorageError::Database(e.to_string()))?;
430        self.clear_caches().await;
431        info!("Created role: {}", role.name);
432        Ok(())
433    }
434
435    async fn update_role(&self, role: &StoredRole) -> StorageResult<()> {
436        self.connection
437            .execute_query(
438                "UPDATE roles SET name = ?, description = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?",
439                &[
440                    &role.name as &dyn DatabaseValue,
441                    &role.description.as_deref().unwrap_or("") as &dyn DatabaseValue,
442                    &role.id as &dyn DatabaseValue,
443                ],
444            )
445            .await
446            .map_err(|e| StorageError::Database(e.to_string()))?;
447        self.role_cache
448            .write()
449            .await
450            .insert(role.id.clone(), role.clone());
451        info!("Updated role: {}", role.name);
452        Ok(())
453    }
454
455    async fn delete_role(&self, role_id: &str) -> StorageResult<()> {
456        self.connection
457            .execute_query(
458                "DELETE FROM roles WHERE id = ?",
459                &[&role_id as &dyn DatabaseValue],
460            )
461            .await
462            .map_err(|e| StorageError::Database(e.to_string()))?;
463        self.role_cache.write().await.remove(role_id);
464        info!("Deleted role: {}", role_id);
465        Ok(())
466    }
467
468    async fn get_role(&self, role_id: &str) -> StorageResult<Option<StoredRole>> {
469        {
470            let cache = self.role_cache.read().await;
471            if let Some(role) = cache.get(role_id) {
472                return Ok(Some(role.clone()));
473            }
474        }
475        let row = match self
476            .connection
477            .fetch_one(
478                "SELECT id, name, description FROM roles WHERE id = ?",
479                &[&role_id as &dyn DatabaseValue],
480            )
481            .await
482        {
483            Ok(row) => row,
484            Err(DatabaseError::Query(_)) => return Ok(None),
485            Err(e) => return Err(StorageError::Database(e.to_string())),
486        };
487        let role = self.row_to_stored_role(&row)?;
488        self.role_cache
489            .write()
490            .await
491            .insert(role_id.to_string(), role.clone());
492        Ok(Some(role))
493    }
494
495    async fn list_roles(&self) -> StorageResult<Vec<StoredRole>> {
496        let rows = self
497            .connection
498            .fetch_all("SELECT id, name, description FROM roles ORDER BY name", &[])
499            .await
500            .map_err(|e| StorageError::Database(e.to_string()))?;
501        rows.iter().map(|r| self.row_to_stored_role(r)).collect()
502    }
503
504    async fn store_permission(&self, id: &str, permission: &StoredPermission) -> StorageResult<()> {
505        self.connection
506            .execute_query(
507                "INSERT INTO permissions (id, action, resource, instance) VALUES (?, ?, ?, ?)",
508                &[
509                    &id as &dyn DatabaseValue,
510                    &permission.action as &dyn DatabaseValue,
511                    &permission.resource as &dyn DatabaseValue,
512                    &permission.instance.as_deref() as &dyn DatabaseValue,
513                ],
514            )
515            .await
516            .map_err(|e| StorageError::Database(e.to_string()))?;
517        self.permission_cache
518            .write()
519            .await
520            .insert(id.to_string(), permission.clone());
521        info!(
522            "Created permission: {}:{}",
523            permission.action, permission.resource
524        );
525        Ok(())
526    }
527
528    async fn get_permission(&self, permission_id: &str) -> StorageResult<Option<StoredPermission>> {
529        {
530            let cache = self.permission_cache.read().await;
531            if let Some(p) = cache.get(permission_id) {
532                return Ok(Some(p.clone()));
533            }
534        }
535        let row = match self
536            .connection
537            .fetch_one(
538                "SELECT action, resource, instance FROM permissions WHERE id = ?",
539                &[&permission_id as &dyn DatabaseValue],
540            )
541            .await
542        {
543            Ok(row) => row,
544            Err(DatabaseError::Query(_)) => return Ok(None),
545            Err(e) => return Err(StorageError::Database(e.to_string())),
546        };
547        let perm = self.row_to_stored_permission(&row)?;
548        self.permission_cache
549            .write()
550            .await
551            .insert(permission_id.to_string(), perm.clone());
552        Ok(Some(perm))
553    }
554
555    async fn assign_role(&self, assignment: &RoleAssignment) -> StorageResult<()> {
556        self.connection
557            .execute_query(
558                "INSERT OR REPLACE INTO user_roles (user_id, role_id, assigned_by) VALUES (?, ?, ?)",
559                &[
560                    &assignment.user_id as &dyn DatabaseValue,
561                    &assignment.role_id as &dyn DatabaseValue,
562                    &assignment.assigned_by.as_deref() as &dyn DatabaseValue,
563                ],
564            )
565            .await
566            .map_err(|e| StorageError::Database(e.to_string()))?;
567        info!(
568            "Assigned role {} to user {}",
569            assignment.role_id, assignment.user_id
570        );
571        Ok(())
572    }
573
574    async fn revoke_role(&self, user_id: &str, role_id: &str) -> StorageResult<()> {
575        self.connection
576            .execute_query(
577                "DELETE FROM user_roles WHERE user_id = ? AND role_id = ?",
578                &[
579                    &user_id as &dyn DatabaseValue,
580                    &role_id as &dyn DatabaseValue,
581                ],
582            )
583            .await
584            .map_err(|e| StorageError::Database(e.to_string()))?;
585        info!("Revoked role {} from user {}", role_id, user_id);
586        Ok(())
587    }
588
589    async fn get_user_roles(&self, user_id: &str) -> StorageResult<Vec<String>> {
590        let rows = self
591            .connection
592            .fetch_all(
593                "SELECT role_id FROM user_roles WHERE user_id = ? AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)",
594                &[&user_id as &dyn DatabaseValue],
595            )
596            .await
597            .map_err(|e| StorageError::Database(e.to_string()))?;
598        Ok(rows
599            .iter()
600            .filter_map(|r| match r.columns.get("role_id") {
601                Some(DatabaseColumnValue::String(s)) => Some(s.clone()),
602                _ => None,
603            })
604            .collect())
605    }
606
607    async fn get_role_permissions(&self, role_id: &str) -> StorageResult<Vec<String>> {
608        let rows = self
609            .connection
610            .fetch_all(
611                "SELECT permission_id FROM role_permissions WHERE role_id = ?",
612                &[&role_id as &dyn DatabaseValue],
613            )
614            .await
615            .map_err(|e| StorageError::Database(e.to_string()))?;
616        Ok(rows
617            .iter()
618            .filter_map(|r| match r.columns.get("permission_id") {
619                Some(DatabaseColumnValue::String(s)) => Some(s.clone()),
620                _ => None,
621            })
622            .collect())
623    }
624
625    async fn log_audit_entry(&self, entry: &AuditEntry) -> StorageResult<()> {
626        let context_json = serde_json::to_string(&entry.context)
627            .map_err(|e| StorageError::Serialization(e.to_string()))?;
628        self.connection
629            .execute_query(
630                "INSERT INTO audit_log (user_id, action, resource, result, context) VALUES (?, ?, ?, ?, ?)",
631                &[
632                    &entry.user_id.as_deref() as &dyn DatabaseValue,
633                    &entry.action as &dyn DatabaseValue,
634                    &entry.resource.as_deref() as &dyn DatabaseValue,
635                    &entry.result as &dyn DatabaseValue,
636                    &context_json as &dyn DatabaseValue,
637                ],
638            )
639            .await
640            .map_err(|e| StorageError::Database(e.to_string()))?;
641        debug!(
642            "Logged audit entry for user {:?}: {}",
643            entry.user_id, entry.action
644        );
645        Ok(())
646    }
647}
648
649// ── In-memory storage (for testing and development) ─────────────────────────
650
651/// In-memory storage adapter for testing and development
652pub struct MemoryRbacStorage {
653    roles: Arc<RwLock<HashMap<String, StoredRole>>>,
654    permissions: Arc<RwLock<HashMap<String, StoredPermission>>>,
655    user_roles: Arc<RwLock<HashMap<String, Vec<String>>>>,
656    role_permissions: Arc<RwLock<HashMap<String, Vec<String>>>>,
657    audit_log: Arc<RwLock<Vec<AuditEntry>>>,
658}
659
660impl Default for MemoryRbacStorage {
661    fn default() -> Self {
662        Self::new()
663    }
664}
665
666impl MemoryRbacStorage {
667    pub fn new() -> Self {
668        Self {
669            roles: Arc::new(RwLock::new(HashMap::new())),
670            permissions: Arc::new(RwLock::new(HashMap::new())),
671            user_roles: Arc::new(RwLock::new(HashMap::new())),
672            role_permissions: Arc::new(RwLock::new(HashMap::new())),
673            audit_log: Arc::new(RwLock::new(Vec::new())),
674        }
675    }
676
677    /// Clear all data (useful for testing)
678    pub async fn clear(&self) {
679        self.roles.write().await.clear();
680        self.permissions.write().await.clear();
681        self.user_roles.write().await.clear();
682        self.role_permissions.write().await.clear();
683        self.audit_log.write().await.clear();
684    }
685}
686
687#[async_trait]
688impl RoleStorage for MemoryRbacStorage {
689    async fn create_role(&self, role: &StoredRole) -> StorageResult<()> {
690        self.roles
691            .write()
692            .await
693            .insert(role.id.clone(), role.clone());
694        info!("Created role in memory: {}", role.name);
695        Ok(())
696    }
697
698    async fn update_role(&self, role: &StoredRole) -> StorageResult<()> {
699        self.roles
700            .write()
701            .await
702            .insert(role.id.clone(), role.clone());
703        info!("Updated role in memory: {}", role.name);
704        Ok(())
705    }
706
707    async fn delete_role(&self, role_id: &str) -> StorageResult<()> {
708        self.roles.write().await.remove(role_id);
709        info!("Deleted role from memory: {}", role_id);
710        Ok(())
711    }
712
713    async fn get_role(&self, role_id: &str) -> StorageResult<Option<StoredRole>> {
714        Ok(self.roles.read().await.get(role_id).cloned())
715    }
716
717    async fn list_roles(&self) -> StorageResult<Vec<StoredRole>> {
718        Ok(self.roles.read().await.values().cloned().collect())
719    }
720
721    async fn store_permission(&self, id: &str, permission: &StoredPermission) -> StorageResult<()> {
722        self.permissions
723            .write()
724            .await
725            .insert(id.to_string(), permission.clone());
726        info!(
727            "Created permission in memory: {}:{}",
728            permission.action, permission.resource
729        );
730        Ok(())
731    }
732
733    async fn get_permission(&self, permission_id: &str) -> StorageResult<Option<StoredPermission>> {
734        Ok(self.permissions.read().await.get(permission_id).cloned())
735    }
736
737    async fn assign_role(&self, assignment: &RoleAssignment) -> StorageResult<()> {
738        self.user_roles
739            .write()
740            .await
741            .entry(assignment.user_id.clone())
742            .or_default()
743            .push(assignment.role_id.clone());
744        info!(
745            "Assigned role in memory: {} to {}",
746            assignment.role_id, assignment.user_id
747        );
748        Ok(())
749    }
750
751    async fn revoke_role(&self, user_id: &str, role_id: &str) -> StorageResult<()> {
752        if let Some(roles) = self.user_roles.write().await.get_mut(user_id) {
753            roles.retain(|r| r != role_id);
754        }
755        info!("Revoked role from memory: {} from {}", role_id, user_id);
756        Ok(())
757    }
758
759    async fn get_user_roles(&self, user_id: &str) -> StorageResult<Vec<String>> {
760        Ok(self
761            .user_roles
762            .read()
763            .await
764            .get(user_id)
765            .cloned()
766            .unwrap_or_default())
767    }
768
769    async fn get_role_permissions(&self, role_id: &str) -> StorageResult<Vec<String>> {
770        Ok(self
771            .role_permissions
772            .read()
773            .await
774            .get(role_id)
775            .cloned()
776            .unwrap_or_default())
777    }
778
779    async fn log_audit_entry(&self, entry: &AuditEntry) -> StorageResult<()> {
780        self.audit_log.write().await.push(entry.clone());
781        debug!(
782            "Logged audit entry in memory for user {:?}: {}",
783            entry.user_id, entry.action
784        );
785        Ok(())
786    }
787}
788
789#[cfg(test)]
790mod tests {
791    use super::*;
792
793    #[tokio::test]
794    async fn test_memory_storage_basic_operations() {
795        let storage = MemoryRbacStorage::new();
796
797        let role = StoredRole {
798            id: "test_role".to_string(),
799            name: "Test Role".to_string(),
800            description: Some("A test role".to_string()),
801            permissions: Vec::new(),
802        };
803
804        storage.create_role(&role).await.unwrap();
805
806        let retrieved = storage.get_role("test_role").await.unwrap();
807        assert!(retrieved.is_some());
808        assert_eq!(retrieved.unwrap().name, "Test Role");
809
810        let roles = storage.list_roles().await.unwrap();
811        assert_eq!(roles.len(), 1);
812
813        storage.delete_role("test_role").await.unwrap();
814        let retrieved = storage.get_role("test_role").await.unwrap();
815        assert!(retrieved.is_none());
816    }
817
818    #[tokio::test]
819    async fn test_memory_storage_permissions() {
820        let storage = MemoryRbacStorage::new();
821
822        let permission = StoredPermission {
823            action: "read".to_string(),
824            resource: "users".to_string(),
825            instance: None,
826        };
827
828        storage
829            .store_permission("test_perm", &permission)
830            .await
831            .unwrap();
832
833        let retrieved = storage.get_permission("test_perm").await.unwrap();
834        assert!(retrieved.is_some());
835        assert_eq!(retrieved.unwrap().action, "read");
836    }
837
838    #[tokio::test]
839    async fn test_memory_storage_role_assignment() {
840        let storage = MemoryRbacStorage::new();
841
842        let assignment = RoleAssignment {
843            user_id: "user1".to_string(),
844            role_id: "admin".to_string(),
845            assigned_by: Some("system".to_string()),
846            expires_at: None,
847        };
848
849        storage.assign_role(&assignment).await.unwrap();
850        let user_roles = storage.get_user_roles("user1").await.unwrap();
851        assert_eq!(user_roles, vec!["admin"]);
852
853        storage.revoke_role("user1", "admin").await.unwrap();
854        let user_roles = storage.get_user_roles("user1").await.unwrap();
855        assert!(user_roles.is_empty());
856    }
857
858    #[tokio::test]
859    async fn test_stored_role_conversion_roundtrip() {
860        let stored = StoredRole {
861            id: "role1".to_string(),
862            name: "Admin".to_string(),
863            description: Some("Administrator role".to_string()),
864            permissions: vec![StoredPermission {
865                action: "read".to_string(),
866                resource: "users".to_string(),
867                instance: None,
868            }],
869        };
870
871        let rs_role = stored.to_role_system();
872        assert_eq!(rs_role.id(), "role1");
873        assert_eq!(rs_role.name(), "Admin");
874        assert_eq!(rs_role.description(), Some("Administrator role"));
875
876        let back = StoredRole::from_role_system(&rs_role);
877        assert_eq!(back.id, "role1");
878        assert_eq!(back.name, "Admin");
879        assert_eq!(back.permissions.len(), 1);
880        assert_eq!(back.permissions[0].action, "read");
881    }
882
883    #[tokio::test]
884    async fn test_audit_logging() {
885        let storage = MemoryRbacStorage::new();
886
887        let entry = AuditEntry {
888            user_id: Some("user1".to_string()),
889            action: "login".to_string(),
890            resource: Some("auth".to_string()),
891            result: "success".to_string(),
892            context: HashMap::from([("ip".to_string(), "127.0.0.1".to_string())]),
893        };
894
895        storage.log_audit_entry(&entry).await.unwrap();
896        let log = storage.audit_log.read().await;
897        assert_eq!(log.len(), 1);
898        assert_eq!(log[0].action, "login");
899    }
900}