pub struct IAMStatement {
pub sid: Option<String>,
pub effect: Effect,
pub principal: Option<Principal>,
pub not_principal: Option<Principal>,
pub action: Option<Action>,
pub not_action: Option<Action>,
pub resource: Option<Resource>,
pub not_resource: Option<Resource>,
pub condition: Option<ConditionBlock>,
}
Expand description
Represents a single statement in an IAM policy
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_statement.html
Fields§
§sid: Option<String>
Optional statement ID
You can provide a Sid
(statement ID) as an optional identifier for the policy statement.
You can assign a Sid
value to each statement in a statement array.
You can use the Sid
value as a description for the policy statement.
In services that let you specify an ID element, such as AWS SQS and AWS SNS, the Sid
value is just a sub-ID of the policy document ID.
In IAM, the Sid
value must be unique within a JSON policy.
The Sid element supports ASCII uppercase letters (A-Z), lowercase letters (a-z), and numbers (0-9).
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_sid.html
effect: Effect
The effect of the statement (Allow or Deny)
The Effect
element is required and specifies whether the statement results in an allow or an explicit deny.
Valid values for Effect are Allow and Deny.
The Effect value is case sensitive.
By default, access to resources is denied. To allow access to a resource, you must set the Effect element to Allow. To override an allow (for example, to override an allow that is otherwise in force), you set the Effect element to Deny.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_effect.html
principal: Option<Principal>
Optional principal(s) - who the statement applies to
Use the Principal
element in a resource-based JSON policy to specify the principal that is allowed or denied access to a resource.
You must use the Principal
element in resource-based policies.
You cannot use the Principal
element in an identity-based policy.
Identity-based policies are permissions policies that you attach to IAM identities (users, groups, or roles). In those cases, the principal is implicitly the identity where the policy is attached.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html
not_principal: Option<Principal>
Optional not principal(s) - who the statement does not apply to
The NotPrincipal
element uses “Effect”:“Deny” to deny access to all principals except the principal specified in the NotPrincipal
element.
A principal can usually be a user, federated user, role, assumed role, account, service, or other principal type.
NotPrincipal
must be used with "Effect":"Deny"
. Using it with "Effect":"Allow"
is not supported.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notprincipal.html
action: Option<Action>
Optional action(s) - what actions are allowed/denied
The Action
element describes the specific action or actions that will be allowed or denied.
Statements must include either an Action
or NotAction
element.
Each service has its own set of actions that describe tasks that you can perform with that service.
For example:
- the list of actions for Amazon S3 can be found at Specifying Permissions in a Policy in the Amazon Simple Storage Service User Guide
- the list of actions for Amazon EC2 can be found in the Amazon EC2 API Reference
- the list of actions for AWS Identity and Access Management can be found in the IAM API Reference
To find the list of actions for other AWS services, consult the API reference documentation for the service. For non-AWS services, consult the service documentation for the actions that are supported by that service.
You specify a value using a service namespace as an action prefix (iam
, ec2
, sqs
, sns
, s3
, etc.) followed by the name of the action to allow or deny.
The name must match an action that is supported by the service.
The prefix and the action name are case insensitive.
For example, iam:ListAccessKeys
is the same as IAM:listaccesskeys
.
The following examples show Action elements for different services:
Action: "sqs:SendMessage"
- allows theSendMessage
action on SQS.Action: "ec2:StartInstances"
- allows theStartInstances
action on EC2.Action: "iam:ChangePassword"
- allows theChangePassword
action on IAM.Action: "s3:GetObject"
- allows theGetObject
action on S3.
You can specify multiple values for the Action element:
Action: [ "sqs:SendMessage", "sqs:ReceiveMessage", "ec2:StartInstances", "iam:ChangePassword", "s3:GetObject" ]
You can use wildcards to match multiple actions:
Action: "s3:*"
- allows all actions on S3.
You can also use wildcards (*
or ?
) as part of the action name. For example, the following Action element applies to all IAM actions that include the string AccessKey
, including CreateAccessKey
, DeleteAccessKey
, ListAccessKeys
, and UpdateAccessKey
:
"Action": "iam:*AccessKey*"
Some services let you limit the actions that are available.
For example, Amazon SQS lets you make available just a subset of all the possible Amazon SQS actions.
In that case, the *
wildcard doesn’t allow complete control of the queue; it allows only the subset of actions that you’ve shared.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
not_action: Option<Action>
Optional not action(s) - what actions are not covered
NotAction
is an advanced policy element that explicitly matches everything except the specified list of actions.
Using NotAction
can result in a shorter policy by listing only a few actions that should not match, rather than including a long list of actions that will match.
Actions specified in NotAction
are not impacted by the Allow or Deny effect in a policy statement.
This, in turn, means that all of the applicable actions or services that are not listed are allowed if you use the Allow effect.
In addition, such unlisted actions or services are denied if you use the Deny effect.
When you use NotAction
with the Resource element, you provide scope for the policy.
This is how AWS determines which actions or services are applicable.
For more information, see the following example policy.
§NotAction
with Allow
You can use the NotAction
element in a statement with "Effect": "Allow"
to provide access to all of the actions in an AWS service, except for the actions specified in NotAction
.
You can use it with the Resource element to provide scope for the policy, limiting the allowed actions to the actions that can be performed on the specified resource.
Example: Allow all S3 actions except deleting a bucket:
"Effect": "Allow",
"NotAction": "s3:DeleteBucket",
"Resource": "arn:aws:s3:::*"
Example: Allow all actions except IAM:
"Effect": "Allow",
"NotAction": "iam:*",
"Resource": "*"
Be careful using NotAction
with "Effect": "Allow"
as it could grant more permissions than intended.
§NotAction
with Deny
You can use the NotAction
element in a statement with "Effect": "Deny"
to deny access to all of the listed resources except for the actions specified in NotAction
.
This combination does not allow the listed items, but instead explicitly denies the actions not listed.
Example: Deny all actions except IAM actions if not using MFA:
{
"Sid": "DenyAllUsersNotUsingMFA",
"Effect": "Deny",
"NotAction": "iam:*",
"Resource": "*",
"Condition": {"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}}
}
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notaction.html
resource: Option<Resource>
Optional resource(s) - what resources the statement applies to
The Resource
element specifies the object(s) that the statement applies to.
You must include either a Resource
or a NotResource
element in a statement.
You specify a resource using an Amazon Resource Name (ARN). The ARN format depends on the AWS service and the specific resource. For more information about ARNs, see: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns
Some AWS services do not support resource-level permissions. In those cases, use the wildcard character (*
) in the Resource element.
Examples:
- Specific SQS queue:
"Resource": "arn:aws:sqs:us-east-2:account-ID-without-hyphens:queue1"
- Specific IAM user (user name is case sensitive):
"Resource": "arn:aws:iam::account-ID-without-hyphens:user/Bob"
§Using wildcards in resource ARNs
You can use wildcard characters (*
and ?
) within the individual segments of an ARN (the parts separated by colons) to represent:
- Any combination of characters (
*
) - Any single character (
?
)
You can use multiple *
or ?
characters in each segment.
If the *
wildcard is the last character of a resource ARN segment, it can expand to match beyond the colon boundaries.
It is recommended to use wildcards within ARN segments separated by a colon.
Note: You can’t use a wildcard in the service segment that identifies the AWS product.
§Examples
All IAM users whose path is /accounting
:
"Resource": "arn:aws:iam::account-ID-without-hyphens:user/accounting/*"
All items within a specific Amazon S3 bucket:
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
Wildcards can match across slashes and other characters:
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*/test/*"
This matches:
- amzn-s3-demo-bucket/1/test/object.jpg
- amzn-s3-demo-bucket/1/2/test/object.jpg
- amzn-s3-demo-bucket/1/2/test/3/object.jpg
- amzn-s3-demo-bucket/1/2/3/test/4/object.jpg
- amzn-s3-demo-bucket/1///test///object.jpg
- amzn-s3-demo-bucket/1/test/.jpg
- amzn-s3-demo-bucket//test/object.jpg
- amzn-s3-demo-bucket/1/test/
But does not match:
- amzn-s3-demo-bucket/1-test/object.jpg
- amzn-s3-demo-bucket/test/object.jpg
- amzn-s3-demo-bucket/1/2/test.jpg
§Specifying multiple resources
You can specify multiple resources in the Resource
element by using an array of ARNs:
"Resource": [
"arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/books_table",
"arn:aws:dynamodb:us-east-2:account-ID-without-hyphens:table/magazines_table"
]
§Using policy variables in resource ARNs
You can use JSON policy variables in the part of the ARN that identifies the specific resource. For example:
"Resource": "arn:aws:dynamodb:us-east-2:account-id:table/${aws:username}"
This allows access to a DynamoDB
table that matches the current user’s name.
For more information about JSON policy variables, see IAM policy elements: Variables and tags.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html
not_resource: Option<Resource>
Optional not resource(s) - what resources are not covered
NotResource
is an advanced policy element that explicitly matches every resource except those specified.
Using NotResource
can result in a shorter policy by listing only a few resources that should not match, rather than including a long list of resources that will match.
This is particularly useful for policies that apply within a single AWS service.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notresource.html
condition: Option<ConditionBlock>
Optional conditions for the statement
The Condition
element (or Condition block) lets you specify conditions for when a policy is in effect. The Condition element is optional.
In the Condition element, you build expressions in which you use condition operators (equal, less than, and others) to match the context keys and values in the policy against keys and values in the request context. To learn more about the request context, see Components of a request.
"Condition" : { "{condition-operator}" : { "{condition-key}" : "{condition-value}" }}
The context key that you specify in a policy condition can be a global condition context key or a service-specific context key.
-
Global condition context keys have the aws: prefix.
-
Service-specific context keys have the service’s prefix.
For example, Amazon EC2 lets you write a condition using the ec2:InstanceType context key, which is unique to that service.
Context key names are not case-sensitive.
For example, including the aws:SourceIP context key is equivalent to testing for AWS:SourceIp
.
Case-sensitivity of context key values depends on the condition operator that you use.
For example, the following condition includes the StringEquals
operator to make sure that only requests made by john match.
Users named John are denied access.
"Condition" : { "StringEquals" : { "aws:username" : "john" }}
The following condition uses the StringEqualsIgnoreCase
operator to match users named john or John.
"Condition" : { "StringEqualsIgnoreCase" : { "aws:username" : "john" }}
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html
Implementations§
Source§impl IAMStatement
impl IAMStatement
Sourcepub fn new(effect: Effect) -> Self
pub fn new(effect: Effect) -> Self
Creates a new IAM statement with the specified effect
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 println!("=== IAM ARN Validator Demo ===\n");
5
6 // Example 1: Parse and validate ARNs
7 println!("1. Parsing and validating ARNs:");
8
9 let valid_arns = vec![
10 "arn:aws:s3:::my-bucket/folder/file.txt",
11 "arn:aws:iam::123456789012:user/alice",
12 "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13 "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14 "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15 "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16 ];
17
18 for arn_str in &valid_arns {
19 match Arn::parse(arn_str) {
20 Ok(arn) => {
21 println!("✓ Valid ARN: {}", arn);
22 println!(" - Service: {}", arn.service);
23 println!(
24 " - Region: {}",
25 if arn.region.is_empty() {
26 "global"
27 } else {
28 &arn.region
29 }
30 );
31 println!(
32 " - Account: {}",
33 if arn.account_id.is_empty() {
34 "none"
35 } else {
36 &arn.account_id
37 }
38 );
39 if let Some(resource_type) = arn.resource_type() {
40 println!(" - Resource Type: {}", resource_type);
41 }
42 if let Some(resource_id) = arn.resource_id() {
43 println!(" - Resource ID: {}", resource_id);
44 }
45 println!();
46 }
47 Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48 }
49 }
50
51 // Example 2: Wildcard matching
52 println!("2. Wildcard pattern matching:");
53
54 let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55 println!("Resource ARN: {}", resource_arn);
56
57 let patterns = vec![
58 "arn:aws:s3:::my-bucket/*",
59 "arn:aws:s3:::my-bucket/uploads/*",
60 "arn:aws:s3:::my-bucket/uploads/user123/*",
61 "arn:aws:s3:::*/uploads/user123/document.pdf",
62 "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63 "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64 "arn:aws:s3:::other-bucket/*",
65 "arn:aws:ec2:*:*:instance/*",
66 ];
67
68 for pattern in &patterns {
69 match resource_arn.matches(pattern) {
70 Ok(matches) => {
71 let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72 println!(" {} Pattern: {}", status, pattern);
73 }
74 Err(e) => println!(" ✗ ERROR Pattern: {} ({})", pattern, e),
75 }
76 }
77
78 // Example 3: Integration with IAM policies
79 println!("\n3. Using ARNs in IAM policies:");
80
81 let policy = IAMPolicy::new()
82 .with_id("s3-access-policy")
83 .add_statement(
84 IAMStatement::new(Effect::Allow)
85 .with_sid("AllowS3Read")
86 .with_action(Action::Multiple(vec![
87 "s3:GetObject".to_string(),
88 "s3:ListBucket".to_string(),
89 ]))
90 .with_resource(Resource::Multiple(vec![
91 "arn:aws:s3:::my-bucket".to_string(),
92 "arn:aws:s3:::my-bucket/*".to_string(),
93 ])),
94 )
95 .add_statement(
96 IAMStatement::new(Effect::Allow)
97 .with_sid("AllowS3Write")
98 .with_action(Action::Single("s3:PutObject".to_string()))
99 .with_resource(Resource::Single(
100 "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101 )),
102 );
103
104 let policy_json = policy.to_json()?;
105 println!("Generated IAM Policy:");
106 println!("{}", policy_json);
107
108 // Example 4: Validate all ARNs in the policy
109 println!("\n4. Validating ARNs in policy:");
110
111 for (i, statement) in policy.statement.iter().enumerate() {
112 println!(
113 "Statement {}: {}",
114 i + 1,
115 statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116 );
117
118 let resources = match &statement.resource {
119 Some(Resource::Single(arn)) => vec![arn.clone()],
120 Some(Resource::Multiple(arns)) => arns.clone(),
121 None => vec![],
122 };
123
124 for resource in resources {
125 match Arn::parse(&resource) {
126 Ok(arn) => {
127 let validity = if arn.is_valid() {
128 "✓ Valid"
129 } else {
130 "⚠ Invalid"
131 };
132 println!(" {} Resource: {}", validity, resource);
133 }
134 Err(e) => println!(" ✗ Parse Error: {} ({})", resource, e),
135 }
136 }
137 }
138
139 Ok(())
140}
More examples
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 // Example 1: Valid Policy
11 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 // Example 2: Invalid Policy - Missing Required Fields
40 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); // Missing action and resource
42
43 if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 // Example 3: Policy with Multiple Validation Errors
54 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") // Empty ID
57 .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) // Invalid action format
60 .with_resource(Resource::Single("invalid-resource".to_string())), // Invalid resource
61 )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") // Duplicate SID
71 .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 // Example 4: Comprehensive Validation
93 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") // Short ID - will fail validation
96 .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), // Numeric operator with string value - will fail
104 ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 // Example 5: Logical Policy Errors
115 println!("5. Logical Policy Errors:");
116
117 // NotPrincipal with Allow effect (invalid)
118 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 // Both Action and NotAction (invalid)
134 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 // Example 6: Component Validation
147 println!("6. Individual Component Validation:");
148
149 // Invalid action
150 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 // Invalid principal
157 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 // Valid service principal
166 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10 // Example 1: Simple Allow Policy
11 println!("1. Simple Allow Policy:");
12 let allow_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19 );
20
21 let request = IAMRequest::new(
22 "arn:aws:iam::123456789012:user/alice",
23 "s3:GetObject",
24 "arn:aws:s3:::my-bucket/file.txt",
25 );
26
27 match evaluate_policy(&allow_policy, &request)? {
28 Decision::Allow => println!("✓ Access ALLOWED"),
29 Decision::Deny => println!("✗ Access DENIED"),
30 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
31 }
32 println!();
33
34 // Example 2: Simple Deny Policy
35 println!("2. Simple Deny Policy:");
36 let deny_policy = IAMPolicy::new()
37 .with_id("550e8400-e29b-41d4-a716-446655440001")
38 .add_statement(
39 IAMStatement::new(Effect::Deny)
40 .with_sid("DenyS3Delete")
41 .with_action(Action::Single("s3:DeleteObject".to_string()))
42 .with_resource(Resource::Single(
43 "arn:aws:s3:::protected-bucket/*".to_string(),
44 )),
45 );
46
47 let delete_request = IAMRequest::new(
48 "arn:aws:iam::123456789012:user/alice",
49 "s3:DeleteObject",
50 "arn:aws:s3:::protected-bucket/important.txt",
51 );
52
53 match evaluate_policy(&deny_policy, &delete_request)? {
54 Decision::Allow => println!("✓ Access ALLOWED"),
55 Decision::Deny => println!("✗ Access DENIED"),
56 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
57 }
58 println!();
59
60 // Example 3: Wildcard Action Matching
61 println!("3. Wildcard Action Matching:");
62 let wildcard_policy = IAMPolicy::new()
63 .with_id("550e8400-e29b-41d4-a716-446655440002")
64 .add_statement(
65 IAMStatement::new(Effect::Allow)
66 .with_sid("AllowAllS3")
67 .with_action(Action::Single("s3:*".to_string()))
68 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
69 );
70
71 let wildcard_request = IAMRequest::new(
72 "arn:aws:iam::123456789012:user/alice",
73 "s3:PutObject",
74 "arn:aws:s3:::my-bucket/new-file.txt",
75 );
76
77 match evaluate_policy(&wildcard_policy, &wildcard_request)? {
78 Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
79 Decision::Deny => println!("✗ Access DENIED"),
80 Decision::NotApplicable => println!("? No applicable policy"),
81 }
82 println!();
83
84 // Example 4: Condition-Based Policy
85 println!("4. Condition-Based Policy:");
86 let mut context = Context::new();
87 context.insert(
88 "aws:userid".to_string(),
89 ContextValue::String("alice".to_string()),
90 );
91 context.insert(
92 "aws:CurrentTime".to_string(),
93 ContextValue::String("2024-01-15T10:00:00Z".to_string()),
94 );
95
96 let condition_policy = IAMPolicy::new()
97 .with_id("550e8400-e29b-41d4-a716-446655440003")
98 .add_statement(
99 IAMStatement::new(Effect::Allow)
100 .with_sid("AllowWithCondition")
101 .with_action(Action::Single("s3:GetObject".to_string()))
102 .with_resource(Resource::Single(
103 "arn:aws:s3:::private-bucket/*".to_string(),
104 ))
105 .with_condition(
106 Operator::StringEquals,
107 "aws:userid".to_string(),
108 json!("alice"),
109 ),
110 );
111
112 let condition_request = IAMRequest::new_with_context(
113 "arn:aws:iam::123456789012:user/alice",
114 "s3:GetObject",
115 "arn:aws:s3:::private-bucket/personal.txt",
116 context,
117 );
118
119 match evaluate_policy(&condition_policy, &condition_request)? {
120 Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
121 Decision::Deny => println!("✗ Access DENIED"),
122 Decision::NotApplicable => println!("? Condition not satisfied"),
123 }
124 println!();
125
126 // Example 5: Failed Condition
127 println!("5. Failed Condition:");
128 let mut wrong_context = Context::new();
129 wrong_context.insert(
130 "aws:userid".to_string(),
131 ContextValue::String("bob".to_string()),
132 );
133
134 let failed_condition_request = IAMRequest::new_with_context(
135 "arn:aws:iam::123456789012:user/bob",
136 "s3:GetObject",
137 "arn:aws:s3:::private-bucket/personal.txt",
138 wrong_context,
139 );
140
141 match evaluate_policy(&condition_policy, &failed_condition_request)? {
142 Decision::Allow => println!("✓ Access ALLOWED"),
143 Decision::Deny => println!("✗ Access DENIED"),
144 Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
145 }
146 println!();
147
148 // Example 6: Explicit Deny Overrides Allow
149 println!("6. Explicit Deny Overrides Allow:");
150 let combined_policies = vec![
151 IAMPolicy::new()
152 .with_id("550e8400-e29b-41d4-a716-446655440004")
153 .add_statement(
154 IAMStatement::new(Effect::Allow)
155 .with_sid("AllowAll")
156 .with_action(Action::Single("s3:*".to_string()))
157 .with_resource(Resource::Single("*".to_string())),
158 ),
159 IAMPolicy::new()
160 .with_id("550e8400-e29b-41d4-a716-446655440005")
161 .add_statement(
162 IAMStatement::new(Effect::Deny)
163 .with_sid("DenyProtected")
164 .with_action(Action::Single("s3:DeleteObject".to_string()))
165 .with_resource(Resource::Single(
166 "arn:aws:s3:::protected-bucket/*".to_string(),
167 )),
168 ),
169 ];
170
171 let evaluator = PolicyEvaluator::with_policies(combined_policies);
172 let protected_request = IAMRequest::new(
173 "arn:aws:iam::123456789012:user/alice",
174 "s3:DeleteObject",
175 "arn:aws:s3:::protected-bucket/critical.txt",
176 );
177
178 match evaluator.evaluate(&protected_request)?.decision {
179 Decision::Allow => println!("✓ Access ALLOWED"),
180 Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
181 Decision::NotApplicable => println!("? No applicable policy"),
182 }
183 println!();
184
185 // Example 7: Numeric Condition
186 println!("7. Numeric Condition:");
187 let mut numeric_context = Context::new();
188 numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
189
190 let numeric_policy = IAMPolicy::new()
191 .with_id("550e8400-e29b-41d4-a716-446655440006")
192 .add_statement(
193 IAMStatement::new(Effect::Allow)
194 .with_sid("AllowLimitedRequests")
195 .with_action(Action::Single("s3:GetObject".to_string()))
196 .with_resource(Resource::Single("*".to_string()))
197 .with_condition(
198 Operator::NumericLessThan,
199 "aws:RequestCount".to_string(),
200 json!(10),
201 ),
202 );
203
204 let numeric_request = IAMRequest::new_with_context(
205 "arn:aws:iam::123456789012:user/alice",
206 "s3:GetObject",
207 "arn:aws:s3:::any-bucket/file.txt",
208 numeric_context,
209 );
210
211 match evaluate_policy(&numeric_policy, &numeric_request)? {
212 Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
213 Decision::Deny => println!("✗ Access DENIED"),
214 Decision::NotApplicable => println!("? Numeric condition failed"),
215 }
216 println!();
217
218 // Example 8: Detailed Evaluation with Options
219 println!("8. Detailed Evaluation with Options:");
220 let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
221 .with_options(EvaluationOptions {
222 collect_match_details: true,
223 stop_on_explicit_deny: false,
224 max_statements: 100,
225 });
226
227 let detailed_result = detailed_evaluator.evaluate(&request)?;
228 println!("Decision: {:?}", detailed_result.decision);
229 println!("Matched Statements:");
230 for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
231 println!(
232 " {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
233 i + 1,
234 statement_match.sid,
235 statement_match.effect,
236 statement_match.conditions_satisfied,
237 statement_match.reason
238 );
239 }
240 println!();
241
242 // Example 9: No Applicable Policy (Implicit Deny)
243 println!("9. No Applicable Policy (Implicit Deny):");
244 let unrelated_request = IAMRequest::new(
245 "arn:aws:iam::123456789012:user/alice",
246 "ec2:DescribeInstances",
247 "arn:aws:ec2:us-east-1:123456789012:instance/*",
248 );
249
250 match evaluate_policy(&allow_policy, &unrelated_request)? {
251 Decision::Allow => println!("✓ Access ALLOWED"),
252 Decision::Deny => println!("✗ Access DENIED"),
253 Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
254 }
255 println!();
256
257 // Example 10: Resource Pattern Matching
258 println!("10. Resource Pattern Matching:");
259 let pattern_policy = IAMPolicy::new()
260 .with_id("550e8400-e29b-41d4-a716-446655440007")
261 .add_statement(
262 IAMStatement::new(Effect::Allow)
263 .with_sid("AllowBucketAccess")
264 .with_action(Action::Multiple(vec![
265 "s3:GetObject".to_string(),
266 "s3:PutObject".to_string(),
267 ]))
268 .with_resource(Resource::Single("arn:aws:s3:::user-data-*/*".to_string())),
269 );
270
271 let pattern_request = IAMRequest::new(
272 "arn:aws:iam::123456789012:user/alice",
273 "s3:GetObject",
274 "arn:aws:s3:::user-data-alice/profile.json",
275 );
276
277 match evaluate_policy(&pattern_policy, &pattern_request)? {
278 Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
279 Decision::Deny => println!("✗ Access DENIED"),
280 Decision::NotApplicable => println!("? Resource pattern didn't match"),
281 }
282
283 println!("\n=== Policy Evaluation Demo Complete ===");
284 println!("\nThe Policy Evaluation Engine successfully:");
285 println!("• ✅ Evaluates Allow/Deny effects");
286 println!("• ✅ Handles wildcard actions and resources");
287 println!("• ✅ Processes condition blocks with various operators");
288 println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
289 println!("• ✅ Supports detailed evaluation with match information");
290 println!("• ✅ Handles multiple policies with complex interactions");
291 println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
292
293 Ok(())
294}
Sourcepub fn with_sid<S: Into<String>>(self, sid: S) -> Self
pub fn with_sid<S: Into<String>>(self, sid: S) -> Self
Sets the statement ID
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 println!("=== IAM ARN Validator Demo ===\n");
5
6 // Example 1: Parse and validate ARNs
7 println!("1. Parsing and validating ARNs:");
8
9 let valid_arns = vec![
10 "arn:aws:s3:::my-bucket/folder/file.txt",
11 "arn:aws:iam::123456789012:user/alice",
12 "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13 "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14 "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15 "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16 ];
17
18 for arn_str in &valid_arns {
19 match Arn::parse(arn_str) {
20 Ok(arn) => {
21 println!("✓ Valid ARN: {}", arn);
22 println!(" - Service: {}", arn.service);
23 println!(
24 " - Region: {}",
25 if arn.region.is_empty() {
26 "global"
27 } else {
28 &arn.region
29 }
30 );
31 println!(
32 " - Account: {}",
33 if arn.account_id.is_empty() {
34 "none"
35 } else {
36 &arn.account_id
37 }
38 );
39 if let Some(resource_type) = arn.resource_type() {
40 println!(" - Resource Type: {}", resource_type);
41 }
42 if let Some(resource_id) = arn.resource_id() {
43 println!(" - Resource ID: {}", resource_id);
44 }
45 println!();
46 }
47 Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48 }
49 }
50
51 // Example 2: Wildcard matching
52 println!("2. Wildcard pattern matching:");
53
54 let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55 println!("Resource ARN: {}", resource_arn);
56
57 let patterns = vec![
58 "arn:aws:s3:::my-bucket/*",
59 "arn:aws:s3:::my-bucket/uploads/*",
60 "arn:aws:s3:::my-bucket/uploads/user123/*",
61 "arn:aws:s3:::*/uploads/user123/document.pdf",
62 "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63 "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64 "arn:aws:s3:::other-bucket/*",
65 "arn:aws:ec2:*:*:instance/*",
66 ];
67
68 for pattern in &patterns {
69 match resource_arn.matches(pattern) {
70 Ok(matches) => {
71 let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72 println!(" {} Pattern: {}", status, pattern);
73 }
74 Err(e) => println!(" ✗ ERROR Pattern: {} ({})", pattern, e),
75 }
76 }
77
78 // Example 3: Integration with IAM policies
79 println!("\n3. Using ARNs in IAM policies:");
80
81 let policy = IAMPolicy::new()
82 .with_id("s3-access-policy")
83 .add_statement(
84 IAMStatement::new(Effect::Allow)
85 .with_sid("AllowS3Read")
86 .with_action(Action::Multiple(vec![
87 "s3:GetObject".to_string(),
88 "s3:ListBucket".to_string(),
89 ]))
90 .with_resource(Resource::Multiple(vec![
91 "arn:aws:s3:::my-bucket".to_string(),
92 "arn:aws:s3:::my-bucket/*".to_string(),
93 ])),
94 )
95 .add_statement(
96 IAMStatement::new(Effect::Allow)
97 .with_sid("AllowS3Write")
98 .with_action(Action::Single("s3:PutObject".to_string()))
99 .with_resource(Resource::Single(
100 "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101 )),
102 );
103
104 let policy_json = policy.to_json()?;
105 println!("Generated IAM Policy:");
106 println!("{}", policy_json);
107
108 // Example 4: Validate all ARNs in the policy
109 println!("\n4. Validating ARNs in policy:");
110
111 for (i, statement) in policy.statement.iter().enumerate() {
112 println!(
113 "Statement {}: {}",
114 i + 1,
115 statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116 );
117
118 let resources = match &statement.resource {
119 Some(Resource::Single(arn)) => vec![arn.clone()],
120 Some(Resource::Multiple(arns)) => arns.clone(),
121 None => vec![],
122 };
123
124 for resource in resources {
125 match Arn::parse(&resource) {
126 Ok(arn) => {
127 let validity = if arn.is_valid() {
128 "✓ Valid"
129 } else {
130 "⚠ Invalid"
131 };
132 println!(" {} Resource: {}", validity, resource);
133 }
134 Err(e) => println!(" ✗ Parse Error: {} ({})", resource, e),
135 }
136 }
137 }
138
139 Ok(())
140}
More examples
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 // Example 1: Valid Policy
11 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 // Example 2: Invalid Policy - Missing Required Fields
40 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); // Missing action and resource
42
43 if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 // Example 3: Policy with Multiple Validation Errors
54 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") // Empty ID
57 .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) // Invalid action format
60 .with_resource(Resource::Single("invalid-resource".to_string())), // Invalid resource
61 )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") // Duplicate SID
71 .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 // Example 4: Comprehensive Validation
93 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") // Short ID - will fail validation
96 .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), // Numeric operator with string value - will fail
104 ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 // Example 5: Logical Policy Errors
115 println!("5. Logical Policy Errors:");
116
117 // NotPrincipal with Allow effect (invalid)
118 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 // Both Action and NotAction (invalid)
134 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 // Example 6: Component Validation
147 println!("6. Individual Component Validation:");
148
149 // Invalid action
150 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 // Invalid principal
157 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 // Valid service principal
166 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10 // Example 1: Simple Allow Policy
11 println!("1. Simple Allow Policy:");
12 let allow_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19 );
20
21 let request = IAMRequest::new(
22 "arn:aws:iam::123456789012:user/alice",
23 "s3:GetObject",
24 "arn:aws:s3:::my-bucket/file.txt",
25 );
26
27 match evaluate_policy(&allow_policy, &request)? {
28 Decision::Allow => println!("✓ Access ALLOWED"),
29 Decision::Deny => println!("✗ Access DENIED"),
30 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
31 }
32 println!();
33
34 // Example 2: Simple Deny Policy
35 println!("2. Simple Deny Policy:");
36 let deny_policy = IAMPolicy::new()
37 .with_id("550e8400-e29b-41d4-a716-446655440001")
38 .add_statement(
39 IAMStatement::new(Effect::Deny)
40 .with_sid("DenyS3Delete")
41 .with_action(Action::Single("s3:DeleteObject".to_string()))
42 .with_resource(Resource::Single(
43 "arn:aws:s3:::protected-bucket/*".to_string(),
44 )),
45 );
46
47 let delete_request = IAMRequest::new(
48 "arn:aws:iam::123456789012:user/alice",
49 "s3:DeleteObject",
50 "arn:aws:s3:::protected-bucket/important.txt",
51 );
52
53 match evaluate_policy(&deny_policy, &delete_request)? {
54 Decision::Allow => println!("✓ Access ALLOWED"),
55 Decision::Deny => println!("✗ Access DENIED"),
56 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
57 }
58 println!();
59
60 // Example 3: Wildcard Action Matching
61 println!("3. Wildcard Action Matching:");
62 let wildcard_policy = IAMPolicy::new()
63 .with_id("550e8400-e29b-41d4-a716-446655440002")
64 .add_statement(
65 IAMStatement::new(Effect::Allow)
66 .with_sid("AllowAllS3")
67 .with_action(Action::Single("s3:*".to_string()))
68 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
69 );
70
71 let wildcard_request = IAMRequest::new(
72 "arn:aws:iam::123456789012:user/alice",
73 "s3:PutObject",
74 "arn:aws:s3:::my-bucket/new-file.txt",
75 );
76
77 match evaluate_policy(&wildcard_policy, &wildcard_request)? {
78 Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
79 Decision::Deny => println!("✗ Access DENIED"),
80 Decision::NotApplicable => println!("? No applicable policy"),
81 }
82 println!();
83
84 // Example 4: Condition-Based Policy
85 println!("4. Condition-Based Policy:");
86 let mut context = Context::new();
87 context.insert(
88 "aws:userid".to_string(),
89 ContextValue::String("alice".to_string()),
90 );
91 context.insert(
92 "aws:CurrentTime".to_string(),
93 ContextValue::String("2024-01-15T10:00:00Z".to_string()),
94 );
95
96 let condition_policy = IAMPolicy::new()
97 .with_id("550e8400-e29b-41d4-a716-446655440003")
98 .add_statement(
99 IAMStatement::new(Effect::Allow)
100 .with_sid("AllowWithCondition")
101 .with_action(Action::Single("s3:GetObject".to_string()))
102 .with_resource(Resource::Single(
103 "arn:aws:s3:::private-bucket/*".to_string(),
104 ))
105 .with_condition(
106 Operator::StringEquals,
107 "aws:userid".to_string(),
108 json!("alice"),
109 ),
110 );
111
112 let condition_request = IAMRequest::new_with_context(
113 "arn:aws:iam::123456789012:user/alice",
114 "s3:GetObject",
115 "arn:aws:s3:::private-bucket/personal.txt",
116 context,
117 );
118
119 match evaluate_policy(&condition_policy, &condition_request)? {
120 Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
121 Decision::Deny => println!("✗ Access DENIED"),
122 Decision::NotApplicable => println!("? Condition not satisfied"),
123 }
124 println!();
125
126 // Example 5: Failed Condition
127 println!("5. Failed Condition:");
128 let mut wrong_context = Context::new();
129 wrong_context.insert(
130 "aws:userid".to_string(),
131 ContextValue::String("bob".to_string()),
132 );
133
134 let failed_condition_request = IAMRequest::new_with_context(
135 "arn:aws:iam::123456789012:user/bob",
136 "s3:GetObject",
137 "arn:aws:s3:::private-bucket/personal.txt",
138 wrong_context,
139 );
140
141 match evaluate_policy(&condition_policy, &failed_condition_request)? {
142 Decision::Allow => println!("✓ Access ALLOWED"),
143 Decision::Deny => println!("✗ Access DENIED"),
144 Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
145 }
146 println!();
147
148 // Example 6: Explicit Deny Overrides Allow
149 println!("6. Explicit Deny Overrides Allow:");
150 let combined_policies = vec![
151 IAMPolicy::new()
152 .with_id("550e8400-e29b-41d4-a716-446655440004")
153 .add_statement(
154 IAMStatement::new(Effect::Allow)
155 .with_sid("AllowAll")
156 .with_action(Action::Single("s3:*".to_string()))
157 .with_resource(Resource::Single("*".to_string())),
158 ),
159 IAMPolicy::new()
160 .with_id("550e8400-e29b-41d4-a716-446655440005")
161 .add_statement(
162 IAMStatement::new(Effect::Deny)
163 .with_sid("DenyProtected")
164 .with_action(Action::Single("s3:DeleteObject".to_string()))
165 .with_resource(Resource::Single(
166 "arn:aws:s3:::protected-bucket/*".to_string(),
167 )),
168 ),
169 ];
170
171 let evaluator = PolicyEvaluator::with_policies(combined_policies);
172 let protected_request = IAMRequest::new(
173 "arn:aws:iam::123456789012:user/alice",
174 "s3:DeleteObject",
175 "arn:aws:s3:::protected-bucket/critical.txt",
176 );
177
178 match evaluator.evaluate(&protected_request)?.decision {
179 Decision::Allow => println!("✓ Access ALLOWED"),
180 Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
181 Decision::NotApplicable => println!("? No applicable policy"),
182 }
183 println!();
184
185 // Example 7: Numeric Condition
186 println!("7. Numeric Condition:");
187 let mut numeric_context = Context::new();
188 numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
189
190 let numeric_policy = IAMPolicy::new()
191 .with_id("550e8400-e29b-41d4-a716-446655440006")
192 .add_statement(
193 IAMStatement::new(Effect::Allow)
194 .with_sid("AllowLimitedRequests")
195 .with_action(Action::Single("s3:GetObject".to_string()))
196 .with_resource(Resource::Single("*".to_string()))
197 .with_condition(
198 Operator::NumericLessThan,
199 "aws:RequestCount".to_string(),
200 json!(10),
201 ),
202 );
203
204 let numeric_request = IAMRequest::new_with_context(
205 "arn:aws:iam::123456789012:user/alice",
206 "s3:GetObject",
207 "arn:aws:s3:::any-bucket/file.txt",
208 numeric_context,
209 );
210
211 match evaluate_policy(&numeric_policy, &numeric_request)? {
212 Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
213 Decision::Deny => println!("✗ Access DENIED"),
214 Decision::NotApplicable => println!("? Numeric condition failed"),
215 }
216 println!();
217
218 // Example 8: Detailed Evaluation with Options
219 println!("8. Detailed Evaluation with Options:");
220 let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
221 .with_options(EvaluationOptions {
222 collect_match_details: true,
223 stop_on_explicit_deny: false,
224 max_statements: 100,
225 });
226
227 let detailed_result = detailed_evaluator.evaluate(&request)?;
228 println!("Decision: {:?}", detailed_result.decision);
229 println!("Matched Statements:");
230 for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
231 println!(
232 " {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
233 i + 1,
234 statement_match.sid,
235 statement_match.effect,
236 statement_match.conditions_satisfied,
237 statement_match.reason
238 );
239 }
240 println!();
241
242 // Example 9: No Applicable Policy (Implicit Deny)
243 println!("9. No Applicable Policy (Implicit Deny):");
244 let unrelated_request = IAMRequest::new(
245 "arn:aws:iam::123456789012:user/alice",
246 "ec2:DescribeInstances",
247 "arn:aws:ec2:us-east-1:123456789012:instance/*",
248 );
249
250 match evaluate_policy(&allow_policy, &unrelated_request)? {
251 Decision::Allow => println!("✓ Access ALLOWED"),
252 Decision::Deny => println!("✗ Access DENIED"),
253 Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
254 }
255 println!();
256
257 // Example 10: Resource Pattern Matching
258 println!("10. Resource Pattern Matching:");
259 let pattern_policy = IAMPolicy::new()
260 .with_id("550e8400-e29b-41d4-a716-446655440007")
261 .add_statement(
262 IAMStatement::new(Effect::Allow)
263 .with_sid("AllowBucketAccess")
264 .with_action(Action::Multiple(vec![
265 "s3:GetObject".to_string(),
266 "s3:PutObject".to_string(),
267 ]))
268 .with_resource(Resource::Single("arn:aws:s3:::user-data-*/*".to_string())),
269 );
270
271 let pattern_request = IAMRequest::new(
272 "arn:aws:iam::123456789012:user/alice",
273 "s3:GetObject",
274 "arn:aws:s3:::user-data-alice/profile.json",
275 );
276
277 match evaluate_policy(&pattern_policy, &pattern_request)? {
278 Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
279 Decision::Deny => println!("✗ Access DENIED"),
280 Decision::NotApplicable => println!("? Resource pattern didn't match"),
281 }
282
283 println!("\n=== Policy Evaluation Demo Complete ===");
284 println!("\nThe Policy Evaluation Engine successfully:");
285 println!("• ✅ Evaluates Allow/Deny effects");
286 println!("• ✅ Handles wildcard actions and resources");
287 println!("• ✅ Processes condition blocks with various operators");
288 println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
289 println!("• ✅ Supports detailed evaluation with match information");
290 println!("• ✅ Handles multiple policies with complex interactions");
291 println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
292
293 Ok(())
294}
Sourcepub fn with_principal(self, principal: Principal) -> Self
pub fn with_principal(self, principal: Principal) -> Self
Sets the principal
Sourcepub fn with_action(self, action: Action) -> Self
pub fn with_action(self, action: Action) -> Self
Sets the action
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 println!("=== IAM ARN Validator Demo ===\n");
5
6 // Example 1: Parse and validate ARNs
7 println!("1. Parsing and validating ARNs:");
8
9 let valid_arns = vec![
10 "arn:aws:s3:::my-bucket/folder/file.txt",
11 "arn:aws:iam::123456789012:user/alice",
12 "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13 "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14 "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15 "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16 ];
17
18 for arn_str in &valid_arns {
19 match Arn::parse(arn_str) {
20 Ok(arn) => {
21 println!("✓ Valid ARN: {}", arn);
22 println!(" - Service: {}", arn.service);
23 println!(
24 " - Region: {}",
25 if arn.region.is_empty() {
26 "global"
27 } else {
28 &arn.region
29 }
30 );
31 println!(
32 " - Account: {}",
33 if arn.account_id.is_empty() {
34 "none"
35 } else {
36 &arn.account_id
37 }
38 );
39 if let Some(resource_type) = arn.resource_type() {
40 println!(" - Resource Type: {}", resource_type);
41 }
42 if let Some(resource_id) = arn.resource_id() {
43 println!(" - Resource ID: {}", resource_id);
44 }
45 println!();
46 }
47 Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48 }
49 }
50
51 // Example 2: Wildcard matching
52 println!("2. Wildcard pattern matching:");
53
54 let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55 println!("Resource ARN: {}", resource_arn);
56
57 let patterns = vec![
58 "arn:aws:s3:::my-bucket/*",
59 "arn:aws:s3:::my-bucket/uploads/*",
60 "arn:aws:s3:::my-bucket/uploads/user123/*",
61 "arn:aws:s3:::*/uploads/user123/document.pdf",
62 "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63 "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64 "arn:aws:s3:::other-bucket/*",
65 "arn:aws:ec2:*:*:instance/*",
66 ];
67
68 for pattern in &patterns {
69 match resource_arn.matches(pattern) {
70 Ok(matches) => {
71 let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72 println!(" {} Pattern: {}", status, pattern);
73 }
74 Err(e) => println!(" ✗ ERROR Pattern: {} ({})", pattern, e),
75 }
76 }
77
78 // Example 3: Integration with IAM policies
79 println!("\n3. Using ARNs in IAM policies:");
80
81 let policy = IAMPolicy::new()
82 .with_id("s3-access-policy")
83 .add_statement(
84 IAMStatement::new(Effect::Allow)
85 .with_sid("AllowS3Read")
86 .with_action(Action::Multiple(vec![
87 "s3:GetObject".to_string(),
88 "s3:ListBucket".to_string(),
89 ]))
90 .with_resource(Resource::Multiple(vec![
91 "arn:aws:s3:::my-bucket".to_string(),
92 "arn:aws:s3:::my-bucket/*".to_string(),
93 ])),
94 )
95 .add_statement(
96 IAMStatement::new(Effect::Allow)
97 .with_sid("AllowS3Write")
98 .with_action(Action::Single("s3:PutObject".to_string()))
99 .with_resource(Resource::Single(
100 "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101 )),
102 );
103
104 let policy_json = policy.to_json()?;
105 println!("Generated IAM Policy:");
106 println!("{}", policy_json);
107
108 // Example 4: Validate all ARNs in the policy
109 println!("\n4. Validating ARNs in policy:");
110
111 for (i, statement) in policy.statement.iter().enumerate() {
112 println!(
113 "Statement {}: {}",
114 i + 1,
115 statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116 );
117
118 let resources = match &statement.resource {
119 Some(Resource::Single(arn)) => vec![arn.clone()],
120 Some(Resource::Multiple(arns)) => arns.clone(),
121 None => vec![],
122 };
123
124 for resource in resources {
125 match Arn::parse(&resource) {
126 Ok(arn) => {
127 let validity = if arn.is_valid() {
128 "✓ Valid"
129 } else {
130 "⚠ Invalid"
131 };
132 println!(" {} Resource: {}", validity, resource);
133 }
134 Err(e) => println!(" ✗ Parse Error: {} ({})", resource, e),
135 }
136 }
137 }
138
139 Ok(())
140}
More examples
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 // Example 1: Valid Policy
11 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 // Example 2: Invalid Policy - Missing Required Fields
40 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); // Missing action and resource
42
43 if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 // Example 3: Policy with Multiple Validation Errors
54 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") // Empty ID
57 .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) // Invalid action format
60 .with_resource(Resource::Single("invalid-resource".to_string())), // Invalid resource
61 )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") // Duplicate SID
71 .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 // Example 4: Comprehensive Validation
93 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") // Short ID - will fail validation
96 .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), // Numeric operator with string value - will fail
104 ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 // Example 5: Logical Policy Errors
115 println!("5. Logical Policy Errors:");
116
117 // NotPrincipal with Allow effect (invalid)
118 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 // Both Action and NotAction (invalid)
134 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 // Example 6: Component Validation
147 println!("6. Individual Component Validation:");
148
149 // Invalid action
150 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 // Invalid principal
157 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 // Valid service principal
166 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10 // Example 1: Simple Allow Policy
11 println!("1. Simple Allow Policy:");
12 let allow_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19 );
20
21 let request = IAMRequest::new(
22 "arn:aws:iam::123456789012:user/alice",
23 "s3:GetObject",
24 "arn:aws:s3:::my-bucket/file.txt",
25 );
26
27 match evaluate_policy(&allow_policy, &request)? {
28 Decision::Allow => println!("✓ Access ALLOWED"),
29 Decision::Deny => println!("✗ Access DENIED"),
30 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
31 }
32 println!();
33
34 // Example 2: Simple Deny Policy
35 println!("2. Simple Deny Policy:");
36 let deny_policy = IAMPolicy::new()
37 .with_id("550e8400-e29b-41d4-a716-446655440001")
38 .add_statement(
39 IAMStatement::new(Effect::Deny)
40 .with_sid("DenyS3Delete")
41 .with_action(Action::Single("s3:DeleteObject".to_string()))
42 .with_resource(Resource::Single(
43 "arn:aws:s3:::protected-bucket/*".to_string(),
44 )),
45 );
46
47 let delete_request = IAMRequest::new(
48 "arn:aws:iam::123456789012:user/alice",
49 "s3:DeleteObject",
50 "arn:aws:s3:::protected-bucket/important.txt",
51 );
52
53 match evaluate_policy(&deny_policy, &delete_request)? {
54 Decision::Allow => println!("✓ Access ALLOWED"),
55 Decision::Deny => println!("✗ Access DENIED"),
56 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
57 }
58 println!();
59
60 // Example 3: Wildcard Action Matching
61 println!("3. Wildcard Action Matching:");
62 let wildcard_policy = IAMPolicy::new()
63 .with_id("550e8400-e29b-41d4-a716-446655440002")
64 .add_statement(
65 IAMStatement::new(Effect::Allow)
66 .with_sid("AllowAllS3")
67 .with_action(Action::Single("s3:*".to_string()))
68 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
69 );
70
71 let wildcard_request = IAMRequest::new(
72 "arn:aws:iam::123456789012:user/alice",
73 "s3:PutObject",
74 "arn:aws:s3:::my-bucket/new-file.txt",
75 );
76
77 match evaluate_policy(&wildcard_policy, &wildcard_request)? {
78 Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
79 Decision::Deny => println!("✗ Access DENIED"),
80 Decision::NotApplicable => println!("? No applicable policy"),
81 }
82 println!();
83
84 // Example 4: Condition-Based Policy
85 println!("4. Condition-Based Policy:");
86 let mut context = Context::new();
87 context.insert(
88 "aws:userid".to_string(),
89 ContextValue::String("alice".to_string()),
90 );
91 context.insert(
92 "aws:CurrentTime".to_string(),
93 ContextValue::String("2024-01-15T10:00:00Z".to_string()),
94 );
95
96 let condition_policy = IAMPolicy::new()
97 .with_id("550e8400-e29b-41d4-a716-446655440003")
98 .add_statement(
99 IAMStatement::new(Effect::Allow)
100 .with_sid("AllowWithCondition")
101 .with_action(Action::Single("s3:GetObject".to_string()))
102 .with_resource(Resource::Single(
103 "arn:aws:s3:::private-bucket/*".to_string(),
104 ))
105 .with_condition(
106 Operator::StringEquals,
107 "aws:userid".to_string(),
108 json!("alice"),
109 ),
110 );
111
112 let condition_request = IAMRequest::new_with_context(
113 "arn:aws:iam::123456789012:user/alice",
114 "s3:GetObject",
115 "arn:aws:s3:::private-bucket/personal.txt",
116 context,
117 );
118
119 match evaluate_policy(&condition_policy, &condition_request)? {
120 Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
121 Decision::Deny => println!("✗ Access DENIED"),
122 Decision::NotApplicable => println!("? Condition not satisfied"),
123 }
124 println!();
125
126 // Example 5: Failed Condition
127 println!("5. Failed Condition:");
128 let mut wrong_context = Context::new();
129 wrong_context.insert(
130 "aws:userid".to_string(),
131 ContextValue::String("bob".to_string()),
132 );
133
134 let failed_condition_request = IAMRequest::new_with_context(
135 "arn:aws:iam::123456789012:user/bob",
136 "s3:GetObject",
137 "arn:aws:s3:::private-bucket/personal.txt",
138 wrong_context,
139 );
140
141 match evaluate_policy(&condition_policy, &failed_condition_request)? {
142 Decision::Allow => println!("✓ Access ALLOWED"),
143 Decision::Deny => println!("✗ Access DENIED"),
144 Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
145 }
146 println!();
147
148 // Example 6: Explicit Deny Overrides Allow
149 println!("6. Explicit Deny Overrides Allow:");
150 let combined_policies = vec![
151 IAMPolicy::new()
152 .with_id("550e8400-e29b-41d4-a716-446655440004")
153 .add_statement(
154 IAMStatement::new(Effect::Allow)
155 .with_sid("AllowAll")
156 .with_action(Action::Single("s3:*".to_string()))
157 .with_resource(Resource::Single("*".to_string())),
158 ),
159 IAMPolicy::new()
160 .with_id("550e8400-e29b-41d4-a716-446655440005")
161 .add_statement(
162 IAMStatement::new(Effect::Deny)
163 .with_sid("DenyProtected")
164 .with_action(Action::Single("s3:DeleteObject".to_string()))
165 .with_resource(Resource::Single(
166 "arn:aws:s3:::protected-bucket/*".to_string(),
167 )),
168 ),
169 ];
170
171 let evaluator = PolicyEvaluator::with_policies(combined_policies);
172 let protected_request = IAMRequest::new(
173 "arn:aws:iam::123456789012:user/alice",
174 "s3:DeleteObject",
175 "arn:aws:s3:::protected-bucket/critical.txt",
176 );
177
178 match evaluator.evaluate(&protected_request)?.decision {
179 Decision::Allow => println!("✓ Access ALLOWED"),
180 Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
181 Decision::NotApplicable => println!("? No applicable policy"),
182 }
183 println!();
184
185 // Example 7: Numeric Condition
186 println!("7. Numeric Condition:");
187 let mut numeric_context = Context::new();
188 numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
189
190 let numeric_policy = IAMPolicy::new()
191 .with_id("550e8400-e29b-41d4-a716-446655440006")
192 .add_statement(
193 IAMStatement::new(Effect::Allow)
194 .with_sid("AllowLimitedRequests")
195 .with_action(Action::Single("s3:GetObject".to_string()))
196 .with_resource(Resource::Single("*".to_string()))
197 .with_condition(
198 Operator::NumericLessThan,
199 "aws:RequestCount".to_string(),
200 json!(10),
201 ),
202 );
203
204 let numeric_request = IAMRequest::new_with_context(
205 "arn:aws:iam::123456789012:user/alice",
206 "s3:GetObject",
207 "arn:aws:s3:::any-bucket/file.txt",
208 numeric_context,
209 );
210
211 match evaluate_policy(&numeric_policy, &numeric_request)? {
212 Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
213 Decision::Deny => println!("✗ Access DENIED"),
214 Decision::NotApplicable => println!("? Numeric condition failed"),
215 }
216 println!();
217
218 // Example 8: Detailed Evaluation with Options
219 println!("8. Detailed Evaluation with Options:");
220 let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
221 .with_options(EvaluationOptions {
222 collect_match_details: true,
223 stop_on_explicit_deny: false,
224 max_statements: 100,
225 });
226
227 let detailed_result = detailed_evaluator.evaluate(&request)?;
228 println!("Decision: {:?}", detailed_result.decision);
229 println!("Matched Statements:");
230 for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
231 println!(
232 " {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
233 i + 1,
234 statement_match.sid,
235 statement_match.effect,
236 statement_match.conditions_satisfied,
237 statement_match.reason
238 );
239 }
240 println!();
241
242 // Example 9: No Applicable Policy (Implicit Deny)
243 println!("9. No Applicable Policy (Implicit Deny):");
244 let unrelated_request = IAMRequest::new(
245 "arn:aws:iam::123456789012:user/alice",
246 "ec2:DescribeInstances",
247 "arn:aws:ec2:us-east-1:123456789012:instance/*",
248 );
249
250 match evaluate_policy(&allow_policy, &unrelated_request)? {
251 Decision::Allow => println!("✓ Access ALLOWED"),
252 Decision::Deny => println!("✗ Access DENIED"),
253 Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
254 }
255 println!();
256
257 // Example 10: Resource Pattern Matching
258 println!("10. Resource Pattern Matching:");
259 let pattern_policy = IAMPolicy::new()
260 .with_id("550e8400-e29b-41d4-a716-446655440007")
261 .add_statement(
262 IAMStatement::new(Effect::Allow)
263 .with_sid("AllowBucketAccess")
264 .with_action(Action::Multiple(vec![
265 "s3:GetObject".to_string(),
266 "s3:PutObject".to_string(),
267 ]))
268 .with_resource(Resource::Single("arn:aws:s3:::user-data-*/*".to_string())),
269 );
270
271 let pattern_request = IAMRequest::new(
272 "arn:aws:iam::123456789012:user/alice",
273 "s3:GetObject",
274 "arn:aws:s3:::user-data-alice/profile.json",
275 );
276
277 match evaluate_policy(&pattern_policy, &pattern_request)? {
278 Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
279 Decision::Deny => println!("✗ Access DENIED"),
280 Decision::NotApplicable => println!("? Resource pattern didn't match"),
281 }
282
283 println!("\n=== Policy Evaluation Demo Complete ===");
284 println!("\nThe Policy Evaluation Engine successfully:");
285 println!("• ✅ Evaluates Allow/Deny effects");
286 println!("• ✅ Handles wildcard actions and resources");
287 println!("• ✅ Processes condition blocks with various operators");
288 println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
289 println!("• ✅ Supports detailed evaluation with match information");
290 println!("• ✅ Handles multiple policies with complex interactions");
291 println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
292
293 Ok(())
294}
Sourcepub fn with_resource(self, resource: Resource) -> Self
pub fn with_resource(self, resource: Resource) -> Self
Sets the resource
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 println!("=== IAM ARN Validator Demo ===\n");
5
6 // Example 1: Parse and validate ARNs
7 println!("1. Parsing and validating ARNs:");
8
9 let valid_arns = vec![
10 "arn:aws:s3:::my-bucket/folder/file.txt",
11 "arn:aws:iam::123456789012:user/alice",
12 "arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0",
13 "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
14 "arn:aws:dynamodb:us-east-1:123456789012:table/MyTable",
15 "arn:aws-eu-gov:dynamodb:us-east-1:123456789012:table/MyTable",
16 ];
17
18 for arn_str in &valid_arns {
19 match Arn::parse(arn_str) {
20 Ok(arn) => {
21 println!("✓ Valid ARN: {}", arn);
22 println!(" - Service: {}", arn.service);
23 println!(
24 " - Region: {}",
25 if arn.region.is_empty() {
26 "global"
27 } else {
28 &arn.region
29 }
30 );
31 println!(
32 " - Account: {}",
33 if arn.account_id.is_empty() {
34 "none"
35 } else {
36 &arn.account_id
37 }
38 );
39 if let Some(resource_type) = arn.resource_type() {
40 println!(" - Resource Type: {}", resource_type);
41 }
42 if let Some(resource_id) = arn.resource_id() {
43 println!(" - Resource ID: {}", resource_id);
44 }
45 println!();
46 }
47 Err(e) => println!("✗ Invalid ARN {}: {}", arn_str, e),
48 }
49 }
50
51 // Example 2: Wildcard matching
52 println!("2. Wildcard pattern matching:");
53
54 let resource_arn = Arn::parse("arn:aws:s3:::my-bucket/uploads/user123/document.pdf")?;
55 println!("Resource ARN: {}", resource_arn);
56
57 let patterns = vec![
58 "arn:aws:s3:::my-bucket/*",
59 "arn:aws:s3:::my-bucket/uploads/*",
60 "arn:aws:s3:::my-bucket/uploads/user123/*",
61 "arn:aws:s3:::*/uploads/user123/document.pdf",
62 "arn:aws:s3:::my-bucket/uploads/*/document.pdf",
63 "arn:aws:s3:::my-bucket/uploads/user???/document.pdf",
64 "arn:aws:s3:::other-bucket/*",
65 "arn:aws:ec2:*:*:instance/*",
66 ];
67
68 for pattern in &patterns {
69 match resource_arn.matches(pattern) {
70 Ok(matches) => {
71 let status = if matches { "✓ MATCH" } else { "✗ NO MATCH" };
72 println!(" {} Pattern: {}", status, pattern);
73 }
74 Err(e) => println!(" ✗ ERROR Pattern: {} ({})", pattern, e),
75 }
76 }
77
78 // Example 3: Integration with IAM policies
79 println!("\n3. Using ARNs in IAM policies:");
80
81 let policy = IAMPolicy::new()
82 .with_id("s3-access-policy")
83 .add_statement(
84 IAMStatement::new(Effect::Allow)
85 .with_sid("AllowS3Read")
86 .with_action(Action::Multiple(vec![
87 "s3:GetObject".to_string(),
88 "s3:ListBucket".to_string(),
89 ]))
90 .with_resource(Resource::Multiple(vec![
91 "arn:aws:s3:::my-bucket".to_string(),
92 "arn:aws:s3:::my-bucket/*".to_string(),
93 ])),
94 )
95 .add_statement(
96 IAMStatement::new(Effect::Allow)
97 .with_sid("AllowS3Write")
98 .with_action(Action::Single("s3:PutObject".to_string()))
99 .with_resource(Resource::Single(
100 "arn:aws:s3:::my-bucket/uploads/*".to_string(),
101 )),
102 );
103
104 let policy_json = policy.to_json()?;
105 println!("Generated IAM Policy:");
106 println!("{}", policy_json);
107
108 // Example 4: Validate all ARNs in the policy
109 println!("\n4. Validating ARNs in policy:");
110
111 for (i, statement) in policy.statement.iter().enumerate() {
112 println!(
113 "Statement {}: {}",
114 i + 1,
115 statement.sid.as_ref().unwrap_or(&"(no sid)".to_string())
116 );
117
118 let resources = match &statement.resource {
119 Some(Resource::Single(arn)) => vec![arn.clone()],
120 Some(Resource::Multiple(arns)) => arns.clone(),
121 None => vec![],
122 };
123
124 for resource in resources {
125 match Arn::parse(&resource) {
126 Ok(arn) => {
127 let validity = if arn.is_valid() {
128 "✓ Valid"
129 } else {
130 "⚠ Invalid"
131 };
132 println!(" {} Resource: {}", validity, resource);
133 }
134 Err(e) => println!(" ✗ Parse Error: {} ({})", resource, e),
135 }
136 }
137 }
138
139 Ok(())
140}
More examples
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 // Example 1: Valid Policy
11 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 // Example 2: Invalid Policy - Missing Required Fields
40 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); // Missing action and resource
42
43 if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 // Example 3: Policy with Multiple Validation Errors
54 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") // Empty ID
57 .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) // Invalid action format
60 .with_resource(Resource::Single("invalid-resource".to_string())), // Invalid resource
61 )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") // Duplicate SID
71 .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 // Example 4: Comprehensive Validation
93 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") // Short ID - will fail validation
96 .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), // Numeric operator with string value - will fail
104 ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 // Example 5: Logical Policy Errors
115 println!("5. Logical Policy Errors:");
116
117 // NotPrincipal with Allow effect (invalid)
118 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 // Both Action and NotAction (invalid)
134 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 // Example 6: Component Validation
147 println!("6. Individual Component Validation:");
148
149 // Invalid action
150 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 // Invalid principal
157 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 // Valid service principal
166 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Evaluation Engine Demo ===\n");
9
10 // Example 1: Simple Allow Policy
11 println!("1. Simple Allow Policy:");
12 let allow_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19 );
20
21 let request = IAMRequest::new(
22 "arn:aws:iam::123456789012:user/alice",
23 "s3:GetObject",
24 "arn:aws:s3:::my-bucket/file.txt",
25 );
26
27 match evaluate_policy(&allow_policy, &request)? {
28 Decision::Allow => println!("✓ Access ALLOWED"),
29 Decision::Deny => println!("✗ Access DENIED"),
30 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
31 }
32 println!();
33
34 // Example 2: Simple Deny Policy
35 println!("2. Simple Deny Policy:");
36 let deny_policy = IAMPolicy::new()
37 .with_id("550e8400-e29b-41d4-a716-446655440001")
38 .add_statement(
39 IAMStatement::new(Effect::Deny)
40 .with_sid("DenyS3Delete")
41 .with_action(Action::Single("s3:DeleteObject".to_string()))
42 .with_resource(Resource::Single(
43 "arn:aws:s3:::protected-bucket/*".to_string(),
44 )),
45 );
46
47 let delete_request = IAMRequest::new(
48 "arn:aws:iam::123456789012:user/alice",
49 "s3:DeleteObject",
50 "arn:aws:s3:::protected-bucket/important.txt",
51 );
52
53 match evaluate_policy(&deny_policy, &delete_request)? {
54 Decision::Allow => println!("✓ Access ALLOWED"),
55 Decision::Deny => println!("✗ Access DENIED"),
56 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
57 }
58 println!();
59
60 // Example 3: Wildcard Action Matching
61 println!("3. Wildcard Action Matching:");
62 let wildcard_policy = IAMPolicy::new()
63 .with_id("550e8400-e29b-41d4-a716-446655440002")
64 .add_statement(
65 IAMStatement::new(Effect::Allow)
66 .with_sid("AllowAllS3")
67 .with_action(Action::Single("s3:*".to_string()))
68 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
69 );
70
71 let wildcard_request = IAMRequest::new(
72 "arn:aws:iam::123456789012:user/alice",
73 "s3:PutObject",
74 "arn:aws:s3:::my-bucket/new-file.txt",
75 );
76
77 match evaluate_policy(&wildcard_policy, &wildcard_request)? {
78 Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
79 Decision::Deny => println!("✗ Access DENIED"),
80 Decision::NotApplicable => println!("? No applicable policy"),
81 }
82 println!();
83
84 // Example 4: Condition-Based Policy
85 println!("4. Condition-Based Policy:");
86 let mut context = Context::new();
87 context.insert(
88 "aws:userid".to_string(),
89 ContextValue::String("alice".to_string()),
90 );
91 context.insert(
92 "aws:CurrentTime".to_string(),
93 ContextValue::String("2024-01-15T10:00:00Z".to_string()),
94 );
95
96 let condition_policy = IAMPolicy::new()
97 .with_id("550e8400-e29b-41d4-a716-446655440003")
98 .add_statement(
99 IAMStatement::new(Effect::Allow)
100 .with_sid("AllowWithCondition")
101 .with_action(Action::Single("s3:GetObject".to_string()))
102 .with_resource(Resource::Single(
103 "arn:aws:s3:::private-bucket/*".to_string(),
104 ))
105 .with_condition(
106 Operator::StringEquals,
107 "aws:userid".to_string(),
108 json!("alice"),
109 ),
110 );
111
112 let condition_request = IAMRequest::new_with_context(
113 "arn:aws:iam::123456789012:user/alice",
114 "s3:GetObject",
115 "arn:aws:s3:::private-bucket/personal.txt",
116 context,
117 );
118
119 match evaluate_policy(&condition_policy, &condition_request)? {
120 Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
121 Decision::Deny => println!("✗ Access DENIED"),
122 Decision::NotApplicable => println!("? Condition not satisfied"),
123 }
124 println!();
125
126 // Example 5: Failed Condition
127 println!("5. Failed Condition:");
128 let mut wrong_context = Context::new();
129 wrong_context.insert(
130 "aws:userid".to_string(),
131 ContextValue::String("bob".to_string()),
132 );
133
134 let failed_condition_request = IAMRequest::new_with_context(
135 "arn:aws:iam::123456789012:user/bob",
136 "s3:GetObject",
137 "arn:aws:s3:::private-bucket/personal.txt",
138 wrong_context,
139 );
140
141 match evaluate_policy(&condition_policy, &failed_condition_request)? {
142 Decision::Allow => println!("✓ Access ALLOWED"),
143 Decision::Deny => println!("✗ Access DENIED"),
144 Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
145 }
146 println!();
147
148 // Example 6: Explicit Deny Overrides Allow
149 println!("6. Explicit Deny Overrides Allow:");
150 let combined_policies = vec![
151 IAMPolicy::new()
152 .with_id("550e8400-e29b-41d4-a716-446655440004")
153 .add_statement(
154 IAMStatement::new(Effect::Allow)
155 .with_sid("AllowAll")
156 .with_action(Action::Single("s3:*".to_string()))
157 .with_resource(Resource::Single("*".to_string())),
158 ),
159 IAMPolicy::new()
160 .with_id("550e8400-e29b-41d4-a716-446655440005")
161 .add_statement(
162 IAMStatement::new(Effect::Deny)
163 .with_sid("DenyProtected")
164 .with_action(Action::Single("s3:DeleteObject".to_string()))
165 .with_resource(Resource::Single(
166 "arn:aws:s3:::protected-bucket/*".to_string(),
167 )),
168 ),
169 ];
170
171 let evaluator = PolicyEvaluator::with_policies(combined_policies);
172 let protected_request = IAMRequest::new(
173 "arn:aws:iam::123456789012:user/alice",
174 "s3:DeleteObject",
175 "arn:aws:s3:::protected-bucket/critical.txt",
176 );
177
178 match evaluator.evaluate(&protected_request)?.decision {
179 Decision::Allow => println!("✓ Access ALLOWED"),
180 Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
181 Decision::NotApplicable => println!("? No applicable policy"),
182 }
183 println!();
184
185 // Example 7: Numeric Condition
186 println!("7. Numeric Condition:");
187 let mut numeric_context = Context::new();
188 numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
189
190 let numeric_policy = IAMPolicy::new()
191 .with_id("550e8400-e29b-41d4-a716-446655440006")
192 .add_statement(
193 IAMStatement::new(Effect::Allow)
194 .with_sid("AllowLimitedRequests")
195 .with_action(Action::Single("s3:GetObject".to_string()))
196 .with_resource(Resource::Single("*".to_string()))
197 .with_condition(
198 Operator::NumericLessThan,
199 "aws:RequestCount".to_string(),
200 json!(10),
201 ),
202 );
203
204 let numeric_request = IAMRequest::new_with_context(
205 "arn:aws:iam::123456789012:user/alice",
206 "s3:GetObject",
207 "arn:aws:s3:::any-bucket/file.txt",
208 numeric_context,
209 );
210
211 match evaluate_policy(&numeric_policy, &numeric_request)? {
212 Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
213 Decision::Deny => println!("✗ Access DENIED"),
214 Decision::NotApplicable => println!("? Numeric condition failed"),
215 }
216 println!();
217
218 // Example 8: Detailed Evaluation with Options
219 println!("8. Detailed Evaluation with Options:");
220 let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
221 .with_options(EvaluationOptions {
222 collect_match_details: true,
223 stop_on_explicit_deny: false,
224 max_statements: 100,
225 });
226
227 let detailed_result = detailed_evaluator.evaluate(&request)?;
228 println!("Decision: {:?}", detailed_result.decision);
229 println!("Matched Statements:");
230 for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
231 println!(
232 " {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
233 i + 1,
234 statement_match.sid,
235 statement_match.effect,
236 statement_match.conditions_satisfied,
237 statement_match.reason
238 );
239 }
240 println!();
241
242 // Example 9: No Applicable Policy (Implicit Deny)
243 println!("9. No Applicable Policy (Implicit Deny):");
244 let unrelated_request = IAMRequest::new(
245 "arn:aws:iam::123456789012:user/alice",
246 "ec2:DescribeInstances",
247 "arn:aws:ec2:us-east-1:123456789012:instance/*",
248 );
249
250 match evaluate_policy(&allow_policy, &unrelated_request)? {
251 Decision::Allow => println!("✓ Access ALLOWED"),
252 Decision::Deny => println!("✗ Access DENIED"),
253 Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
254 }
255 println!();
256
257 // Example 10: Resource Pattern Matching
258 println!("10. Resource Pattern Matching:");
259 let pattern_policy = IAMPolicy::new()
260 .with_id("550e8400-e29b-41d4-a716-446655440007")
261 .add_statement(
262 IAMStatement::new(Effect::Allow)
263 .with_sid("AllowBucketAccess")
264 .with_action(Action::Multiple(vec![
265 "s3:GetObject".to_string(),
266 "s3:PutObject".to_string(),
267 ]))
268 .with_resource(Resource::Single("arn:aws:s3:::user-data-*/*".to_string())),
269 );
270
271 let pattern_request = IAMRequest::new(
272 "arn:aws:iam::123456789012:user/alice",
273 "s3:GetObject",
274 "arn:aws:s3:::user-data-alice/profile.json",
275 );
276
277 match evaluate_policy(&pattern_policy, &pattern_request)? {
278 Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
279 Decision::Deny => println!("✗ Access DENIED"),
280 Decision::NotApplicable => println!("? Resource pattern didn't match"),
281 }
282
283 println!("\n=== Policy Evaluation Demo Complete ===");
284 println!("\nThe Policy Evaluation Engine successfully:");
285 println!("• ✅ Evaluates Allow/Deny effects");
286 println!("• ✅ Handles wildcard actions and resources");
287 println!("• ✅ Processes condition blocks with various operators");
288 println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
289 println!("• ✅ Supports detailed evaluation with match information");
290 println!("• ✅ Handles multiple policies with complex interactions");
291 println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
292
293 Ok(())
294}
Sourcepub fn with_condition(
self,
operator: Operator,
key: String,
value: Value,
) -> Self
pub fn with_condition( self, operator: Operator, key: String, value: Value, ) -> Self
Adds a condition to the statement
Examples found in repository?
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 // Example 1: Valid Policy
11 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 // Example 2: Invalid Policy - Missing Required Fields
40 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); // Missing action and resource
42
43 if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 // Example 3: Policy with Multiple Validation Errors
54 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") // Empty ID
57 .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) // Invalid action format
60 .with_resource(Resource::Single("invalid-resource".to_string())), // Invalid resource
61 )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") // Duplicate SID
71 .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 // Example 4: Comprehensive Validation
93 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") // Short ID - will fail validation
96 .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), // Numeric operator with string value - will fail
104 ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 // Example 5: Logical Policy Errors
115 println!("5. Logical Policy Errors:");
116
117 // NotPrincipal with Allow effect (invalid)
118 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 // Both Action and NotAction (invalid)
134 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 // Example 6: Component Validation
147 println!("6. Individual Component Validation:");
148
149 // Invalid action
150 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 // Invalid principal
157 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 // Valid service principal
166 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}
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(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
19 );
20
21 let request = IAMRequest::new(
22 "arn:aws:iam::123456789012:user/alice",
23 "s3:GetObject",
24 "arn:aws:s3:::my-bucket/file.txt",
25 );
26
27 match evaluate_policy(&allow_policy, &request)? {
28 Decision::Allow => println!("✓ Access ALLOWED"),
29 Decision::Deny => println!("✗ Access DENIED"),
30 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
31 }
32 println!();
33
34 // Example 2: Simple Deny Policy
35 println!("2. Simple Deny Policy:");
36 let deny_policy = IAMPolicy::new()
37 .with_id("550e8400-e29b-41d4-a716-446655440001")
38 .add_statement(
39 IAMStatement::new(Effect::Deny)
40 .with_sid("DenyS3Delete")
41 .with_action(Action::Single("s3:DeleteObject".to_string()))
42 .with_resource(Resource::Single(
43 "arn:aws:s3:::protected-bucket/*".to_string(),
44 )),
45 );
46
47 let delete_request = IAMRequest::new(
48 "arn:aws:iam::123456789012:user/alice",
49 "s3:DeleteObject",
50 "arn:aws:s3:::protected-bucket/important.txt",
51 );
52
53 match evaluate_policy(&deny_policy, &delete_request)? {
54 Decision::Allow => println!("✓ Access ALLOWED"),
55 Decision::Deny => println!("✗ Access DENIED"),
56 Decision::NotApplicable => println!("? No applicable policy (implicit deny)"),
57 }
58 println!();
59
60 // Example 3: Wildcard Action Matching
61 println!("3. Wildcard Action Matching:");
62 let wildcard_policy = IAMPolicy::new()
63 .with_id("550e8400-e29b-41d4-a716-446655440002")
64 .add_statement(
65 IAMStatement::new(Effect::Allow)
66 .with_sid("AllowAllS3")
67 .with_action(Action::Single("s3:*".to_string()))
68 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string())),
69 );
70
71 let wildcard_request = IAMRequest::new(
72 "arn:aws:iam::123456789012:user/alice",
73 "s3:PutObject",
74 "arn:aws:s3:::my-bucket/new-file.txt",
75 );
76
77 match evaluate_policy(&wildcard_policy, &wildcard_request)? {
78 Decision::Allow => println!("✓ Wildcard action matched - Access ALLOWED"),
79 Decision::Deny => println!("✗ Access DENIED"),
80 Decision::NotApplicable => println!("? No applicable policy"),
81 }
82 println!();
83
84 // Example 4: Condition-Based Policy
85 println!("4. Condition-Based Policy:");
86 let mut context = Context::new();
87 context.insert(
88 "aws:userid".to_string(),
89 ContextValue::String("alice".to_string()),
90 );
91 context.insert(
92 "aws:CurrentTime".to_string(),
93 ContextValue::String("2024-01-15T10:00:00Z".to_string()),
94 );
95
96 let condition_policy = IAMPolicy::new()
97 .with_id("550e8400-e29b-41d4-a716-446655440003")
98 .add_statement(
99 IAMStatement::new(Effect::Allow)
100 .with_sid("AllowWithCondition")
101 .with_action(Action::Single("s3:GetObject".to_string()))
102 .with_resource(Resource::Single(
103 "arn:aws:s3:::private-bucket/*".to_string(),
104 ))
105 .with_condition(
106 Operator::StringEquals,
107 "aws:userid".to_string(),
108 json!("alice"),
109 ),
110 );
111
112 let condition_request = IAMRequest::new_with_context(
113 "arn:aws:iam::123456789012:user/alice",
114 "s3:GetObject",
115 "arn:aws:s3:::private-bucket/personal.txt",
116 context,
117 );
118
119 match evaluate_policy(&condition_policy, &condition_request)? {
120 Decision::Allow => println!("✓ Condition satisfied - Access ALLOWED"),
121 Decision::Deny => println!("✗ Access DENIED"),
122 Decision::NotApplicable => println!("? Condition not satisfied"),
123 }
124 println!();
125
126 // Example 5: Failed Condition
127 println!("5. Failed Condition:");
128 let mut wrong_context = Context::new();
129 wrong_context.insert(
130 "aws:userid".to_string(),
131 ContextValue::String("bob".to_string()),
132 );
133
134 let failed_condition_request = IAMRequest::new_with_context(
135 "arn:aws:iam::123456789012:user/bob",
136 "s3:GetObject",
137 "arn:aws:s3:::private-bucket/personal.txt",
138 wrong_context,
139 );
140
141 match evaluate_policy(&condition_policy, &failed_condition_request)? {
142 Decision::Allow => println!("✓ Access ALLOWED"),
143 Decision::Deny => println!("✗ Access DENIED"),
144 Decision::NotApplicable => println!("? Condition failed - No applicable policy"),
145 }
146 println!();
147
148 // Example 6: Explicit Deny Overrides Allow
149 println!("6. Explicit Deny Overrides Allow:");
150 let combined_policies = vec![
151 IAMPolicy::new()
152 .with_id("550e8400-e29b-41d4-a716-446655440004")
153 .add_statement(
154 IAMStatement::new(Effect::Allow)
155 .with_sid("AllowAll")
156 .with_action(Action::Single("s3:*".to_string()))
157 .with_resource(Resource::Single("*".to_string())),
158 ),
159 IAMPolicy::new()
160 .with_id("550e8400-e29b-41d4-a716-446655440005")
161 .add_statement(
162 IAMStatement::new(Effect::Deny)
163 .with_sid("DenyProtected")
164 .with_action(Action::Single("s3:DeleteObject".to_string()))
165 .with_resource(Resource::Single(
166 "arn:aws:s3:::protected-bucket/*".to_string(),
167 )),
168 ),
169 ];
170
171 let evaluator = PolicyEvaluator::with_policies(combined_policies);
172 let protected_request = IAMRequest::new(
173 "arn:aws:iam::123456789012:user/alice",
174 "s3:DeleteObject",
175 "arn:aws:s3:::protected-bucket/critical.txt",
176 );
177
178 match evaluator.evaluate(&protected_request)?.decision {
179 Decision::Allow => println!("✓ Access ALLOWED"),
180 Decision::Deny => println!("✗ Explicit DENY overrides Allow"),
181 Decision::NotApplicable => println!("? No applicable policy"),
182 }
183 println!();
184
185 // Example 7: Numeric Condition
186 println!("7. Numeric Condition:");
187 let mut numeric_context = Context::new();
188 numeric_context.insert("aws:RequestCount".to_string(), ContextValue::Number(5.0));
189
190 let numeric_policy = IAMPolicy::new()
191 .with_id("550e8400-e29b-41d4-a716-446655440006")
192 .add_statement(
193 IAMStatement::new(Effect::Allow)
194 .with_sid("AllowLimitedRequests")
195 .with_action(Action::Single("s3:GetObject".to_string()))
196 .with_resource(Resource::Single("*".to_string()))
197 .with_condition(
198 Operator::NumericLessThan,
199 "aws:RequestCount".to_string(),
200 json!(10),
201 ),
202 );
203
204 let numeric_request = IAMRequest::new_with_context(
205 "arn:aws:iam::123456789012:user/alice",
206 "s3:GetObject",
207 "arn:aws:s3:::any-bucket/file.txt",
208 numeric_context,
209 );
210
211 match evaluate_policy(&numeric_policy, &numeric_request)? {
212 Decision::Allow => println!("✓ Numeric condition satisfied - Access ALLOWED"),
213 Decision::Deny => println!("✗ Access DENIED"),
214 Decision::NotApplicable => println!("? Numeric condition failed"),
215 }
216 println!();
217
218 // Example 8: Detailed Evaluation with Options
219 println!("8. Detailed Evaluation with Options:");
220 let detailed_evaluator = PolicyEvaluator::with_policies(vec![allow_policy.clone()])
221 .with_options(EvaluationOptions {
222 collect_match_details: true,
223 stop_on_explicit_deny: false,
224 max_statements: 100,
225 });
226
227 let detailed_result = detailed_evaluator.evaluate(&request)?;
228 println!("Decision: {:?}", detailed_result.decision);
229 println!("Matched Statements:");
230 for (i, statement_match) in detailed_result.matched_statements.iter().enumerate() {
231 println!(
232 " {}. SID: {:?}, Effect: {:?}, Satisfied: {}, Reason: {}",
233 i + 1,
234 statement_match.sid,
235 statement_match.effect,
236 statement_match.conditions_satisfied,
237 statement_match.reason
238 );
239 }
240 println!();
241
242 // Example 9: No Applicable Policy (Implicit Deny)
243 println!("9. No Applicable Policy (Implicit Deny):");
244 let unrelated_request = IAMRequest::new(
245 "arn:aws:iam::123456789012:user/alice",
246 "ec2:DescribeInstances",
247 "arn:aws:ec2:us-east-1:123456789012:instance/*",
248 );
249
250 match evaluate_policy(&allow_policy, &unrelated_request)? {
251 Decision::Allow => println!("✓ Access ALLOWED"),
252 Decision::Deny => println!("✗ Access DENIED"),
253 Decision::NotApplicable => println!("? No applicable policy - Implicit DENY"),
254 }
255 println!();
256
257 // Example 10: Resource Pattern Matching
258 println!("10. Resource Pattern Matching:");
259 let pattern_policy = IAMPolicy::new()
260 .with_id("550e8400-e29b-41d4-a716-446655440007")
261 .add_statement(
262 IAMStatement::new(Effect::Allow)
263 .with_sid("AllowBucketAccess")
264 .with_action(Action::Multiple(vec![
265 "s3:GetObject".to_string(),
266 "s3:PutObject".to_string(),
267 ]))
268 .with_resource(Resource::Single("arn:aws:s3:::user-data-*/*".to_string())),
269 );
270
271 let pattern_request = IAMRequest::new(
272 "arn:aws:iam::123456789012:user/alice",
273 "s3:GetObject",
274 "arn:aws:s3:::user-data-alice/profile.json",
275 );
276
277 match evaluate_policy(&pattern_policy, &pattern_request)? {
278 Decision::Allow => println!("✓ Resource pattern matched - Access ALLOWED"),
279 Decision::Deny => println!("✗ Access DENIED"),
280 Decision::NotApplicable => println!("? Resource pattern didn't match"),
281 }
282
283 println!("\n=== Policy Evaluation Demo Complete ===");
284 println!("\nThe Policy Evaluation Engine successfully:");
285 println!("• ✅ Evaluates Allow/Deny effects");
286 println!("• ✅ Handles wildcard actions and resources");
287 println!("• ✅ Processes condition blocks with various operators");
288 println!("• ✅ Implements proper IAM logic (explicit deny overrides)");
289 println!("• ✅ Supports detailed evaluation with match information");
290 println!("• ✅ Handles multiple policies with complex interactions");
291 println!("• ✅ Provides clear Allow/Deny/NotApplicable decisions");
292
293 Ok(())
294}
Sourcepub fn with_condition_struct(self, condition: Condition) -> Self
pub fn with_condition_struct(self, condition: Condition) -> Self
Adds a condition using the Condition struct
Trait Implementations§
Source§impl Clone for IAMStatement
impl Clone for IAMStatement
Source§fn clone(&self) -> IAMStatement
fn clone(&self) -> IAMStatement
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read more