use dashmap::DashMap;
use std::collections::HashMap;
use std::sync::Arc;
use super::PermissionResult;
pub use linkrs_core::{Permission, RoleType};
pub const GOD_SPACE_ID: i64 = -1;
pub struct PermissionManager {
user_roles: Arc<DashMap<String, HashMap<i64, RoleType>>>,
}
impl Clone for PermissionManager {
fn clone(&self) -> Self {
Self {
user_roles: Arc::clone(&self.user_roles),
}
}
}
impl PermissionManager {
pub fn new() -> Self {
let user_roles = DashMap::new();
let mut root_roles = HashMap::new();
root_roles.insert(GOD_SPACE_ID, RoleType::God);
user_roles.insert("root".into(), root_roles);
Self {
user_roles: Arc::new(user_roles),
}
}
pub fn clear(&self) {
self.user_roles.clear();
}
pub fn remove_user(&self, username: &str) {
self.user_roles.remove(username);
}
pub fn rebuild_from_snapshot(&self, roles: Vec<(String, Vec<(i64, RoleType)>)>) {
self.user_roles.clear();
for (username, entries) in roles {
let mut map = HashMap::new();
for (space_id, role) in entries {
map.insert(space_id, role);
}
self.user_roles.insert(username, map);
}
}
pub fn grant_role(
&self,
username: &str,
space_id: i64,
role: RoleType,
) -> PermissionResult<()> {
self.user_roles
.entry(username.to_string())
.or_default()
.insert(space_id, role);
Ok(())
}
pub fn revoke_role(&self, username: &str, space_id: i64) -> PermissionResult<()> {
if let Some(mut roles) = self.user_roles.get_mut(username) {
roles.remove(&space_id);
}
Ok(())
}
pub fn get_role(&self, username: &str, space_id: i64) -> Option<RoleType> {
self.user_roles
.get(username)
.and_then(|roles| roles.get(&space_id).copied())
}
pub fn get_user_roles(&self, username: &str) -> HashMap<i64, RoleType> {
self.user_roles
.get(username)
.map(|roles| roles.clone())
.unwrap_or_default()
}
pub fn list_user_roles(&self, username: &str) -> Vec<(i64, RoleType)> {
self.user_roles
.get(username)
.map(|roles| {
roles
.iter()
.map(|(&space_id, &role)| (space_id, role))
.collect()
})
.unwrap_or_default()
}
pub fn list_space_users(&self, space_id: i64) -> Vec<(String, RoleType)> {
self.user_roles
.iter()
.filter_map(|entry| {
let username = entry.key();
let roles = entry.value();
roles.get(&space_id).map(|&role| (username.clone(), role))
})
.collect()
}
pub fn list_all_users(&self) -> Vec<(String, Vec<(i64, RoleType)>)> {
self.user_roles
.iter()
.map(|entry| {
let roles = entry
.value()
.iter()
.map(|(&space_id, &role)| (space_id, role))
.collect();
(entry.key().clone(), roles)
})
.collect()
}
pub fn highest_role(&self, username: &str) -> Option<RoleType> {
self.user_roles
.get(username)
.and_then(|roles| roles.values().copied().min_by_key(|role| role.to_byte()))
}
pub fn is_god(&self, username: &str) -> bool {
self.user_roles
.get(username)
.map(|roles| roles.values().any(|&role| role == RoleType::God))
.unwrap_or(false)
}
pub fn is_admin(&self, username: &str) -> bool {
self.user_roles
.get(username)
.map(|roles| {
roles
.values()
.any(|&role| matches!(role, RoleType::God | RoleType::Admin))
})
.unwrap_or(false)
}
pub fn is_schema_admin(&self, username: &str) -> bool {
self.user_roles
.get(username)
.map(|roles| {
roles
.values()
.any(|&role| matches!(role, RoleType::God | RoleType::Admin | RoleType::Dba))
})
.unwrap_or(false)
}
pub fn has_role(&self, username: &str, space_id: i64, role: RoleType) -> bool {
self.get_role(username, space_id)
.map(|r| r == role)
.unwrap_or(false)
}
pub fn check_permission(
&self,
username: &str,
space_id: i64,
permission: Permission,
) -> PermissionResult<()> {
use crate::permission::PermissionError;
let role = self
.get_role(username, space_id)
.or_else(|| self.get_role(username, GOD_SPACE_ID))
.ok_or_else(|| PermissionError::no_role_in_space(username.to_string(), space_id))?;
if role.has_permission(permission) {
Ok(())
} else {
Err(PermissionError::permission_denied(
format!("{:?}", permission),
username.to_string(),
))
}
}
pub fn can_grant_role(&self, granter: &str, space_id: i64, target_role: RoleType) -> bool {
self.user_roles
.get(granter)
.and_then(|roles| roles.get(&space_id).copied())
.map(|role| role.can_grant(target_role))
.unwrap_or(false)
}
pub fn can_revoke_role(&self, revoker: &str, space_id: i64, target_role: RoleType) -> bool {
self.can_grant_role(revoker, space_id, target_role)
}
}
impl Default for PermissionManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grant_and_get_role() {
let pm = PermissionManager::new();
pm.grant_role("user1", 1, RoleType::Admin)
.expect("Failed to grant role");
assert_eq!(pm.get_role("user1", 1), Some(RoleType::Admin));
assert_eq!(pm.get_role("user1", 2), None);
assert_eq!(pm.get_role("nonexistent", 1), None);
}
#[test]
fn test_is_god() {
let pm = PermissionManager::new();
assert!(pm.is_god("root"));
let _ = pm.grant_role("user1", 1, RoleType::Admin);
assert!(!pm.is_god("user1"));
let _ = pm.grant_role("user2", GOD_SPACE_ID, RoleType::God);
assert!(pm.is_god("user2"));
}
#[test]
fn test_check_permission() {
let pm = PermissionManager::new();
pm.grant_role("user1", 1, RoleType::User)
.expect("Failed to grant role");
pm.grant_role("guest1", 1, RoleType::Guest)
.expect("Failed to grant role");
assert!(pm.check_permission("user1", 1, Permission::Read).is_ok());
assert!(pm.check_permission("user1", 1, Permission::Write).is_ok());
assert!(pm.check_permission("guest1", 1, Permission::Read).is_ok());
assert!(pm.check_permission("guest1", 1, Permission::Write).is_err());
assert!(pm
.check_permission("nonexistent", 1, Permission::Read)
.is_err());
}
#[test]
fn test_can_grant_role() {
let pm = PermissionManager::new();
pm.grant_role("admin", 1, RoleType::Admin)
.expect("Failed to grant role");
pm.grant_role("user", 1, RoleType::User)
.expect("Failed to grant role");
assert!(pm.can_grant_role("admin", 1, RoleType::User));
assert!(pm.can_grant_role("admin", 1, RoleType::Guest));
assert!(!pm.can_grant_role("admin", 1, RoleType::Admin));
assert!(!pm.can_grant_role("admin", 1, RoleType::God));
assert!(!pm.can_grant_role("user", 1, RoleType::Guest));
}
#[test]
fn test_god_role_global_permission() {
let pm = PermissionManager::new();
assert!(pm.check_permission("root", 999, Permission::Write).is_ok());
assert!(pm.check_permission("root", 999, Permission::Read).is_ok());
}
#[test]
fn test_list_user_roles() {
let pm = PermissionManager::new();
pm.grant_role("multi_user", 1, RoleType::Admin)
.expect("Failed to grant role");
pm.grant_role("multi_user", 2, RoleType::User)
.expect("Failed to grant role");
pm.grant_role("multi_user", 3, RoleType::Guest)
.expect("Failed to grant role");
let roles = pm.list_user_roles("multi_user");
assert_eq!(roles.len(), 3);
let role_map: HashMap<i64, RoleType> = roles.into_iter().collect();
assert_eq!(role_map.get(&1), Some(&RoleType::Admin));
assert_eq!(role_map.get(&2), Some(&RoleType::User));
assert_eq!(role_map.get(&3), Some(&RoleType::Guest));
let empty_roles = pm.list_user_roles("nonexistent");
assert!(empty_roles.is_empty());
}
#[test]
fn test_list_space_users() {
let pm = PermissionManager::new();
let space_id = 1i64;
pm.grant_role("user1", space_id, RoleType::User)
.expect("Failed to grant role");
pm.grant_role("user2", space_id, RoleType::Admin)
.expect("Failed to grant role");
pm.grant_role("user3", space_id, RoleType::Guest)
.expect("Failed to grant role");
let users = pm.list_space_users(space_id);
assert_eq!(users.len(), 3);
let user_map: HashMap<String, RoleType> = users.into_iter().collect();
assert_eq!(user_map.get("user1"), Some(&RoleType::User));
assert_eq!(user_map.get("user2"), Some(&RoleType::Admin));
assert_eq!(user_map.get("user3"), Some(&RoleType::Guest));
let empty_users = pm.list_space_users(999);
assert!(empty_users.is_empty());
}
}