Skip to main content

ringkernel_core/
rbac.rs

1//! Role-Based Access Control (RBAC) for RingKernel.
2//!
3//! This module provides fine-grained access control for kernel operations
4//! with predefined roles and customizable permission policies.
5//!
6//! # Example
7//!
8//! ```rust,ignore
9//! use ringkernel_core::rbac::{RbacPolicy, Role, Permission, PolicyEvaluator};
10//! use ringkernel_core::auth::AuthContext;
11//!
12//! let policy = RbacPolicy::new()
13//!     .with_role(Role::admin())
14//!     .with_role(Role::operator())
15//!     .with_role(Role::developer())
16//!     .with_role(Role::readonly());
17//!
18//! let evaluator = PolicyEvaluator::new(policy);
19//!
20//! // Check if user can launch kernels
21//! if evaluator.is_allowed(&auth_context, Permission::KernelLaunch) {
22//!     // Launch kernel
23//! }
24//! ```
25
26use parking_lot::RwLock;
27use std::collections::{HashMap, HashSet};
28use std::fmt;
29
30use crate::KernelId;
31
32// ============================================================================
33// PERMISSIONS
34// ============================================================================
35
36/// Fine-grained permissions for kernel operations.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum Permission {
39    // Kernel lifecycle
40    /// Launch new kernels.
41    KernelLaunch,
42    /// Terminate kernels.
43    KernelTerminate,
44    /// Activate kernels.
45    KernelActivate,
46    /// View kernel status.
47    KernelView,
48    /// Migrate kernels between GPUs.
49    KernelMigrate,
50    /// Create kernel checkpoints.
51    KernelCheckpoint,
52    /// Restore kernels from checkpoints.
53    KernelRestore,
54
55    // Messaging
56    /// Send messages to kernels.
57    MessageSend,
58    /// Receive messages from kernels.
59    MessageReceive,
60    /// Send K2K messages.
61    K2KSend,
62    /// Register K2K endpoints.
63    K2KRegister,
64    /// Publish to topics.
65    PubSubPublish,
66    /// Subscribe to topics.
67    PubSubSubscribe,
68
69    // Resource management
70    /// Allocate GPU memory.
71    MemoryAllocate,
72    /// View memory usage.
73    MemoryView,
74    /// Configure resource limits.
75    ResourceConfigure,
76
77    // Configuration
78    /// View configuration.
79    ConfigView,
80    /// Modify configuration.
81    ConfigModify,
82    /// View security settings.
83    SecurityView,
84    /// Modify security settings.
85    SecurityModify,
86
87    // Observability
88    /// View metrics.
89    MetricsView,
90    /// Export metrics.
91    MetricsExport,
92    /// View audit logs.
93    AuditView,
94    /// Export audit logs.
95    AuditExport,
96    /// View traces.
97    TracesView,
98
99    // Admin
100    /// Full administrative access.
101    Admin,
102    /// Manage users and roles.
103    UserManage,
104    /// Manage tenants.
105    TenantManage,
106}
107
108impl Permission {
109    /// Get the permission name.
110    pub fn as_str(&self) -> &'static str {
111        match self {
112            Self::KernelLaunch => "kernel:launch",
113            Self::KernelTerminate => "kernel:terminate",
114            Self::KernelActivate => "kernel:activate",
115            Self::KernelView => "kernel:view",
116            Self::KernelMigrate => "kernel:migrate",
117            Self::KernelCheckpoint => "kernel:checkpoint",
118            Self::KernelRestore => "kernel:restore",
119            Self::MessageSend => "message:send",
120            Self::MessageReceive => "message:receive",
121            Self::K2KSend => "k2k:send",
122            Self::K2KRegister => "k2k:register",
123            Self::PubSubPublish => "pubsub:publish",
124            Self::PubSubSubscribe => "pubsub:subscribe",
125            Self::MemoryAllocate => "memory:allocate",
126            Self::MemoryView => "memory:view",
127            Self::ResourceConfigure => "resource:configure",
128            Self::ConfigView => "config:view",
129            Self::ConfigModify => "config:modify",
130            Self::SecurityView => "security:view",
131            Self::SecurityModify => "security:modify",
132            Self::MetricsView => "metrics:view",
133            Self::MetricsExport => "metrics:export",
134            Self::AuditView => "audit:view",
135            Self::AuditExport => "audit:export",
136            Self::TracesView => "traces:view",
137            Self::Admin => "admin",
138            Self::UserManage => "user:manage",
139            Self::TenantManage => "tenant:manage",
140        }
141    }
142
143    /// Parse permission from string representation.
144    pub fn parse(s: &str) -> Option<Self> {
145        match s {
146            "kernel:launch" => Some(Self::KernelLaunch),
147            "kernel:terminate" => Some(Self::KernelTerminate),
148            "kernel:activate" => Some(Self::KernelActivate),
149            "kernel:view" => Some(Self::KernelView),
150            "kernel:migrate" => Some(Self::KernelMigrate),
151            "kernel:checkpoint" => Some(Self::KernelCheckpoint),
152            "kernel:restore" => Some(Self::KernelRestore),
153            "message:send" => Some(Self::MessageSend),
154            "message:receive" => Some(Self::MessageReceive),
155            "k2k:send" => Some(Self::K2KSend),
156            "k2k:register" => Some(Self::K2KRegister),
157            "pubsub:publish" => Some(Self::PubSubPublish),
158            "pubsub:subscribe" => Some(Self::PubSubSubscribe),
159            "memory:allocate" => Some(Self::MemoryAllocate),
160            "memory:view" => Some(Self::MemoryView),
161            "resource:configure" => Some(Self::ResourceConfigure),
162            "config:view" => Some(Self::ConfigView),
163            "config:modify" => Some(Self::ConfigModify),
164            "security:view" => Some(Self::SecurityView),
165            "security:modify" => Some(Self::SecurityModify),
166            "metrics:view" => Some(Self::MetricsView),
167            "metrics:export" => Some(Self::MetricsExport),
168            "audit:view" => Some(Self::AuditView),
169            "audit:export" => Some(Self::AuditExport),
170            "traces:view" => Some(Self::TracesView),
171            "admin" => Some(Self::Admin),
172            "user:manage" => Some(Self::UserManage),
173            "tenant:manage" => Some(Self::TenantManage),
174            _ => None,
175        }
176    }
177
178    /// Get all permissions in a category.
179    pub fn category_permissions(category: &str) -> Vec<Permission> {
180        match category {
181            "kernel" => vec![
182                Self::KernelLaunch,
183                Self::KernelTerminate,
184                Self::KernelActivate,
185                Self::KernelView,
186                Self::KernelMigrate,
187                Self::KernelCheckpoint,
188                Self::KernelRestore,
189            ],
190            "message" => vec![Self::MessageSend, Self::MessageReceive],
191            "k2k" => vec![Self::K2KSend, Self::K2KRegister],
192            "pubsub" => vec![Self::PubSubPublish, Self::PubSubSubscribe],
193            "memory" => vec![Self::MemoryAllocate, Self::MemoryView],
194            "config" => vec![Self::ConfigView, Self::ConfigModify],
195            "security" => vec![Self::SecurityView, Self::SecurityModify],
196            "metrics" => vec![Self::MetricsView, Self::MetricsExport],
197            "audit" => vec![Self::AuditView, Self::AuditExport],
198            _ => vec![],
199        }
200    }
201}
202
203impl fmt::Display for Permission {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        write!(f, "{}", self.as_str())
206    }
207}
208
209// ============================================================================
210// ROLE
211// ============================================================================
212
213/// A role with a set of permissions.
214#[derive(Debug, Clone)]
215pub struct Role {
216    /// Role name.
217    pub name: String,
218    /// Role description.
219    pub description: String,
220    /// Granted permissions.
221    pub permissions: HashSet<Permission>,
222    /// Whether this role has all permissions.
223    pub is_superuser: bool,
224}
225
226impl Role {
227    /// Create a new role.
228    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
229        Self {
230            name: name.into(),
231            description: description.into(),
232            permissions: HashSet::new(),
233            is_superuser: false,
234        }
235    }
236
237    /// Add a permission.
238    pub fn with_permission(mut self, permission: Permission) -> Self {
239        self.permissions.insert(permission);
240        self
241    }
242
243    /// Add multiple permissions.
244    pub fn with_permissions<I>(mut self, permissions: I) -> Self
245    where
246        I: IntoIterator<Item = Permission>,
247    {
248        self.permissions.extend(permissions);
249        self
250    }
251
252    /// Mark as superuser (has all permissions).
253    pub fn with_superuser(mut self, is_superuser: bool) -> Self {
254        self.is_superuser = is_superuser;
255        self
256    }
257
258    /// Check if role has a permission.
259    pub fn has_permission(&self, permission: Permission) -> bool {
260        self.is_superuser || self.permissions.contains(&permission)
261    }
262
263    /// Create the Admin role (full access).
264    pub fn admin() -> Self {
265        Self::new("admin", "Full administrative access to all operations").with_superuser(true)
266    }
267
268    /// Create the Operator role (run/manage kernels, no config changes).
269    pub fn operator() -> Self {
270        Self::new("operator", "Can launch, manage, and monitor kernels").with_permissions([
271            Permission::KernelLaunch,
272            Permission::KernelTerminate,
273            Permission::KernelActivate,
274            Permission::KernelView,
275            Permission::KernelMigrate,
276            Permission::KernelCheckpoint,
277            Permission::KernelRestore,
278            Permission::MessageSend,
279            Permission::MessageReceive,
280            Permission::K2KSend,
281            Permission::K2KRegister,
282            Permission::PubSubPublish,
283            Permission::PubSubSubscribe,
284            Permission::MemoryAllocate,
285            Permission::MemoryView,
286            Permission::MetricsView,
287            Permission::TracesView,
288        ])
289    }
290
291    /// Create the Developer role (launch and debug kernels).
292    pub fn developer() -> Self {
293        Self::new("developer", "Can launch kernels and view debug information").with_permissions([
294            Permission::KernelLaunch,
295            Permission::KernelActivate,
296            Permission::KernelView,
297            Permission::MessageSend,
298            Permission::MessageReceive,
299            Permission::K2KSend,
300            Permission::MemoryView,
301            Permission::MetricsView,
302            Permission::TracesView,
303            Permission::ConfigView,
304        ])
305    }
306
307    /// Create the ReadOnly role (view only).
308    pub fn readonly() -> Self {
309        Self::new("readonly", "Can only view status and metrics").with_permissions([
310            Permission::KernelView,
311            Permission::MemoryView,
312            Permission::MetricsView,
313            Permission::ConfigView,
314        ])
315    }
316
317    /// Create the Service role (for automated systems).
318    pub fn service() -> Self {
319        Self::new("service", "For automated services and integrations").with_permissions([
320            Permission::KernelLaunch,
321            Permission::KernelTerminate,
322            Permission::KernelActivate,
323            Permission::KernelView,
324            Permission::MessageSend,
325            Permission::MessageReceive,
326            Permission::K2KSend,
327            Permission::K2KRegister,
328            Permission::PubSubPublish,
329            Permission::PubSubSubscribe,
330            Permission::MemoryAllocate,
331            Permission::MemoryView,
332            Permission::MetricsView,
333            Permission::MetricsExport,
334        ])
335    }
336}
337
338// ============================================================================
339// RBAC POLICY
340// ============================================================================
341
342/// RBAC policy definition.
343#[derive(Debug, Clone)]
344pub struct RbacPolicy {
345    /// Defined roles.
346    roles: HashMap<String, Role>,
347    /// Resource-specific rules.
348    resource_rules: Vec<ResourceRule>,
349    /// Default deny (if true, explicit allow required).
350    default_deny: bool,
351}
352
353/// A rule for specific resources.
354#[derive(Debug, Clone)]
355pub struct ResourceRule {
356    /// Resource pattern (supports wildcards).
357    pub resource_pattern: String,
358    /// Required permissions for this resource.
359    pub required_permissions: HashSet<Permission>,
360    /// Additional required roles.
361    pub required_roles: HashSet<String>,
362    /// Explicit deny (overrides allows).
363    pub deny: bool,
364}
365
366impl ResourceRule {
367    /// Create a new resource rule.
368    pub fn new(pattern: impl Into<String>) -> Self {
369        Self {
370            resource_pattern: pattern.into(),
371            required_permissions: HashSet::new(),
372            required_roles: HashSet::new(),
373            deny: false,
374        }
375    }
376
377    /// Require a permission.
378    pub fn require_permission(mut self, permission: Permission) -> Self {
379        self.required_permissions.insert(permission);
380        self
381    }
382
383    /// Require a role.
384    pub fn require_role(mut self, role: impl Into<String>) -> Self {
385        self.required_roles.insert(role.into());
386        self
387    }
388
389    /// Make this a deny rule.
390    pub fn deny(mut self) -> Self {
391        self.deny = true;
392        self
393    }
394
395    /// Check if resource matches pattern.
396    pub fn matches(&self, resource: &str) -> bool {
397        if self.resource_pattern == "*" {
398            return true;
399        }
400
401        if self.resource_pattern.ends_with('*') {
402            let prefix = &self.resource_pattern[..self.resource_pattern.len() - 1];
403            resource.starts_with(prefix)
404        } else {
405            resource == self.resource_pattern
406        }
407    }
408}
409
410impl RbacPolicy {
411    /// Create a new RBAC policy.
412    pub fn new() -> Self {
413        Self {
414            roles: HashMap::new(),
415            resource_rules: Vec::new(),
416            default_deny: true,
417        }
418    }
419
420    /// Set default deny mode.
421    pub fn with_default_deny(mut self, deny: bool) -> Self {
422        self.default_deny = deny;
423        self
424    }
425
426    /// Add a role.
427    pub fn with_role(mut self, role: Role) -> Self {
428        self.roles.insert(role.name.clone(), role);
429        self
430    }
431
432    /// Add a resource rule.
433    pub fn with_resource_rule(mut self, rule: ResourceRule) -> Self {
434        self.resource_rules.push(rule);
435        self
436    }
437
438    /// Get a role by name.
439    pub fn get_role(&self, name: &str) -> Option<&Role> {
440        self.roles.get(name)
441    }
442
443    /// Create a standard policy with default roles.
444    pub fn standard() -> Self {
445        Self::new()
446            .with_role(Role::admin())
447            .with_role(Role::operator())
448            .with_role(Role::developer())
449            .with_role(Role::readonly())
450            .with_role(Role::service())
451    }
452}
453
454impl Default for RbacPolicy {
455    fn default() -> Self {
456        Self::new()
457    }
458}
459
460// ============================================================================
461// POLICY EVALUATOR
462// ============================================================================
463
464/// Error type for RBAC evaluation.
465#[derive(Debug, Clone)]
466pub enum RbacError {
467    /// Permission denied.
468    PermissionDenied(String),
469    /// Role not found.
470    RoleNotFound(String),
471    /// Invalid resource.
472    InvalidResource(String),
473}
474
475impl fmt::Display for RbacError {
476    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477        match self {
478            Self::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
479            Self::RoleNotFound(msg) => write!(f, "Role not found: {}", msg),
480            Self::InvalidResource(msg) => write!(f, "Invalid resource: {}", msg),
481        }
482    }
483}
484
485impl std::error::Error for RbacError {}
486
487/// Result type for RBAC evaluation.
488pub type RbacResult<T> = Result<T, RbacError>;
489
490/// Subject for RBAC evaluation (who is making the request).
491#[derive(Debug, Clone)]
492pub struct Subject {
493    /// Subject ID (user ID, service account, etc.).
494    pub id: String,
495    /// Assigned roles.
496    pub roles: HashSet<String>,
497    /// Direct permissions (in addition to role permissions).
498    pub permissions: HashSet<String>,
499    /// Tenant ID (for multi-tenant isolation).
500    pub tenant_id: Option<String>,
501}
502
503impl Subject {
504    /// Create a new subject.
505    pub fn new(id: impl Into<String>) -> Self {
506        Self {
507            id: id.into(),
508            roles: HashSet::new(),
509            permissions: HashSet::new(),
510            tenant_id: None,
511        }
512    }
513
514    /// Add a role.
515    pub fn with_role(mut self, role: impl Into<String>) -> Self {
516        self.roles.insert(role.into());
517        self
518    }
519
520    /// Add roles.
521    pub fn with_roles<I, S>(mut self, roles: I) -> Self
522    where
523        I: IntoIterator<Item = S>,
524        S: Into<String>,
525    {
526        self.roles.extend(roles.into_iter().map(Into::into));
527        self
528    }
529
530    /// Add a direct permission.
531    pub fn with_permission(mut self, permission: impl Into<String>) -> Self {
532        self.permissions.insert(permission.into());
533        self
534    }
535
536    /// Set tenant ID.
537    pub fn with_tenant(mut self, tenant_id: impl Into<String>) -> Self {
538        self.tenant_id = Some(tenant_id.into());
539        self
540    }
541
542    /// Create from auth context.
543    pub fn from_auth_context(ctx: &crate::auth::AuthContext) -> Self {
544        let mut subject = Self::new(&ctx.identity.id)
545            .with_roles(ctx.roles.iter().cloned())
546            .with_permission(
547                ctx.permissions
548                    .iter()
549                    .cloned()
550                    .collect::<Vec<_>>()
551                    .join(","),
552            );
553
554        if let Some(tenant) = &ctx.identity.tenant_id {
555            subject = subject.with_tenant(tenant);
556        }
557
558        subject
559    }
560}
561
562/// RBAC policy evaluator.
563pub struct PolicyEvaluator {
564    /// The policy to evaluate.
565    policy: RbacPolicy,
566    /// Evaluation cache.
567    cache: RwLock<HashMap<(String, String), bool>>,
568    /// Cache TTL.
569    cache_ttl: std::time::Duration,
570}
571
572impl PolicyEvaluator {
573    /// Create a new policy evaluator.
574    pub fn new(policy: RbacPolicy) -> Self {
575        Self {
576            policy,
577            cache: RwLock::new(HashMap::new()),
578            cache_ttl: std::time::Duration::from_secs(60),
579        }
580    }
581
582    /// Set cache TTL.
583    pub fn with_cache_ttl(mut self, ttl: std::time::Duration) -> Self {
584        self.cache_ttl = ttl;
585        self
586    }
587
588    /// Clear the evaluation cache.
589    pub fn clear_cache(&self) {
590        self.cache.write().clear();
591    }
592
593    /// Check if subject has a permission.
594    pub fn is_allowed(&self, subject: &Subject, permission: Permission) -> bool {
595        // Check direct permissions
596        if subject.permissions.contains(permission.as_str()) {
597            return true;
598        }
599
600        // Check role permissions
601        for role_name in &subject.roles {
602            if let Some(role) = self.policy.get_role(role_name) {
603                if role.has_permission(permission) {
604                    return true;
605                }
606            }
607        }
608
609        !self.policy.default_deny
610    }
611
612    /// Check if subject can access a resource.
613    pub fn can_access(
614        &self,
615        subject: &Subject,
616        resource: &str,
617        permission: Permission,
618    ) -> RbacResult<()> {
619        // Check basic permission first
620        if !self.is_allowed(subject, permission) {
621            return Err(RbacError::PermissionDenied(format!(
622                "Subject {} lacks permission {} for resource {}",
623                subject.id, permission, resource
624            )));
625        }
626
627        // Check resource-specific rules
628        for rule in &self.policy.resource_rules {
629            if rule.matches(resource) {
630                // Check deny rules first
631                if rule.deny {
632                    return Err(RbacError::PermissionDenied(format!(
633                        "Resource {} is denied by policy",
634                        resource
635                    )));
636                }
637
638                // Check required permissions
639                for required_perm in &rule.required_permissions {
640                    if !self.is_allowed(subject, *required_perm) {
641                        return Err(RbacError::PermissionDenied(format!(
642                            "Resource {} requires permission {}",
643                            resource, required_perm
644                        )));
645                    }
646                }
647
648                // Check required roles
649                for required_role in &rule.required_roles {
650                    if !subject.roles.contains(required_role) {
651                        return Err(RbacError::PermissionDenied(format!(
652                            "Resource {} requires role {}",
653                            resource, required_role
654                        )));
655                    }
656                }
657            }
658        }
659
660        Ok(())
661    }
662
663    /// Check kernel access.
664    pub fn can_access_kernel(
665        &self,
666        subject: &Subject,
667        kernel_id: &KernelId,
668        permission: Permission,
669    ) -> RbacResult<()> {
670        let resource = format!("kernel:{}", kernel_id.as_str());
671        self.can_access(subject, &resource, permission)
672    }
673
674    /// Get all permissions for a subject.
675    pub fn get_permissions(&self, subject: &Subject) -> HashSet<Permission> {
676        let mut permissions = HashSet::new();
677
678        // Collect from roles
679        for role_name in &subject.roles {
680            if let Some(role) = self.policy.get_role(role_name) {
681                if role.is_superuser {
682                    // Return all permissions for superuser
683                    return [
684                        Permission::KernelLaunch,
685                        Permission::KernelTerminate,
686                        Permission::KernelActivate,
687                        Permission::KernelView,
688                        Permission::KernelMigrate,
689                        Permission::KernelCheckpoint,
690                        Permission::KernelRestore,
691                        Permission::MessageSend,
692                        Permission::MessageReceive,
693                        Permission::K2KSend,
694                        Permission::K2KRegister,
695                        Permission::PubSubPublish,
696                        Permission::PubSubSubscribe,
697                        Permission::MemoryAllocate,
698                        Permission::MemoryView,
699                        Permission::ResourceConfigure,
700                        Permission::ConfigView,
701                        Permission::ConfigModify,
702                        Permission::SecurityView,
703                        Permission::SecurityModify,
704                        Permission::MetricsView,
705                        Permission::MetricsExport,
706                        Permission::AuditView,
707                        Permission::AuditExport,
708                        Permission::TracesView,
709                        Permission::Admin,
710                        Permission::UserManage,
711                        Permission::TenantManage,
712                    ]
713                    .into_iter()
714                    .collect();
715                }
716                permissions.extend(role.permissions.iter().cloned());
717            }
718        }
719
720        // Add direct permissions
721        for perm_str in &subject.permissions {
722            if let Some(perm) = Permission::parse(perm_str) {
723                permissions.insert(perm);
724            }
725        }
726
727        permissions
728    }
729}
730
731// ============================================================================
732// TESTS
733// ============================================================================
734
735#[cfg(test)]
736mod tests {
737    use super::*;
738
739    #[test]
740    fn test_permission_string() {
741        assert_eq!(Permission::KernelLaunch.as_str(), "kernel:launch");
742        assert_eq!(
743            Permission::parse("kernel:launch"),
744            Some(Permission::KernelLaunch)
745        );
746        assert_eq!(Permission::parse("invalid"), None);
747    }
748
749    #[test]
750    fn test_role_permissions() {
751        let role = Role::new("test", "Test role")
752            .with_permission(Permission::KernelLaunch)
753            .with_permission(Permission::KernelView);
754
755        assert!(role.has_permission(Permission::KernelLaunch));
756        assert!(role.has_permission(Permission::KernelView));
757        assert!(!role.has_permission(Permission::KernelTerminate));
758    }
759
760    #[test]
761    fn test_superuser_role() {
762        let admin = Role::admin();
763        assert!(admin.is_superuser);
764        assert!(admin.has_permission(Permission::KernelLaunch));
765        assert!(admin.has_permission(Permission::Admin));
766        assert!(admin.has_permission(Permission::SecurityModify));
767    }
768
769    #[test]
770    fn test_predefined_roles() {
771        let operator = Role::operator();
772        assert!(operator.has_permission(Permission::KernelLaunch));
773        assert!(operator.has_permission(Permission::KernelTerminate));
774        assert!(!operator.has_permission(Permission::ConfigModify));
775
776        let developer = Role::developer();
777        assert!(developer.has_permission(Permission::KernelLaunch));
778        assert!(!developer.has_permission(Permission::KernelTerminate));
779
780        let readonly = Role::readonly();
781        assert!(readonly.has_permission(Permission::KernelView));
782        assert!(!readonly.has_permission(Permission::KernelLaunch));
783    }
784
785    #[test]
786    fn test_resource_rule_matching() {
787        let rule = ResourceRule::new("kernel:*");
788        assert!(rule.matches("kernel:test"));
789        assert!(rule.matches("kernel:production"));
790        assert!(!rule.matches("message:test"));
791
792        let exact_rule = ResourceRule::new("kernel:specific");
793        assert!(exact_rule.matches("kernel:specific"));
794        assert!(!exact_rule.matches("kernel:other"));
795    }
796
797    #[test]
798    fn test_policy_evaluation() {
799        let policy = RbacPolicy::standard();
800        let evaluator = PolicyEvaluator::new(policy);
801
802        let admin_subject = Subject::new("admin_user").with_role("admin");
803        assert!(evaluator.is_allowed(&admin_subject, Permission::KernelLaunch));
804        assert!(evaluator.is_allowed(&admin_subject, Permission::SecurityModify));
805
806        let readonly_subject = Subject::new("readonly_user").with_role("readonly");
807        assert!(evaluator.is_allowed(&readonly_subject, Permission::KernelView));
808        assert!(!evaluator.is_allowed(&readonly_subject, Permission::KernelLaunch));
809    }
810
811    #[test]
812    fn test_resource_access() {
813        let policy = RbacPolicy::standard()
814            .with_resource_rule(ResourceRule::new("kernel:production*").require_role("operator"));
815
816        let evaluator = PolicyEvaluator::new(policy);
817
818        let developer = Subject::new("dev").with_role("developer");
819        let operator = Subject::new("ops").with_role("operator");
820
821        // Developers can't access production kernels
822        assert!(evaluator
823            .can_access(&developer, "kernel:production-1", Permission::KernelView)
824            .is_err());
825
826        // Operators can
827        assert!(evaluator
828            .can_access(&operator, "kernel:production-1", Permission::KernelView)
829            .is_ok());
830    }
831
832    #[test]
833    fn test_get_permissions() {
834        let policy = RbacPolicy::standard();
835        let evaluator = PolicyEvaluator::new(policy);
836
837        let operator = Subject::new("ops").with_role("operator");
838        let permissions = evaluator.get_permissions(&operator);
839
840        assert!(permissions.contains(&Permission::KernelLaunch));
841        assert!(permissions.contains(&Permission::KernelTerminate));
842        assert!(!permissions.contains(&Permission::ConfigModify));
843    }
844}