04_simple_multi_tenant/
04_simple_multi_tenant.rs1use std::sync::Arc;
13use tokio::sync::RwLock;
14use wami::arn::{TenantPath, WamiArn};
15use wami::context::WamiContext;
16use wami::service::{TenantService, UserService};
17use wami::store::memory::InMemoryWamiStore;
18use wami::wami::identity::user::requests::{CreateUserRequest, ListUsersRequest};
19
20#[tokio::main]
21async fn main() -> Result<(), Box<dyn std::error::Error>> {
22 println!("=== Simple Multi-Tenant Setup ===\n");
23
24 let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
25
26 let root_context = WamiContext::builder()
28 .instance_id("123456789012")
29 .tenant_path(TenantPath::single(0)) .caller_arn(
31 WamiArn::builder()
32 .service(wami::arn::Service::Iam)
33 .tenant_path(TenantPath::single(0))
34 .wami_instance("123456789012")
35 .resource("user", "admin")
36 .build()?,
37 )
38 .is_root(true)
39 .build()?;
40
41 println!("Step 1: Creating tenants...\n");
43
44 let tenant_service = TenantService::new(store.clone());
45
46 let company_a = tenant_service
48 .create_tenant(
49 &root_context,
50 "company-a".to_string(),
51 Some("Company A Corp".to_string()),
52 None, )
54 .await?;
55 let company_a_id = company_a.id.clone();
56 println!("✓ Created tenant: company-a (ID: {})", company_a.id);
57
58 let company_b = tenant_service
60 .create_tenant(
61 &root_context,
62 "company-b".to_string(),
63 Some("Company B Inc".to_string()),
64 None,
65 )
66 .await?;
67 let company_b_id = company_b.id.clone();
68 println!("✓ Created tenant: company-b (ID: {})", company_b.id);
69
70 println!("\nStep 2: Creating users in each tenant...\n");
72
73 let company_a_tenant_id = company_a_id.segments()[0];
75 let company_b_tenant_id = company_b_id.segments()[0];
76
77 let company_a_context = WamiContext::builder()
79 .instance_id("123456789012")
80 .tenant_path(TenantPath::single(company_a_tenant_id))
81 .caller_arn(
82 WamiArn::builder()
83 .service(wami::arn::Service::Iam)
84 .tenant_path(TenantPath::single(company_a_tenant_id))
85 .wami_instance("123456789012")
86 .resource("user", "admin")
87 .build()?,
88 )
89 .is_root(false)
90 .build()?;
91
92 let company_b_context = WamiContext::builder()
94 .instance_id("123456789012")
95 .tenant_path(TenantPath::single(company_b_tenant_id))
96 .caller_arn(
97 WamiArn::builder()
98 .service(wami::arn::Service::Iam)
99 .tenant_path(TenantPath::single(company_b_tenant_id))
100 .wami_instance("123456789012")
101 .resource("user", "admin")
102 .build()?,
103 )
104 .is_root(false)
105 .build()?;
106
107 println!("Creating users for Company A...");
109 let user_service = UserService::new(store.clone());
110
111 let alice_req = CreateUserRequest {
112 user_name: "alice".to_string(),
113 path: Some("/company-a/".to_string()),
114 permissions_boundary: None,
115 tags: None,
116 };
117 let alice = user_service
118 .create_user(&company_a_context, alice_req)
119 .await?;
120 println!("✓ Created alice in company-a");
121 println!(" ARN: {}", alice.arn);
122
123 let bob_req = CreateUserRequest {
124 user_name: "bob".to_string(),
125 path: Some("/company-a/".to_string()),
126 permissions_boundary: None,
127 tags: None,
128 };
129 user_service
130 .create_user(&company_a_context, bob_req)
131 .await?;
132 println!("✓ Created bob in company-a");
133
134 println!("\nCreating users for Company B...");
136
137 let charlie_req = CreateUserRequest {
138 user_name: "charlie".to_string(),
139 path: Some("/company-b/".to_string()),
140 permissions_boundary: None,
141 tags: None,
142 };
143 let charlie = user_service
144 .create_user(&company_b_context, charlie_req)
145 .await?;
146 println!("✓ Created charlie in company-b");
147 println!(" ARN: {}", charlie.arn);
148
149 let diana_req = CreateUserRequest {
150 user_name: "diana".to_string(),
151 path: Some("/company-b/".to_string()),
152 permissions_boundary: None,
153 tags: None,
154 };
155 user_service
156 .create_user(&company_b_context, diana_req)
157 .await?;
158 println!("✓ Created diana in company-b");
159
160 println!("\n\nStep 3: Demonstrating tenant isolation...\n");
162
163 let (all_users, _, _) = user_service
165 .list_users(
166 &root_context,
167 ListUsersRequest {
168 path_prefix: None,
169 pagination: None,
170 },
171 )
172 .await?;
173 println!("Total users across all tenants: {}", all_users.len());
174
175 let (company_a_users, _, _) = user_service
177 .list_users(
178 &company_a_context,
179 ListUsersRequest {
180 path_prefix: Some("/company-a/".to_string()),
181 pagination: None,
182 },
183 )
184 .await?;
185 println!(
186 "\nCompany A users (filtered by path): {}",
187 company_a_users.len()
188 );
189 for user in &company_a_users {
190 println!(" - {} (path: {})", user.user_name, user.path);
191 }
192
193 let (company_b_users, _, _) = user_service
195 .list_users(
196 &company_b_context,
197 ListUsersRequest {
198 path_prefix: Some("/company-b/".to_string()),
199 pagination: None,
200 },
201 )
202 .await?;
203 println!(
204 "\nCompany B users (filtered by path): {}",
205 company_b_users.len()
206 );
207 for user in &company_b_users {
208 println!(" - {} (path: {})", user.user_name, user.path);
209 }
210
211 println!("\n\nStep 4: Listing all tenants...\n");
213 let all_tenants = tenant_service.list_tenants().await?;
214 println!("✓ Found {} tenants:", all_tenants.len());
215 for tenant in &all_tenants {
216 println!(" - {} ({})", tenant.name, tenant.id);
217 }
218
219 println!("\n✅ Example completed successfully!");
220 println!("Key takeaways:");
221 println!("- Each tenant has its own namespace for resources");
222 println!("- WamiContext is used to scope operations to specific tenants");
223 println!("- ARNs reflect the tenant ownership via tenant_path");
224 println!("- Tenant isolation ensures data security in multi-tenant apps");
225
226 Ok(())
227}