mockforge_collab/
permissions.rs

1//! Permission checking and role-based access control
2
3use crate::error::{CollabError, Result};
4use crate::models::UserRole;
5use serde::{Deserialize, Serialize};
6
7/// Permission types in the system
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
9pub enum Permission {
10    // Workspace permissions
11    WorkspaceCreate,
12    WorkspaceRead,
13    WorkspaceUpdate,
14    WorkspaceDelete,
15    WorkspaceArchive,
16    WorkspaceManageMembers,
17
18    // Mock/Route permissions
19    MockCreate,
20    MockRead,
21    MockUpdate,
22    MockDelete,
23
24    // Collaboration permissions
25    InviteMembers,
26    RemoveMembers,
27    ChangeRoles,
28
29    // History permissions
30    ViewHistory,
31    CreateSnapshot,
32    RestoreSnapshot,
33
34    // Settings permissions
35    ManageSettings,
36    ManageIntegrations,
37
38    // Scenario-specific permissions
39    /// Modify chaos rules for scenarios (typically QA only)
40    ScenarioModifyChaosRules,
41    /// Change reality-level defaults for scenarios (typically Platform team only)
42    ScenarioModifyRealityDefaults,
43    /// Promote scenarios between environments
44    ScenarioPromote,
45    /// Approve scenario promotions
46    ScenarioApprove,
47    /// Modify drift budgets for scenarios
48    ScenarioModifyDriftBudgets,
49}
50
51/// Role permissions mapping
52pub struct RolePermissions;
53
54impl RolePermissions {
55    /// Get all permissions for a role
56    pub fn get_permissions(role: UserRole) -> Vec<Permission> {
57        match role {
58            UserRole::Admin => vec![
59                // Full access to everything
60                Permission::WorkspaceCreate,
61                Permission::WorkspaceRead,
62                Permission::WorkspaceUpdate,
63                Permission::WorkspaceDelete,
64                Permission::WorkspaceArchive,
65                Permission::WorkspaceManageMembers,
66                Permission::MockCreate,
67                Permission::MockRead,
68                Permission::MockUpdate,
69                Permission::MockDelete,
70                Permission::InviteMembers,
71                Permission::RemoveMembers,
72                Permission::ChangeRoles,
73                Permission::ViewHistory,
74                Permission::CreateSnapshot,
75                Permission::RestoreSnapshot,
76                Permission::ManageSettings,
77                Permission::ManageIntegrations,
78                // Scenario permissions - admins have all
79                Permission::ScenarioModifyChaosRules,
80                Permission::ScenarioModifyRealityDefaults,
81                Permission::ScenarioPromote,
82                Permission::ScenarioApprove,
83                Permission::ScenarioModifyDriftBudgets,
84            ],
85            UserRole::Editor => vec![
86                // Can edit but not manage workspace or members
87                Permission::WorkspaceRead,
88                Permission::MockCreate,
89                Permission::MockRead,
90                Permission::MockUpdate,
91                Permission::MockDelete,
92                Permission::ViewHistory,
93                Permission::CreateSnapshot,
94                // Editors can promote scenarios but not approve or modify sensitive configs
95                Permission::ScenarioPromote,
96            ],
97            UserRole::Viewer => vec![
98                // Read-only access
99                Permission::WorkspaceRead,
100                Permission::MockRead,
101                Permission::ViewHistory,
102            ],
103        }
104    }
105
106    /// Check if a role has a specific permission
107    pub fn has_permission(role: UserRole, permission: Permission) -> bool {
108        Self::get_permissions(role).contains(&permission)
109    }
110}
111
112/// Permission checker for authorization
113pub struct PermissionChecker;
114
115impl PermissionChecker {
116    /// Check if a user has permission to perform an action
117    pub fn check(user_role: UserRole, required_permission: Permission) -> Result<()> {
118        if RolePermissions::has_permission(user_role, required_permission) {
119            Ok(())
120        } else {
121            Err(CollabError::AuthorizationFailed(format!(
122                "Role {:?} does not have permission {:?}",
123                user_role, required_permission
124            )))
125        }
126    }
127
128    /// Check multiple permissions (must have all)
129    pub fn check_all(user_role: UserRole, required_permissions: &[Permission]) -> Result<()> {
130        for permission in required_permissions {
131            Self::check(user_role, *permission)?;
132        }
133        Ok(())
134    }
135
136    /// Check multiple permissions (must have at least one)
137    pub fn check_any(user_role: UserRole, required_permissions: &[Permission]) -> Result<()> {
138        for permission in required_permissions {
139            if RolePermissions::has_permission(user_role, *permission) {
140                return Ok(());
141            }
142        }
143        Err(CollabError::AuthorizationFailed(format!(
144            "Role {:?} does not have any of the required permissions",
145            user_role
146        )))
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_admin_permissions() {
156        let permissions = RolePermissions::get_permissions(UserRole::Admin);
157        assert!(permissions.contains(&Permission::WorkspaceDelete));
158        assert!(permissions.contains(&Permission::MockCreate));
159        assert!(permissions.contains(&Permission::ChangeRoles));
160    }
161
162    #[test]
163    fn test_editor_permissions() {
164        let permissions = RolePermissions::get_permissions(UserRole::Editor);
165        assert!(permissions.contains(&Permission::MockCreate));
166        assert!(permissions.contains(&Permission::MockUpdate));
167        assert!(!permissions.contains(&Permission::WorkspaceDelete));
168        assert!(!permissions.contains(&Permission::ChangeRoles));
169    }
170
171    #[test]
172    fn test_viewer_permissions() {
173        let permissions = RolePermissions::get_permissions(UserRole::Viewer);
174        assert!(permissions.contains(&Permission::WorkspaceRead));
175        assert!(permissions.contains(&Permission::MockRead));
176        assert!(!permissions.contains(&Permission::MockCreate));
177        assert!(!permissions.contains(&Permission::MockUpdate));
178    }
179
180    #[test]
181    fn test_permission_check() {
182        assert!(PermissionChecker::check(UserRole::Admin, Permission::WorkspaceDelete).is_ok());
183        assert!(PermissionChecker::check(UserRole::Editor, Permission::MockCreate).is_ok());
184        assert!(PermissionChecker::check(UserRole::Viewer, Permission::MockCreate).is_err());
185    }
186
187    #[test]
188    fn test_check_all() {
189        let permissions = vec![Permission::MockRead, Permission::MockCreate];
190        assert!(PermissionChecker::check_all(UserRole::Editor, &permissions).is_ok());
191        assert!(PermissionChecker::check_all(UserRole::Viewer, &permissions).is_err());
192    }
193
194    #[test]
195    fn test_check_any() {
196        let permissions = vec![Permission::MockCreate, Permission::WorkspaceDelete];
197        assert!(PermissionChecker::check_any(UserRole::Editor, &permissions).is_ok());
198
199        let admin_only = vec![Permission::WorkspaceDelete, Permission::ChangeRoles];
200        assert!(PermissionChecker::check_any(UserRole::Viewer, &admin_only).is_err());
201    }
202}