oxigdal_security/access_control/
mod.rs1pub mod abac;
4pub mod permissions;
5pub mod policies;
6pub mod rbac;
7pub mod roles;
8
9use crate::error::Result;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15pub struct Subject {
16 pub id: String,
18 pub subject_type: SubjectType,
20 pub attributes: HashMap<String, String>,
22}
23
24impl Subject {
25 pub fn new(id: String, subject_type: SubjectType) -> Self {
27 Self {
28 id,
29 subject_type,
30 attributes: HashMap::new(),
31 }
32 }
33
34 pub fn with_attribute(mut self, key: String, value: String) -> Self {
36 self.attributes.insert(key, value);
37 self
38 }
39
40 pub fn get_attribute(&self, key: &str) -> Option<&String> {
42 self.attributes.get(key)
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub enum SubjectType {
49 User,
51 Service,
53 ApiKey,
55 System,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61pub struct Resource {
62 pub id: String,
64 pub resource_type: ResourceType,
66 pub attributes: HashMap<String, String>,
68 pub parent: Option<Box<Resource>>,
70}
71
72impl Resource {
73 pub fn new(id: String, resource_type: ResourceType) -> Self {
75 Self {
76 id,
77 resource_type,
78 attributes: HashMap::new(),
79 parent: None,
80 }
81 }
82
83 pub fn with_attribute(mut self, key: String, value: String) -> Self {
85 self.attributes.insert(key, value);
86 self
87 }
88
89 pub fn with_parent(mut self, parent: Resource) -> Self {
91 self.parent = Some(Box::new(parent));
92 self
93 }
94
95 pub fn get_attribute(&self, key: &str) -> Option<&String> {
97 self.attributes.get(key)
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
103pub enum ResourceType {
104 Dataset,
106 Layer,
108 Feature,
110 Raster,
112 File,
114 Directory,
116 Service,
118 Tenant,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
124pub enum Action {
125 Read,
127 Write,
129 Delete,
131 Execute,
133 List,
135 Create,
137 Update,
139 Admin,
141}
142
143impl Action {
144 pub fn all() -> Vec<Action> {
146 vec![
147 Action::Read,
148 Action::Write,
149 Action::Delete,
150 Action::Execute,
151 Action::List,
152 Action::Create,
153 Action::Update,
154 Action::Admin,
155 ]
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
161pub enum AccessDecision {
162 Allow,
164 Deny,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct AccessRequest {
171 pub subject: Subject,
173 pub resource: Resource,
175 pub action: Action,
177 pub context: AccessContext,
179}
180
181impl AccessRequest {
182 pub fn new(
184 subject: Subject,
185 resource: Resource,
186 action: Action,
187 context: AccessContext,
188 ) -> Self {
189 Self {
190 subject,
191 resource,
192 action,
193 context,
194 }
195 }
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct AccessContext {
201 pub timestamp: chrono::DateTime<chrono::Utc>,
203 pub source_ip: Option<String>,
205 pub tenant_id: Option<String>,
207 pub attributes: HashMap<String, String>,
209}
210
211impl Default for AccessContext {
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217impl AccessContext {
218 pub fn new() -> Self {
220 Self {
221 timestamp: chrono::Utc::now(),
222 source_ip: None,
223 tenant_id: None,
224 attributes: HashMap::new(),
225 }
226 }
227
228 pub fn with_source_ip(mut self, ip: String) -> Self {
230 self.source_ip = Some(ip);
231 self
232 }
233
234 pub fn with_tenant_id(mut self, tenant_id: String) -> Self {
236 self.tenant_id = Some(tenant_id);
237 self
238 }
239
240 pub fn with_attribute(mut self, key: String, value: String) -> Self {
242 self.attributes.insert(key, value);
243 self
244 }
245
246 pub fn get_attribute(&self, key: &str) -> Option<&String> {
248 self.attributes.get(key)
249 }
250}
251
252pub trait AccessControlEvaluator: Send + Sync {
254 fn evaluate(&self, request: &AccessRequest) -> Result<AccessDecision>;
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_subject_creation() {
264 let subject = Subject::new("user-123".to_string(), SubjectType::User)
265 .with_attribute("department".to_string(), "engineering".to_string());
266
267 assert_eq!(subject.id, "user-123");
268 assert_eq!(subject.subject_type, SubjectType::User);
269 assert_eq!(
270 subject.get_attribute("department"),
271 Some(&"engineering".to_string())
272 );
273 }
274
275 #[test]
276 fn test_resource_creation() {
277 let resource = Resource::new("dataset-456".to_string(), ResourceType::Dataset)
278 .with_attribute("classification".to_string(), "confidential".to_string());
279
280 assert_eq!(resource.id, "dataset-456");
281 assert_eq!(resource.resource_type, ResourceType::Dataset);
282 assert_eq!(
283 resource.get_attribute("classification"),
284 Some(&"confidential".to_string())
285 );
286 }
287
288 #[test]
289 fn test_access_context() {
290 let context = AccessContext::new()
291 .with_source_ip("192.168.1.1".to_string())
292 .with_tenant_id("tenant-001".to_string())
293 .with_attribute("region".to_string(), "us-west-2".to_string());
294
295 assert_eq!(context.source_ip, Some("192.168.1.1".to_string()));
296 assert_eq!(context.tenant_id, Some("tenant-001".to_string()));
297 assert_eq!(
298 context.get_attribute("region"),
299 Some(&"us-west-2".to_string())
300 );
301 }
302
303 #[test]
304 fn test_all_actions() {
305 let actions = Action::all();
306 assert_eq!(actions.len(), 8);
307 assert!(actions.contains(&Action::Read));
308 assert!(actions.contains(&Action::Write));
309 }
310}