Skip to main content

11_hybrid_cloud_setup/
11_hybrid_cloud_setup.rs

1//! Hybrid Cloud Setup
2//!
3//! This example demonstrates:
4//! - Implementing a custom CloudProvider for on-premise systems
5//! - Mixing custom providers with public cloud providers
6//! - Federating identities across hybrid environments
7//!
8//! Scenario: Company with on-premise datacenter and AWS cloud.
9//!
10//! Run with: `cargo run --example 11_hybrid_cloud_setup`
11
12use std::sync::Arc;
13use tokio::sync::RwLock;
14use wami::error::Result;
15use wami::provider::{AwsProvider, CloudProvider, ResourceLimits, ResourceType};
16use wami::service::UserService;
17use wami::store::memory::InMemoryWamiStore;
18use wami::wami::identity::user::requests::{CreateUserRequest, ListUsersRequest};
19
20// === CUSTOM ON-PREMISE PROVIDER ===
21#[derive(Debug, Clone)]
22struct OnPremiseProvider {
23    #[allow(dead_code)]
24    datacenter_id: String,
25    limits: ResourceLimits,
26}
27
28impl OnPremiseProvider {
29    fn new(datacenter_id: String) -> Self {
30        Self {
31            datacenter_id,
32            limits: ResourceLimits::default(),
33        }
34    }
35}
36
37impl CloudProvider for OnPremiseProvider {
38    fn name(&self) -> &str {
39        "onpremise"
40    }
41
42    fn generate_resource_identifier(
43        &self,
44        resource_type: ResourceType,
45        account_id: &str,
46        path: &str,
47        name: &str,
48    ) -> String {
49        let resource_name = match resource_type {
50            ResourceType::User => "user",
51            ResourceType::Group => "group",
52            ResourceType::Role => "role",
53            ResourceType::Policy => "policy",
54            _ => "resource",
55        };
56        format!(
57            "arn:onprem:iam::{}:{}{}{}",
58            account_id, resource_name, path, name
59        )
60    }
61
62    fn generate_resource_id(&self, resource_type: ResourceType) -> String {
63        let prefix = match resource_type {
64            ResourceType::User => "ONPU",
65            ResourceType::Group => "ONPG",
66            ResourceType::Role => "ONPR",
67            ResourceType::Policy => "ONPP",
68            _ => "ONPR",
69        };
70        let uuid_part = uuid::Uuid::new_v4()
71            .to_string()
72            .replace('-', "")
73            .chars()
74            .take(17)
75            .collect::<String>()
76            .to_uppercase();
77        format!("{}{}", prefix, uuid_part)
78    }
79
80    fn resource_limits(&self) -> &ResourceLimits {
81        &self.limits
82    }
83
84    fn validate_service_name(&self, _service: &str) -> Result<()> {
85        // On-premise systems can have custom service names
86        Ok(())
87    }
88
89    fn validate_path(&self, path: &str) -> Result<()> {
90        // Use AWS-style path validation for consistency
91        if !path.starts_with('/') || !path.ends_with('/') {
92            return Err(wami::error::AmiError::InvalidParameter {
93                message: format!(
94                    "Invalid path: '{}'. Paths must start and end with '/'",
95                    path
96                ),
97            });
98        }
99        Ok(())
100    }
101
102    fn generate_service_linked_role_name(
103        &self,
104        service_name: &str,
105        custom_suffix: Option<&str>,
106    ) -> String {
107        if let Some(suffix) = custom_suffix {
108            format!("OnPremServiceRoleFor{}_{}", service_name, suffix)
109        } else {
110            format!("OnPremServiceRoleFor{}", service_name)
111        }
112    }
113
114    fn generate_service_linked_role_path(&self, service_name: &str) -> String {
115        format!("/onprem-service-role/{}/", service_name)
116    }
117}
118
119#[tokio::main]
120async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
121    println!("=== Hybrid Cloud Setup ===\n");
122
123    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
124
125    // === INITIALIZE PROVIDERS (for ARN transformation) ===
126    println!("Step 1: Note about providers...\n");
127
128    let _onprem_provider = Arc::new(OnPremiseProvider::new("dc1-prod".to_string()));
129    let _aws_provider = Arc::new(AwsProvider::new());
130
131    println!("✓ Providers can be used for ARN transformation:");
132    println!("  - On-Premise (datacenter: dc1-prod)");
133    println!("  - AWS (public cloud)");
134    println!("  - Services now use WamiContext instead of providers");
135
136    // Create on-premise context
137    let onprem_context = wami::context::WamiContext::builder()
138        .instance_id("dc1-prod")
139        .tenant_path(wami::arn::TenantPath::single(60000001)) // Numeric tenant ID for on-prem
140        .caller_arn(
141            wami::arn::WamiArn::builder()
142                .service(wami::arn::Service::Iam)
143                .tenant_path(wami::arn::TenantPath::single(60000001))
144                .wami_instance("dc1-prod")
145                .resource("user", "admin")
146                .build()?,
147        )
148        .is_root(false)
149        .build()?;
150
151    // Create AWS context
152    let aws_context = wami::context::WamiContext::builder()
153        .instance_id("123456789012")
154        .tenant_path(wami::arn::TenantPath::single(70000001)) // Numeric tenant ID for AWS
155        .caller_arn(
156            wami::arn::WamiArn::builder()
157                .service(wami::arn::Service::Iam)
158                .tenant_path(wami::arn::TenantPath::single(70000001))
159                .wami_instance("123456789012")
160                .resource("user", "admin")
161                .build()?,
162        )
163        .is_root(false)
164        .build()?;
165
166    // === CREATE USER IN ON-PREMISE ===
167    println!("\n\nStep 2: Creating user in on-premise environment...\n");
168
169    let user_service = UserService::new(store.clone());
170
171    let onprem_req = CreateUserRequest {
172        user_name: "alice-onprem".to_string(),
173        path: Some("/employees/".to_string()),
174        permissions_boundary: None,
175        tags: Some(vec![
176            wami::types::Tag {
177                key: "Environment".to_string(),
178                value: "OnPremise".to_string(),
179            },
180            wami::types::Tag {
181                key: "Datacenter".to_string(),
182                value: "dc1-prod".to_string(),
183            },
184        ]),
185    };
186
187    let alice_onprem = user_service
188        .create_user(&onprem_context, onprem_req)
189        .await?;
190    println!("✓ Created alice-onprem in on-premise:");
191    println!("  - ARN: {}", alice_onprem.arn);
192    println!("  - WAMI ARN: {}", alice_onprem.wami_arn);
193    println!("  - Resource ID: {}", alice_onprem.user_id);
194
195    // === CREATE USER IN AWS ===
196    println!("\n\nStep 3: Creating user in AWS cloud...\n");
197
198    let aws_req = CreateUserRequest {
199        user_name: "alice-cloud".to_string(),
200        path: Some("/employees/".to_string()),
201        permissions_boundary: None,
202        tags: Some(vec![
203            wami::types::Tag {
204                key: "Environment".to_string(),
205                value: "AWS".to_string(),
206            },
207            wami::types::Tag {
208                key: "Region".to_string(),
209                value: "us-east-1".to_string(),
210            },
211        ]),
212    };
213
214    let alice_aws = user_service.create_user(&aws_context, aws_req).await?;
215    println!("✓ Created alice-cloud in AWS:");
216    println!("  - ARN: {}", alice_aws.arn);
217    println!("  - WAMI ARN: {}", alice_aws.wami_arn);
218    println!("  - Resource ID: {}", alice_aws.user_id);
219
220    // === DEMONSTRATE FEDERATED IDENTITY ===
221    println!("\n\nStep 4: Understanding hybrid identity federation...\n");
222
223    println!("Both identities represent the same person (alice@company.com):");
224    println!();
225    println!("On-Premise Identity:");
226    println!("  - ARN: {}", alice_onprem.arn);
227    println!("  - For: Legacy applications, internal systems");
228    println!();
229    println!("Cloud Identity:");
230    println!("  - ARN: {}", alice_aws.arn);
231    println!("  - For: Cloud-native applications, external APIs");
232
233    // === LIST ALL USERS ===
234    println!("\n\nStep 4: Unified view across hybrid environment...\n");
235
236    let (all_users, _, _) = user_service
237        .list_users(
238            &onprem_context,
239            ListUsersRequest {
240                path_prefix: None,
241                pagination: None,
242            },
243        )
244        .await?;
245    println!(
246        "✓ Total users across hybrid environment: {}",
247        all_users.len()
248    );
249    for user in &all_users {
250        let env = if user.wami_arn.to_string().contains("onprem") {
251            "On-Premise"
252        } else {
253            "Cloud"
254        };
255        println!("  - {} ({}) → {}", user.user_name, env, user.wami_arn);
256    }
257
258    // === USE CASES ===
259    println!("\n\nStep 5: Hybrid cloud use cases...\n");
260
261    println!("WAMI context-based architecture enables:");
262    println!("- On-premise to cloud migration paths");
263    println!("- Unified identity management with WamiContext");
264    println!("- Hybrid application architectures");
265    println!("- Edge computing with centralized IAM");
266    println!("- Consistent resource identification with WAMI ARNs");
267
268    println!("\n✅ Example completed successfully!");
269    println!("Key takeaways:");
270    println!("- WamiContext replaces provider-specific service configuration");
271    println!("- Use different contexts for different environments");
272    println!("- WAMI ARNs provide unified identity layer");
273    println!("- Same store works across all contexts");
274    println!("- Providers can still be used for ARN transformation when needed");
275
276    Ok(())
277}