1use crate::capabilities::{CapabilitySet, ResourceType, Action};
9use crate::error::{SecurityError, Result};
10use crate::rbac::{RBACService, RoleAssignment, PrincipalId};
11use crate::abac::{ABACService, PolicyDecision, UserAttributeProvider, ResourceAttributeProvider, EnvironmentAttributeProvider};
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub enum PolicyMode {
18 RBACOnly,
20 ABACOnly,
22 RBACFirst,
24 ABACFirst,
26 Combined,
28}
29
30#[derive(Debug, Clone, PartialEq)]
32pub enum UnifiedPolicyDecision {
33 Allow,
34 Deny,
35 NotApplicable,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct PolicyEngineConfig {
41 pub mode: PolicyMode,
42 pub rbac_enabled: bool,
43 pub abac_enabled: bool,
44 pub default_deny: bool, }
46
47impl Default for PolicyEngineConfig {
48 fn default() -> Self {
49 Self {
50 mode: PolicyMode::Combined,
51 rbac_enabled: true,
52 abac_enabled: true,
53 default_deny: false,
54 }
55 }
56}
57
58pub struct UnifiedPolicyEngine {
60 config: PolicyEngineConfig,
61 rbac_service: Option<RBACService>,
62 abac_service: Option<ABACService>,
63}
64
65impl UnifiedPolicyEngine {
66 pub fn new(config: PolicyEngineConfig) -> Self {
68 Self {
69 config,
70 rbac_service: None,
71 abac_service: None,
72 }
73 }
74
75 pub fn with_rbac(mut self, rbac_service: RBACService) -> Self {
77 self.rbac_service = Some(rbac_service);
78 self
79 }
80
81 pub fn with_abac(mut self, abac_service: ABACService) -> Self {
83 self.abac_service = Some(abac_service);
84 self
85 }
86
87 pub fn set_rbac_service(&mut self, rbac_service: RBACService) {
89 self.rbac_service = Some(rbac_service);
90 }
91
92 pub fn set_abac_service(&mut self, abac_service: ABACService) {
94 self.abac_service = Some(abac_service);
95 }
96
97 pub async fn evaluate_access(
99 &self,
100 principal_id: &PrincipalId,
101 resource_type: &ResourceType,
102 resource_id: Option<&crate::abac::ResourceId>,
103 action: &Action,
104 ) -> Result<UnifiedPolicyDecision> {
105 match self.config.mode {
106 PolicyMode::RBACOnly => {
107 self.evaluate_rbac_only(principal_id, resource_type, action)
108 }
109 PolicyMode::ABACOnly => {
110 self.evaluate_abac_only(principal_id, resource_type, resource_id, action).await
111 }
112 PolicyMode::RBACFirst => {
113 self.evaluate_rbac_first(principal_id, resource_type, resource_id, action).await
114 }
115 PolicyMode::ABACFirst => {
116 self.evaluate_abac_first(principal_id, resource_type, resource_id, action).await
117 }
118 PolicyMode::Combined => {
119 self.evaluate_combined(principal_id, resource_type, resource_id, action).await
120 }
121 }
122 }
123
124 fn evaluate_rbac_only(
126 &self,
127 principal_id: &PrincipalId,
128 resource_type: &ResourceType,
129 action: &Action,
130 ) -> Result<UnifiedPolicyDecision> {
131 if !self.config.rbac_enabled {
132 return Ok(UnifiedPolicyDecision::NotApplicable);
133 }
134
135 let rbac_service = self.rbac_service.as_ref()
136 .ok_or_else(|| SecurityError::Configuration("RBAC service not configured".to_string()))?;
137
138 match rbac_service.check_permission(principal_id, resource_type, action, None)? {
139 true => Ok(UnifiedPolicyDecision::Allow),
140 false => Ok(UnifiedPolicyDecision::Deny),
141 }
142 }
143
144 async fn evaluate_abac_only(
146 &self,
147 principal_id: &PrincipalId,
148 resource_type: &ResourceType,
149 resource_id: Option<&crate::abac::ResourceId>,
150 action: &Action,
151 ) -> Result<UnifiedPolicyDecision> {
152 if !self.config.abac_enabled {
153 return Ok(UnifiedPolicyDecision::NotApplicable);
154 }
155
156 let abac_service = self.abac_service.as_ref()
157 .ok_or_else(|| SecurityError::Configuration("ABAC service not configured".to_string()))?;
158
159 match abac_service.check_access(principal_id, resource_type, resource_id, action).await? {
160 PolicyDecision::Allow => Ok(UnifiedPolicyDecision::Allow),
161 PolicyDecision::Deny => Ok(UnifiedPolicyDecision::Deny),
162 PolicyDecision::NotApplicable => Ok(UnifiedPolicyDecision::NotApplicable),
163 }
164 }
165
166 async fn evaluate_rbac_first(
168 &self,
169 principal_id: &PrincipalId,
170 resource_type: &ResourceType,
171 resource_id: Option<&crate::abac::ResourceId>,
172 action: &Action,
173 ) -> Result<UnifiedPolicyDecision> {
174 let rbac_result = self.evaluate_rbac_only(principal_id, resource_type, action)?;
176
177 match rbac_result {
178 UnifiedPolicyDecision::Allow | UnifiedPolicyDecision::Deny => {
179 Ok(rbac_result)
181 }
182 UnifiedPolicyDecision::NotApplicable => {
183 self.evaluate_abac_only(principal_id, resource_type, resource_id, action).await
185 }
186 }
187 }
188
189 async fn evaluate_abac_first(
191 &self,
192 principal_id: &PrincipalId,
193 resource_type: &ResourceType,
194 resource_id: Option<&crate::abac::ResourceId>,
195 action: &Action,
196 ) -> Result<UnifiedPolicyDecision> {
197 let abac_result = self.evaluate_abac_only(principal_id, resource_type, resource_id, action).await?;
199
200 match abac_result {
201 UnifiedPolicyDecision::Allow | UnifiedPolicyDecision::Deny => {
202 Ok(abac_result)
204 }
205 UnifiedPolicyDecision::NotApplicable => {
206 self.evaluate_rbac_only(principal_id, resource_type, action)
208 }
209 }
210 }
211
212 async fn evaluate_combined(
214 &self,
215 principal_id: &PrincipalId,
216 resource_type: &ResourceType,
217 resource_id: Option<&crate::abac::ResourceId>,
218 action: &Action,
219 ) -> Result<UnifiedPolicyDecision> {
220 let rbac_result = if self.config.rbac_enabled {
221 Some(self.evaluate_rbac_only(principal_id, resource_type, action)?)
222 } else {
223 None
224 };
225
226 let abac_result = if self.config.abac_enabled {
227 Some(self.evaluate_abac_only(principal_id, resource_type, resource_id, action).await?)
228 } else {
229 None
230 };
231
232 let mut has_allow = false;
234 let mut has_deny = false;
235
236 if let Some(UnifiedPolicyDecision::Allow) = rbac_result {
237 has_allow = true;
238 }
239 if let Some(UnifiedPolicyDecision::Deny) = rbac_result {
240 has_deny = true;
241 }
242
243 if let Some(UnifiedPolicyDecision::Allow) = abac_result {
244 has_allow = true;
245 }
246 if let Some(UnifiedPolicyDecision::Deny) = abac_result {
247 has_deny = true;
248 }
249
250 if has_deny {
252 Ok(UnifiedPolicyDecision::Deny)
253 } else if has_allow {
254 Ok(UnifiedPolicyDecision::Allow)
255 } else if rbac_result.is_some() || abac_result.is_some() {
256 if self.config.default_deny {
258 Ok(UnifiedPolicyDecision::Deny)
259 } else {
260 Ok(UnifiedPolicyDecision::NotApplicable)
261 }
262 } else {
263 Ok(UnifiedPolicyDecision::NotApplicable)
265 }
266 }
267
268 pub fn get_principal_capabilities(&self, principal_id: &PrincipalId) -> Result<CapabilitySet> {
270 let rbac_service = self.rbac_service.as_ref()
271 .ok_or_else(|| SecurityError::Configuration("RBAC service not configured".to_string()))?;
272
273 rbac_service.get_principal_capabilities(principal_id)
274 }
275
276 pub fn add_rbac_role(&mut self, role: crate::rbac::Role) -> Result<()> {
278 let rbac_service = self.rbac_service.as_mut()
279 .ok_or_else(|| SecurityError::Configuration("RBAC service not configured".to_string()))?;
280
281 rbac_service.add_role(role)
282 }
283
284 pub fn assign_rbac_role(&mut self, assignment: RoleAssignment) -> Result<()> {
286 let rbac_service = self.rbac_service.as_mut()
287 .ok_or_else(|| SecurityError::Configuration("RBAC service not configured".to_string()))?;
288
289 rbac_service.assign_role(assignment)
290 }
291
292 pub fn add_abac_policy(&mut self, policy: crate::abac::Policy) -> Result<()> {
294 let abac_service = self.abac_service.as_mut()
295 .ok_or_else(|| SecurityError::Configuration("ABAC service not configured".to_string()))?;
296
297 abac_service.add_policy(policy)
298 }
299
300 pub fn setup_common_policies(&mut self) -> Result<()> {
302 if let Some(rbac_service) = &mut self.rbac_service {
304 rbac_service.create_common_roles()?;
305 }
306
307 if let Some(abac_service) = &mut self.abac_service {
309 abac_service.setup_common_policies()?;
310 }
311
312 Ok(())
313 }
314
315 pub fn config(&self) -> &PolicyEngineConfig {
317 &self.config
318 }
319
320 pub fn set_config(&mut self, config: PolicyEngineConfig) {
322 self.config = config;
323 }
324}
325
326impl Default for UnifiedPolicyEngine {
327 fn default() -> Self {
328 Self::new(PolicyEngineConfig::default())
329 }
330}
331
332pub struct PolicyService {
334 engine: UnifiedPolicyEngine,
335}
336
337impl PolicyService {
338 pub fn new() -> Self {
340 Self {
341 engine: UnifiedPolicyEngine::new(PolicyEngineConfig::default()),
342 }
343 }
344
345 pub fn with_config(config: PolicyEngineConfig) -> Self {
347 Self {
348 engine: UnifiedPolicyEngine::new(config),
349 }
350 }
351
352 pub fn with_services(
354 config: PolicyEngineConfig,
355 rbac_service: Option<RBACService>,
356 abac_service: Option<ABACService>,
357 ) -> Self {
358 let mut engine = UnifiedPolicyEngine::new(config);
359
360 if let Some(rbac) = rbac_service {
361 engine.set_rbac_service(rbac);
362 }
363
364 if let Some(abac) = abac_service {
365 engine.set_abac_service(abac);
366 }
367
368 Self { engine }
369 }
370
371 pub async fn check_permission(
373 &self,
374 principal_id: &PrincipalId,
375 resource_type: &ResourceType,
376 resource_id: Option<&crate::abac::ResourceId>,
377 action: &Action,
378 ) -> Result<bool> {
379 match self.engine.evaluate_access(principal_id, resource_type, resource_id, action).await? {
380 UnifiedPolicyDecision::Allow => Ok(true),
381 UnifiedPolicyDecision::Deny => Ok(false),
382 UnifiedPolicyDecision::NotApplicable => {
383 Ok(!self.engine.config().default_deny)
385 }
386 }
387 }
388
389 pub async fn authorize(
391 &self,
392 principal_id: &PrincipalId,
393 resource_type: &ResourceType,
394 resource_id: Option<&crate::abac::ResourceId>,
395 action: &Action,
396 ) -> Result<UnifiedPolicyDecision> {
397 self.engine.evaluate_access(principal_id, resource_type, resource_id, action).await
398 }
399
400 pub fn add_role(&mut self, role: crate::rbac::Role) -> Result<()> {
402 self.engine.add_rbac_role(role)
403 }
404
405 pub fn assign_role(&mut self, assignment: RoleAssignment) -> Result<()> {
407 self.engine.assign_rbac_role(assignment)
408 }
409
410 pub fn add_policy(&mut self, policy: crate::abac::Policy) -> Result<()> {
412 self.engine.add_abac_policy(policy)
413 }
414
415 pub fn setup_common_policies(&mut self) -> Result<()> {
417 self.engine.setup_common_policies()
418 }
419
420 pub fn engine(&self) -> &UnifiedPolicyEngine {
422 &self.engine
423 }
424
425 pub fn engine_mut(&mut self) -> &mut UnifiedPolicyEngine {
427 &mut self.engine
428 }
429}
430
431impl Default for PolicyService {
432 fn default() -> Self {
433 Self::new()
434 }
435}
436
437#[cfg(test)]
438mod tests {
439 use super::*;
440 use crate::abac::{SimpleUserAttributeProvider, SimpleResourceAttributeProvider, SimpleEnvironmentAttributeProvider, UserAttributes, ResourceAttributes, AttributeValue};
441 use crate::rbac::Role;
442
443 #[test]
444 fn test_policy_engine_config() {
445 let config = PolicyEngineConfig::default();
446 assert_eq!(config.mode, PolicyMode::Combined);
447 assert!(config.rbac_enabled);
448 assert!(config.abac_enabled);
449 }
450
451 #[tokio::test]
452 async fn test_unified_policy_engine_rbac_only() {
453 let config = PolicyEngineConfig {
454 mode: PolicyMode::RBACOnly,
455 rbac_enabled: true,
456 abac_enabled: false,
457 default_deny: false,
458 };
459
460 let mut rbac_service = RBACService::new();
461 let role = Role::new("reader".to_string(), "Reader".to_string());
462 rbac_service.add_role(role).unwrap();
463
464 let mut engine = UnifiedPolicyEngine::new(config).with_rbac(rbac_service);
465
466 let result = engine.evaluate_access(
468 &"user1".to_string(),
469 &ResourceType::Graph,
470 None,
471 &Action::Read,
472 ).await.unwrap();
473
474 assert_eq!(result, UnifiedPolicyDecision::Deny);
475 }
476
477 #[tokio::test]
478 async fn test_unified_policy_engine_abac_only() {
479 let config = PolicyEngineConfig {
480 mode: PolicyMode::ABACOnly,
481 rbac_enabled: false,
482 abac_enabled: true,
483 default_deny: false,
484 };
485
486 let user_provider = Box::new(
487 SimpleUserAttributeProvider::new()
488 .add_user(
489 "user1".to_string(),
490 UserAttributes::new()
491 .with_attribute("role".to_string(), AttributeValue::String("admin".to_string())),
492 )
493 );
494
495 let resource_provider = Box::new(
496 SimpleResourceAttributeProvider::new()
497 .add_resource(
498 "graph".to_string(),
499 ResourceAttributes::new(ResourceType::Graph, None),
500 )
501 );
502
503 let env_provider = Box::new(SimpleEnvironmentAttributeProvider::new());
504
505 let abac_service = ABACService::new(user_provider, resource_provider, env_provider);
506 let mut engine = UnifiedPolicyEngine::new(config).with_abac(abac_service);
507
508 let result = engine.evaluate_access(
510 &"user1".to_string(),
511 &ResourceType::Graph,
512 None,
513 &Action::Read,
514 ).await.unwrap();
515
516 assert_eq!(result, UnifiedPolicyDecision::Allow);
517 }
518
519 #[tokio::test]
520 async fn test_policy_service() {
521 let config = PolicyEngineConfig {
522 mode: PolicyMode::Combined,
523 rbac_enabled: true,
524 abac_enabled: true,
525 default_deny: false,
526 };
527
528 let user_provider = Box::new(
529 SimpleUserAttributeProvider::new()
530 .add_user(
531 "user1".to_string(),
532 UserAttributes::new()
533 .with_attribute("role".to_string(), AttributeValue::String("admin".to_string())),
534 )
535 );
536
537 let resource_provider = Box::new(
538 SimpleResourceAttributeProvider::new()
539 .add_resource(
540 "graph".to_string(),
541 ResourceAttributes::new(ResourceType::Graph, None),
542 )
543 );
544
545 let env_provider = Box::new(SimpleEnvironmentAttributeProvider::new());
546
547 let abac_service = ABACService::new(user_provider, resource_provider, env_provider);
548 let rbac_service = RBACService::new();
549
550 let mut policy_service = PolicyService::with_services(
551 config,
552 Some(rbac_service),
553 Some(abac_service),
554 );
555
556 policy_service.setup_common_policies().unwrap();
557
558 let allowed = policy_service.check_permission(
560 &"user1".to_string(),
561 &ResourceType::Graph,
562 None,
563 &Action::Read,
564 ).await.unwrap();
565
566 assert!(allowed);
567 }
568}