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 if tokio::runtime::Handle::try_current().is_ok() {
281 futures::executor::block_on(async {
282 self.initialize_default_roles_async().await
283 });
284 } else {
285 let rt = crate::py_runtime::py_runtime();
286 rt.block_on(async {
287 self.initialize_default_roles_async().await
288 });
289 }
290 }
291
292 async fn initialize_default_roles_async(&self) {
293 let admin_permissions: HashSet<String> = vec![
294 "*".to_string(),
295 ].into_iter().collect();
296
297 let admin_role = RiRole {
298 id: "admin".to_string(),
299 name: "Administrator".to_string(),
300 description: "Full system access".to_string(),
301 permissions: admin_permissions,
302 is_system: true,
303 };
304
305 self.roles.insert("admin".to_string(), admin_role).await;
306
307 let user_permissions: HashSet<String> = vec![
308 "read:profile".to_string(),
309 "update:profile".to_string(),
310 "read:own_data".to_string(),
311 ].into_iter().collect();
312
313 let user_role = RiRole {
314 id: "user".to_string(),
315 name: "User".to_string(),
316 description: "Standard user access".to_string(),
317 permissions: user_permissions,
318 is_system: true,
319 };
320
321 self.roles.insert("user".to_string(), user_role).await;
322 }
323
324 pub async fn create_permission(&self, permission: RiPermission) -> crate::core::RiResult<()> {
325 self.permissions.insert(permission.id.clone(), permission).await;
326 Ok(())
327 }
328
329 pub async fn get_permission(&self, permission_id: &str) -> crate::core::RiResult<Option<RiPermission>> {
330 Ok(self.permissions.get(permission_id).await)
331 }
332
333 pub async fn create_role(&self, role: RiRole) -> crate::core::RiResult<()> {
334 self.roles.insert(role.id.clone(), role).await;
335 Ok(())
336 }
337
338 pub async fn get_role(&self, role_id: &str) -> crate::core::RiResult<Option<RiRole>> {
339 Ok(self.roles.get(role_id).await)
340 }
341
342 pub async fn assign_role_to_user(&self, user_id: String, role_id: String) -> crate::core::RiResult<bool> {
357 let role = self.roles.get(&role_id).await;
359 if role.is_none() {
360 log::warn!(
361 "[Ri.Permission] Attempted to assign non-existent role '{}' to user '{}'",
362 role_id, user_id
363 );
364 return Ok(false);
365 }
366
367 if role.as_ref().map_or(false, |r| r.is_system) {
371 log::warn!(
372 "[Ri.Permission] Attempted to assign system role '{}' to user '{}' - blocked for security",
373 role_id, user_id
374 );
375 return Ok(false);
376 }
377
378 log::info!(
380 "[Ri.Permission] Assigning role '{}' to user '{}'",
381 role_id, user_id
382 );
383
384 let user_role_set = self.user_roles.get(&user_id).await.unwrap_or_default();
385
386 if user_role_set.contains(&role_id) {
388 log::debug!(
389 "[Ri.Permission] User '{}' already has role '{}'",
390 user_id, role_id
391 );
392 return Ok(false);
393 }
394
395 let mut new_set = user_role_set.clone();
396 let was_added = new_set.insert(role_id.clone());
397 self.user_roles.insert(user_id.clone(), new_set).await;
398
399 log::info!(
401 "[Ri.Permission] Successfully assigned role '{}' to user '{}'",
402 role_id, user_id
403 );
404
405 Ok(was_added)
406 }
407
408 pub async fn remove_role_from_user(&self, user_id: &str, role_id: &str) -> crate::core::RiResult<bool> {
422 log::info!(
424 "[Ri.Permission] Removing role '{}' from user '{}'",
425 role_id, user_id
426 );
427
428 let user_role_set = self.user_roles.get(user_id).await;
429
430 match user_role_set {
431 Some(mut set) => {
432 let was_removed = set.remove(role_id);
433 if set.is_empty() {
434 self.user_roles.remove(user_id).await;
435 } else {
436 self.user_roles.insert(user_id.to_string(), set).await;
437 }
438
439 if was_removed {
440 log::info!(
441 "[Ri.Permission] Successfully removed role '{}' from user '{}'",
442 role_id, user_id
443 );
444 }
445
446 Ok(was_removed)
447 }
448 None => Ok(false),
449 }
450 }
451
452 pub async fn get_user_roles(&self, user_id: &str) -> crate::core::RiResult<Vec<RiRole>> {
453 let user_role_set = self.user_roles.get(user_id).await;
454
455 match user_role_set {
456 Some(role_ids) => {
457 let mut result = Vec::with_capacity(role_ids.len());
458 for role_id in role_ids {
459 if let Some(role) = self.roles.get(&role_id).await {
460 result.push(role);
461 }
462 }
463 Ok(result)
464 }
465 None => Ok(Vec::new()),
466 }
467 }
468
469 pub async fn has_permission(&self, user_id: &str, permission_id: &str) -> crate::core::RiResult<bool> {
470 let user_role_set = self.user_roles.get(user_id).await;
471
472 if let Some(role_ids) = user_role_set {
473 for role_id in role_ids {
474 if let Some(role) = self.roles.get(&role_id).await {
475 if role.permissions.contains("*") {
476 return Ok(true);
477 }
478
479 if role.permissions.contains(permission_id) {
480 return Ok(true);
481 }
482 }
483 }
484 }
485
486 Ok(false)
487 }
488
489 pub async fn has_any_permission(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
490 for permission in permissions {
491 if self.has_permission(user_id, permission).await? {
492 return Ok(true);
493 }
494 }
495 Ok(false)
496 }
497
498 pub async fn has_all_permissions(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
499 for permission in permissions {
500 if !self.has_permission(user_id, permission).await? {
501 return Ok(false);
502 }
503 }
504 Ok(true)
505 }
506
507 pub async fn get_user_permissions(&self, user_id: &str) -> crate::core::RiResult<HashSet<String>> {
508 let user_role_set = self.user_roles.get(user_id).await;
509
510 let mut permissions = HashSet::new();
511
512 if let Some(role_ids) = user_role_set {
513 for role_id in role_ids {
514 if let Some(role) = self.roles.get(&role_id).await {
515 permissions.extend(role.permissions);
516 }
517 }
518 }
519
520 Ok(permissions)
521 }
522
523 pub async fn delete_permission(&self, permission_id: &str) -> crate::core::RiResult<bool> {
524 Ok(self.permissions.remove(permission_id).await.is_some())
525 }
526
527 pub async fn delete_role(&self, role_id: &str) -> crate::core::RiResult<bool> {
528 let role = self.roles.get(role_id).await;
529 if let Some(r) = role {
530 if r.is_system {
531 return Ok(false);
532 }
533 }
534
535 let was_deleted = self.roles.remove(role_id).await.is_some();
536
537 if was_deleted {
538 self.user_roles.for_each_mut(|_, role_set| {
539 role_set.remove(role_id);
540 }).await;
541 }
542
543 Ok(was_deleted)
544 }
545
546 pub async fn list_permissions(&self) -> crate::core::RiResult<Vec<RiPermission>> {
547 Ok(self.permissions.collect_all().await.into_values().collect())
548 }
549
550 pub async fn list_roles(&self) -> crate::core::RiResult<Vec<RiRole>> {
551 Ok(self.roles.collect_all().await.into_values().collect())
552 }
553}
554
555#[cfg(feature = "pyo3")]
556#[pyo3::prelude::pymethods]
597impl RiPermissionManager {
598 #[new]
599 fn py_new() -> PyResult<Self> {
600 Ok(Self::new())
601 }
602
603 #[pyo3(name = "create_permission")]
604 fn create_permission_impl(&self, permission: RiPermission) -> PyResult<()> {
605 let rt = crate::py_runtime::py_runtime();
606 rt.block_on(async {
607 self.create_permission(permission).await
608 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
609 })
610 }
611
612 #[pyo3(name = "create_role")]
613 fn create_role_impl(&self, role: RiRole) -> PyResult<()> {
614 let rt = crate::py_runtime::py_runtime();
615 rt.block_on(async {
616 self.create_role(role).await
617 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
618 })
619 }
620
621 #[pyo3(name = "assign_role_to_user")]
622 fn assign_role_to_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
623 let rt = crate::py_runtime::py_runtime();
624 rt.block_on(async {
625 self.assign_role_to_user(user_id, role_id).await
626 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
627 })
628 }
629
630 #[pyo3(name = "has_permission")]
631 fn has_permission_impl(&self, user_id: String, permission_id: String) -> PyResult<bool> {
632 let rt = crate::py_runtime::py_runtime();
633 rt.block_on(async {
634 self.has_permission(&user_id, &permission_id).await
635 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
636 })
637 }
638
639 #[pyo3(name = "get_user_roles")]
640 fn get_user_roles_impl(&self, user_id: String) -> PyResult<Vec<RiRole>> {
641 let rt = crate::py_runtime::py_runtime();
642 rt.block_on(async {
643 self.get_user_roles(&user_id).await
644 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
645 })
646 }
647
648 #[pyo3(name = "get_user_permissions")]
649 fn get_user_permissions_impl(&self, user_id: String) -> PyResult<Vec<String>> {
650 let rt = crate::py_runtime::py_runtime();
651 rt.block_on(async {
652 self.get_user_permissions(&user_id).await
653 .map(|perms| perms.into_iter().collect())
654 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
655 })
656 }
657
658 #[pyo3(name = "remove_role_from_user")]
659 fn remove_role_from_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
660 let rt = crate::py_runtime::py_runtime();
661 rt.block_on(async {
662 self.remove_role_from_user(&user_id, &role_id).await
663 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
664 })
665 }
666
667 #[pyo3(name = "list_roles")]
668 fn list_roles_impl(&self) -> PyResult<Vec<RiRole>> {
669 let rt = crate::py_runtime::py_runtime();
670 rt.block_on(async {
671 self.list_roles().await
672 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
673 })
674 }
675
676 #[pyo3(name = "list_permissions")]
677 fn list_permissions_impl(&self) -> PyResult<Vec<RiPermission>> {
678 let rt = crate::py_runtime::py_runtime();
679 rt.block_on(async {
680 self.list_permissions().await
681 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
682 })
683 }
684
685 #[pyo3(name = "delete_role")]
686 fn delete_role_impl(&self, role_id: String) -> PyResult<bool> {
687 let rt = crate::py_runtime::py_runtime();
688 rt.block_on(async {
689 self.delete_role(&role_id).await
690 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
691 })
692 }
693
694 #[pyo3(name = "delete_permission")]
695 fn delete_permission_impl(&self, permission_id: String) -> PyResult<bool> {
696 let rt = crate::py_runtime::py_runtime();
697 rt.block_on(async {
698 self.delete_permission(&permission_id).await
699 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
700 })
701 }
702
703 #[pyo3(name = "get_role")]
704 fn get_role_impl(&self, role_id: String) -> PyResult<Option<RiRole>> {
705 let rt = crate::py_runtime::py_runtime();
706 rt.block_on(async {
707 self.get_role(&role_id).await
708 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
709 })
710 }
711
712 #[pyo3(name = "get_permission")]
713 fn get_permission_impl(&self, permission_id: String) -> PyResult<Option<RiPermission>> {
714 let rt = crate::py_runtime::py_runtime();
715 rt.block_on(async {
716 self.get_permission(&permission_id).await
717 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
718 })
719 }
720
721 #[pyo3(name = "has_any_permission")]
722 fn has_any_permission_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
723 let rt = crate::py_runtime::py_runtime();
724 rt.block_on(async {
725 self.has_any_permission(&user_id, &permissions).await
726 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
727 })
728 }
729
730 #[pyo3(name = "has_all_permissions")]
731 fn has_all_permissions_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
732 let rt = crate::py_runtime::py_runtime();
733 rt.block_on(async {
734 self.has_all_permissions(&user_id, &permissions).await
735 .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
736 })
737 }
738}
739