Skip to main content

oxicode_sdk/security/capability/
resolve.rs

1//! CSpace resolution — resolve a template name to a concrete CSpace.
2//!
3//! Provides `resolve_cspace()` which maps template names like "standard",
4//! "worker", "admin" to pre-built CSpace configurations.
5
6use uuid::Uuid;
7
8use super::types::CSpaceBuilder;
9
10/// Resolve a CSpace from a template name and optional overrides.
11///
12/// # Arguments
13/// * `template` - Template name: "standard", "worker", "admin", or None for empty.
14/// * `workspace` - Optional workspace path override for path capabilities.
15/// * `extra_domains` - Optional extra kernel domains to grant execute on.
16/// * `agent_id` - UUID of the owning agent.
17pub fn resolve_cspace(
18    template: Option<&str>,
19    workspace: Option<&str>,
20    extra_domains: Option<&[String]>,
21    agent_id: Uuid,
22) -> super::types::CSpace {
23    let name = template.unwrap_or("empty");
24    let mut builder = CSpaceBuilder::new(agent_id).name(name);
25
26    match name {
27        "admin" | "superuser" => {
28            builder = builder.all_access();
29        }
30        "standard" | "default" => {
31            builder = builder.standard();
32            // Add workspace path if provided
33            if let Some(ws) = workspace {
34                use super::types::{ResourceRef, Rights};
35                builder = builder.grant(
36                    ResourceRef::Path {
37                        pattern: format!("{ws}/**"),
38                    },
39                    vec![Rights::Read, Rights::Write, Rights::Execute],
40                );
41            }
42        }
43        "worker" => {
44            builder = builder.worker();
45            if let Some(ws) = workspace {
46                use super::types::{ResourceRef, Rights};
47                builder = builder.grant(
48                    ResourceRef::Path {
49                        pattern: format!("{ws}/**"),
50                    },
51                    vec![Rights::Read, Rights::Write, Rights::Execute],
52                );
53            }
54        }
55        _ => {
56            // Unknown template → empty CSpace (least privilege)
57        }
58    }
59
60    // Grant extra domains if provided
61    if let Some(domains) = extra_domains {
62        use super::types::{ResourceRef, Rights};
63        for domain in domains {
64            builder = builder.grant(
65                ResourceRef::KernelDomain {
66                    domain: domain.clone(),
67                },
68                vec![Rights::Read, Rights::Write, Rights::Execute],
69            );
70        }
71    }
72
73    builder.build()
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::security::capability::types::{ResourceRef, Rights};
80
81    #[test]
82    fn test_resolve_standard() {
83        let id = Uuid::new_v4();
84        let cs = resolve_cspace(Some("standard"), None, None, id);
85        assert!(cs.can(
86            &ResourceRef::KernelDomain {
87                domain: "read".into()
88            },
89            Rights::Execute
90        ));
91        assert!(cs.can(
92            &ResourceRef::KernelDomain {
93                domain: "memory".into()
94            },
95            Rights::Read
96        ));
97    }
98
99    #[test]
100    fn test_resolve_admin() {
101        let id = Uuid::new_v4();
102        let cs = resolve_cspace(Some("admin"), None, None, id);
103        assert!(cs.can(
104            &ResourceRef::KernelDomain {
105                domain: "anything".into()
106            },
107            Rights::Grant
108        ));
109    }
110
111    #[test]
112    fn test_resolve_worker() {
113        let id = Uuid::new_v4();
114        let cs = resolve_cspace(Some("worker"), None, None, id);
115        assert!(cs.can(
116            &ResourceRef::KernelDomain {
117                domain: "subagent".into()
118            },
119            Rights::Execute
120        ));
121    }
122
123    #[test]
124    fn test_resolve_unknown_is_empty() {
125        let id = Uuid::new_v4();
126        let cs = resolve_cspace(Some("nonexistent"), None, None, id);
127        assert!(cs.is_empty());
128    }
129
130    #[test]
131    fn test_resolve_with_workspace() {
132        let id = Uuid::new_v4();
133        let cs = resolve_cspace(Some("standard"), Some("/my/project"), None, id);
134        assert!(cs.can(
135            &ResourceRef::Path {
136                pattern: "/my/project/src/lib.rs".into()
137            },
138            Rights::Read
139        ));
140    }
141
142    #[test]
143    fn test_resolve_with_extra_domains() {
144        let id = Uuid::new_v4();
145        let cs = resolve_cspace(
146            Some("standard"),
147            None,
148            Some(&["custom_tool".to_string()]),
149            id,
150        );
151        assert!(cs.can(
152            &ResourceRef::KernelDomain {
153                domain: "custom_tool".into()
154            },
155            Rights::Execute
156        ));
157    }
158}