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