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

pub fn from_json(json: &str) -> Result<Self, Error>

Parses an IAM policy from a JSON string

Source

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

Serializes the IAM policy to a JSON string

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(Effect::Allow)
85                .with_sid("AllowS3Read")
86                .with_action(Action::Multiple(vec![
87                    "s3:GetObject".to_string(),
88                    "s3:ListBucket".to_string(),
89                ]))
90                .with_resource(Resource::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(Effect::Allow)
97                .with_sid("AllowS3Write")
98                .with_action(Action::Single("s3:PutObject".to_string()))
99                .with_resource(Resource::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(Resource::Single(arn)) => vec![arn.clone()],
120            Some(Resource::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

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)
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>,