IAMPolicy

Struct IAMPolicy 

Source
pub struct IAMPolicy {
    pub version: IAMVersion,
    pub id: Option<String>,
    pub statement: Vec<IAMStatement>,
}
Expand description

JSON policy documents are made up of elements. The elements are listed here in the general order you use them in a policy. The order of the elements doesn’t matter—for example, the Resource element can come before the Action element. You’re not required to specify any Condition elements in the policy. To learn more about the general structure and purpose of a JSON policy document, see Overview of JSON policies.

Some JSON policy elements are mutually exclusive. This means that you cannot create a policy that uses both. For example, you cannot use both Action and NotAction in the same policy statement. Other pairs that are mutually exclusive include Principal/NotPrincipal and Resource/NotResource.

The details of what goes into a policy vary for each service, depending on what actions the service makes available, what types of resources it contains, and so on. When you’re writing policies for a specific service, it’s helpful to see examples of policies for that service.

When you create or edit a JSON policy, iam-rw can perform policy validation to help you create an effective policy.

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html

Fields§

§version: IAMVersion

The Version policy element specifies the language syntax rules that are to be used to process a policy.

To use all of the available policy features, include the following Version element outside the Statement element in all policies. Version is a required element in all IAM policies and must always be set to at least 2012-10-17.

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_version.html

§id: Option<String>

The Id element specifies an optional identifier for the policy.

The ID is used differently in different services. ID is allowed in resource-based policies, but not in identity-based policies.

Recommendation is to use a UUID (GUID) for the value, or incorporate a UUID as part of the ID to ensure uniqueness.

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_id.html

§statement: Vec<IAMStatement>

The Statement element is the main element for a policy.

The Statement element can contain a single statement or an array of individual statements. Each individual statement block must be enclosed in curly braces { }. For multiple statements, the array must be enclosed in square brackets [ ].

https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_statement.html

Implementations§

Source§

impl IAMPolicy

Source

pub fn new() -> Self

Creates a new IAM policy with the default version

Examples found in repository?
examples/arn_demo.rs (line 81)
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4    println!("=== IAM ARN Validator Demo ===\n");
5
6    // Example 1: Parse and validate ARNs
7    println!("1. Parsing and validating ARNs:");
8
9    let valid_arns = vec![
10        "arn:aws:s3:::my-bucket/folder/file.txt",
11        "arn:aws:iam::123456789012:user/alice",
12        "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13        "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14        "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15        "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16    ];
17
18    for arn_str in &valid_arns {
19        match Arn::parse(arn_str) {
20            Ok(arn) => {
21                println!("✓ Valid ARN: {}", arn);
22                println!("  - Service: {}", arn.service);
23                println!(
24                    "  - Region: {}",
25                    if arn.region.is_empty() {
26                        "global"
27                    } else {
28                        &arn.region
29                    }
30                );
31                println!(
32                    "  - Account: {}",
33                    if arn.account_id.is_empty() {
34                        "none"
35                    } else {
36                        &arn.account_id
37                    }
38                );
39                if let Some(resource_type) = arn.resource_type() {
40                    println!("  - Resource Type: {}", resource_type);
41                }
42                if let Some(resource_id) = arn.resource_id() {
43                    println!("  - Resource ID: {}", resource_id);
44                }
45                println!();
46            }
47            Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48        }
49    }
50
51    // Example 2: Wildcard matching
52    println!("2. Wildcard pattern matching:");
53
54    let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55    println!("Resource ARN: {}", resource_arn);
56
57    let patterns = vec![
58        "arn:aws:s3:::my-bucket/*",
59        "arn:aws:s3:::my-bucket/uploads/*",
60        "arn:aws:s3:::my-bucket/uploads/user123/*",
61        "arn:aws:s3:::*/uploads/user123/document.pdf",
62        "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63        "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64        "arn:aws:s3:::other-bucket/*",
65        "arn:aws:ec2:*:*:instance/*",
66    ];
67
68    for pattern in &patterns {
69        match resource_arn.matches(pattern) {
70            Ok(matches) => {
71                let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72                println!("  {} Pattern: {}", status, pattern);
73            }
74            Err(e) => println!("  ✗ ERROR Pattern: {} ({})", pattern, e),
75        }
76    }
77
78    // Example 3: Integration with IAM policies
79    println!("\n3. Using ARNs in IAM policies:");
80
81    let policy = IAMPolicy::new()
82        .with_id("s3-access-policy")
83        .add_statement(
84            IAMStatement::new(IAMEffect::Allow)
85                .with_sid("AllowS3Read")
86                .with_action(IAMAction::Multiple(vec![
87                    "s3:GetObject".to_string(),
88                    "s3:ListBucket".to_string(),
89                ]))
90                .with_resource(IAMResource::Multiple(vec![
91                    "arn:aws:s3:::my-bucket".to_string(),
92                    "arn:aws:s3:::my-bucket/*".to_string(),
93                ])),
94        )
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_sid("AllowS3Write")
98                .with_action(IAMAction::Single("s3:PutObject".to_string()))
99                .with_resource(IAMResource::Single(
100                    "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101                )),
102        );
103
104    let policy_json = policy.to_json()?;
105    println!("Generated IAM Policy:");
106    println!("{}", policy_json);
107
108    // Example 4: Validate all ARNs in the policy
109    println!("\n4. Validating ARNs in policy:");
110
111    for (i, statement) in policy.statement.iter().enumerate() {
112        println!(
113            "Statement {}: {}",
114            i + 1,
115            statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116        );
117
118        let resources = match &statement.resource {
119            Some(IAMResource::Single(arn)) => vec![arn.clone()],
120            Some(IAMResource::Multiple(arns)) => arns.clone(),
121            None => vec![],
122        };
123
124        for resource in resources {
125            match Arn::parse(&resource) {
126                Ok(arn) => {
127                    let validity = if arn.is_valid() {
128                        "✓ Valid"
129                    } else {
130                        "⚠ Invalid"
131                    };
132                    println!("  {} Resource: {}", validity, resource);
133                }
134                Err(e) => println!("  ✗ Parse Error: {} ({})", resource, e),
135            }
136        }
137    }
138
139    Ok(())
140}
More examples
Hide additional examples
examples/validation_demo.rs (line 11)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    println!("=== IAM Policy Validation Demo ===\n");
8
9    // Example 1: Valid Policy
10    println!("1. Valid Policy Validation:");
11    let valid_policy = IAMPolicy::new()
12        .with_id("550e8400-e29b-41d4-a716-446655440000")
13        .add_statement(
14            IAMStatement::new(IAMEffect::Allow)
15                .with_sid("AllowS3Read")
16                .with_action(IAMAction::Single("s3:GetObject".to_string()))
17                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string()))
18                .with_condition(
19                    IAMOperator::StringEquals,
20                    "aws:PrincipalTag/department".to_string(),
21                    ConditionValue::String("engineering".to_string()),
22                ),
23        );
24
25    if valid_policy.is_valid() {
26        println!("✓ Policy is valid!");
27    } else {
28        println!("✗ Policy is invalid");
29    }
30
31    match valid_policy.validate_result() {
32        Ok(()) => println!("✓ Policy passes validation"),
33        Err(e) => println!("✗ Policy fails validation: {}", e),
34    }
35
36    println!();
37
38    // Example 2: Invalid Policy - Missing Required Fields
39    println!("2. Invalid Policy - Missing Required Fields:");
40    let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(IAMEffect::Allow)); // Missing action and resource
41
42    if !invalid_policy.is_valid() {
43        println!("✗ Policy is invalid (as expected)");
44        match invalid_policy.validate_result() {
45            Err(e) => println!("   Validation errors: {}", e),
46            Ok(()) => println!("   Unexpected: validation passed"),
47        }
48    }
49
50    println!();
51
52    // Example 3: Policy with Multiple Validation Errors
53    println!("3. Policy with Multiple Validation Errors:");
54    let multi_error_policy = IAMPolicy::new()
55        .with_id("") // Empty ID
56        .add_statement(
57            IAMStatement::new(IAMEffect::Allow)
58                .with_action(IAMAction::Single("invalid-action".to_string())) // Invalid action format
59                .with_resource(IAMResource::Single("invalid-resource".to_string())), // Invalid resource
60        )
61        .add_statement(
62            IAMStatement::new(IAMEffect::Allow)
63                .with_sid("DuplicateId")
64                .with_action(IAMAction::Single("s3:GetObject".to_string()))
65                .with_resource(IAMResource::Single("*".to_string())),
66        )
67        .add_statement(
68            IAMStatement::new(IAMEffect::Deny)
69                .with_sid("DuplicateId") // Duplicate SID
70                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
71                .with_resource(IAMResource::Single("*".to_string())),
72        );
73
74    match multi_error_policy.validate_result() {
75        Err(ValidationError::Multiple(errors)) => {
76            println!("✗ Found {} validation errors:", errors.len());
77            for (i, error) in errors.iter().enumerate() {
78                println!("   {}. {}", i + 1, error);
79            }
80        }
81        Err(e) => {
82            println!("✗ Single validation error: {}", e);
83        }
84        Ok(()) => {
85            println!("✓ Unexpected: validation passed");
86        }
87    }
88
89    println!();
90
91    // Example 4: Comprehensive Validation
92    println!("4. Comprehensive Validation:");
93    let comprehensive_policy = IAMPolicy::new()
94        .with_id("short") // Short ID - will fail validation
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_action(IAMAction::Single("s3:GetObject".to_string()))
98                .with_resource(IAMResource::Single("*".to_string()))
99                .with_condition(
100                    IAMOperator::NumericEquals,
101                    "aws:RequestedRegion".to_string(),
102                    ConditionValue::String("not-a-number".to_string()), // Numeric operator with string value - will fail
103                ),
104        );
105
106    match comprehensive_policy.validate_result() {
107        Ok(()) => println!("✓ Policy passes validation"),
108        Err(e) => println!("✗ Policy fails validation: {}", e),
109    }
110
111    println!();
112
113    // Example 5: Logical Policy Errors
114    println!("5. Logical Policy Errors:");
115
116    // NotPrincipal with Allow effect (invalid)
117    let mut logical_error_policy = IAMStatement::new(IAMEffect::Allow);
118    logical_error_policy.action = Some(IAMAction::Single("s3:GetObject".to_string()));
119    logical_error_policy.resource = Some(IAMResource::Single("*".to_string()));
120    logical_error_policy.not_principal = Some(Principal::Aws(iam_rs::PrincipalId::String(
121        "arn:aws:iam::123456789012:user/test".to_string(),
122    )));
123
124    match logical_error_policy.validate_result() {
125        Err(e) => println!("✗ Logical error detected: {}", e),
126        Ok(()) => println!("✓ Unexpected: validation passed"),
127    }
128
129    // Both Action and NotAction (invalid)
130    let mut conflicting_statement = IAMStatement::new(IAMEffect::Allow);
131    conflicting_statement.action = Some(IAMAction::Single("s3:GetObject".to_string()));
132    conflicting_statement.not_action = Some(IAMAction::Single("s3:PutObject".to_string()));
133    conflicting_statement.resource = Some(IAMResource::Single("*".to_string()));
134
135    match conflicting_statement.validate_result() {
136        Err(e) => println!("✗ Logical error detected: {}", e),
137        Ok(()) => println!("✓ Unexpected: validation passed"),
138    }
139
140    println!();
141
142    // Example 6: Component Validation
143    println!("6. Individual Component Validation:");
144
145    // Invalid action
146    let invalid_action = IAMAction::Single("invalid-action".to_string());
147    match invalid_action.validate_result() {
148        Err(e) => println!("✗ Invalid action: {}", e),
149        Ok(()) => println!("✓ Action is valid"),
150    }
151
152    // Invalid principal
153    let invalid_principal = Principal::Aws(PrincipalId::String("invalid-principal".to_string()));
154    match invalid_principal.validate_result() {
155        Err(e) => println!("✗ Invalid principal: {}", e),
156        Ok(()) => println!("✓ Principal is valid"),
157    }
158
159    // Valid service principal
160    let service_principal =
161        Principal::Service(PrincipalId::String("lambda.amazonaws.com".to_string()));
162    if service_principal.is_valid() {
163        println!("✓ Service principal is valid");
164    } else {
165        println!("✗ Service principal is invalid");
166    }
167
168    println!("\n=== Validation Demo Complete ===");
169
170    Ok(())
171}
examples/evaluation_demo.rs (line 12)
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 with_version(version: IAMVersion) -> Self

Creates a new IAM policy with a specific version

Source

pub fn add_statement(self, statement: IAMStatement) -> Self

Adds a statement to the policy

Examples found in repository?
examples/arn_demo.rs (lines 83-94)
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4    println!("=== IAM ARN Validator Demo ===\n");
5
6    // Example 1: Parse and validate ARNs
7    println!("1. Parsing and validating ARNs:");
8
9    let valid_arns = vec![
10        "arn:aws:s3:::my-bucket/folder/file.txt",
11        "arn:aws:iam::123456789012:user/alice",
12        "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13        "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14        "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15        "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16    ];
17
18    for arn_str in &valid_arns {
19        match Arn::parse(arn_str) {
20            Ok(arn) => {
21                println!("✓ Valid ARN: {}", arn);
22                println!("  - Service: {}", arn.service);
23                println!(
24                    "  - Region: {}",
25                    if arn.region.is_empty() {
26                        "global"
27                    } else {
28                        &arn.region
29                    }
30                );
31                println!(
32                    "  - Account: {}",
33                    if arn.account_id.is_empty() {
34                        "none"
35                    } else {
36                        &arn.account_id
37                    }
38                );
39                if let Some(resource_type) = arn.resource_type() {
40                    println!("  - Resource Type: {}", resource_type);
41                }
42                if let Some(resource_id) = arn.resource_id() {
43                    println!("  - Resource ID: {}", resource_id);
44                }
45                println!();
46            }
47            Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48        }
49    }
50
51    // Example 2: Wildcard matching
52    println!("2. Wildcard pattern matching:");
53
54    let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55    println!("Resource ARN: {}", resource_arn);
56
57    let patterns = vec![
58        "arn:aws:s3:::my-bucket/*",
59        "arn:aws:s3:::my-bucket/uploads/*",
60        "arn:aws:s3:::my-bucket/uploads/user123/*",
61        "arn:aws:s3:::*/uploads/user123/document.pdf",
62        "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63        "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64        "arn:aws:s3:::other-bucket/*",
65        "arn:aws:ec2:*:*:instance/*",
66    ];
67
68    for pattern in &patterns {
69        match resource_arn.matches(pattern) {
70            Ok(matches) => {
71                let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72                println!("  {} Pattern: {}", status, pattern);
73            }
74            Err(e) => println!("  ✗ ERROR Pattern: {} ({})", pattern, e),
75        }
76    }
77
78    // Example 3: Integration with IAM policies
79    println!("\n3. Using ARNs in IAM policies:");
80
81    let policy = IAMPolicy::new()
82        .with_id("s3-access-policy")
83        .add_statement(
84            IAMStatement::new(IAMEffect::Allow)
85                .with_sid("AllowS3Read")
86                .with_action(IAMAction::Multiple(vec![
87                    "s3:GetObject".to_string(),
88                    "s3:ListBucket".to_string(),
89                ]))
90                .with_resource(IAMResource::Multiple(vec![
91                    "arn:aws:s3:::my-bucket".to_string(),
92                    "arn:aws:s3:::my-bucket/*".to_string(),
93                ])),
94        )
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_sid("AllowS3Write")
98                .with_action(IAMAction::Single("s3:PutObject".to_string()))
99                .with_resource(IAMResource::Single(
100                    "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101                )),
102        );
103
104    let policy_json = policy.to_json()?;
105    println!("Generated IAM Policy:");
106    println!("{}", policy_json);
107
108    // Example 4: Validate all ARNs in the policy
109    println!("\n4. Validating ARNs in policy:");
110
111    for (i, statement) in policy.statement.iter().enumerate() {
112        println!(
113            "Statement {}: {}",
114            i + 1,
115            statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116        );
117
118        let resources = match &statement.resource {
119            Some(IAMResource::Single(arn)) => vec![arn.clone()],
120            Some(IAMResource::Multiple(arns)) => arns.clone(),
121            None => vec![],
122        };
123
124        for resource in resources {
125            match Arn::parse(&resource) {
126                Ok(arn) => {
127                    let validity = if arn.is_valid() {
128                        "✓ Valid"
129                    } else {
130                        "⚠ Invalid"
131                    };
132                    println!("  {} Resource: {}", validity, resource);
133                }
134                Err(e) => println!("  ✗ Parse Error: {} ({})", resource, e),
135            }
136        }
137    }
138
139    Ok(())
140}
More examples
Hide additional examples
examples/validation_demo.rs (lines 13-23)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    println!("=== IAM Policy Validation Demo ===\n");
8
9    // Example 1: Valid Policy
10    println!("1. Valid Policy Validation:");
11    let valid_policy = IAMPolicy::new()
12        .with_id("550e8400-e29b-41d4-a716-446655440000")
13        .add_statement(
14            IAMStatement::new(IAMEffect::Allow)
15                .with_sid("AllowS3Read")
16                .with_action(IAMAction::Single("s3:GetObject".to_string()))
17                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string()))
18                .with_condition(
19                    IAMOperator::StringEquals,
20                    "aws:PrincipalTag/department".to_string(),
21                    ConditionValue::String("engineering".to_string()),
22                ),
23        );
24
25    if valid_policy.is_valid() {
26        println!("✓ Policy is valid!");
27    } else {
28        println!("✗ Policy is invalid");
29    }
30
31    match valid_policy.validate_result() {
32        Ok(()) => println!("✓ Policy passes validation"),
33        Err(e) => println!("✗ Policy fails validation: {}", e),
34    }
35
36    println!();
37
38    // Example 2: Invalid Policy - Missing Required Fields
39    println!("2. Invalid Policy - Missing Required Fields:");
40    let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(IAMEffect::Allow)); // Missing action and resource
41
42    if !invalid_policy.is_valid() {
43        println!("✗ Policy is invalid (as expected)");
44        match invalid_policy.validate_result() {
45            Err(e) => println!("   Validation errors: {}", e),
46            Ok(()) => println!("   Unexpected: validation passed"),
47        }
48    }
49
50    println!();
51
52    // Example 3: Policy with Multiple Validation Errors
53    println!("3. Policy with Multiple Validation Errors:");
54    let multi_error_policy = IAMPolicy::new()
55        .with_id("") // Empty ID
56        .add_statement(
57            IAMStatement::new(IAMEffect::Allow)
58                .with_action(IAMAction::Single("invalid-action".to_string())) // Invalid action format
59                .with_resource(IAMResource::Single("invalid-resource".to_string())), // Invalid resource
60        )
61        .add_statement(
62            IAMStatement::new(IAMEffect::Allow)
63                .with_sid("DuplicateId")
64                .with_action(IAMAction::Single("s3:GetObject".to_string()))
65                .with_resource(IAMResource::Single("*".to_string())),
66        )
67        .add_statement(
68            IAMStatement::new(IAMEffect::Deny)
69                .with_sid("DuplicateId") // Duplicate SID
70                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
71                .with_resource(IAMResource::Single("*".to_string())),
72        );
73
74    match multi_error_policy.validate_result() {
75        Err(ValidationError::Multiple(errors)) => {
76            println!("✗ Found {} validation errors:", errors.len());
77            for (i, error) in errors.iter().enumerate() {
78                println!("   {}. {}", i + 1, error);
79            }
80        }
81        Err(e) => {
82            println!("✗ Single validation error: {}", e);
83        }
84        Ok(()) => {
85            println!("✓ Unexpected: validation passed");
86        }
87    }
88
89    println!();
90
91    // Example 4: Comprehensive Validation
92    println!("4. Comprehensive Validation:");
93    let comprehensive_policy = IAMPolicy::new()
94        .with_id("short") // Short ID - will fail validation
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_action(IAMAction::Single("s3:GetObject".to_string()))
98                .with_resource(IAMResource::Single("*".to_string()))
99                .with_condition(
100                    IAMOperator::NumericEquals,
101                    "aws:RequestedRegion".to_string(),
102                    ConditionValue::String("not-a-number".to_string()), // Numeric operator with string value - will fail
103                ),
104        );
105
106    match comprehensive_policy.validate_result() {
107        Ok(()) => println!("✓ Policy passes validation"),
108        Err(e) => println!("✗ Policy fails validation: {}", e),
109    }
110
111    println!();
112
113    // Example 5: Logical Policy Errors
114    println!("5. Logical Policy Errors:");
115
116    // NotPrincipal with Allow effect (invalid)
117    let mut logical_error_policy = IAMStatement::new(IAMEffect::Allow);
118    logical_error_policy.action = Some(IAMAction::Single("s3:GetObject".to_string()));
119    logical_error_policy.resource = Some(IAMResource::Single("*".to_string()));
120    logical_error_policy.not_principal = Some(Principal::Aws(iam_rs::PrincipalId::String(
121        "arn:aws:iam::123456789012:user/test".to_string(),
122    )));
123
124    match logical_error_policy.validate_result() {
125        Err(e) => println!("✗ Logical error detected: {}", e),
126        Ok(()) => println!("✓ Unexpected: validation passed"),
127    }
128
129    // Both Action and NotAction (invalid)
130    let mut conflicting_statement = IAMStatement::new(IAMEffect::Allow);
131    conflicting_statement.action = Some(IAMAction::Single("s3:GetObject".to_string()));
132    conflicting_statement.not_action = Some(IAMAction::Single("s3:PutObject".to_string()));
133    conflicting_statement.resource = Some(IAMResource::Single("*".to_string()));
134
135    match conflicting_statement.validate_result() {
136        Err(e) => println!("✗ Logical error detected: {}", e),
137        Ok(()) => println!("✓ Unexpected: validation passed"),
138    }
139
140    println!();
141
142    // Example 6: Component Validation
143    println!("6. Individual Component Validation:");
144
145    // Invalid action
146    let invalid_action = IAMAction::Single("invalid-action".to_string());
147    match invalid_action.validate_result() {
148        Err(e) => println!("✗ Invalid action: {}", e),
149        Ok(()) => println!("✓ Action is valid"),
150    }
151
152    // Invalid principal
153    let invalid_principal = Principal::Aws(PrincipalId::String("invalid-principal".to_string()));
154    match invalid_principal.validate_result() {
155        Err(e) => println!("✗ Invalid principal: {}", e),
156        Ok(()) => println!("✓ Principal is valid"),
157    }
158
159    // Valid service principal
160    let service_principal =
161        Principal::Service(PrincipalId::String("lambda.amazonaws.com".to_string()));
162    if service_principal.is_valid() {
163        println!("✓ Service principal is valid");
164    } else {
165        println!("✗ Service principal is invalid");
166    }
167
168    println!("\n=== Validation Demo Complete ===");
169
170    Ok(())
171}
examples/evaluation_demo.rs (lines 14-19)
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 with_id<S: Into<String>>(self, id: S) -> Self

Sets the policy ID

Examples found in repository?
examples/arn_demo.rs (line 82)
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4    println!("=== IAM ARN Validator Demo ===\n");
5
6    // Example 1: Parse and validate ARNs
7    println!("1. Parsing and validating ARNs:");
8
9    let valid_arns = vec![
10        "arn:aws:s3:::my-bucket/folder/file.txt",
11        "arn:aws:iam::123456789012:user/alice",
12        "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13        "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14        "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15        "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16    ];
17
18    for arn_str in &valid_arns {
19        match Arn::parse(arn_str) {
20            Ok(arn) => {
21                println!("✓ Valid ARN: {}", arn);
22                println!("  - Service: {}", arn.service);
23                println!(
24                    "  - Region: {}",
25                    if arn.region.is_empty() {
26                        "global"
27                    } else {
28                        &arn.region
29                    }
30                );
31                println!(
32                    "  - Account: {}",
33                    if arn.account_id.is_empty() {
34                        "none"
35                    } else {
36                        &arn.account_id
37                    }
38                );
39                if let Some(resource_type) = arn.resource_type() {
40                    println!("  - Resource Type: {}", resource_type);
41                }
42                if let Some(resource_id) = arn.resource_id() {
43                    println!("  - Resource ID: {}", resource_id);
44                }
45                println!();
46            }
47            Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48        }
49    }
50
51    // Example 2: Wildcard matching
52    println!("2. Wildcard pattern matching:");
53
54    let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55    println!("Resource ARN: {}", resource_arn);
56
57    let patterns = vec![
58        "arn:aws:s3:::my-bucket/*",
59        "arn:aws:s3:::my-bucket/uploads/*",
60        "arn:aws:s3:::my-bucket/uploads/user123/*",
61        "arn:aws:s3:::*/uploads/user123/document.pdf",
62        "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63        "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64        "arn:aws:s3:::other-bucket/*",
65        "arn:aws:ec2:*:*:instance/*",
66    ];
67
68    for pattern in &patterns {
69        match resource_arn.matches(pattern) {
70            Ok(matches) => {
71                let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72                println!("  {} Pattern: {}", status, pattern);
73            }
74            Err(e) => println!("  ✗ ERROR Pattern: {} ({})", pattern, e),
75        }
76    }
77
78    // Example 3: Integration with IAM policies
79    println!("\n3. Using ARNs in IAM policies:");
80
81    let policy = IAMPolicy::new()
82        .with_id("s3-access-policy")
83        .add_statement(
84            IAMStatement::new(IAMEffect::Allow)
85                .with_sid("AllowS3Read")
86                .with_action(IAMAction::Multiple(vec![
87                    "s3:GetObject".to_string(),
88                    "s3:ListBucket".to_string(),
89                ]))
90                .with_resource(IAMResource::Multiple(vec![
91                    "arn:aws:s3:::my-bucket".to_string(),
92                    "arn:aws:s3:::my-bucket/*".to_string(),
93                ])),
94        )
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_sid("AllowS3Write")
98                .with_action(IAMAction::Single("s3:PutObject".to_string()))
99                .with_resource(IAMResource::Single(
100                    "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101                )),
102        );
103
104    let policy_json = policy.to_json()?;
105    println!("Generated IAM Policy:");
106    println!("{}", policy_json);
107
108    // Example 4: Validate all ARNs in the policy
109    println!("\n4. Validating ARNs in policy:");
110
111    for (i, statement) in policy.statement.iter().enumerate() {
112        println!(
113            "Statement {}: {}",
114            i + 1,
115            statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116        );
117
118        let resources = match &statement.resource {
119            Some(IAMResource::Single(arn)) => vec![arn.clone()],
120            Some(IAMResource::Multiple(arns)) => arns.clone(),
121            None => vec![],
122        };
123
124        for resource in resources {
125            match Arn::parse(&resource) {
126                Ok(arn) => {
127                    let validity = if arn.is_valid() {
128                        "✓ Valid"
129                    } else {
130                        "⚠ Invalid"
131                    };
132                    println!("  {} Resource: {}", validity, resource);
133                }
134                Err(e) => println!("  ✗ Parse Error: {} ({})", resource, e),
135            }
136        }
137    }
138
139    Ok(())
140}
More examples
Hide additional examples
examples/validation_demo.rs (line 12)
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    println!("=== IAM Policy Validation Demo ===\n");
8
9    // Example 1: Valid Policy
10    println!("1. Valid Policy Validation:");
11    let valid_policy = IAMPolicy::new()
12        .with_id("550e8400-e29b-41d4-a716-446655440000")
13        .add_statement(
14            IAMStatement::new(IAMEffect::Allow)
15                .with_sid("AllowS3Read")
16                .with_action(IAMAction::Single("s3:GetObject".to_string()))
17                .with_resource(IAMResource::Single("arn:aws:s3:::my-bucket/*".to_string()))
18                .with_condition(
19                    IAMOperator::StringEquals,
20                    "aws:PrincipalTag/department".to_string(),
21                    ConditionValue::String("engineering".to_string()),
22                ),
23        );
24
25    if valid_policy.is_valid() {
26        println!("✓ Policy is valid!");
27    } else {
28        println!("✗ Policy is invalid");
29    }
30
31    match valid_policy.validate_result() {
32        Ok(()) => println!("✓ Policy passes validation"),
33        Err(e) => println!("✗ Policy fails validation: {}", e),
34    }
35
36    println!();
37
38    // Example 2: Invalid Policy - Missing Required Fields
39    println!("2. Invalid Policy - Missing Required Fields:");
40    let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(IAMEffect::Allow)); // Missing action and resource
41
42    if !invalid_policy.is_valid() {
43        println!("✗ Policy is invalid (as expected)");
44        match invalid_policy.validate_result() {
45            Err(e) => println!("   Validation errors: {}", e),
46            Ok(()) => println!("   Unexpected: validation passed"),
47        }
48    }
49
50    println!();
51
52    // Example 3: Policy with Multiple Validation Errors
53    println!("3. Policy with Multiple Validation Errors:");
54    let multi_error_policy = IAMPolicy::new()
55        .with_id("") // Empty ID
56        .add_statement(
57            IAMStatement::new(IAMEffect::Allow)
58                .with_action(IAMAction::Single("invalid-action".to_string())) // Invalid action format
59                .with_resource(IAMResource::Single("invalid-resource".to_string())), // Invalid resource
60        )
61        .add_statement(
62            IAMStatement::new(IAMEffect::Allow)
63                .with_sid("DuplicateId")
64                .with_action(IAMAction::Single("s3:GetObject".to_string()))
65                .with_resource(IAMResource::Single("*".to_string())),
66        )
67        .add_statement(
68            IAMStatement::new(IAMEffect::Deny)
69                .with_sid("DuplicateId") // Duplicate SID
70                .with_action(IAMAction::Single("s3:DeleteObject".to_string()))
71                .with_resource(IAMResource::Single("*".to_string())),
72        );
73
74    match multi_error_policy.validate_result() {
75        Err(ValidationError::Multiple(errors)) => {
76            println!("✗ Found {} validation errors:", errors.len());
77            for (i, error) in errors.iter().enumerate() {
78                println!("   {}. {}", i + 1, error);
79            }
80        }
81        Err(e) => {
82            println!("✗ Single validation error: {}", e);
83        }
84        Ok(()) => {
85            println!("✓ Unexpected: validation passed");
86        }
87    }
88
89    println!();
90
91    // Example 4: Comprehensive Validation
92    println!("4. Comprehensive Validation:");
93    let comprehensive_policy = IAMPolicy::new()
94        .with_id("short") // Short ID - will fail validation
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_action(IAMAction::Single("s3:GetObject".to_string()))
98                .with_resource(IAMResource::Single("*".to_string()))
99                .with_condition(
100                    IAMOperator::NumericEquals,
101                    "aws:RequestedRegion".to_string(),
102                    ConditionValue::String("not-a-number".to_string()), // Numeric operator with string value - will fail
103                ),
104        );
105
106    match comprehensive_policy.validate_result() {
107        Ok(()) => println!("✓ Policy passes validation"),
108        Err(e) => println!("✗ Policy fails validation: {}", e),
109    }
110
111    println!();
112
113    // Example 5: Logical Policy Errors
114    println!("5. Logical Policy Errors:");
115
116    // NotPrincipal with Allow effect (invalid)
117    let mut logical_error_policy = IAMStatement::new(IAMEffect::Allow);
118    logical_error_policy.action = Some(IAMAction::Single("s3:GetObject".to_string()));
119    logical_error_policy.resource = Some(IAMResource::Single("*".to_string()));
120    logical_error_policy.not_principal = Some(Principal::Aws(iam_rs::PrincipalId::String(
121        "arn:aws:iam::123456789012:user/test".to_string(),
122    )));
123
124    match logical_error_policy.validate_result() {
125        Err(e) => println!("✗ Logical error detected: {}", e),
126        Ok(()) => println!("✓ Unexpected: validation passed"),
127    }
128
129    // Both Action and NotAction (invalid)
130    let mut conflicting_statement = IAMStatement::new(IAMEffect::Allow);
131    conflicting_statement.action = Some(IAMAction::Single("s3:GetObject".to_string()));
132    conflicting_statement.not_action = Some(IAMAction::Single("s3:PutObject".to_string()));
133    conflicting_statement.resource = Some(IAMResource::Single("*".to_string()));
134
135    match conflicting_statement.validate_result() {
136        Err(e) => println!("✗ Logical error detected: {}", e),
137        Ok(()) => println!("✓ Unexpected: validation passed"),
138    }
139
140    println!();
141
142    // Example 6: Component Validation
143    println!("6. Individual Component Validation:");
144
145    // Invalid action
146    let invalid_action = IAMAction::Single("invalid-action".to_string());
147    match invalid_action.validate_result() {
148        Err(e) => println!("✗ Invalid action: {}", e),
149        Ok(()) => println!("✓ Action is valid"),
150    }
151
152    // Invalid principal
153    let invalid_principal = Principal::Aws(PrincipalId::String("invalid-principal".to_string()));
154    match invalid_principal.validate_result() {
155        Err(e) => println!("✗ Invalid principal: {}", e),
156        Ok(()) => println!("✓ Principal is valid"),
157    }
158
159    // Valid service principal
160    let service_principal =
161        Principal::Service(PrincipalId::String("lambda.amazonaws.com".to_string()));
162    if service_principal.is_valid() {
163        println!("✓ Service principal is valid");
164    } else {
165        println!("✗ Service principal is invalid");
166    }
167
168    println!("\n=== Validation Demo Complete ===");
169
170    Ok(())
171}
examples/evaluation_demo.rs (line 13)
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 from_json(json: &str) -> Result<Self, Error>

Parses an IAM policy from a JSON string

§Errors

Returns a JSON parsing error if the input string is not valid JSON or does not match the expected IAM policy structure.

Source

pub fn to_json(&self) -> Result<String, Error>

Serializes the IAM policy to a JSON string

§Errors

Returns a JSON serialization error if the policy cannot be converted to JSON.

Examples found in repository?
examples/arn_demo.rs (line 104)
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4    println!("=== IAM ARN Validator Demo ===\n");
5
6    // Example 1: Parse and validate ARNs
7    println!("1. Parsing and validating ARNs:");
8
9    let valid_arns = vec![
10        "arn:aws:s3:::my-bucket/folder/file.txt",
11        "arn:aws:iam::123456789012:user/alice",
12        "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13        "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14        "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15        "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16    ];
17
18    for arn_str in &valid_arns {
19        match Arn::parse(arn_str) {
20            Ok(arn) => {
21                println!("✓ Valid ARN: {}", arn);
22                println!("  - Service: {}", arn.service);
23                println!(
24                    "  - Region: {}",
25                    if arn.region.is_empty() {
26                        "global"
27                    } else {
28                        &arn.region
29                    }
30                );
31                println!(
32                    "  - Account: {}",
33                    if arn.account_id.is_empty() {
34                        "none"
35                    } else {
36                        &arn.account_id
37                    }
38                );
39                if let Some(resource_type) = arn.resource_type() {
40                    println!("  - Resource Type: {}", resource_type);
41                }
42                if let Some(resource_id) = arn.resource_id() {
43                    println!("  - Resource ID: {}", resource_id);
44                }
45                println!();
46            }
47            Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48        }
49    }
50
51    // Example 2: Wildcard matching
52    println!("2. Wildcard pattern matching:");
53
54    let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55    println!("Resource ARN: {}", resource_arn);
56
57    let patterns = vec![
58        "arn:aws:s3:::my-bucket/*",
59        "arn:aws:s3:::my-bucket/uploads/*",
60        "arn:aws:s3:::my-bucket/uploads/user123/*",
61        "arn:aws:s3:::*/uploads/user123/document.pdf",
62        "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63        "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64        "arn:aws:s3:::other-bucket/*",
65        "arn:aws:ec2:*:*:instance/*",
66    ];
67
68    for pattern in &patterns {
69        match resource_arn.matches(pattern) {
70            Ok(matches) => {
71                let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72                println!("  {} Pattern: {}", status, pattern);
73            }
74            Err(e) => println!("  ✗ ERROR Pattern: {} ({})", pattern, e),
75        }
76    }
77
78    // Example 3: Integration with IAM policies
79    println!("\n3. Using ARNs in IAM policies:");
80
81    let policy = IAMPolicy::new()
82        .with_id("s3-access-policy")
83        .add_statement(
84            IAMStatement::new(IAMEffect::Allow)
85                .with_sid("AllowS3Read")
86                .with_action(IAMAction::Multiple(vec![
87                    "s3:GetObject".to_string(),
88                    "s3:ListBucket".to_string(),
89                ]))
90                .with_resource(IAMResource::Multiple(vec![
91                    "arn:aws:s3:::my-bucket".to_string(),
92                    "arn:aws:s3:::my-bucket/*".to_string(),
93                ])),
94        )
95        .add_statement(
96            IAMStatement::new(IAMEffect::Allow)
97                .with_sid("AllowS3Write")
98                .with_action(IAMAction::Single("s3:PutObject".to_string()))
99                .with_resource(IAMResource::Single(
100                    "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101                )),
102        );
103
104    let policy_json = policy.to_json()?;
105    println!("Generated IAM Policy:");
106    println!("{}", policy_json);
107
108    // Example 4: Validate all ARNs in the policy
109    println!("\n4. Validating ARNs in policy:");
110
111    for (i, statement) in policy.statement.iter().enumerate() {
112        println!(
113            "Statement {}: {}",
114            i + 1,
115            statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116        );
117
118        let resources = match &statement.resource {
119            Some(IAMResource::Single(arn)) => vec![arn.clone()],
120            Some(IAMResource::Multiple(arns)) => arns.clone(),
121            None => vec![],
122        };
123
124        for resource in resources {
125            match Arn::parse(&resource) {
126                Ok(arn) => {
127                    let validity = if arn.is_valid() {
128                        "✓ Valid"
129                    } else {
130                        "⚠ Invalid"
131                    };
132                    println!("  {} Resource: {}", validity, resource);
133                }
134                Err(e) => println!("  ✗ Parse Error: {} ({})", resource, e),
135            }
136        }
137    }
138
139    Ok(())
140}

Trait Implementations§

Source§

impl Clone for IAMPolicy

Source§

fn clone(&self) -> IAMPolicy

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 IAMPolicy

Source§

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

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

impl Default for IAMPolicy

Source§

fn default() -> Self

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

impl<'de> Deserialize<'de> for IAMPolicy

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for IAMPolicy

Source§

fn eq(&self, other: &IAMPolicy) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for IAMPolicy

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Validate for IAMPolicy

Source§

fn validate(&self, context: &mut ValidationContext) -> ValidationResult

Validate the component within the given context Read more
Source§

fn is_valid(&self) -> bool

Convenience method for basic validation
Source§

fn validate_result(&self) -> ValidationResult

Validate with detailed errors (same as regular validation) Read more
Source§

impl Eq for IAMPolicy

Source§

impl StructuralPartialEq for IAMPolicy

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.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,