use crate::Permission;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Role {
pub name: String,
allowed: HashSet<Permission>,
denied: HashSet<Permission>,
}
impl Role {
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into(), allowed: HashSet::new(), denied: HashSet::new() }
}
pub fn allow(mut self, permission: Permission) -> Self {
self.allowed.insert(permission);
self
}
pub fn deny(mut self, permission: Permission) -> Self {
self.denied.insert(permission);
self
}
pub fn can_access(&self, permission: &Permission) -> bool {
for denied in &self.denied {
if denied.covers(permission) {
return false;
}
}
for allowed in &self.allowed {
if allowed.covers(permission) {
return true;
}
}
false
}
pub fn allowed_permissions(&self) -> &HashSet<Permission> {
&self.allowed
}
pub fn denied_permissions(&self) -> &HashSet<Permission> {
&self.denied
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_role_allow() {
let role = Role::new("user")
.allow(Permission::Tool("search".into()))
.allow(Permission::Tool("summarize".into()));
assert!(role.can_access(&Permission::Tool("search".into())));
assert!(role.can_access(&Permission::Tool("summarize".into())));
assert!(!role.can_access(&Permission::Tool("other".into())));
}
#[test]
fn test_role_deny_precedence() {
let role = Role::new("restricted")
.allow(Permission::AllTools)
.deny(Permission::Tool("code_exec".into()));
assert!(role.can_access(&Permission::Tool("search".into())));
assert!(!role.can_access(&Permission::Tool("code_exec".into())));
}
#[test]
fn test_admin_role() {
let admin = Role::new("admin").allow(Permission::AllTools).allow(Permission::AllAgents);
assert!(admin.can_access(&Permission::Tool("anything".into())));
assert!(admin.can_access(&Permission::Agent("any_agent".into())));
assert!(admin.can_access(&Permission::AllTools));
assert!(admin.can_access(&Permission::AllAgents));
}
#[test]
fn test_empty_role_denies_all() {
let empty = Role::new("empty");
assert!(!empty.can_access(&Permission::Tool("search".into())));
assert!(!empty.can_access(&Permission::AllTools));
}
}