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: IAMVersionThe 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
impl IAMPolicy
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new IAM policy with the default version
Examples found in repository?
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
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}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}Sourcepub fn with_version(version: IAMVersion) -> Self
pub fn with_version(version: IAMVersion) -> Self
Creates a new IAM policy with a specific version
Sourcepub fn add_statement(self, statement: IAMStatement) -> Self
pub fn add_statement(self, statement: IAMStatement) -> Self
Adds a statement to the policy
Examples found in repository?
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
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}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}Sourcepub fn with_id<S: Into<String>>(self, id: S) -> Self
pub fn with_id<S: Into<String>>(self, id: S) -> Self
Sets the policy ID
Examples found in repository?
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
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}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}Sourcepub fn to_json(&self) -> Result<String, Error>
pub fn to_json(&self) -> Result<String, Error>
Serializes the IAM policy to a JSON string
Examples found in repository?
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}