PolicyEvaluator

Struct PolicyEvaluator 

Source
pub struct PolicyEvaluator { /* private fields */ }
Expand description

Policy evaluation engine

Implementations§

Source§

impl PolicyEvaluator

Source

pub fn new() -> Self

Create a new policy evaluator

Source

pub fn with_policies(policies: Vec<IAMPolicy>) -> Self

Create evaluator with policies

Examples found in repository?
examples/evaluation_demo.rs (line 181)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10    // Example 1: Simple Allow Policy
11    println!("1. Simple Allow Policy:");
12    let allow_policy = IAMPolicy::new()
13        .with_id("550e8400-e29b-41d4-a716-446655440000")
14        .add_statement(
15            IAMStatement::new(IAMEffect::Allow)
16                .with_sid("AllowS3Read")
17                .with_action(IAMAction::Single("s3:GetObject".to_string()))
18                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19        );
20
21    let request = IAMRequest::new(
22        Principal::Aws(PrincipalId::String(
23            "arn:aws:iam::123456789012:user/alice".to_string(),
24        )),
25        "s3:GetObject",
26        Arn::parse("arn:aws:s3:::my-bucket/file.txt").unwrap(),
27    );
28
29    match evaluate_policy(&allow_policy, &request)? {
30        Decision::Allow => println!("✓ Access ALLOWED"),
31        Decision::Deny => println!("✗ Access DENIED"),
32        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
33    }
34    println!();
35
36    // Example 2: Simple Deny Policy
37    println!("2. Simple Deny Policy:");
38    let deny_policy = IAMPolicy::new()
39        .with_id("550e8400-e29b-41d4-a716-446655440001")
40        .add_statement(
41            IAMStatement::new(IAMEffect::Deny)
42                .with_sid("DenyS3Delete")
43                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
44                .with_resource(IAMResource::Single(
45                    "arn:aws:s3:::protected-bucket/*".to_string(),
46                )),
47        );
48
49    let delete_request = IAMRequest::new(
50        Principal::Aws(PrincipalId::String(
51            "arn:aws:iam::123456789012:user/alice".to_string(),
52        )),
53        "s3:DeleteObject",
54        Arn::parse("arn:aws:s3:::protected-bucket/important.txt").unwrap(),
55    );
56
57    match evaluate_policy(&deny_policy, &delete_request)? {
58        Decision::Allow => println!("✓ Access ALLOWED"),
59        Decision::Deny => println!("✗ Access DENIED"),
60        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
61    }
62    println!();
63
64    // Example 3: Wildcard Action Matching
65    println!("3. Wildcard Action Matching:");
66    let wildcard_policy = IAMPolicy::new()
67        .with_id("550e8400-e29b-41d4-a716-446655440002")
68        .add_statement(
69            IAMStatement::new(IAMEffect::Allow)
70                .with_sid("AllowAllS3")
71                .with_action(IAMAction::Single("s3:*".to_string()))
72                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
73        );
74
75    let wildcard_request = IAMRequest::new(
76        Principal::Aws(PrincipalId::String(
77            "arn:aws:iam::123456789012:user/alice".to_string(),
78        )),
79        "s3:PutObject",
80        Arn::parse("arn:aws:s3:::my-bucket/new-file.txt").unwrap(),
81    );
82
83    match evaluate_policy(&wildcard_policy, &wildcard_request)? {
84        Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
85        Decision::Deny => println!("✗ Access DENIED"),
86        Decision::NotApplicable => println!("? No applicable policy"),
87    }
88    println!();
89
90    // Example 4: Condition-Based Policy
91    println!("4. Condition-Based Policy:");
92    let mut context = Context::new();
93    context.insert(
94        "aws:userid".to_string(),
95        ContextValue::String("alice".to_string()),
96    );
97    context.insert(
98        "aws:CurrentTime".to_string(),
99        ContextValue::String("2024-01-15T10:00:00Z".to_string()),
100    );
101
102    let condition_policy = IAMPolicy::new()
103        .with_id("550e8400-e29b-41d4-a716-446655440003")
104        .add_statement(
105            IAMStatement::new(IAMEffect::Allow)
106                .with_sid("AllowWithCondition")
107                .with_action(IAMAction::Single("s3:GetObject".to_string()))
108                .with_resource(IAMResource::Single(
109                    "arn:aws:s3:::private-bucket/*".to_string(),
110                ))
111                .with_condition(
112                    IAMOperator::StringEquals,
113                    "aws:userid".to_string(),
114                    ConditionValue::String("alice".to_string()),
115                ),
116        );
117
118    let condition_request = IAMRequest::new_with_context(
119        Principal::Aws(PrincipalId::String(
120            "arn:aws:iam::123456789012:user/alice".to_string(),
121        )),
122        "s3:GetObject",
123        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
124        context,
125    );
126
127    match evaluate_policy(&condition_policy, &condition_request)? {
128        Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
129        Decision::Deny => println!("✗ Access DENIED"),
130        Decision::NotApplicable => println!("? Condition not satisfied"),
131    }
132    println!();
133
134    // Example 5: Failed Condition
135    println!("5. Failed Condition:");
136    let mut wrong_context = Context::new();
137    wrong_context.insert(
138        "aws:userid".to_string(),
139        ContextValue::String("bob".to_string()),
140    );
141
142    let failed_condition_request = IAMRequest::new_with_context(
143        Principal::Aws(PrincipalId::String(
144            "arn:aws:iam::123456789012:user/bob".to_string(),
145        )),
146        "s3:GetObject",
147        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
148        wrong_context,
149    );
150
151    match evaluate_policy(&condition_policy, &failed_condition_request)? {
152        Decision::Allow => println!("✓ Access ALLOWED"),
153        Decision::Deny => println!("✗ Access DENIED"),
154        Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
155    }
156    println!();
157
158    // Example 6: Explicit Deny Overrides Allow
159    println!("6. Explicit Deny Overrides Allow:");
160    let combined_policies = vec![
161        IAMPolicy::new()
162            .with_id("550e8400-e29b-41d4-a716-446655440004")
163            .add_statement(
164                IAMStatement::new(IAMEffect::Allow)
165                    .with_sid("AllowAll")
166                    .with_action(IAMAction::Single("s3:*".to_string()))
167                    .with_resource(IAMResource::Single("*".to_string())),
168            ),
169        IAMPolicy::new()
170            .with_id("550e8400-e29b-41d4-a716-446655440005")
171            .add_statement(
172                IAMStatement::new(IAMEffect::Deny)
173                    .with_sid("DenyProtected")
174                    .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
175                    .with_resource(IAMResource::Single(
176                        "arn:aws:s3:::protected-bucket/*".to_string(),
177                    )),
178            ),
179    ];
180
181    let evaluator = PolicyEvaluator::with_policies(combined_policies);
182    let protected_request = IAMRequest::new(
183        Principal::Aws(PrincipalId::String(
184            "arn:aws:iam::123456789012:user/alice".to_string(),
185        )),
186        "s3:DeleteObject",
187        Arn::parse("arn:aws:s3:::protected-bucket/critical.txt").unwrap(),
188    );
189
190    match evaluator.evaluate(&protected_request)?.decision {
191        Decision::Allow => println!("✓ Access ALLOWED"),
192        Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
193        Decision::NotApplicable => println!("? No applicable policy"),
194    }
195    println!();
196
197    // Example 7: Numeric Condition
198    println!("7. Numeric Condition:");
199    let mut numeric_context = Context::new();
200    numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
201
202    let numeric_policy = IAMPolicy::new()
203        .with_id("550e8400-e29b-41d4-a716-446655440006")
204        .add_statement(
205            IAMStatement::new(IAMEffect::Allow)
206                .with_sid("AllowLimitedRequests")
207                .with_action(IAMAction::Single("s3:GetObject".to_string()))
208                .with_resource(IAMResource::Single("*".to_string()))
209                .with_condition(
210                    IAMOperator::NumericLessThan,
211                    "aws:RequestCount".to_string(),
212                    ConditionValue::Number(10),
213                ),
214        );
215
216    let numeric_request = IAMRequest::new_with_context(
217        Principal::Aws(PrincipalId::String(
218            "arn:aws:iam::123456789012:user/alice".to_string(),
219        )),
220        "s3:GetObject",
221        Arn::parse("arn:aws:s3:::any-bucket/file.txt").unwrap(),
222        numeric_context,
223    );
224
225    match evaluate_policy(&numeric_policy, &numeric_request)? {
226        Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
227        Decision::Deny => println!("✗ Access DENIED"),
228        Decision::NotApplicable => println!("? Numeric condition failed"),
229    }
230    println!();
231
232    // Example 8: Detailed Evaluation with Options
233    println!("8. Detailed Evaluation with Options:");
234    let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
235        .with_options(EvaluationOptions {
236            collect_match_details: true,
237            stop_on_explicit_deny: false,
238            max_statements: 100,
239            ignore_resource_constraints: false,
240        });
241
242    let detailed_result = detailed_evaluator.evaluate(&request)?;
243    println!("Decision: {:?}", detailed_result.decision);
244    println!("Matched Statements:");
245    for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
246        println!(
247            "  {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
248            i + 1,
249            statement_match.sid,
250            statement_match.effect,
251            statement_match.conditions_satisfied,
252            statement_match.reason
253        );
254    }
255    println!();
256
257    // Example 9: No Applicable Policy (Implicit Deny)
258    println!("9. No Applicable Policy (Implicit Deny):");
259    let unrelated_request = IAMRequest::new(
260        Principal::Aws(PrincipalId::String(
261            "arn:aws:iam::123456789012:user/alice".to_string(),
262        )),
263        "ec2:DescribeInstances",
264        Arn::parse("arn:aws:ec2:us-east-1:123456789012:instance/*").unwrap(),
265    );
266
267    match evaluate_policy(&allow_policy, &unrelated_request)? {
268        Decision::Allow => println!("✓ Access ALLOWED"),
269        Decision::Deny => println!("✗ Access DENIED"),
270        Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
271    }
272    println!();
273
274    // Example 10: Resource Pattern Matching
275    println!("10. Resource Pattern Matching:");
276    let pattern_policy = IAMPolicy::new()
277        .with_id("550e8400-e29b-41d4-a716-446655440007")
278        .add_statement(
279            IAMStatement::new(IAMEffect::Allow)
280                .with_sid("AllowBucketAccess")
281                .with_action(IAMAction::Multiple(vec![
282                    "s3:GetObject".to_string(),
283                    "s3:PutObject".to_string(),
284                ]))
285                .with_resource(IAMResource::Single(
286                    "arn:aws:s3:::user-data-*/*".to_string(),
287                )),
288        );
289
290    let pattern_request = IAMRequest::new(
291        Principal::Aws(PrincipalId::String(
292            "arn:aws:iam::123456789012:user/alice".to_string(),
293        )),
294        "s3:GetObject",
295        Arn::parse("arn:aws:s3:::user-data-alice/profile.json").unwrap(),
296    );
297
298    match evaluate_policy(&pattern_policy, &pattern_request)? {
299        Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
300        Decision::Deny => println!("✗ Access DENIED"),
301        Decision::NotApplicable => println!("? Resource pattern didn't match"),
302    }
303
304    println!("\n=== Policy Evaluation Demo Complete ===");
305    println!("\nThe Policy Evaluation Engine successfully:");
306    println!("• ✅ Evaluates Allow/Deny effects");
307    println!("• ✅ Handles wildcard actions and resources");
308    println!("• ✅ Processes condition blocks with various operators");
309    println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
310    println!("• ✅ Supports detailed evaluation with match information");
311    println!("• ✅ Handles multiple policies with complex interactions");
312    println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
313
314    Ok(())
315}
Source

pub fn add_policy(&mut self, policy: IAMPolicy)

Add a policy to the evaluator

Source

pub fn with_options(self, options: EvaluationOptions) -> Self

Set evaluation options

Examples found in repository?
examples/evaluation_demo.rs (lines 235-240)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10    // Example 1: Simple Allow Policy
11    println!("1. Simple Allow Policy:");
12    let allow_policy = IAMPolicy::new()
13        .with_id("550e8400-e29b-41d4-a716-446655440000")
14        .add_statement(
15            IAMStatement::new(IAMEffect::Allow)
16                .with_sid("AllowS3Read")
17                .with_action(IAMAction::Single("s3:GetObject".to_string()))
18                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19        );
20
21    let request = IAMRequest::new(
22        Principal::Aws(PrincipalId::String(
23            "arn:aws:iam::123456789012:user/alice".to_string(),
24        )),
25        "s3:GetObject",
26        Arn::parse("arn:aws:s3:::my-bucket/file.txt").unwrap(),
27    );
28
29    match evaluate_policy(&allow_policy, &request)? {
30        Decision::Allow => println!("✓ Access ALLOWED"),
31        Decision::Deny => println!("✗ Access DENIED"),
32        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
33    }
34    println!();
35
36    // Example 2: Simple Deny Policy
37    println!("2. Simple Deny Policy:");
38    let deny_policy = IAMPolicy::new()
39        .with_id("550e8400-e29b-41d4-a716-446655440001")
40        .add_statement(
41            IAMStatement::new(IAMEffect::Deny)
42                .with_sid("DenyS3Delete")
43                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
44                .with_resource(IAMResource::Single(
45                    "arn:aws:s3:::protected-bucket/*".to_string(),
46                )),
47        );
48
49    let delete_request = IAMRequest::new(
50        Principal::Aws(PrincipalId::String(
51            "arn:aws:iam::123456789012:user/alice".to_string(),
52        )),
53        "s3:DeleteObject",
54        Arn::parse("arn:aws:s3:::protected-bucket/important.txt").unwrap(),
55    );
56
57    match evaluate_policy(&deny_policy, &delete_request)? {
58        Decision::Allow => println!("✓ Access ALLOWED"),
59        Decision::Deny => println!("✗ Access DENIED"),
60        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
61    }
62    println!();
63
64    // Example 3: Wildcard Action Matching
65    println!("3. Wildcard Action Matching:");
66    let wildcard_policy = IAMPolicy::new()
67        .with_id("550e8400-e29b-41d4-a716-446655440002")
68        .add_statement(
69            IAMStatement::new(IAMEffect::Allow)
70                .with_sid("AllowAllS3")
71                .with_action(IAMAction::Single("s3:*".to_string()))
72                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
73        );
74
75    let wildcard_request = IAMRequest::new(
76        Principal::Aws(PrincipalId::String(
77            "arn:aws:iam::123456789012:user/alice".to_string(),
78        )),
79        "s3:PutObject",
80        Arn::parse("arn:aws:s3:::my-bucket/new-file.txt").unwrap(),
81    );
82
83    match evaluate_policy(&wildcard_policy, &wildcard_request)? {
84        Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
85        Decision::Deny => println!("✗ Access DENIED"),
86        Decision::NotApplicable => println!("? No applicable policy"),
87    }
88    println!();
89
90    // Example 4: Condition-Based Policy
91    println!("4. Condition-Based Policy:");
92    let mut context = Context::new();
93    context.insert(
94        "aws:userid".to_string(),
95        ContextValue::String("alice".to_string()),
96    );
97    context.insert(
98        "aws:CurrentTime".to_string(),
99        ContextValue::String("2024-01-15T10:00:00Z".to_string()),
100    );
101
102    let condition_policy = IAMPolicy::new()
103        .with_id("550e8400-e29b-41d4-a716-446655440003")
104        .add_statement(
105            IAMStatement::new(IAMEffect::Allow)
106                .with_sid("AllowWithCondition")
107                .with_action(IAMAction::Single("s3:GetObject".to_string()))
108                .with_resource(IAMResource::Single(
109                    "arn:aws:s3:::private-bucket/*".to_string(),
110                ))
111                .with_condition(
112                    IAMOperator::StringEquals,
113                    "aws:userid".to_string(),
114                    ConditionValue::String("alice".to_string()),
115                ),
116        );
117
118    let condition_request = IAMRequest::new_with_context(
119        Principal::Aws(PrincipalId::String(
120            "arn:aws:iam::123456789012:user/alice".to_string(),
121        )),
122        "s3:GetObject",
123        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
124        context,
125    );
126
127    match evaluate_policy(&condition_policy, &condition_request)? {
128        Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
129        Decision::Deny => println!("✗ Access DENIED"),
130        Decision::NotApplicable => println!("? Condition not satisfied"),
131    }
132    println!();
133
134    // Example 5: Failed Condition
135    println!("5. Failed Condition:");
136    let mut wrong_context = Context::new();
137    wrong_context.insert(
138        "aws:userid".to_string(),
139        ContextValue::String("bob".to_string()),
140    );
141
142    let failed_condition_request = IAMRequest::new_with_context(
143        Principal::Aws(PrincipalId::String(
144            "arn:aws:iam::123456789012:user/bob".to_string(),
145        )),
146        "s3:GetObject",
147        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
148        wrong_context,
149    );
150
151    match evaluate_policy(&condition_policy, &failed_condition_request)? {
152        Decision::Allow => println!("✓ Access ALLOWED"),
153        Decision::Deny => println!("✗ Access DENIED"),
154        Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
155    }
156    println!();
157
158    // Example 6: Explicit Deny Overrides Allow
159    println!("6. Explicit Deny Overrides Allow:");
160    let combined_policies = vec![
161        IAMPolicy::new()
162            .with_id("550e8400-e29b-41d4-a716-446655440004")
163            .add_statement(
164                IAMStatement::new(IAMEffect::Allow)
165                    .with_sid("AllowAll")
166                    .with_action(IAMAction::Single("s3:*".to_string()))
167                    .with_resource(IAMResource::Single("*".to_string())),
168            ),
169        IAMPolicy::new()
170            .with_id("550e8400-e29b-41d4-a716-446655440005")
171            .add_statement(
172                IAMStatement::new(IAMEffect::Deny)
173                    .with_sid("DenyProtected")
174                    .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
175                    .with_resource(IAMResource::Single(
176                        "arn:aws:s3:::protected-bucket/*".to_string(),
177                    )),
178            ),
179    ];
180
181    let evaluator = PolicyEvaluator::with_policies(combined_policies);
182    let protected_request = IAMRequest::new(
183        Principal::Aws(PrincipalId::String(
184            "arn:aws:iam::123456789012:user/alice".to_string(),
185        )),
186        "s3:DeleteObject",
187        Arn::parse("arn:aws:s3:::protected-bucket/critical.txt").unwrap(),
188    );
189
190    match evaluator.evaluate(&protected_request)?.decision {
191        Decision::Allow => println!("✓ Access ALLOWED"),
192        Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
193        Decision::NotApplicable => println!("? No applicable policy"),
194    }
195    println!();
196
197    // Example 7: Numeric Condition
198    println!("7. Numeric Condition:");
199    let mut numeric_context = Context::new();
200    numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
201
202    let numeric_policy = IAMPolicy::new()
203        .with_id("550e8400-e29b-41d4-a716-446655440006")
204        .add_statement(
205            IAMStatement::new(IAMEffect::Allow)
206                .with_sid("AllowLimitedRequests")
207                .with_action(IAMAction::Single("s3:GetObject".to_string()))
208                .with_resource(IAMResource::Single("*".to_string()))
209                .with_condition(
210                    IAMOperator::NumericLessThan,
211                    "aws:RequestCount".to_string(),
212                    ConditionValue::Number(10),
213                ),
214        );
215
216    let numeric_request = IAMRequest::new_with_context(
217        Principal::Aws(PrincipalId::String(
218            "arn:aws:iam::123456789012:user/alice".to_string(),
219        )),
220        "s3:GetObject",
221        Arn::parse("arn:aws:s3:::any-bucket/file.txt").unwrap(),
222        numeric_context,
223    );
224
225    match evaluate_policy(&numeric_policy, &numeric_request)? {
226        Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
227        Decision::Deny => println!("✗ Access DENIED"),
228        Decision::NotApplicable => println!("? Numeric condition failed"),
229    }
230    println!();
231
232    // Example 8: Detailed Evaluation with Options
233    println!("8. Detailed Evaluation with Options:");
234    let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
235        .with_options(EvaluationOptions {
236            collect_match_details: true,
237            stop_on_explicit_deny: false,
238            max_statements: 100,
239            ignore_resource_constraints: false,
240        });
241
242    let detailed_result = detailed_evaluator.evaluate(&request)?;
243    println!("Decision: {:?}", detailed_result.decision);
244    println!("Matched Statements:");
245    for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
246        println!(
247            "  {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
248            i + 1,
249            statement_match.sid,
250            statement_match.effect,
251            statement_match.conditions_satisfied,
252            statement_match.reason
253        );
254    }
255    println!();
256
257    // Example 9: No Applicable Policy (Implicit Deny)
258    println!("9. No Applicable Policy (Implicit Deny):");
259    let unrelated_request = IAMRequest::new(
260        Principal::Aws(PrincipalId::String(
261            "arn:aws:iam::123456789012:user/alice".to_string(),
262        )),
263        "ec2:DescribeInstances",
264        Arn::parse("arn:aws:ec2:us-east-1:123456789012:instance/*").unwrap(),
265    );
266
267    match evaluate_policy(&allow_policy, &unrelated_request)? {
268        Decision::Allow => println!("✓ Access ALLOWED"),
269        Decision::Deny => println!("✗ Access DENIED"),
270        Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
271    }
272    println!();
273
274    // Example 10: Resource Pattern Matching
275    println!("10. Resource Pattern Matching:");
276    let pattern_policy = IAMPolicy::new()
277        .with_id("550e8400-e29b-41d4-a716-446655440007")
278        .add_statement(
279            IAMStatement::new(IAMEffect::Allow)
280                .with_sid("AllowBucketAccess")
281                .with_action(IAMAction::Multiple(vec![
282                    "s3:GetObject".to_string(),
283                    "s3:PutObject".to_string(),
284                ]))
285                .with_resource(IAMResource::Single(
286                    "arn:aws:s3:::user-data-*/*".to_string(),
287                )),
288        );
289
290    let pattern_request = IAMRequest::new(
291        Principal::Aws(PrincipalId::String(
292            "arn:aws:iam::123456789012:user/alice".to_string(),
293        )),
294        "s3:GetObject",
295        Arn::parse("arn:aws:s3:::user-data-alice/profile.json").unwrap(),
296    );
297
298    match evaluate_policy(&pattern_policy, &pattern_request)? {
299        Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
300        Decision::Deny => println!("✗ Access DENIED"),
301        Decision::NotApplicable => println!("? Resource pattern didn't match"),
302    }
303
304    println!("\n=== Policy Evaluation Demo Complete ===");
305    println!("\nThe Policy Evaluation Engine successfully:");
306    println!("• ✅ Evaluates Allow/Deny effects");
307    println!("• ✅ Handles wildcard actions and resources");
308    println!("• ✅ Processes condition blocks with various operators");
309    println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
310    println!("• ✅ Supports detailed evaluation with match information");
311    println!("• ✅ Handles multiple policies with complex interactions");
312    println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
313
314    Ok(())
315}
Source

pub fn evaluate( &self, request: &IAMRequest, ) -> Result<EvaluationResult, EvaluationError>

Evaluate an authorization request against all policies

§Errors

Returns EvaluationError if:

  • The request context is invalid
  • ARN format errors occur during evaluation
  • Variable interpolation fails
  • Condition evaluation fails
  • Maximum statement evaluation limit is exceeded
Examples found in repository?
examples/evaluation_demo.rs (line 190)
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8    println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10    // Example 1: Simple Allow Policy
11    println!("1. Simple Allow Policy:");
12    let allow_policy = IAMPolicy::new()
13        .with_id("550e8400-e29b-41d4-a716-446655440000")
14        .add_statement(
15            IAMStatement::new(IAMEffect::Allow)
16                .with_sid("AllowS3Read")
17                .with_action(IAMAction::Single("s3:GetObject".to_string()))
18                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19        );
20
21    let request = IAMRequest::new(
22        Principal::Aws(PrincipalId::String(
23            "arn:aws:iam::123456789012:user/alice".to_string(),
24        )),
25        "s3:GetObject",
26        Arn::parse("arn:aws:s3:::my-bucket/file.txt").unwrap(),
27    );
28
29    match evaluate_policy(&allow_policy, &request)? {
30        Decision::Allow => println!("✓ Access ALLOWED"),
31        Decision::Deny => println!("✗ Access DENIED"),
32        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
33    }
34    println!();
35
36    // Example 2: Simple Deny Policy
37    println!("2. Simple Deny Policy:");
38    let deny_policy = IAMPolicy::new()
39        .with_id("550e8400-e29b-41d4-a716-446655440001")
40        .add_statement(
41            IAMStatement::new(IAMEffect::Deny)
42                .with_sid("DenyS3Delete")
43                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
44                .with_resource(IAMResource::Single(
45                    "arn:aws:s3:::protected-bucket/*".to_string(),
46                )),
47        );
48
49    let delete_request = IAMRequest::new(
50        Principal::Aws(PrincipalId::String(
51            "arn:aws:iam::123456789012:user/alice".to_string(),
52        )),
53        "s3:DeleteObject",
54        Arn::parse("arn:aws:s3:::protected-bucket/important.txt").unwrap(),
55    );
56
57    match evaluate_policy(&deny_policy, &delete_request)? {
58        Decision::Allow => println!("✓ Access ALLOWED"),
59        Decision::Deny => println!("✗ Access DENIED"),
60        Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
61    }
62    println!();
63
64    // Example 3: Wildcard Action Matching
65    println!("3. Wildcard Action Matching:");
66    let wildcard_policy = IAMPolicy::new()
67        .with_id("550e8400-e29b-41d4-a716-446655440002")
68        .add_statement(
69            IAMStatement::new(IAMEffect::Allow)
70                .with_sid("AllowAllS3")
71                .with_action(IAMAction::Single("s3:*".to_string()))
72                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string())),
73        );
74
75    let wildcard_request = IAMRequest::new(
76        Principal::Aws(PrincipalId::String(
77            "arn:aws:iam::123456789012:user/alice".to_string(),
78        )),
79        "s3:PutObject",
80        Arn::parse("arn:aws:s3:::my-bucket/new-file.txt").unwrap(),
81    );
82
83    match evaluate_policy(&wildcard_policy, &wildcard_request)? {
84        Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
85        Decision::Deny => println!("✗ Access DENIED"),
86        Decision::NotApplicable => println!("? No applicable policy"),
87    }
88    println!();
89
90    // Example 4: Condition-Based Policy
91    println!("4. Condition-Based Policy:");
92    let mut context = Context::new();
93    context.insert(
94        "aws:userid".to_string(),
95        ContextValue::String("alice".to_string()),
96    );
97    context.insert(
98        "aws:CurrentTime".to_string(),
99        ContextValue::String("2024-01-15T10:00:00Z".to_string()),
100    );
101
102    let condition_policy = IAMPolicy::new()
103        .with_id("550e8400-e29b-41d4-a716-446655440003")
104        .add_statement(
105            IAMStatement::new(IAMEffect::Allow)
106                .with_sid("AllowWithCondition")
107                .with_action(IAMAction::Single("s3:GetObject".to_string()))
108                .with_resource(IAMResource::Single(
109                    "arn:aws:s3:::private-bucket/*".to_string(),
110                ))
111                .with_condition(
112                    IAMOperator::StringEquals,
113                    "aws:userid".to_string(),
114                    ConditionValue::String("alice".to_string()),
115                ),
116        );
117
118    let condition_request = IAMRequest::new_with_context(
119        Principal::Aws(PrincipalId::String(
120            "arn:aws:iam::123456789012:user/alice".to_string(),
121        )),
122        "s3:GetObject",
123        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
124        context,
125    );
126
127    match evaluate_policy(&condition_policy, &condition_request)? {
128        Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
129        Decision::Deny => println!("✗ Access DENIED"),
130        Decision::NotApplicable => println!("? Condition not satisfied"),
131    }
132    println!();
133
134    // Example 5: Failed Condition
135    println!("5. Failed Condition:");
136    let mut wrong_context = Context::new();
137    wrong_context.insert(
138        "aws:userid".to_string(),
139        ContextValue::String("bob".to_string()),
140    );
141
142    let failed_condition_request = IAMRequest::new_with_context(
143        Principal::Aws(PrincipalId::String(
144            "arn:aws:iam::123456789012:user/bob".to_string(),
145        )),
146        "s3:GetObject",
147        Arn::parse("arn:aws:s3:::private-bucket/personal.txt").unwrap(),
148        wrong_context,
149    );
150
151    match evaluate_policy(&condition_policy, &failed_condition_request)? {
152        Decision::Allow => println!("✓ Access ALLOWED"),
153        Decision::Deny => println!("✗ Access DENIED"),
154        Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
155    }
156    println!();
157
158    // Example 6: Explicit Deny Overrides Allow
159    println!("6. Explicit Deny Overrides Allow:");
160    let combined_policies = vec![
161        IAMPolicy::new()
162            .with_id("550e8400-e29b-41d4-a716-446655440004")
163            .add_statement(
164                IAMStatement::new(IAMEffect::Allow)
165                    .with_sid("AllowAll")
166                    .with_action(IAMAction::Single("s3:*".to_string()))
167                    .with_resource(IAMResource::Single("*".to_string())),
168            ),
169        IAMPolicy::new()
170            .with_id("550e8400-e29b-41d4-a716-446655440005")
171            .add_statement(
172                IAMStatement::new(IAMEffect::Deny)
173                    .with_sid("DenyProtected")
174                    .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
175                    .with_resource(IAMResource::Single(
176                        "arn:aws:s3:::protected-bucket/*".to_string(),
177                    )),
178            ),
179    ];
180
181    let evaluator = PolicyEvaluator::with_policies(combined_policies);
182    let protected_request = IAMRequest::new(
183        Principal::Aws(PrincipalId::String(
184            "arn:aws:iam::123456789012:user/alice".to_string(),
185        )),
186        "s3:DeleteObject",
187        Arn::parse("arn:aws:s3:::protected-bucket/critical.txt").unwrap(),
188    );
189
190    match evaluator.evaluate(&protected_request)?.decision {
191        Decision::Allow => println!("✓ Access ALLOWED"),
192        Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
193        Decision::NotApplicable => println!("? No applicable policy"),
194    }
195    println!();
196
197    // Example 7: Numeric Condition
198    println!("7. Numeric Condition:");
199    let mut numeric_context = Context::new();
200    numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
201
202    let numeric_policy = IAMPolicy::new()
203        .with_id("550e8400-e29b-41d4-a716-446655440006")
204        .add_statement(
205            IAMStatement::new(IAMEffect::Allow)
206                .with_sid("AllowLimitedRequests")
207                .with_action(IAMAction::Single("s3:GetObject".to_string()))
208                .with_resource(IAMResource::Single("*".to_string()))
209                .with_condition(
210                    IAMOperator::NumericLessThan,
211                    "aws:RequestCount".to_string(),
212                    ConditionValue::Number(10),
213                ),
214        );
215
216    let numeric_request = IAMRequest::new_with_context(
217        Principal::Aws(PrincipalId::String(
218            "arn:aws:iam::123456789012:user/alice".to_string(),
219        )),
220        "s3:GetObject",
221        Arn::parse("arn:aws:s3:::any-bucket/file.txt").unwrap(),
222        numeric_context,
223    );
224
225    match evaluate_policy(&numeric_policy, &numeric_request)? {
226        Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
227        Decision::Deny => println!("✗ Access DENIED"),
228        Decision::NotApplicable => println!("? Numeric condition failed"),
229    }
230    println!();
231
232    // Example 8: Detailed Evaluation with Options
233    println!("8. Detailed Evaluation with Options:");
234    let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
235        .with_options(EvaluationOptions {
236            collect_match_details: true,
237            stop_on_explicit_deny: false,
238            max_statements: 100,
239            ignore_resource_constraints: false,
240        });
241
242    let detailed_result = detailed_evaluator.evaluate(&request)?;
243    println!("Decision: {:?}", detailed_result.decision);
244    println!("Matched Statements:");
245    for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
246        println!(
247            "  {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
248            i + 1,
249            statement_match.sid,
250            statement_match.effect,
251            statement_match.conditions_satisfied,
252            statement_match.reason
253        );
254    }
255    println!();
256
257    // Example 9: No Applicable Policy (Implicit Deny)
258    println!("9. No Applicable Policy (Implicit Deny):");
259    let unrelated_request = IAMRequest::new(
260        Principal::Aws(PrincipalId::String(
261            "arn:aws:iam::123456789012:user/alice".to_string(),
262        )),
263        "ec2:DescribeInstances",
264        Arn::parse("arn:aws:ec2:us-east-1:123456789012:instance/*").unwrap(),
265    );
266
267    match evaluate_policy(&allow_policy, &unrelated_request)? {
268        Decision::Allow => println!("✓ Access ALLOWED"),
269        Decision::Deny => println!("✗ Access DENIED"),
270        Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
271    }
272    println!();
273
274    // Example 10: Resource Pattern Matching
275    println!("10. Resource Pattern Matching:");
276    let pattern_policy = IAMPolicy::new()
277        .with_id("550e8400-e29b-41d4-a716-446655440007")
278        .add_statement(
279            IAMStatement::new(IAMEffect::Allow)
280                .with_sid("AllowBucketAccess")
281                .with_action(IAMAction::Multiple(vec![
282                    "s3:GetObject".to_string(),
283                    "s3:PutObject".to_string(),
284                ]))
285                .with_resource(IAMResource::Single(
286                    "arn:aws:s3:::user-data-*/*".to_string(),
287                )),
288        );
289
290    let pattern_request = IAMRequest::new(
291        Principal::Aws(PrincipalId::String(
292            "arn:aws:iam::123456789012:user/alice".to_string(),
293        )),
294        "s3:GetObject",
295        Arn::parse("arn:aws:s3:::user-data-alice/profile.json").unwrap(),
296    );
297
298    match evaluate_policy(&pattern_policy, &pattern_request)? {
299        Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
300        Decision::Deny => println!("✗ Access DENIED"),
301        Decision::NotApplicable => println!("? Resource pattern didn't match"),
302    }
303
304    println!("\n=== Policy Evaluation Demo Complete ===");
305    println!("\nThe Policy Evaluation Engine successfully:");
306    println!("• ✅ Evaluates Allow/Deny effects");
307    println!("• ✅ Handles wildcard actions and resources");
308    println!("• ✅ Processes condition blocks with various operators");
309    println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
310    println!("• ✅ Supports detailed evaluation with match information");
311    println!("• ✅ Handles multiple policies with complex interactions");
312    println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
313
314    Ok(())
315}

Trait Implementations§

Source§

impl Clone for PolicyEvaluator

Source§

fn clone(&self) -> PolicyEvaluator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PolicyEvaluator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PolicyEvaluator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.