#![allow(non_snake_case)]
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use crate::core::concurrent::RiShardedLock;
#[cfg(feature = "pyo3")]
use pyo3::PyResult;
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiPermission {
pub id: String,
pub name: String,
pub description: String,
pub resource: String,
pub action: String,
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiPermission {
#[new]
fn py_new(
id: Option<String>,
name: String,
description: String,
resource: String,
action: String,
) -> Self {
Self {
id: id.unwrap_or_else(|| format!("{}:{}", resource, action)),
name,
description,
resource,
action,
}
}
}
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiRole {
pub id: String,
pub name: String,
pub description: String,
pub permissions: HashSet<String>,
pub is_system: bool,
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiRole {
#[new]
fn py_new(
id: Option<String>,
name: String,
description: String,
permissions: Vec<String>,
is_system: bool,
) -> Self {
Self {
id: id.unwrap_or_else(|| name.to_lowercase().replace(' ', "_")),
name,
description,
permissions: permissions.into_iter().collect(),
is_system,
}
}
}
impl RiRole {
pub fn new(id: String, name: String, description: String, permissions: HashSet<String>) -> Self {
Self {
id,
name,
description,
permissions,
is_system: false,
}
}
#[inline]
pub fn has_permission(&self, permission_id: &str) -> bool {
self.permissions.contains(permission_id)
}
#[inline]
pub fn add_permission(&mut self, permission_id: String) {
self.permissions.insert(permission_id);
}
#[inline]
pub fn remove_permission(&mut self, permission_id: &str) {
self.permissions.remove(permission_id);
}
}
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiPermissionManager {
permissions: RiShardedLock<String, RiPermission>,
roles: RiShardedLock<String, RiRole>,
user_roles: RiShardedLock<String, HashSet<String>>,
}
impl Default for RiPermissionManager {
fn default() -> Self {
Self::new()
}
}
impl RiPermissionManager {
pub fn new() -> Self {
let mut manager = Self {
permissions: RiShardedLock::with_default_shards(),
roles: RiShardedLock::with_default_shards(),
user_roles: RiShardedLock::with_default_shards(),
};
manager.initialize_default_roles();
manager
}
pub async fn new_async() -> Self {
let manager = Self {
permissions: RiShardedLock::with_default_shards(),
roles: RiShardedLock::with_default_shards(),
user_roles: RiShardedLock::with_default_shards(),
};
manager.initialize_default_roles_async().await;
manager
}
fn initialize_default_roles(&mut self) {
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime");
rt.block_on(async {
self.initialize_default_roles_async().await
});
}
async fn initialize_default_roles_async(&self) {
let admin_permissions: HashSet<String> = vec![
"*".to_string(),
].into_iter().collect();
let admin_role = RiRole {
id: "admin".to_string(),
name: "Administrator".to_string(),
description: "Full system access".to_string(),
permissions: admin_permissions,
is_system: true,
};
self.roles.insert("admin".to_string(), admin_role).await;
let user_permissions: HashSet<String> = vec![
"read:profile".to_string(),
"update:profile".to_string(),
"read:own_data".to_string(),
].into_iter().collect();
let user_role = RiRole {
id: "user".to_string(),
name: "User".to_string(),
description: "Standard user access".to_string(),
permissions: user_permissions,
is_system: true,
};
self.roles.insert("user".to_string(), user_role).await;
}
pub async fn create_permission(&self, permission: RiPermission) -> crate::core::RiResult<()> {
self.permissions.insert(permission.id.clone(), permission).await;
Ok(())
}
pub async fn get_permission(&self, permission_id: &str) -> crate::core::RiResult<Option<RiPermission>> {
Ok(self.permissions.get(permission_id).await)
}
pub async fn create_role(&self, role: RiRole) -> crate::core::RiResult<()> {
self.roles.insert(role.id.clone(), role).await;
Ok(())
}
pub async fn get_role(&self, role_id: &str) -> crate::core::RiResult<Option<RiRole>> {
Ok(self.roles.get(role_id).await)
}
pub async fn assign_role_to_user(&self, user_id: String, role_id: String) -> crate::core::RiResult<bool> {
let role = self.roles.get(&role_id).await;
if role.is_none() {
log::warn!(
"[Ri.Permission] Attempted to assign non-existent role '{}' to user '{}'",
role_id, user_id
);
return Ok(false);
}
if role.as_ref().map_or(false, |r| r.is_system) {
log::warn!(
"[Ri.Permission] Attempted to assign system role '{}' to user '{}' - blocked for security",
role_id, user_id
);
return Ok(false);
}
log::info!(
"[Ri.Permission] Assigning role '{}' to user '{}'",
role_id, user_id
);
let user_role_set = self.user_roles.get(&user_id).await.unwrap_or_default();
if user_role_set.contains(&role_id) {
log::debug!(
"[Ri.Permission] User '{}' already has role '{}'",
user_id, role_id
);
return Ok(false);
}
let mut new_set = user_role_set.clone();
let was_added = new_set.insert(role_id.clone());
self.user_roles.insert(user_id.clone(), new_set).await;
log::info!(
"[Ri.Permission] Successfully assigned role '{}' to user '{}'",
role_id, user_id
);
Ok(was_added)
}
pub async fn remove_role_from_user(&self, user_id: &str, role_id: &str) -> crate::core::RiResult<bool> {
log::info!(
"[Ri.Permission] Removing role '{}' from user '{}'",
role_id, user_id
);
let user_role_set = self.user_roles.get(user_id).await;
match user_role_set {
Some(mut set) => {
let was_removed = set.remove(role_id);
if set.is_empty() {
self.user_roles.remove(user_id).await;
} else {
self.user_roles.insert(user_id.to_string(), set).await;
}
if was_removed {
log::info!(
"[Ri.Permission] Successfully removed role '{}' from user '{}'",
role_id, user_id
);
}
Ok(was_removed)
}
None => Ok(false),
}
}
pub async fn get_user_roles(&self, user_id: &str) -> crate::core::RiResult<Vec<RiRole>> {
let user_role_set = self.user_roles.get(user_id).await;
match user_role_set {
Some(role_ids) => {
let mut result = Vec::with_capacity(role_ids.len());
for role_id in role_ids {
if let Some(role) = self.roles.get(&role_id).await {
result.push(role);
}
}
Ok(result)
}
None => Ok(Vec::new()),
}
}
pub async fn has_permission(&self, user_id: &str, permission_id: &str) -> crate::core::RiResult<bool> {
let user_role_set = self.user_roles.get(user_id).await;
if let Some(role_ids) = user_role_set {
for role_id in role_ids {
if let Some(role) = self.roles.get(&role_id).await {
if role.permissions.contains("*") {
return Ok(true);
}
if role.permissions.contains(permission_id) {
return Ok(true);
}
}
}
}
Ok(false)
}
pub async fn has_any_permission(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
for permission in permissions {
if self.has_permission(user_id, permission).await? {
return Ok(true);
}
}
Ok(false)
}
pub async fn has_all_permissions(&self, user_id: &str, permissions: &[String]) -> crate::core::RiResult<bool> {
for permission in permissions {
if !self.has_permission(user_id, permission).await? {
return Ok(false);
}
}
Ok(true)
}
pub async fn get_user_permissions(&self, user_id: &str) -> crate::core::RiResult<HashSet<String>> {
let user_role_set = self.user_roles.get(user_id).await;
let mut permissions = HashSet::new();
if let Some(role_ids) = user_role_set {
for role_id in role_ids {
if let Some(role) = self.roles.get(&role_id).await {
permissions.extend(role.permissions);
}
}
}
Ok(permissions)
}
pub async fn delete_permission(&self, permission_id: &str) -> crate::core::RiResult<bool> {
Ok(self.permissions.remove(permission_id).await.is_some())
}
pub async fn delete_role(&self, role_id: &str) -> crate::core::RiResult<bool> {
let role = self.roles.get(role_id).await;
if let Some(r) = role {
if r.is_system {
return Ok(false);
}
}
let was_deleted = self.roles.remove(role_id).await.is_some();
if was_deleted {
self.user_roles.for_each_mut(|_, role_set| {
role_set.remove(role_id);
}).await;
}
Ok(was_deleted)
}
pub async fn list_permissions(&self) -> crate::core::RiResult<Vec<RiPermission>> {
Ok(self.permissions.collect_all().await.into_values().collect())
}
pub async fn list_roles(&self) -> crate::core::RiResult<Vec<RiRole>> {
Ok(self.roles.collect_all().await.into_values().collect())
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiPermissionManager {
#[new]
fn py_new() -> PyResult<Self> {
Ok(Self::new())
}
#[pyo3(name = "create_permission")]
fn create_permission_impl(&self, permission: RiPermission) -> PyResult<()> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.create_permission(permission).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "create_role")]
fn create_role_impl(&self, role: RiRole) -> PyResult<()> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.create_role(role).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "assign_role_to_user")]
fn assign_role_to_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.assign_role_to_user(user_id, role_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "has_permission")]
fn has_permission_impl(&self, user_id: String, permission_id: String) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.has_permission(&user_id, &permission_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "get_user_roles")]
fn get_user_roles_impl(&self, user_id: String) -> PyResult<Vec<RiRole>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.get_user_roles(&user_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "get_user_permissions")]
fn get_user_permissions_impl(&self, user_id: String) -> PyResult<Vec<String>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.get_user_permissions(&user_id).await
.map(|perms| perms.into_iter().collect())
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "remove_role_from_user")]
fn remove_role_from_user_impl(&self, user_id: String, role_id: String) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.remove_role_from_user(&user_id, &role_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "list_roles")]
fn list_roles_impl(&self) -> PyResult<Vec<RiRole>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.list_roles().await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "list_permissions")]
fn list_permissions_impl(&self) -> PyResult<Vec<RiPermission>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.list_permissions().await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "delete_role")]
fn delete_role_impl(&self, role_id: String) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.delete_role(&role_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "delete_permission")]
fn delete_permission_impl(&self, permission_id: String) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.delete_permission(&permission_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "get_role")]
fn get_role_impl(&self, role_id: String) -> PyResult<Option<RiRole>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.get_role(&role_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "get_permission")]
fn get_permission_impl(&self, permission_id: String) -> PyResult<Option<RiPermission>> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.get_permission(&permission_id).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "has_any_permission")]
fn has_any_permission_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.has_any_permission(&user_id, &permissions).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
#[pyo3(name = "has_all_permissions")]
fn has_all_permissions_impl(&self, user_id: String, permissions: Vec<String>) -> PyResult<bool> {
let rt = tokio::runtime::Runtime::new().map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;
rt.block_on(async {
self.has_all_permissions(&user_id, &permissions).await
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
})
}
}