validation_demo/
validation_demo.rs1use iam_rs::{
2 Action, Effect, IAMPolicy, IAMStatement, Operator, Principal, PrincipalType, Resource,
3 Validate, ValidationError,
4};
5use serde_json::json;
6
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("=== IAM Policy Validation Demo ===\n");
9
10 println!("1. Valid Policy Validation:");
12 let valid_policy = IAMPolicy::new()
13 .with_id("550e8400-e29b-41d4-a716-446655440000")
14 .add_statement(
15 IAMStatement::new(Effect::Allow)
16 .with_sid("AllowS3Read")
17 .with_action(Action::Single("s3:GetObject".to_string()))
18 .with_resource(Resource::Single("arn:aws:s3:::my-bucket/*".to_string()))
19 .with_condition(
20 Operator::StringEquals,
21 "aws:PrincipalTag/department".to_string(),
22 json!("engineering"),
23 ),
24 );
25
26 if valid_policy.is_valid() {
27 println!("✓ Policy is valid!");
28 } else {
29 println!("✗ Policy is invalid");
30 }
31
32 match valid_policy.validate_result() {
33 Ok(()) => println!("✓ Policy passes validation"),
34 Err(e) => println!("✗ Policy fails validation: {}", e),
35 }
36
37 println!();
38
39 println!("2. Invalid Policy - Missing Required Fields:");
41 let invalid_policy = IAMPolicy::new().add_statement(IAMStatement::new(Effect::Allow)); if !invalid_policy.is_valid() {
44 println!("✗ Policy is invalid (as expected)");
45 match invalid_policy.validate_result() {
46 Err(e) => println!(" Validation errors: {}", e),
47 Ok(()) => println!(" Unexpected: validation passed"),
48 }
49 }
50
51 println!();
52
53 println!("3. Policy with Multiple Validation Errors:");
55 let multi_error_policy = IAMPolicy::new()
56 .with_id("") .add_statement(
58 IAMStatement::new(Effect::Allow)
59 .with_action(Action::Single("invalid-action".to_string())) .with_resource(Resource::Single("invalid-resource".to_string())), )
62 .add_statement(
63 IAMStatement::new(Effect::Allow)
64 .with_sid("DuplicateId")
65 .with_action(Action::Single("s3:GetObject".to_string()))
66 .with_resource(Resource::Single("*".to_string())),
67 )
68 .add_statement(
69 IAMStatement::new(Effect::Deny)
70 .with_sid("DuplicateId") .with_action(Action::Single("s3:DeleteObject".to_string()))
72 .with_resource(Resource::Single("*".to_string())),
73 );
74
75 match multi_error_policy.validate_result() {
76 Err(ValidationError::Multiple(errors)) => {
77 println!("✗ Found {} validation errors:", errors.len());
78 for (i, error) in errors.iter().enumerate() {
79 println!(" {}. {}", i + 1, error);
80 }
81 }
82 Err(e) => {
83 println!("✗ Single validation error: {}", e);
84 }
85 Ok(()) => {
86 println!("✓ Unexpected: validation passed");
87 }
88 }
89
90 println!();
91
92 println!("4. Comprehensive Validation:");
94 let comprehensive_policy = IAMPolicy::new()
95 .with_id("short") .add_statement(
97 IAMStatement::new(Effect::Allow)
98 .with_action(Action::Single("s3:GetObject".to_string()))
99 .with_resource(Resource::Single("*".to_string()))
100 .with_condition(
101 Operator::NumericEquals,
102 "aws:RequestedRegion".to_string(),
103 json!("not-a-number"), ),
105 );
106
107 match comprehensive_policy.validate_result() {
108 Ok(()) => println!("✓ Policy passes validation"),
109 Err(e) => println!("✗ Policy fails validation: {}", e),
110 }
111
112 println!();
113
114 println!("5. Logical Policy Errors:");
116
117 let mut logical_error_policy = IAMStatement::new(Effect::Allow);
119 logical_error_policy.action = Some(Action::Single("s3:GetObject".to_string()));
120 logical_error_policy.resource = Some(Resource::Single("*".to_string()));
121 let mut principal_map = std::collections::HashMap::new();
122 principal_map.insert(
123 PrincipalType::Aws,
124 serde_json::json!("arn:aws:iam::123456789012:user/test"),
125 );
126 logical_error_policy.not_principal = Some(Principal::Mapped(principal_map));
127
128 match logical_error_policy.validate_result() {
129 Err(e) => println!("✗ Logical error detected: {}", e),
130 Ok(()) => println!("✓ Unexpected: validation passed"),
131 }
132
133 let mut conflicting_statement = IAMStatement::new(Effect::Allow);
135 conflicting_statement.action = Some(Action::Single("s3:GetObject".to_string()));
136 conflicting_statement.not_action = Some(Action::Single("s3:PutObject".to_string()));
137 conflicting_statement.resource = Some(Resource::Single("*".to_string()));
138
139 match conflicting_statement.validate_result() {
140 Err(e) => println!("✗ Logical error detected: {}", e),
141 Ok(()) => println!("✓ Unexpected: validation passed"),
142 }
143
144 println!();
145
146 println!("6. Individual Component Validation:");
148
149 let invalid_action = Action::Single("invalid-action".to_string());
151 match invalid_action.validate_result() {
152 Err(e) => println!("✗ Invalid action: {}", e),
153 Ok(()) => println!("✓ Action is valid"),
154 }
155
156 let mut invalid_map = std::collections::HashMap::new();
158 invalid_map.insert(PrincipalType::Aws, serde_json::json!("invalid-principal"));
159 let invalid_principal = Principal::Mapped(invalid_map);
160 match invalid_principal.validate_result() {
161 Err(e) => println!("✗ Invalid principal: {}", e),
162 Ok(()) => println!("✓ Principal is valid"),
163 }
164
165 let service_principal = Principal::Mapped(
167 [(PrincipalType::Service, json!("lambda.amazonaws.com"))]
168 .iter()
169 .cloned()
170 .collect(),
171 );
172 if service_principal.is_valid() {
173 println!("✓ Service principal is valid");
174 } else {
175 println!("✗ Service principal is invalid");
176 }
177
178 println!("\n=== Validation Demo Complete ===");
179
180 Ok(())
181}