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