23_permissions_boundaries/
23_permissions_boundaries.rs

1//! Example 23: Permissions Boundaries
2//!
3//! This example demonstrates how to use permissions boundaries to set the maximum
4//! permissions that identity-based policies can grant to users and roles.
5//!
6//! Key Concepts:
7//! - Permissions boundaries act as a ceiling for effective permissions
8//! - Effective permissions = identity-based policies ∩ permissions boundary
9//! - Both must allow an action for it to be permitted
10//!
11//! Run with: cargo run --example 23_permissions_boundaries
12
13use std::sync::{Arc, RwLock};
14use wami::{
15    provider::AwsProvider,
16    service::{
17        EvaluationService, PermissionsBoundaryService, PolicyService, RoleService, UserService,
18    },
19    store::memory::InMemoryWamiStore,
20    wami::identity::role::requests::CreateRoleRequest,
21    wami::identity::user::requests::CreateUserRequest,
22    wami::policies::evaluation::SimulatePrincipalPolicyRequest,
23    wami::policies::permissions_boundary::{
24        DeletePermissionsBoundaryRequest, PrincipalType, PutPermissionsBoundaryRequest,
25    },
26    wami::policies::policy::requests::CreatePolicyRequest,
27};
28
29#[tokio::main]
30async fn main() -> Result<(), Box<dyn std::error::Error>> {
31    println!("=== WAMI Example 23: Permissions Boundaries ===\n");
32
33    // Initialize store and services
34    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
35    let account_id = "123456789012";
36    let _provider = Arc::new(AwsProvider::new());
37
38    let user_service = UserService::new(store.clone(), account_id.to_string());
39    let role_service = RoleService::new(store.clone(), account_id.to_string());
40    let policy_service = PolicyService::new(store.clone(), account_id.to_string());
41    let boundary_service = PermissionsBoundaryService::new(store.clone(), account_id.to_string());
42    let evaluation_service = EvaluationService::new(store.clone(), account_id.to_string());
43
44    // ==========================================
45    // Part 1: Create User with Admin Policy
46    // ==========================================
47    println!("šŸ“‹ Part 1: Creating User with Admin Policy\n");
48
49    // Create a user
50    let alice_req = CreateUserRequest {
51        user_name: "alice".to_string(),
52        path: Some("/developers/".to_string()),
53        permissions_boundary: None,
54        tags: None,
55    };
56    let alice = user_service.create_user(alice_req).await?;
57    println!("āœ… Created user: {}", alice.user_name);
58    println!("   ARN: {}\n", alice.arn);
59
60    // Create an admin policy (allows all actions)
61    let admin_policy_doc = r#"{
62        "Version": "2012-10-17",
63        "Statement": [{
64            "Effect": "Allow",
65            "Action": "*",
66            "Resource": "*"
67        }]
68    }"#;
69    let admin_policy = policy_service
70        .create_policy(CreatePolicyRequest {
71            policy_name: "AdminPolicy".to_string(),
72            policy_document: admin_policy_doc.to_string(),
73            path: Some("/".to_string()),
74            description: Some("Full admin access".to_string()),
75            tags: None,
76        })
77        .await?;
78    println!("āœ… Created admin policy: {}", admin_policy.policy_name);
79    println!("   ARN: {}", admin_policy.arn);
80    println!("   Allows: All actions on all resources\n");
81
82    // ==========================================
83    // Part 2: Create S3-Only Boundary Policy
84    // ==========================================
85    println!("šŸ“‹ Part 2: Creating S3-Only Boundary Policy\n");
86
87    let s3_boundary_doc = r#"{
88        "Version": "2012-10-17",
89        "Statement": [{
90            "Effect": "Allow",
91            "Action": "s3:*",
92            "Resource": "*"
93        }]
94    }"#;
95    let s3_boundary = policy_service
96        .create_policy(CreatePolicyRequest {
97            policy_name: "S3OnlyBoundary".to_string(),
98            policy_document: s3_boundary_doc.to_string(),
99            path: Some("/boundaries/".to_string()),
100            description: Some("Limits permissions to S3 only".to_string()),
101            tags: None,
102        })
103        .await?;
104    println!("āœ… Created boundary policy: {}", s3_boundary.policy_name);
105    println!("   ARN: {}", s3_boundary.arn);
106    println!("   Allows: Only S3 actions\n");
107
108    // ==========================================
109    // Part 3: Test Without Boundary
110    // ==========================================
111    println!("šŸ“‹ Part 3: Testing Permissions WITHOUT Boundary\n");
112
113    // Simulate alice's permissions (admin policy allows everything)
114    let sim_req = SimulatePrincipalPolicyRequest {
115        policy_source_arn: alice.arn.clone(),
116        action_names: vec![
117            "s3:GetObject".to_string(),
118            "ec2:RunInstances".to_string(),
119            "iam:CreateUser".to_string(),
120        ],
121        resource_arns: Some(vec!["*".to_string()]),
122        policy_input_list: Some(vec![admin_policy_doc.to_string()]),
123        context_entries: None,
124    };
125
126    let results = evaluation_service
127        .simulate_principal_policy(sim_req)
128        .await?;
129
130    println!("Action Evaluation Results:");
131    for result in &results.evaluation_results {
132        println!(
133            "  • {} on {} → {}",
134            result.eval_action_name, result.eval_resource_name, result.eval_decision
135        );
136    }
137    println!();
138
139    // ==========================================
140    // Part 4: Attach Boundary to User
141    // ==========================================
142    println!("šŸ“‹ Part 4: Attaching S3-Only Boundary to User\n");
143
144    let put_boundary_req = PutPermissionsBoundaryRequest {
145        principal_type: PrincipalType::User,
146        principal_name: "alice".to_string(),
147        permissions_boundary: s3_boundary.arn.clone(),
148    };
149    boundary_service
150        .put_permissions_boundary(put_boundary_req)
151        .await?;
152    println!("āœ… Attached permissions boundary to alice");
153    println!("   Boundary: {}", s3_boundary.arn);
154    println!("   Effect: Alice's permissions are now limited to S3 actions only\n");
155
156    // ==========================================
157    // Part 5: Test WITH Boundary
158    // ==========================================
159    println!("šŸ“‹ Part 5: Testing Permissions WITH Boundary\n");
160
161    let sim_req_with_boundary = SimulatePrincipalPolicyRequest {
162        policy_source_arn: alice.arn.clone(),
163        action_names: vec![
164            "s3:GetObject".to_string(),
165            "s3:PutObject".to_string(),
166            "ec2:RunInstances".to_string(),
167            "iam:CreateUser".to_string(),
168        ],
169        resource_arns: Some(vec!["*".to_string()]),
170        policy_input_list: Some(vec![admin_policy_doc.to_string()]),
171        context_entries: None,
172    };
173
174    let results_with_boundary = evaluation_service
175        .simulate_principal_policy(sim_req_with_boundary)
176        .await?;
177
178    println!("Action Evaluation Results (with boundary):");
179    for result in &results_with_boundary.evaluation_results {
180        let status = match result.eval_decision.as_str() {
181            "allowed" => "āœ… ALLOWED",
182            "denied" => "āŒ DENIED",
183            _ => "āš ļø  IMPLICIT DENY",
184        };
185        println!(
186            "  {} → {} ({})",
187            result.eval_action_name, status, result.eval_decision
188        );
189    }
190    println!("\nšŸ“ Notice:");
191    println!("   • S3 actions are ALLOWED (both policy and boundary allow)");
192    println!("   • EC2 and IAM actions are DENIED (boundary restricts them)\n");
193
194    // ==========================================
195    // Part 6: Boundary with Roles
196    // ==========================================
197    println!("šŸ“‹ Part 6: Using Boundaries with Roles\n");
198
199    // Create a role with assume policy
200    let assume_policy_doc = r#"{
201        "Version": "2012-10-17",
202        "Statement": [{
203            "Effect": "Allow",
204            "Principal": {"Service": "ec2.amazonaws.com"},
205            "Action": "sts:AssumeRole"
206        }]
207    }"#;
208
209    let dev_role_req = CreateRoleRequest {
210        role_name: "DeveloperRole".to_string(),
211        assume_role_policy_document: assume_policy_doc.to_string(),
212        path: Some("/roles/".to_string()),
213        description: Some("Role for developers".to_string()),
214        max_session_duration: None,
215        permissions_boundary: None,
216        tags: None,
217    };
218    let dev_role = role_service.create_role(dev_role_req).await?;
219    println!("āœ… Created role: {}", dev_role.role_name);
220    println!("   ARN: {}\n", dev_role.arn);
221
222    // Create a read-only boundary
223    let read_only_boundary_doc = r#"{
224        "Version": "2012-10-17",
225        "Statement": [{
226            "Effect": "Allow",
227            "Action": [
228                "s3:Get*",
229                "s3:List*",
230                "ec2:Describe*"
231            ],
232            "Resource": "*"
233        }]
234    }"#;
235    let read_only_boundary = policy_service
236        .create_policy(CreatePolicyRequest {
237            policy_name: "ReadOnlyBoundary".to_string(),
238            policy_document: read_only_boundary_doc.to_string(),
239            path: Some("/boundaries/".to_string()),
240            description: Some("Limits to read-only operations".to_string()),
241            tags: None,
242        })
243        .await?;
244    println!(
245        "āœ… Created read-only boundary: {}",
246        read_only_boundary.policy_name
247    );
248
249    // Attach boundary to role
250    let put_role_boundary = PutPermissionsBoundaryRequest {
251        principal_type: PrincipalType::Role,
252        principal_name: "DeveloperRole".to_string(),
253        permissions_boundary: read_only_boundary.arn.clone(),
254    };
255    boundary_service
256        .put_permissions_boundary(put_role_boundary)
257        .await?;
258    println!("āœ… Attached read-only boundary to DeveloperRole\n");
259
260    // Test role with boundary
261    let role_sim_req = SimulatePrincipalPolicyRequest {
262        policy_source_arn: dev_role.arn.clone(),
263        action_names: vec![
264            "s3:GetObject".to_string(),
265            "s3:PutObject".to_string(),
266            "ec2:DescribeInstances".to_string(),
267            "ec2:RunInstances".to_string(),
268        ],
269        resource_arns: Some(vec!["*".to_string()]),
270        policy_input_list: Some(vec![admin_policy_doc.to_string()]),
271        context_entries: None,
272    };
273
274    let role_results = evaluation_service
275        .simulate_principal_policy(role_sim_req)
276        .await?;
277
278    println!("Role Action Evaluation (with read-only boundary):");
279    for result in &role_results.evaluation_results {
280        let status = match result.eval_decision.as_str() {
281            "allowed" => "āœ… ALLOWED",
282            "denied" => "āŒ DENIED",
283            _ => "āš ļø  IMPLICIT DENY",
284        };
285        println!("  {} → {} ", result.eval_action_name, status);
286    }
287    println!("\nšŸ“ Notice:");
288    println!("   • Read operations (Get*, Describe*) are ALLOWED");
289    println!("   • Write operations (Put*, Run*) are DENIED by boundary\n");
290
291    // ==========================================
292    // Part 7: Removing Boundaries
293    // ==========================================
294    println!("šŸ“‹ Part 7: Removing Permissions Boundaries\n");
295
296    let delete_user_boundary = DeletePermissionsBoundaryRequest {
297        principal_type: PrincipalType::User,
298        principal_name: "alice".to_string(),
299    };
300    boundary_service
301        .delete_permissions_boundary(delete_user_boundary)
302        .await?;
303    println!("āœ… Removed boundary from alice");
304    println!("   Effect: Alice now has full admin permissions again\n");
305
306    let delete_role_boundary = DeletePermissionsBoundaryRequest {
307        principal_type: PrincipalType::Role,
308        principal_name: "DeveloperRole".to_string(),
309    };
310    boundary_service
311        .delete_permissions_boundary(delete_role_boundary)
312        .await?;
313    println!("āœ… Removed boundary from DeveloperRole\n");
314
315    // ==========================================
316    // Part 8: Real-World Use Cases
317    // ==========================================
318    println!("šŸ“‹ Part 8: Real-World Use Cases\n");
319    println!("Use Case 1: Sandbox Environments");
320    println!("  - Attach boundaries to prevent developers from:");
321    println!("    • Creating IAM users/roles");
322    println!("    • Modifying billing/account settings");
323    println!("    • Accessing production resources\n");
324
325    println!("Use Case 2: Contractor Access");
326    println!("  - Limit contractors to specific services:");
327    println!("    • Allow S3 and Lambda only");
328    println!("    • Prevent infrastructure changes");
329    println!("    • Ensure audit trail compliance\n");
330
331    println!("Use Case 3: Delegated Administration");
332    println!("  - Allow team leads to create users but:");
333    println!("    • Boundary prevents privilege escalation");
334    println!("    • New users inherit safe permission limits");
335    println!("    • Central security team controls boundaries\n");
336
337    println!("Use Case 4: Multi-Tenant SaaS");
338    println!("  - Each tenant gets a boundary policy:");
339    println!("    • Restricts access to tenant-specific resources");
340    println!("    • Prevents cross-tenant data access");
341    println!("    • Simplifies per-tenant permission management\n");
342
343    println!("=== Example 23 Complete ===");
344    println!("\nšŸŽ“ Key Takeaways:");
345    println!("  1. Boundaries set maximum permissions (ceiling)");
346    println!("  2. Effective permissions = identity policies ∩ boundary");
347    println!("  3. Both identity policy AND boundary must allow an action");
348    println!("  4. Boundaries prevent privilege escalation");
349    println!("  5. Use for security controls, sandboxes, and delegation");
350
351    Ok(())
352}