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