//! Control Center Main Binary //! //! JWT authentication service with user management, role-based access control, //! and real-time WebSocket events. use std::time::Duration; use std::{path::PathBuf, sync::Arc}; use axum::{ middleware, routing::{get, post}, Router, }; use clap::Parser; use control_center::handlers::{ auth::*, iac_deployment::{ create_deployment, get_deployment, get_deployment_status, list_deployments, submit_deployment, update_deployment, }, iac_detection::{analyze_project, get_detection, list_detections}, iac_rules::{ create_rule, delete_rule, get_rule, list_org_rules, list_rules, test_rule, update_rule, }, permission::list_permissions, secrets::{ create_grant, create_secret, delete_secret, force_rotate_secret, get_alert_summary, get_dashboard_metrics, get_expiring_secrets, get_rotation_status, get_secret, get_secret_history, list_secrets, restore_secret_version, revoke_grant, update_secret, }, websocket::websocket_handler, }; use control_center::middleware::{ auth::auth_middleware, cors::create_cors_from_env, rate_limit::{RateLimitConfig, RateLimitLayer}, }; use control_center::{AppState, Config, Result}; use hyper::http::StatusCode; use tokio::signal; use tower::ServiceBuilder; use tower_http::{compression::CompressionLayer, timeout::TimeoutLayer, trace::TraceLayer}; use tracing::{error, info}; use tracing_subscriber::EnvFilter; #[derive(Parser)] #[command(name = "control-center")] #[command(about = "Control Center - JWT Authentication & User Management Service")] #[command(version = env!("CARGO_PKG_VERSION"))] struct Cli { /// Configuration file path #[arg(short, long, default_value = "config.toml")] config: Option, /// Server port (overrides config file) #[arg(short, long)] port: Option, /// Server host (overrides config file) #[arg(long)] host: Option, /// Enable debug logging #[arg(short, long)] debug: bool, /// Generate default configuration file #[arg(long)] generate_config: bool, } #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); // Generate default config if requested if cli.generate_config { let config_path = cli.config.unwrap_or_else(|| PathBuf::from("config.toml")); control_center::simple_config::create_default_config_file(&config_path)?; info!("Default configuration file created at: {:?}", config_path); return Ok(()); } // Initialize logging let log_level = if cli.debug { "debug" } else { "info" }; let filter = EnvFilter::new(format!("control_center={},tower_http=info", log_level)); tracing_subscriber::fmt() .with_env_filter(filter) .with_target(false) .init(); // Load configuration let mut config = if let Some(config_path) = cli.config { Config::load_from_file(config_path)? } else { Config::load()? }; // Apply CLI overrides if let Some(port) = cli.port { config.server.port = port; } if let Some(host) = cli.host { config.server.host = host; } info!( "Starting Control Center v{} on {}", env!("CARGO_PKG_VERSION"), config.bind_address() ); // Initialize application state let app_state = Arc::new(AppState::new(config.clone()).await?); // Health check if let Err(e) = app_state.health_check().await { error!("Health check failed: {}", e); return Err(e); } info!("All services initialized successfully"); // Create router let app = create_router(app_state.clone()).await?; // Start session cleanup task start_background_tasks(app_state.clone()).await; // Start server let bind_address = config.bind_address(); let listener = tokio::net::TcpListener::bind(&bind_address).await?; info!("Control Center listening on http://{}", bind_address); info!("WebSocket endpoint available at: ws://{}/ws", bind_address); // Graceful shutdown axum::serve(listener, app) .with_graceful_shutdown(shutdown_signal()) .await?; info!("Control Center shutdown complete"); Ok(()) } /// Create the main application router async fn create_router(app_state: Arc) -> Result { // Create rate limiting layers let auth_rate_limit = RateLimitLayer::new(RateLimitConfig::auth()); let general_rate_limit = RateLimitLayer::new(RateLimitConfig::default()); // Create CORS layer let cors_layer = create_cors_from_env(); // Public routes (no authentication required) let public_routes = Router::new() .route("/health", get(health_check)) .route("/auth/login", post(login)) .route("/auth/refresh", post(refresh_token)) .layer(auth_rate_limit); // Protected routes (authentication required) let protected_routes = Router::new() // Authentication routes .route("/auth/logout", post(logout)) // .route("/auth/verify", get(verify_token)) // .route("/auth/sessions", get(get_sessions)) // .route("/auth/sessions/invalidate", post(invalidate_all_sessions)) // .route("/auth/change-password", post(change_password)) // User routes // .route("/users", get(list_users).post(create_user)) // .route("/users/me", get(get_current_user).put(update_current_user)) // .route( // "/users/:id", // get(get_user_by_id) // .put(update_user_by_id) // .delete(delete_user), // ) // .route("/users/:id/activate", post(activate_user)) // .route("/users/:id/deactivate", post(deactivate_user)) // .route("/users/:id/verify", post(verify_user)) // .route("/users/:id/roles/:role", post(add_user_role).delete(remove_user_role)) // Role routes // .route("/roles", get(list_roles).post(create_role)) // .route("/roles/:id", get(get_role_by_id).put(update_role).delete(delete_role)) // Permission routes .route("/permissions", get(list_permissions)) // .route("/permissions/resources", get(get_resources)) // .route("/permissions/actions", get(get_actions)) // Detection routes (Infrastructure-from-Code) .route("/detections", get(list_detections)) .route("/detections/:id", get(get_detection)) .route("/detections/analyze", post(analyze_project)) // Rules routes (Inference rules) .route("/rules", get(list_rules).post(create_rule)) .route("/rules/org/:org", get(list_org_rules)) .route( "/rules/:id", get(get_rule).put(update_rule).delete(delete_rule), ) .route("/rules/:id/test", post(test_rule)) // Deployment routes .route( "/deployments", get(list_deployments).post(create_deployment), ) .route( "/deployments/:id", get(get_deployment).put(update_deployment), ) .route("/deployments/:id/submit", post(submit_deployment)) .route("/deployments/:id/status", get(get_deployment_status)) // Secrets routes (Phase 1.5 - Now active with SecretsService state initialization) .route("/secrets", post(create_secret).get(list_secrets)) .route( "/secrets/:path", get(get_secret).put(update_secret).delete(delete_secret), ) .route("/secrets/:path/history", get(get_secret_history)) .route( "/secrets/:path/restore/:version", post(restore_secret_version), ) // Secrets Phase 3.1: Rotation routes .route("/secrets/:path/rotate", post(force_rotate_secret)) .route("/secrets/:path/rotation-status", get(get_rotation_status)) // Secrets Phase 3.2: Sharing routes .route("/secrets/:path/grant", post(create_grant)) .route("/secrets/grant/:grant_id/revoke", post(revoke_grant)) // Secrets Phase 3.4: Monitoring routes .route("/secrets/monitoring/dashboard", get(get_dashboard_metrics)) .route("/secrets/monitoring/alerts", get(get_alert_summary)) .route("/secrets/monitoring/expiring", get(get_expiring_secrets)) // WebSocket route .route("/ws", get(websocket_handler)) // Apply authentication middleware to all protected routes .route_layer(middleware::from_fn_with_state( app_state.jwt_service.clone(), auth_middleware, )); // Combine all routes let app = Router::new() .merge(public_routes) .merge(protected_routes) .layer( ServiceBuilder::new() .layer(TraceLayer::new_for_http()) .layer(CompressionLayer::new()) .layer(TimeoutLayer::with_status_code( StatusCode::REQUEST_TIMEOUT, Duration::from_secs(30), )) .layer(cors_layer) .layer(general_rate_limit), ) .with_state(app_state); Ok(app) } /// Start background tasks async fn start_background_tasks(app_state: Arc) { let cleanup_interval = Duration::from_secs(app_state.config.security.session_cleanup_interval_minutes * 60); // Session cleanup task let app_state_cleanup = app_state.clone(); tokio::spawn(async move { let mut interval = tokio::time::interval(cleanup_interval); loop { interval.tick().await; if let Err(e) = app_state_cleanup .auth_service .cleanup_expired_sessions() .await { error!("Failed to cleanup expired sessions: {}", e); } else { info!("Cleaned up expired sessions"); } } }); info!("Background tasks started"); } /// Graceful shutdown signal handler async fn shutdown_signal() { let ctrl_c = async { signal::ctrl_c() .await .expect("Failed to install Ctrl+C handler"); }; #[cfg(unix)] let terminate = async { signal::unix::signal(signal::unix::SignalKind::terminate()) .expect("Failed to install signal handler") .recv() .await; }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { _ = ctrl_c => {}, _ = terminate => {}, } info!("Shutdown signal received, starting graceful shutdown"); }