wami 0.10.0

Who Am I - Multicloud Identity, IAM, STS, and SSO operations library for Rust
Documentation
//! Hybrid Cloud Setup
//!
//! This example demonstrates:
//! - Implementing a custom CloudProvider for on-premise systems
//! - Mixing custom providers with public cloud providers
//! - Federating identities across hybrid environments
//!
//! Scenario: Company with on-premise datacenter and AWS cloud.
//!
//! Run with: `cargo run --example 11_hybrid_cloud_setup`

use std::sync::{Arc, RwLock};
use wami::error::Result;
use wami::provider::{AwsProvider, CloudProvider, ResourceLimits, ResourceType};
use wami::service::UserService;
use wami::store::memory::InMemoryWamiStore;
use wami::wami::identity::user::requests::{CreateUserRequest, ListUsersRequest};

// === CUSTOM ON-PREMISE PROVIDER ===
#[derive(Debug, Clone)]
struct OnPremiseProvider {
    #[allow(dead_code)]
    datacenter_id: String,
    limits: ResourceLimits,
}

impl OnPremiseProvider {
    fn new(datacenter_id: String) -> Self {
        Self {
            datacenter_id,
            limits: ResourceLimits::default(),
        }
    }
}

impl CloudProvider for OnPremiseProvider {
    fn name(&self) -> &str {
        "onpremise"
    }

    fn generate_resource_identifier(
        &self,
        resource_type: ResourceType,
        account_id: &str,
        path: &str,
        name: &str,
    ) -> String {
        let resource_name = match resource_type {
            ResourceType::User => "user",
            ResourceType::Group => "group",
            ResourceType::Role => "role",
            ResourceType::Policy => "policy",
            _ => "resource",
        };
        format!(
            "arn:onprem:iam::{}:{}{}{}",
            account_id, resource_name, path, name
        )
    }

    fn generate_resource_id(&self, resource_type: ResourceType) -> String {
        let prefix = match resource_type {
            ResourceType::User => "ONPU",
            ResourceType::Group => "ONPG",
            ResourceType::Role => "ONPR",
            ResourceType::Policy => "ONPP",
            _ => "ONPR",
        };
        let uuid_part = uuid::Uuid::new_v4()
            .to_string()
            .replace('-', "")
            .chars()
            .take(17)
            .collect::<String>()
            .to_uppercase();
        format!("{}{}", prefix, uuid_part)
    }

    fn resource_limits(&self) -> &ResourceLimits {
        &self.limits
    }

    fn validate_service_name(&self, _service: &str) -> Result<()> {
        // On-premise systems can have custom service names
        Ok(())
    }

    fn validate_path(&self, path: &str) -> Result<()> {
        // Use AWS-style path validation for consistency
        if !path.starts_with('/') || !path.ends_with('/') {
            return Err(wami::error::AmiError::InvalidParameter {
                message: format!(
                    "Invalid path: '{}'. Paths must start and end with '/'",
                    path
                ),
            });
        }
        Ok(())
    }

    fn generate_service_linked_role_name(
        &self,
        service_name: &str,
        custom_suffix: Option<&str>,
    ) -> String {
        if let Some(suffix) = custom_suffix {
            format!("OnPremServiceRoleFor{}_{}", service_name, suffix)
        } else {
            format!("OnPremServiceRoleFor{}", service_name)
        }
    }

    fn generate_service_linked_role_path(&self, service_name: &str) -> String {
        format!("/onprem-service-role/{}/", service_name)
    }
}

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    println!("=== Hybrid Cloud Setup ===\n");

    let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));

    // === INITIALIZE PROVIDERS ===
    println!("Step 1: Initializing hybrid cloud providers...\n");

    let _onprem_provider = Arc::new(OnPremiseProvider::new("dc1-prod".to_string()));
    let _aws_provider = Arc::new(AwsProvider::new());

    println!("✓ Initialized providers:");
    println!("  - On-Premise (datacenter: dc1-prod)");
    println!("  - AWS (public cloud)");

    // === CREATE USER IN ON-PREMISE ===
    println!("\n\nStep 2: Creating user in on-premise environment...\n");

    let onprem_service = UserService::new(store.clone(), "dc1-prod".to_string());

    let onprem_req = CreateUserRequest {
        user_name: "alice-onprem".to_string(),
        path: Some("/employees/".to_string()),
        permissions_boundary: None,
        tags: Some(vec![
            wami::types::Tag {
                key: "Environment".to_string(),
                value: "OnPremise".to_string(),
            },
            wami::types::Tag {
                key: "Datacenter".to_string(),
                value: "dc1-prod".to_string(),
            },
        ]),
    };

    let alice_onprem = onprem_service.create_user(onprem_req).await?;
    println!("✓ Created alice-onprem in on-premise:");
    println!("  - ARN: {}", alice_onprem.arn);
    println!("  - Resource ID: {}", alice_onprem.user_id);

    // === CREATE USER IN AWS ===
    println!("\n\nStep 3: Creating user in AWS cloud...\n");

    let aws_service = UserService::new(store.clone(), "123456789012".to_string());

    let aws_req = CreateUserRequest {
        user_name: "alice-cloud".to_string(),
        path: Some("/employees/".to_string()),
        permissions_boundary: None,
        tags: Some(vec![
            wami::types::Tag {
                key: "Environment".to_string(),
                value: "AWS".to_string(),
            },
            wami::types::Tag {
                key: "Region".to_string(),
                value: "us-east-1".to_string(),
            },
        ]),
    };

    let alice_aws = aws_service.create_user(aws_req).await?;
    println!("✓ Created alice-cloud in AWS:");
    println!("  - ARN: {}", alice_aws.arn);
    println!("  - Resource ID: {}", alice_aws.user_id);

    // === DEMONSTRATE FEDERATED IDENTITY ===
    println!("\n\nStep 4: Understanding hybrid identity federation...\n");

    println!("Both identities represent the same person (alice@company.com):");
    println!();
    println!("On-Premise Identity:");
    println!("  - ARN: {}", alice_onprem.arn);
    println!("  - For: Legacy applications, internal systems");
    println!();
    println!("Cloud Identity:");
    println!("  - ARN: {}", alice_aws.arn);
    println!("  - For: Cloud-native applications, external APIs");

    // === LIST ALL USERS ===
    println!("\n\nStep 5: Unified view across hybrid environment...\n");

    let (all_users, _, _) = onprem_service
        .list_users(ListUsersRequest {
            path_prefix: None,
            pagination: None,
        })
        .await?;
    println!(
        "✓ Total users across hybrid environment: {}",
        all_users.len()
    );
    for user in &all_users {
        let env = if user.arn.contains("onprem") {
            "On-Premise"
        } else {
            "Cloud"
        };
        println!("  - {} ({}) → {}", user.user_name, env, user.arn);
    }

    // === USE CASES ===
    println!("\n\nStep 6: Hybrid cloud use cases...\n");

    println!("Custom providers enable:");
    println!("- On-premise to cloud migration paths");
    println!("- Unified identity management");
    println!("- Hybrid application architectures");
    println!("- Edge computing with centralized IAM");
    println!("- Custom platform integration (Kubernetes, OpenStack, etc.)");

    println!("\n✅ Example completed successfully!");
    println!("Key takeaways:");
    println!("- CloudProvider trait enables custom platform support");
    println!("- Mix custom and public cloud providers freely");
    println!("- WAMI ARNs provide unified identity layer");
    println!("- Same store works across all provider types");

    Ok(())
}