1use parking_lot::RwLock;
27use std::collections::{HashMap, HashSet};
28use std::fmt;
29
30use crate::KernelId;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum Permission {
39 KernelLaunch,
42 KernelTerminate,
44 KernelActivate,
46 KernelView,
48 KernelMigrate,
50 KernelCheckpoint,
52 KernelRestore,
54
55 MessageSend,
58 MessageReceive,
60 K2KSend,
62 K2KRegister,
64 PubSubPublish,
66 PubSubSubscribe,
68
69 MemoryAllocate,
72 MemoryView,
74 ResourceConfigure,
76
77 ConfigView,
80 ConfigModify,
82 SecurityView,
84 SecurityModify,
86
87 MetricsView,
90 MetricsExport,
92 AuditView,
94 AuditExport,
96 TracesView,
98
99 Admin,
102 UserManage,
104 TenantManage,
106}
107
108impl Permission {
109 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 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 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#[derive(Debug, Clone)]
215pub struct Role {
216 pub name: String,
218 pub description: String,
220 pub permissions: HashSet<Permission>,
222 pub is_superuser: bool,
224}
225
226impl Role {
227 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 pub fn with_permission(mut self, permission: Permission) -> Self {
239 self.permissions.insert(permission);
240 self
241 }
242
243 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 pub fn with_superuser(mut self, is_superuser: bool) -> Self {
254 self.is_superuser = is_superuser;
255 self
256 }
257
258 pub fn has_permission(&self, permission: Permission) -> bool {
260 self.is_superuser || self.permissions.contains(&permission)
261 }
262
263 pub fn admin() -> Self {
265 Self::new("admin", "Full administrative access to all operations").with_superuser(true)
266 }
267
268 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 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 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 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#[derive(Debug, Clone)]
344pub struct RbacPolicy {
345 roles: HashMap<String, Role>,
347 resource_rules: Vec<ResourceRule>,
349 default_deny: bool,
351}
352
353#[derive(Debug, Clone)]
355pub struct ResourceRule {
356 pub resource_pattern: String,
358 pub required_permissions: HashSet<Permission>,
360 pub required_roles: HashSet<String>,
362 pub deny: bool,
364}
365
366impl ResourceRule {
367 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 pub fn require_permission(mut self, permission: Permission) -> Self {
379 self.required_permissions.insert(permission);
380 self
381 }
382
383 pub fn require_role(mut self, role: impl Into<String>) -> Self {
385 self.required_roles.insert(role.into());
386 self
387 }
388
389 pub fn deny(mut self) -> Self {
391 self.deny = true;
392 self
393 }
394
395 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 pub fn new() -> Self {
413 Self {
414 roles: HashMap::new(),
415 resource_rules: Vec::new(),
416 default_deny: true,
417 }
418 }
419
420 pub fn with_default_deny(mut self, deny: bool) -> Self {
422 self.default_deny = deny;
423 self
424 }
425
426 pub fn with_role(mut self, role: Role) -> Self {
428 self.roles.insert(role.name.clone(), role);
429 self
430 }
431
432 pub fn with_resource_rule(mut self, rule: ResourceRule) -> Self {
434 self.resource_rules.push(rule);
435 self
436 }
437
438 pub fn get_role(&self, name: &str) -> Option<&Role> {
440 self.roles.get(name)
441 }
442
443 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#[derive(Debug, Clone)]
466pub enum RbacError {
467 PermissionDenied(String),
469 RoleNotFound(String),
471 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
487pub type RbacResult<T> = Result<T, RbacError>;
489
490#[derive(Debug, Clone)]
492pub struct Subject {
493 pub id: String,
495 pub roles: HashSet<String>,
497 pub permissions: HashSet<String>,
499 pub tenant_id: Option<String>,
501}
502
503impl Subject {
504 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 pub fn with_role(mut self, role: impl Into<String>) -> Self {
516 self.roles.insert(role.into());
517 self
518 }
519
520 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 pub fn with_permission(mut self, permission: impl Into<String>) -> Self {
532 self.permissions.insert(permission.into());
533 self
534 }
535
536 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 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
562pub struct PolicyEvaluator {
564 policy: RbacPolicy,
566 cache: RwLock<HashMap<(String, String), bool>>,
568 cache_ttl: std::time::Duration,
570}
571
572impl PolicyEvaluator {
573 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 pub fn with_cache_ttl(mut self, ttl: std::time::Duration) -> Self {
584 self.cache_ttl = ttl;
585 self
586 }
587
588 pub fn clear_cache(&self) {
590 self.cache.write().clear();
591 }
592
593 pub fn is_allowed(&self, subject: &Subject, permission: Permission) -> bool {
595 if subject.permissions.contains(permission.as_str()) {
597 return true;
598 }
599
600 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 pub fn can_access(
614 &self,
615 subject: &Subject,
616 resource: &str,
617 permission: Permission,
618 ) -> RbacResult<()> {
619 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 for rule in &self.policy.resource_rules {
629 if rule.matches(resource) {
630 if rule.deny {
632 return Err(RbacError::PermissionDenied(format!(
633 "Resource {} is denied by policy",
634 resource
635 )));
636 }
637
638 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 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 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 pub fn get_permissions(&self, subject: &Subject) -> HashSet<Permission> {
676 let mut permissions = HashSet::new();
677
678 for role_name in &subject.roles {
680 if let Some(role) = self.policy.get_role(role_name) {
681 if role.is_superuser {
682 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 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#[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 assert!(evaluator
823 .can_access(&developer, "kernel:production-1", Permission::KernelView)
824 .is_err());
825
826 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}