use super::{PermissionAction, PermissionProvider, PermissionProviderError, RolePolicy, TablePermission};
use dashmap::DashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct RbacProvider {
roles: Arc<DashMap<String, RolePolicy>>,
}
impl RbacProvider {
pub fn new() -> Self {
let roles = DashMap::new();
Self { roles: Arc::new(roles) }
}
pub fn with_default_admin() -> Self {
let roles = DashMap::new();
roles.insert(
"admin".to_string(),
RolePolicy {
tables: vec![TablePermission {
name: "*".to_string(),
operations: vec![
PermissionAction::Select,
PermissionAction::Insert,
PermissionAction::Update,
PermissionAction::Delete,
],
}],
},
);
Self { roles: Arc::new(roles) }
}
pub fn add_role(&self, role: String, policy: RolePolicy) {
self.roles.insert(role, policy);
}
}
impl Default for RbacProvider {
fn default() -> Self {
Self::new()
}
}
impl PermissionProvider for RbacProvider {
fn get_role_policy(&self, role: &str) -> Option<RolePolicy> {
self.roles.get(role).map(|p| p.clone())
}
fn check_access(
&self,
role: &str,
table: &str,
operation: PermissionAction,
) -> Result<bool, PermissionProviderError> {
match self.roles.get(role) {
Some(policy) => {
for table_perm in &policy.tables {
if (table_perm.name == "*" || table_perm.name == table)
&& table_perm.operations.contains(&operation)
{
return Ok(true);
}
}
Ok(false)
}
None => Err(PermissionProviderError::RoleNotFound(role.to_string())),
}
}
fn get_roles(&self) -> Vec<String> {
self.roles.iter().map(|r| r.key().clone()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_creates_empty() {
let p = RbacProvider::new();
assert!(p.get_roles().is_empty());
}
#[test]
fn with_default_admin_creates_admin_role() {
let p = RbacProvider::with_default_admin();
assert_eq!(p.get_roles(), vec!["admin"]);
assert!(p.get_role_policy("admin").is_some());
}
#[test]
fn add_role_and_get_policy() {
let p = RbacProvider::new();
p.add_role(
"editor".into(),
RolePolicy {
tables: vec![TablePermission {
name: "posts".into(),
operations: vec![PermissionAction::Select, PermissionAction::Insert],
}],
},
);
let policy = p.get_role_policy("editor");
assert!(policy.is_some());
}
#[test]
fn check_access_granted() {
let p = RbacProvider::new();
p.add_role(
"user".into(),
RolePolicy {
tables: vec![TablePermission {
name: "articles".into(),
operations: vec![PermissionAction::Select],
}],
},
);
assert!(p.check_access("user", "articles", PermissionAction::Select).unwrap());
assert!(!p.check_access("user", "articles", PermissionAction::Insert).unwrap());
}
#[test]
fn check_access_wildcard_table_granted() {
let p = RbacProvider::new();
p.add_role(
"admin".into(),
RolePolicy {
tables: vec![TablePermission {
name: "*".into(),
operations: vec![PermissionAction::Select, PermissionAction::Delete],
}],
},
);
assert!(p.check_access("admin", "any_table", PermissionAction::Select).unwrap());
assert!(p.check_access("admin", "any_table", PermissionAction::Delete).unwrap());
}
#[test]
fn check_access_role_not_found() {
let p = RbacProvider::new();
assert!(p.check_access("ghost", "t", PermissionAction::Select).is_err());
}
#[test]
fn get_roles_returns_all() {
let p = RbacProvider::new();
p.add_role("a".into(), RolePolicy::default());
p.add_role("b".into(), RolePolicy::default());
let mut roles = p.get_roles();
roles.sort();
assert_eq!(roles, vec!["a", "b"]);
}
#[test]
fn default_is_empty() {
let p = RbacProvider::default();
assert!(p.get_roles().is_empty());
}
}