use serde::{Deserialize, Serialize};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Permission {
KernelExecute,
KernelConfigure,
KernelMonitor,
KernelAdmin,
StateRead,
StateWrite,
SecretsRead,
SecretsWrite,
TenantRead,
TenantAdmin,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KernelPermission {
Execute,
Configure,
Monitor,
Admin,
}
impl From<KernelPermission> for Permission {
fn from(p: KernelPermission) -> Self {
match p {
KernelPermission::Execute => Permission::KernelExecute,
KernelPermission::Configure => Permission::KernelConfigure,
KernelPermission::Monitor => Permission::KernelMonitor,
KernelPermission::Admin => Permission::KernelAdmin,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PermissionSet {
permissions: HashSet<Permission>,
}
impl PermissionSet {
pub fn empty() -> Self {
Self {
permissions: HashSet::new(),
}
}
pub fn all() -> Self {
let mut permissions = HashSet::new();
permissions.insert(Permission::KernelExecute);
permissions.insert(Permission::KernelConfigure);
permissions.insert(Permission::KernelMonitor);
permissions.insert(Permission::KernelAdmin);
permissions.insert(Permission::StateRead);
permissions.insert(Permission::StateWrite);
permissions.insert(Permission::SecretsRead);
permissions.insert(Permission::SecretsWrite);
permissions.insert(Permission::TenantRead);
permissions.insert(Permission::TenantAdmin);
Self { permissions }
}
pub fn contains(&self, permission: Permission) -> bool {
self.permissions.contains(&permission)
}
pub fn add(&mut self, permission: Permission) {
self.permissions.insert(permission);
}
pub fn remove(&mut self, permission: Permission) {
self.permissions.remove(&permission);
}
pub fn union(&self, other: &PermissionSet) -> PermissionSet {
PermissionSet {
permissions: self
.permissions
.union(&other.permissions)
.cloned()
.collect(),
}
}
pub fn intersection(&self, other: &PermissionSet) -> PermissionSet {
PermissionSet {
permissions: self
.permissions
.intersection(&other.permissions)
.cloned()
.collect(),
}
}
pub fn iter(&self) -> impl Iterator<Item = &Permission> {
self.permissions.iter()
}
}
impl FromIterator<Permission> for PermissionSet {
fn from_iter<I: IntoIterator<Item = Permission>>(iter: I) -> Self {
Self {
permissions: iter.into_iter().collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Role {
Viewer,
User,
Operator,
Admin,
Custom(String),
}
impl Role {
pub fn permissions(&self) -> PermissionSet {
match self {
Role::Viewer => [Permission::KernelMonitor, Permission::StateRead]
.into_iter()
.collect(),
Role::User => [
Permission::KernelExecute,
Permission::KernelMonitor,
Permission::StateRead,
]
.into_iter()
.collect(),
Role::Operator => [
Permission::KernelExecute,
Permission::KernelConfigure,
Permission::KernelMonitor,
Permission::StateRead,
Permission::StateWrite,
]
.into_iter()
.collect(),
Role::Admin => PermissionSet::all(),
Role::Custom(_) => PermissionSet::empty(), }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleBinding {
pub name: String,
pub role: Role,
pub subjects: Vec<Subject>,
pub scope: Option<String>,
}
impl RoleBinding {
pub fn new(name: impl Into<String>, role: Role) -> Self {
Self {
name: name.into(),
role,
subjects: Vec::new(),
scope: None,
}
}
pub fn for_user(mut self, user_id: impl Into<String>) -> Self {
self.subjects.push(Subject::User(user_id.into()));
self
}
pub fn for_group(mut self, group: impl Into<String>) -> Self {
self.subjects.push(Subject::Group(group.into()));
self
}
pub fn in_scope(mut self, scope: impl Into<String>) -> Self {
self.scope = Some(scope.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Subject {
User(String),
Group(String),
ServiceAccount(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_permission_set() {
let mut perms = PermissionSet::empty();
assert!(!perms.contains(Permission::KernelExecute));
perms.add(Permission::KernelExecute);
assert!(perms.contains(Permission::KernelExecute));
perms.remove(Permission::KernelExecute);
assert!(!perms.contains(Permission::KernelExecute));
}
#[test]
fn test_all_permissions() {
let perms = PermissionSet::all();
assert!(perms.contains(Permission::KernelExecute));
assert!(perms.contains(Permission::KernelAdmin));
assert!(perms.contains(Permission::SecretsWrite));
}
#[test]
fn test_role_permissions() {
let viewer_perms = Role::Viewer.permissions();
assert!(viewer_perms.contains(Permission::KernelMonitor));
assert!(!viewer_perms.contains(Permission::KernelExecute));
let user_perms = Role::User.permissions();
assert!(user_perms.contains(Permission::KernelExecute));
assert!(!user_perms.contains(Permission::KernelAdmin));
let admin_perms = Role::Admin.permissions();
assert!(admin_perms.contains(Permission::KernelAdmin));
}
#[test]
fn test_permission_union() {
let perms1: PermissionSet = [Permission::KernelExecute].into_iter().collect();
let perms2: PermissionSet = [Permission::KernelMonitor].into_iter().collect();
let union = perms1.union(&perms2);
assert!(union.contains(Permission::KernelExecute));
assert!(union.contains(Permission::KernelMonitor));
}
#[test]
fn test_role_binding() {
let binding = RoleBinding::new("admin-binding", Role::Admin)
.for_user("user-123")
.for_group("admins")
.in_scope("tenant-456");
assert_eq!(binding.role, Role::Admin);
assert_eq!(binding.subjects.len(), 2);
assert_eq!(binding.scope.as_deref(), Some("tenant-456"));
}
}