IAMStatement

Struct IAMStatement 

Source
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

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 the SendMessage action on SQS.
  • Action: "ec2:StartInstances" - allows the StartInstances action on EC2.
  • Action: "iam:ChangePassword" - allows the ChangePassword action on IAM.
  • Action: "s3:GetObject" - allows the GetObject 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

Source

pub fn new(effect: IAMEffect) -> Self

Creates a new IAM statement with the specified effect

Examples found in repository?
examples/arn_demo.rs (line 84)
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
Hide additional examples
examples/validation_demo.rs (line 14)
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}
examples/evaluation_demo.rs (line 15)
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}
Source

pub fn with_sid<S: Into<String>>(self, sid: S) -> Self

Sets the statement ID

Examples found in repository?
examples/arn_demo.rs (line 85)
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
Hide additional examples
examples/validation_demo.rs (line 15)
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}
examples/evaluation_demo.rs (line 16)
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}
Source

pub fn with_principal(self, principal: Principal) -> Self

Sets the principal

Source

pub fn with_action(self, action: IAMAction) -> Self

Sets the action

Examples found in repository?
examples/arn_demo.rs (lines 86-89)
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
Hide additional examples
examples/validation_demo.rs (line 16)
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}
examples/evaluation_demo.rs (line 17)
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}
Source

pub fn with_resource(self, resource: IAMResource) -> Self

Sets the resource

Examples found in repository?
examples/arn_demo.rs (lines 90-93)
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
Hide additional examples
examples/validation_demo.rs (line 17)
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}
examples/evaluation_demo.rs (line 18)
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}
Source

pub fn with_condition( self, operator: IAMOperator, key: String, value: ConditionValue, ) -> Self

Adds a condition to the statement

Examples found in repository?
examples/validation_demo.rs (lines 18-22)
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
Hide additional examples
examples/evaluation_demo.rs (lines 111-115)
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}
Source

pub fn with_condition_struct(self, condition: Condition) -> Self

Adds a condition using the Condition struct

Trait Implementations§

Source§

impl Clone for IAMStatement

Source§

fn clone(&self) -> IAMStatement

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IAMStatement

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for IAMStatement

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for IAMStatement

Source§

fn eq(&self, other: &IAMStatement) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for IAMStatement

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Validate for IAMStatement

Source§

fn validate(&self, context: &mut ValidationContext) -> ValidationResult

Validate the component within the given context Read more
Source§

fn is_valid(&self) -> bool

Convenience method for basic validation
Source§

fn validate_result(&self) -> ValidationResult

Validate with detailed errors (same as regular validation) Read more
Source§

impl Eq for IAMStatement

Source§

impl StructuralPartialEq for IAMStatement

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,