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> {
346 let role = self.roles.get(&role_id).await;
348 if role.is_none() {
349 log::warn!(
350 "[Ri.Permission] Attempted to assign non-existent role '{}' to user '{}'",
351 role_id, user_id
352 );
353 return Ok(false);
354 }
355
356 if role.as_ref().map_or(false, |r| r.is_system) {
360 log::warn!(
361 "[Ri.Permission] Attempted to assign system role '{}' to user '{}' - blocked for security",
362 role_id, user_id
363 );
364 return Ok(false);
365 }
366
367 log::info!(
369 "[Ri.Permission] Assigning role '{}' to user '{}'",
370 role_id, user_id
371 );
372
373 let user_role_set = self.user_roles.get(&user_id).await.unwrap_or_default();
374
375 if user_role_set.contains(&role_id) {
377 log::debug!(
378 "[Ri.Permission] User '{}' already has role '{}'",
379 user_id, role_id
380 );
381 return Ok(false);
382 }
383
384 let mut new_set = user_role_set.clone();
385 let was_added = new_set.insert(role_id.clone());
386 self.user_roles.insert(user_id.clone(), new_set).await;
387
388 log::info!(
390 "[Ri.Permission] Successfully assigned role '{}' to user '{}'",
391 role_id, user_id
392 );
393
394 Ok(was_added)
395 }
396
397 pub async fn remove_role_from_user(&self, user_id: &str, role_id: &str) -> crate::core::RiResult<bool> {
411 log::info!(
413 "[Ri.Permission] Removing role '{}' from user '{}'",
414 role_id, user_id
415 );
416
417 let user_role_set = self.user_roles.get(user_id).await;
418
419 match user_role_set {
420 Some(mut set) => {
421 let was_removed = set.remove(role_id);
422 if set.is_empty() {
423 self.user_roles.remove(user_id).await;
424 } else {
425 self.user_roles.insert(user_id.to_string(), set).await;
426 }
427
428 if was_removed {
429 log::info!(
430 "[Ri.Permission] Successfully removed role '{}' from user '{}'",
431 role_id, user_id
432 );
433 }
434
435 Ok(was_removed)
436 }
437 None => Ok(false),
438 }
439 }
440
441 pub async fn get_user_roles(&self, user_id: &str) -> crate::core::RiResult<Vec<RiRole>> {
442 let user_role_set = self.user_roles.get(user_id).await;
443
444 match user_role_set {
445 Some(role_ids) => {
446 let mut result = Vec::with_capacity(role_ids.len());
447 for role_id in role_ids {
448 if let Some(role) = self.roles.get(&role_id).await {
449 result.push(role);
450 }
451 }
452 Ok(result)
453 }
454 None => Ok(Vec::new()),
455 }
456 }
457
458 pub async fn has_permission(&self, user_id: &str, permission_id: &str) -> crate::core::RiResult<bool> {
459 let user_role_set = self.user_roles.get(user_id).await;
460
461 if let Some(role_ids) = user_role_set {
462 for role_id in role_ids {
463 if let Some(role) = self.roles.get(&role_id).await {
464 if role.permissions.contains("*") {
465 return Ok(true);
466 }
467
468 if role.permissions.contains(permission_id) {
469 return Ok(true);
470 }
471 }
472 }
473 }
474
475 Ok(false)
476 }
477
478 pub async fn has_any_permission(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
479 for permission in permissions {
480 if self.has_permission(user_id, permission).await? {
481 return Ok(true);
482 }
483 }
484 Ok(false)
485 }
486
487 pub async fn has_all_permissions(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
488 for permission in permissions {
489 if !self.has_permission(user_id, permission).await? {
490 return Ok(false);
491 }
492 }
493 Ok(true)
494 }
495
496 pub async fn get_user_permissions(&self, user_id: &str) -> crate::core::RiResult<HashSet<String>> {
497 let user_role_set = self.user_roles.get(user_id).await;
498
499 let mut permissions = HashSet::new();
500
501 if let Some(role_ids) = user_role_set {
502 for role_id in role_ids {
503 if let Some(role) = self.roles.get(&role_id).await {
504 permissions.extend(role.permissions);
505 }
506 }
507 }
508
509 Ok(permissions)
510 }
511
512 pub async fn delete_permission(&self, permission_id: &str) -> crate::core::RiResult<bool> {
513 Ok(self.permissions.remove(permission_id).await.is_some())
514 }
515
516 pub async fn delete_role(&self, role_id: &str) -> crate::core::RiResult<bool> {
517 let role = self.roles.get(role_id).await;
518 if let Some(r) = role {
519 if r.is_system {
520 return Ok(false);
521 }
522 }
523
524 let was_deleted = self.roles.remove(role_id).await.is_some();
525
526 if was_deleted {
527 self.user_roles.for_each_mut(|_, role_set| {
528 role_set.remove(role_id);
529 }).await;
530 }
531
532 Ok(was_deleted)
533 }
534
535 pub async fn list_permissions(&self) -> crate::core::RiResult<Vec<RiPermission>> {
536 Ok(self.permissions.collect_all().await.into_values().collect())
537 }
538
539 pub async fn list_roles(&self) -> crate::core::RiResult<Vec<RiRole>> {
540 Ok(self.roles.collect_all().await.into_values().collect())
541 }
542}
543
544#[cfg(feature = "pyo3")]
545#[pyo3::prelude::pymethods]
586impl RiPermissionManager {
587 #[new]
588 fn py_new() -> PyResult<Self> {
589 Ok(Self::new())
590 }
591
592 #[pyo3(name = "create_permission")]
593 fn create_permission_impl(&self, permission: RiPermission) -> PyResult<()> {
594 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
595 rt.block_on(async {
596 self.create_permission(permission).await
597 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
598 })
599 }
600
601 #[pyo3(name = "create_role")]
602 fn create_role_impl(&self, role: RiRole) -> PyResult<()> {
603 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
604 rt.block_on(async {
605 self.create_role(role).await
606 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
607 })
608 }
609
610 #[pyo3(name = "assign_role_to_user")]
611 fn assign_role_to_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
612 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
613 rt.block_on(async {
614 self.assign_role_to_user(user_id, role_id).await
615 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
616 })
617 }
618
619 #[pyo3(name = "has_permission")]
620 fn has_permission_impl(&self, user_id: String, permission_id: String) -> PyResult<bool> {
621 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
622 rt.block_on(async {
623 self.has_permission(&user_id, &permission_id).await
624 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
625 })
626 }
627
628 #[pyo3(name = "get_user_roles")]
629 fn get_user_roles_impl(&self, user_id: String) -> PyResult<Vec<RiRole>> {
630 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
631 rt.block_on(async {
632 self.get_user_roles(&user_id).await
633 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
634 })
635 }
636
637 #[pyo3(name = "get_user_permissions")]
638 fn get_user_permissions_impl(&self, user_id: String) -> PyResult<Vec<String>> {
639 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
640 rt.block_on(async {
641 self.get_user_permissions(&user_id).await
642 .map(|perms| perms.into_iter().collect())
643 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
644 })
645 }
646
647 #[pyo3(name = "remove_role_from_user")]
648 fn remove_role_from_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
649 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
650 rt.block_on(async {
651 self.remove_role_from_user(&user_id, &role_id).await
652 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
653 })
654 }
655
656 #[pyo3(name = "list_roles")]
657 fn list_roles_impl(&self) -> PyResult<Vec<RiRole>> {
658 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
659 rt.block_on(async {
660 self.list_roles().await
661 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
662 })
663 }
664
665 #[pyo3(name = "list_permissions")]
666 fn list_permissions_impl(&self) -> PyResult<Vec<RiPermission>> {
667 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
668 rt.block_on(async {
669 self.list_permissions().await
670 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
671 })
672 }
673
674 #[pyo3(name = "delete_role")]
675 fn delete_role_impl(&self, role_id: String) -> PyResult<bool> {
676 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
677 rt.block_on(async {
678 self.delete_role(&role_id).await
679 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
680 })
681 }
682
683 #[pyo3(name = "delete_permission")]
684 fn delete_permission_impl(&self, permission_id: String) -> PyResult<bool> {
685 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
686 rt.block_on(async {
687 self.delete_permission(&permission_id).await
688 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
689 })
690 }
691
692 #[pyo3(name = "get_role")]
693 fn get_role_impl(&self, role_id: String) -> PyResult<Option<RiRole>> {
694 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
695 rt.block_on(async {
696 self.get_role(&role_id).await
697 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
698 })
699 }
700
701 #[pyo3(name = "get_permission")]
702 fn get_permission_impl(&self, permission_id: String) -> PyResult<Option<RiPermission>> {
703 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
704 rt.block_on(async {
705 self.get_permission(&permission_id).await
706 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
707 })
708 }
709
710 #[pyo3(name = "has_any_permission")]
711 fn has_any_permission_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
712 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
713 rt.block_on(async {
714 self.has_any_permission(&user_id, &permissions).await
715 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
716 })
717 }
718
719 #[pyo3(name = "has_all_permissions")]
720 fn has_all_permissions_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
721 let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
722 rt.block_on(async {
723 self.has_all_permissions(&user_id, &permissions).await
724 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
725 })
726 }
727}