agent_core_runtime/permissions/
mod.rs1mod batch;
53mod grant;
54mod level;
55mod registry;
56mod target;
57mod tool_mapping;
58
59pub use batch::{
60 compute_suggested_grants, BatchAction, BatchPermissionRequest, BatchPermissionResponse,
61};
62pub use grant::{Grant, PermissionRequest};
63pub use level::PermissionLevel;
64pub use registry::{
65 generate_batch_id, PendingPermissionInfo, PermissionError, PermissionPanelResponse,
66 PermissionRegistry,
67};
68pub use target::GrantTarget;
69pub use tool_mapping::{get_tool_category, ToolCategory, ToolPermissions};
70
71#[cfg(test)]
72mod integration_tests {
73 use super::*;
74
75 #[test]
76 fn test_end_to_end_permission_check() {
77 let grant = Grant::read_path("/project", true);
79
80 let requests = vec![
82 PermissionRequest::file_read("1", "/project/src/main.rs"),
83 PermissionRequest::file_read("2", "/project/Cargo.toml"),
84 PermissionRequest::directory_read("3", "/project/src", true),
85 ];
86
87 for request in &requests {
88 assert!(
89 grant.satisfies(request),
90 "Grant should satisfy request: {}",
91 request.id
92 );
93 }
94
95 let write_request = PermissionRequest::file_write("4", "/project/src/main.rs");
97 assert!(
98 !grant.satisfies(&write_request),
99 "Read grant should not satisfy write request"
100 );
101
102 let outside_request = PermissionRequest::file_read("5", "/other/file.rs");
104 assert!(
105 !grant.satisfies(&outside_request),
106 "Grant should not satisfy request outside path"
107 );
108 }
109
110 #[test]
111 fn test_level_hierarchy() {
112 let admin_grant = Grant::admin_path("/project", true);
114
115 let requests = vec![
116 PermissionRequest::file_read("1", "/project/file.rs"),
117 PermissionRequest::file_write("2", "/project/file.rs"),
118 PermissionRequest::new(
119 "3",
120 GrantTarget::path("/project/file.rs", false),
121 PermissionLevel::Execute,
122 "Execute",
123 ),
124 PermissionRequest::new(
125 "4",
126 GrantTarget::path("/project/file.rs", false),
127 PermissionLevel::Admin,
128 "Admin",
129 ),
130 ];
131
132 for request in &requests {
133 assert!(
134 admin_grant.satisfies(request),
135 "Admin grant should satisfy {} level request",
136 request.required_level
137 );
138 }
139 }
140
141 #[test]
142 fn test_batch_permission_flow() {
143 let requests = vec![
145 PermissionRequest::file_read("tool-1", "/project/src/main.rs"),
146 PermissionRequest::file_read("tool-2", "/project/src/lib.rs"),
147 PermissionRequest::file_read("tool-3", "/project/Cargo.toml"),
148 ];
149
150 let batch = BatchPermissionRequest::new("batch-1", requests.clone());
152
153 assert!(!batch.suggested_grants.is_empty());
155
156 let approved_grant = Grant::read_path("/project", true);
158 let response = BatchPermissionResponse::all_granted("batch-1", vec![approved_grant]);
159
160 for request in &requests {
162 assert!(
163 response.is_granted(&request.id, request),
164 "Request {} should be granted",
165 request.id
166 );
167 }
168 }
169
170 #[test]
171 fn test_different_target_types_independent() {
172 let path_grant = Grant::read_path("/project", true);
174 let domain_request =
175 PermissionRequest::network_access("1", "api.github.com", PermissionLevel::Read);
176 assert!(!path_grant.satisfies(&domain_request));
177
178 let domain_grant = Grant::domain("*", PermissionLevel::Admin);
180 let cmd_request = PermissionRequest::command_execute("2", "git status");
181 assert!(!domain_grant.satisfies(&cmd_request));
182
183 let cmd_grant = Grant::command("*", PermissionLevel::Admin);
185 let path_request = PermissionRequest::file_read("3", "/project/file.rs");
186 assert!(!cmd_grant.satisfies(&path_request));
187 }
188
189 #[test]
190 fn test_serialization_roundtrip() {
191 let grant = Grant::write_path("/project/src", true);
192 let json = serde_json::to_string(&grant).unwrap();
193 let deserialized: Grant = serde_json::from_str(&json).unwrap();
194
195 assert_eq!(grant.level, deserialized.level);
196 assert_eq!(grant.target, deserialized.target);
197
198 let request = PermissionRequest::file_write("test", "/project/src/main.rs")
199 .with_reason("Testing")
200 .with_tool("write_file");
201 let json = serde_json::to_string(&request).unwrap();
202 let deserialized: PermissionRequest = serde_json::from_str(&json).unwrap();
203
204 assert_eq!(request.id, deserialized.id);
205 assert_eq!(request.reason, deserialized.reason);
206 assert_eq!(request.tool_name, deserialized.tool_name);
207 }
208}