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