1use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "snake_case")]
17pub enum Role {
18 Admin,
20 #[default]
22 Operator,
23 Viewer,
25}
26
27impl std::fmt::Display for Role {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 Role::Admin => write!(f, "admin"),
31 Role::Operator => write!(f, "operator"),
32 Role::Viewer => write!(f, "viewer"),
33 }
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub enum Permission {
45 GenerateData,
47 ManageJobs,
49 ViewJobs,
51 ManageConfig,
53 ViewConfig,
55 ViewMetrics,
57 ManageApiKeys,
59}
60
61impl std::fmt::Display for Permission {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 Permission::GenerateData => write!(f, "generate_data"),
65 Permission::ManageJobs => write!(f, "manage_jobs"),
66 Permission::ViewJobs => write!(f, "view_jobs"),
67 Permission::ManageConfig => write!(f, "manage_config"),
68 Permission::ViewConfig => write!(f, "view_config"),
69 Permission::ViewMetrics => write!(f, "view_metrics"),
70 Permission::ManageApiKeys => write!(f, "manage_api_keys"),
71 }
72 }
73}
74
75pub struct RolePermissions;
81
82impl RolePermissions {
83 pub fn has_permission(role: &Role, permission: &Permission) -> bool {
97 match role {
98 Role::Admin => true,
99 Role::Operator => matches!(
100 permission,
101 Permission::GenerateData
102 | Permission::ManageJobs
103 | Permission::ViewJobs
104 | Permission::ViewConfig
105 | Permission::ViewMetrics
106 ),
107 Role::Viewer => matches!(
108 permission,
109 Permission::ViewJobs | Permission::ViewConfig | Permission::ViewMetrics
110 ),
111 }
112 }
113
114 pub fn permissions_for(role: &Role) -> Vec<Permission> {
116 let all = [
117 Permission::GenerateData,
118 Permission::ManageJobs,
119 Permission::ViewJobs,
120 Permission::ManageConfig,
121 Permission::ViewConfig,
122 Permission::ViewMetrics,
123 Permission::ManageApiKeys,
124 ];
125 all.into_iter()
126 .filter(|p| Self::has_permission(role, p))
127 .collect()
128 }
129}
130
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
137pub struct RbacConfig {
138 #[serde(default)]
141 pub enabled: bool,
142}
143
144#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_admin_has_all_permissions() {
154 let all_permissions = [
155 Permission::GenerateData,
156 Permission::ManageJobs,
157 Permission::ViewJobs,
158 Permission::ManageConfig,
159 Permission::ViewConfig,
160 Permission::ViewMetrics,
161 Permission::ManageApiKeys,
162 ];
163 for perm in &all_permissions {
164 assert!(
165 RolePermissions::has_permission(&Role::Admin, perm),
166 "Admin should have permission: {}",
167 perm
168 );
169 }
170 }
171
172 #[test]
173 fn test_viewer_denied_generate() {
174 assert!(!RolePermissions::has_permission(
175 &Role::Viewer,
176 &Permission::GenerateData
177 ));
178 assert!(!RolePermissions::has_permission(
179 &Role::Viewer,
180 &Permission::ManageJobs
181 ));
182 assert!(!RolePermissions::has_permission(
183 &Role::Viewer,
184 &Permission::ManageConfig
185 ));
186 assert!(!RolePermissions::has_permission(
187 &Role::Viewer,
188 &Permission::ManageApiKeys
189 ));
190 }
191
192 #[test]
193 fn test_viewer_allowed_read_only() {
194 assert!(RolePermissions::has_permission(
195 &Role::Viewer,
196 &Permission::ViewJobs
197 ));
198 assert!(RolePermissions::has_permission(
199 &Role::Viewer,
200 &Permission::ViewConfig
201 ));
202 assert!(RolePermissions::has_permission(
203 &Role::Viewer,
204 &Permission::ViewMetrics
205 ));
206 }
207
208 #[test]
209 fn test_operator_permissions() {
210 assert!(RolePermissions::has_permission(
212 &Role::Operator,
213 &Permission::GenerateData
214 ));
215 assert!(RolePermissions::has_permission(
216 &Role::Operator,
217 &Permission::ManageJobs
218 ));
219 assert!(RolePermissions::has_permission(
220 &Role::Operator,
221 &Permission::ViewJobs
222 ));
223 assert!(RolePermissions::has_permission(
224 &Role::Operator,
225 &Permission::ViewConfig
226 ));
227 assert!(RolePermissions::has_permission(
228 &Role::Operator,
229 &Permission::ViewMetrics
230 ));
231
232 assert!(!RolePermissions::has_permission(
234 &Role::Operator,
235 &Permission::ManageConfig
236 ));
237 assert!(!RolePermissions::has_permission(
238 &Role::Operator,
239 &Permission::ManageApiKeys
240 ));
241 }
242
243 #[test]
244 fn test_default_role_is_operator() {
245 let role = Role::default();
246 assert_eq!(role, Role::Operator);
247 }
248
249 #[test]
250 fn test_rbac_config_default_disabled() {
251 let config = RbacConfig::default();
252 assert!(!config.enabled);
253 }
254
255 #[test]
256 fn test_role_serialization_roundtrip() {
257 let role = Role::Admin;
258 let json = serde_json::to_string(&role).unwrap();
259 assert_eq!(json, "\"admin\"");
260 let deserialized: Role = serde_json::from_str(&json).unwrap();
261 assert_eq!(deserialized, Role::Admin);
262 }
263
264 #[test]
265 fn test_permissions_for_role() {
266 let admin_perms = RolePermissions::permissions_for(&Role::Admin);
267 assert_eq!(admin_perms.len(), 7);
268
269 let operator_perms = RolePermissions::permissions_for(&Role::Operator);
270 assert_eq!(operator_perms.len(), 5);
271
272 let viewer_perms = RolePermissions::permissions_for(&Role::Viewer);
273 assert_eq!(viewer_perms.len(), 3);
274 }
275}