pub struct Arn {
pub partition: String,
pub service: String,
pub region: String,
pub account_id: String,
pub resource: String,
}
Expand description
Represents an Amazon Resource Name (ARN)
ARNs uniquely identify AWS resources. The general format is:
arn:partition:service:region:account-id:resource-type/resource-id
Some services use slightly different formats:
arn:partition:service:region:account-id:resource-type:resource-id
arn:partition:service:region:account-id:resource-type/resource-id/sub-resource
Fields§
§partition: String
The partition (e.g., “aws”, “aws-cn”, “aws-us-gov”)
service: String
The service namespace (e.g., “s3”, “ec2”, “iam”)
region: String
The region (e.g., “us-east-1”, can be empty for global services)
account_id: String
The account ID (12-digit number, can be empty for some services)
resource: String
The resource specification (format varies by service)
Implementations§
Source§impl Arn
impl Arn
Sourcepub fn parse(arn_str: &str) -> Result<Self, ArnError>
pub fn parse(arn_str: &str) -> Result<Self, ArnError>
Parse an ARN string into an Arn struct
This method is extremely lenient and only validates bare format requirements.
Use is_valid()
to perform comprehensive validation.
§Errors
Returns ArnError::InvalidPrefix
if the string doesn’t start with “arn:”
Returns ArnError::InvalidFormat
if the ARN has incorrect number of components
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
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 matches(&self, pattern: &str) -> Result<bool, ArnError>
pub fn matches(&self, pattern: &str) -> Result<bool, ArnError>
Check if this ARN matches another ARN or pattern Supports wildcards (* and ?) in any component except service
§Errors
Returns ArnError
if the pattern is not a valid ARN format
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}
Sourcepub fn wildcard_match(text: &str, pattern: &str) -> bool
pub fn wildcard_match(text: &str, pattern: &str) -> bool
Check if a string matches a pattern with wildcards
- matches any sequence of characters ? matches any single character
Sourcepub fn is_valid(&self) -> bool
pub fn is_valid(&self) -> bool
Check if this ARN is valid according to AWS ARN rules
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}
Sourcepub fn resource_type(&self) -> Option<&str>
pub fn resource_type(&self) -> Option<&str>
Get the resource type from the resource string For resources like “bucket/object”, returns “bucket” For resources like “user/username”, returns “user”
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}
Sourcepub fn resource_id(&self) -> Option<&str>
pub fn resource_id(&self) -> Option<&str>
Get the resource ID from the resource string For resources like “bucket/object”, returns “object” For resources like “user/username”, returns “username”
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}