oxios_kernel/access_manager/
permissions.rs1use chrono::{DateTime, Utc};
4use glob::Pattern;
5use serde::{Deserialize, Serialize};
6use std::collections::HashSet;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct AgentPermissions {
15 pub agent_name: String,
17 #[serde(default)]
19 pub allowed_tools: HashSet<String>,
20 #[serde(default)]
22 pub allowed_paths: Vec<String>,
23 #[serde(default)]
25 pub denied_paths: Vec<String>,
26 #[serde(default)]
28 pub network_access: bool,
29 #[serde(default)]
31 pub max_execution_time_secs: u64,
32 #[serde(default)]
34 pub max_memory_mb: u64,
35 #[serde(default)]
37 pub can_fork: bool,
38}
39
40impl Default for AgentPermissions {
41 fn default() -> Self {
42 Self {
43 agent_name: String::new(),
44 allowed_tools: ["read", "write", "edit", "bash", "grep", "find"]
47 .iter()
48 .map(|s| s.to_string())
49 .collect(),
50 allowed_paths: vec!["/workspace/**".to_string()],
51 denied_paths: vec![
52 "/etc/**".to_string(),
53 "/root/**".to_string(),
54 "/sys/**".to_string(),
55 "/proc/**".to_string(),
56 ".oxios/**".to_string(),
57 ],
58 network_access: false,
59 max_execution_time_secs: 300,
60 max_memory_mb: 512,
61 can_fork: false,
62 }
63 }
64}
65
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
68pub struct PermissionUpdate {
69 #[serde(default)]
71 pub allowed_tools: Option<HashSet<String>>,
72 #[serde(default)]
74 pub allowed_paths: Option<Vec<String>>,
75 #[serde(default)]
77 pub denied_paths: Option<Vec<String>>,
78 #[serde(default)]
80 pub network_access: Option<bool>,
81 #[serde(default)]
83 pub max_execution_time_secs: Option<u64>,
84 #[serde(default)]
86 pub max_memory_mb: Option<u64>,
87 #[serde(default)]
89 pub can_fork: Option<bool>,
90}
91
92impl PermissionUpdate {
93 pub fn apply(&self, perms: &mut AgentPermissions) {
95 if let Some(tools) = &self.allowed_tools {
96 perms.allowed_tools = tools.clone();
97 }
98 if let Some(paths) = &self.allowed_paths {
99 perms.allowed_paths = paths.clone();
100 }
101 if let Some(paths) = &self.denied_paths {
102 perms.denied_paths = paths.clone();
103 }
104 if let Some(v) = self.network_access {
105 perms.network_access = v;
106 }
107 if let Some(v) = self.max_execution_time_secs {
108 perms.max_execution_time_secs = v;
109 }
110 if let Some(v) = self.max_memory_mb {
111 perms.max_memory_mb = v;
112 }
113 if let Some(v) = self.can_fork {
114 perms.can_fork = v;
115 }
116 }
117}
118
119impl AgentPermissions {
120 pub fn for_new_agent(agent_name: &str) -> Self {
122 Self {
123 agent_name: agent_name.to_string(),
124 ..Default::default()
125 }
126 }
127
128 pub fn allow_tool(&mut self, tool: &str) {
130 self.allowed_tools.insert(tool.to_string());
131 }
132
133 pub fn deny_tool(&mut self, tool: &str) {
135 self.allowed_tools.remove(tool);
136 }
137
138 pub fn allow_path(&mut self, path: &str) {
140 if !self.allowed_paths.contains(&path.to_string()) {
141 self.allowed_paths.push(path.to_string());
142 }
143 }
144
145 pub fn deny_path(&mut self, path: &str) {
147 if !self.denied_paths.contains(&path.to_string()) {
148 self.denied_paths.push(path.to_string());
149 }
150 }
151
152 pub fn enable_network(&mut self) {
154 self.network_access = true;
155 }
156
157 pub fn enable_forking(&mut self) {
159 self.can_fork = true;
160 }
161
162 pub(crate) fn is_path_denied(&self, path: &str) -> bool {
164 for pattern in &self.denied_paths {
165 if let Ok(p) = Pattern::new(pattern) {
166 if p.matches(path) {
167 return true;
168 }
169 }
170 }
171 false
172 }
173
174 pub(crate) fn is_path_allowed(&self, path: &str) -> bool {
176 for pattern in &self.allowed_paths {
177 if let Ok(p) = Pattern::new(pattern) {
178 if p.matches(path) {
179 return true;
180 }
181 }
182 }
183 false
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct AuditEntry {
190 pub timestamp: DateTime<Utc>,
192 pub agent_name: String,
194 pub action: String,
196 pub resource: String,
198 pub allowed: bool,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub reason: Option<String>,
203}
204
205impl AuditEntry {
206 pub(crate) fn new(
208 agent_name: &str,
209 action: &str,
210 resource: &str,
211 allowed: bool,
212 reason: Option<String>,
213 ) -> Self {
214 Self {
215 timestamp: Utc::now(),
216 agent_name: agent_name.to_string(),
217 action: action.to_string(),
218 resource: resource.to_string(),
219 allowed,
220 reason,
221 }
222 }
223}