1#![allow(non_snake_case)]
68
69use serde::{Deserialize, Serialize};
70use std::collections::HashSet;
71use crate::core::concurrent::RiShardedLock;
72
73#[cfg(feature = "pyo3")]
74use pyo3::PyResult;
75
76#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct RiPermission {
83 pub id: String,
85 pub name: String,
87 pub description: String,
89 pub resource: String,
91 pub action: String,
93}
94
95#[cfg(feature = "pyo3")]
96#[pyo3::prelude::pymethods]
97impl RiPermission {
98 #[new]
99 fn py_new(
100 id: Option<String>,
101 name: String,
102 description: String,
103 resource: String,
104 action: String,
105 ) -> Self {
106 Self {
107 id: id.unwrap_or_else(|| format!("{}:{}", resource, action)),
108 name,
109 description,
110 resource,
111 action,
112 }
113 }
114}
115
116#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct RiRole {
123 pub id: String,
125 pub name: String,
127 pub description: String,
129 pub permissions: HashSet<String>,
131 pub is_system: bool,
133}
134
135#[cfg(feature = "pyo3")]
136#[pyo3::prelude::pymethods]
137impl RiRole {
138 #[new]
139 fn py_new(
140 id: Option<String>,
141 name: String,
142 description: String,
143 permissions: Vec<String>,
144 is_system: bool,
145 ) -> Self {
146 Self {
147 id: id.unwrap_or_else(|| name.to_lowercase().replace(' ', "_")),
148 name,
149 description,
150 permissions: permissions.into_iter().collect(),
151 is_system,
152 }
153 }
154}
155
156impl RiRole {
157 pub fn new(id: String, name: String, description: String, permissions: HashSet<String>) -> Self {
168 Self {
169 id,
170 name,
171 description,
172 permissions,
173 is_system: false,
174 }
175 }
176
177 #[inline]
185 pub fn has_permission(&self, permission_id: &str) -> bool {
186 self.permissions.contains(permission_id)
187 }
188
189 #[inline]
194 pub fn add_permission(&mut self, permission_id: String) {
195 self.permissions.insert(permission_id);
196 }
197
198 #[inline]
203 pub fn remove_permission(&mut self, permission_id: &str) {
204 self.permissions.remove(permission_id);
205 }
206}
207
208#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
216pub struct RiPermissionManager {
217 permissions: RiShardedLock<String, RiPermission>,
218 roles: RiShardedLock<String, RiRole>,
219 user_roles: RiShardedLock<String, HashSet<String>>,
220}
221
222impl Default for RiPermissionManager {
223 fn default() -> Self {
224 Self::new()
225 }
226}
227
228impl RiPermissionManager {
229 pub fn new() -> Self {
242 let mut manager = Self {
243 permissions: RiShardedLock::with_default_shards(),
244 roles: RiShardedLock::with_default_shards(),
245 user_roles: RiShardedLock::with_default_shards(),
246 };
247
248 manager.initialize_default_roles();
249 manager
250 }
251
252 pub async fn new_async() -> Self {
264 let manager = Self {
265 permissions: RiShardedLock::with_default_shards(),
266 roles: RiShardedLock::with_default_shards(),
267 user_roles: RiShardedLock::with_default_shards(),
268 };
269
270 manager.initialize_default_roles_async().await;
271 manager
272 }
273
274 fn initialize_default_roles(&mut self) {
275 let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
276 rt.block_on(async {
277 self.initialize_default_roles_async().await
278 });
279 }
280
281 async fn initialize_default_roles_async(&self) {
282 let admin_permissions: HashSet<String> = vec![
283 "*".to_string(),
284 ].into_iter().collect();
285
286 let admin_role = RiRole {
287 id: "admin".to_string(),
288 name: "Administrator".to_string(),
289 description: "Full system access".to_string(),
290 permissions: admin_permissions,
291 is_system: true,
292 };
293
294 self.roles.insert("admin".to_string(), admin_role).await;
295
296 let user_permissions: HashSet<String> = vec![
297 "read:profile".to_string(),
298 "update:profile".to_string(),
299 "read:own_data".to_string(),
300 ].into_iter().collect();
301
302 let user_role = RiRole {
303 id: "user".to_string(),
304 name: "User".to_string(),
305 description: "Standard user access".to_string(),
306 permissions: user_permissions,
307 is_system: true,
308 };
309
310 self.roles.insert("user".to_string(), user_role).await;
311 }
312
313 pub async fn create_permission(&self, permission: RiPermission) -> crate::core::RiResult<()> {
314 self.permissions.insert(permission.id.clone(), permission).await;
315 Ok(())
316 }
317
318 pub async fn get_permission(&self, permission_id: &str) -> crate::core::RiResult<Option<RiPermission>> {
319 Ok(self.permissions.get(permission_id).await)
320 }
321
322 pub async fn create_role(&self, role: RiRole) -> crate::core::RiResult<()> {
323 self.roles.insert(role.id.clone(), role).await;
324 Ok(())
325 }
326
327 pub async fn get_role(&self, role_id: &str) -> crate::core::RiResult<Option<RiRole>> {
328 Ok(self.roles.get(role_id).await)
329 }
330
331 pub async fn assign_role_to_user(&self, user_id: String, role_id: String) -> crate::core::RiResult<bool> {
332 let role_exists = self.roles.contains_key(&role_id).await;
333 if !role_exists {
334 return Ok(false);
335 }
336
337 let user_role_set = self.user_roles.get(&user_id).await.unwrap_or_default();
338 let mut new_set = user_role_set.clone();
339 let was_added = new_set.insert(role_id);
340 self.user_roles.insert(user_id, new_set).await;
341 Ok(was_added)
342 }
343
344 pub async fn remove_role_from_user(&self, user_id: &str, role_id: &str) -> crate::core::RiResult<bool> {
345 let user_role_set = self.user_roles.get(user_id).await;
346
347 match user_role_set {
348 Some(mut set) => {
349 let was_removed = set.remove(role_id);
350 if set.is_empty() {
351 self.user_roles.remove(user_id).await;
352 } else {
353 self.user_roles.insert(user_id.to_string(), set).await;
354 }
355 Ok(was_removed)
356 }
357 None => Ok(false),
358 }
359 }
360
361 pub async fn get_user_roles(&self, user_id: &str) -> crate::core::RiResult<Vec<RiRole>> {
362 let user_role_set = self.user_roles.get(user_id).await;
363
364 match user_role_set {
365 Some(role_ids) => {
366 let mut result = Vec::with_capacity(role_ids.len());
367 for role_id in role_ids {
368 if let Some(role) = self.roles.get(&role_id).await {
369 result.push(role);
370 }
371 }
372 Ok(result)
373 }
374 None => Ok(Vec::new()),
375 }
376 }
377
378 pub async fn has_permission(&self, user_id: &str, permission_id: &str) -> crate::core::RiResult<bool> {
379 let user_role_set = self.user_roles.get(user_id).await;
380
381 if let Some(role_ids) = user_role_set {
382 for role_id in role_ids {
383 if let Some(role) = self.roles.get(&role_id).await {
384 if role.permissions.contains("*") {
385 return Ok(true);
386 }
387
388 if role.permissions.contains(permission_id) {
389 return Ok(true);
390 }
391 }
392 }
393 }
394
395 Ok(false)
396 }
397
398 pub async fn has_any_permission(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
399 for permission in permissions {
400 if self.has_permission(user_id, permission).await? {
401 return Ok(true);
402 }
403 }
404 Ok(false)
405 }
406
407 pub async fn has_all_permissions(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
408 for permission in permissions {
409 if !self.has_permission(user_id, permission).await? {
410 return Ok(false);
411 }
412 }
413 Ok(true)
414 }
415
416 pub async fn get_user_permissions(&self, user_id: &str) -> crate::core::RiResult<HashSet<String>> {
417 let user_role_set = self.user_roles.get(user_id).await;
418
419 let mut permissions = HashSet::new();
420
421 if let Some(role_ids) = user_role_set {
422 for role_id in role_ids {
423 if let Some(role) = self.roles.get(&role_id).await {
424 permissions.extend(role.permissions);
425 }
426 }
427 }
428
429 Ok(permissions)
430 }
431
432 pub async fn delete_permission(&self, permission_id: &str) -> crate::core::RiResult<bool> {
433 Ok(self.permissions.remove(permission_id).await.is_some())
434 }
435
436 pub async fn delete_role(&self, role_id: &str) -> crate::core::RiResult<bool> {
437 let role = self.roles.get(role_id).await;
438 if let Some(r) = role {
439 if r.is_system {
440 return Ok(false);
441 }
442 }
443
444 let was_deleted = self.roles.remove(role_id).await.is_some();
445
446 if was_deleted {
447 self.user_roles.for_each_mut(|_, role_set| {
448 role_set.remove(role_id);
449 }).await;
450 }
451
452 Ok(was_deleted)
453 }
454
455 pub async fn list_permissions(&self) -> crate::core::RiResult<Vec<RiPermission>> {
456 Ok(self.permissions.collect_all().await.into_values().collect())
457 }
458
459 pub async fn list_roles(&self) -> crate::core::RiResult<Vec<RiRole>> {
460 Ok(self.roles.collect_all().await.into_values().collect())
461 }
462}
463
464#[cfg(feature = "pyo3")]
465#[pyo3::prelude::pymethods]
506impl RiPermissionManager {
507 #[new]
508 fn py_new() -> PyResult<Self> {
509 Ok(Self::new())
510 }
511
512 #[pyo3(name = "create_permission")]
513 fn create_permission_impl(&self, permission: RiPermission) -> PyResult<()> {
514 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
515 rt.block_on(async {
516 self.create_permission(permission).await
517 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
518 })
519 }
520
521 #[pyo3(name = "create_role")]
522 fn create_role_impl(&self, role: RiRole) -> PyResult<()> {
523 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
524 rt.block_on(async {
525 self.create_role(role).await
526 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
527 })
528 }
529
530 #[pyo3(name = "assign_role_to_user")]
531 fn assign_role_to_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
532 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
533 rt.block_on(async {
534 self.assign_role_to_user(user_id, role_id).await
535 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
536 })
537 }
538
539 #[pyo3(name = "has_permission")]
540 fn has_permission_impl(&self, user_id: String, permission_id: String) -> PyResult<bool> {
541 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
542 rt.block_on(async {
543 self.has_permission(&user_id, &permission_id).await
544 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
545 })
546 }
547
548 #[pyo3(name = "get_user_roles")]
549 fn get_user_roles_impl(&self, user_id: String) -> PyResult<Vec<RiRole>> {
550 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
551 rt.block_on(async {
552 self.get_user_roles(&user_id).await
553 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
554 })
555 }
556
557 #[pyo3(name = "get_user_permissions")]
558 fn get_user_permissions_impl(&self, user_id: String) -> PyResult<Vec<String>> {
559 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
560 rt.block_on(async {
561 self.get_user_permissions(&user_id).await
562 .map(|perms| perms.into_iter().collect())
563 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
564 })
565 }
566
567 #[pyo3(name = "remove_role_from_user")]
568 fn remove_role_from_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
569 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
570 rt.block_on(async {
571 self.remove_role_from_user(&user_id, &role_id).await
572 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
573 })
574 }
575
576 #[pyo3(name = "list_roles")]
577 fn list_roles_impl(&self) -> PyResult<Vec<RiRole>> {
578 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
579 rt.block_on(async {
580 self.list_roles().await
581 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
582 })
583 }
584
585 #[pyo3(name = "list_permissions")]
586 fn list_permissions_impl(&self) -> PyResult<Vec<RiPermission>> {
587 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
588 rt.block_on(async {
589 self.list_permissions().await
590 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
591 })
592 }
593
594 #[pyo3(name = "delete_role")]
595 fn delete_role_impl(&self, role_id: String) -> PyResult<bool> {
596 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
597 rt.block_on(async {
598 self.delete_role(&role_id).await
599 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
600 })
601 }
602
603 #[pyo3(name = "delete_permission")]
604 fn delete_permission_impl(&self, permission_id: String) -> PyResult<bool> {
605 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
606 rt.block_on(async {
607 self.delete_permission(&permission_id).await
608 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
609 })
610 }
611
612 #[pyo3(name = "get_role")]
613 fn get_role_impl(&self, role_id: String) -> PyResult<Option<RiRole>> {
614 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
615 rt.block_on(async {
616 self.get_role(&role_id).await
617 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
618 })
619 }
620
621 #[pyo3(name = "get_permission")]
622 fn get_permission_impl(&self, permission_id: String) -> PyResult<Option<RiPermission>> {
623 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
624 rt.block_on(async {
625 self.get_permission(&permission_id).await
626 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
627 })
628 }
629
630 #[pyo3(name = "has_any_permission")]
631 fn has_any_permission_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
632 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
633 rt.block_on(async {
634 self.has_any_permission(&user_id, &permissions).await
635 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
636 })
637 }
638
639 #[pyo3(name = "has_all_permissions")]
640 fn has_all_permissions_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
641 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
642 rt.block_on(async {
643 self.has_all_permissions(&user_id, &permissions).await
644 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
645 })
646 }
647}