use axum::{ Router, routing::{get, post, put}, }; use utoipa::OpenApi; use utoipa_scalar::{Scalar, Servable}; use crate::constants::CANONICAL_BASE; use crate::docs::ApiDoc; use crate::middleware as core_middleware; use crate::presentation::http::handlers::{ app, auth, authorization, client, jwks, permission, platform, role, sso, tenant, user, }; use crate::presentation::http::state::AppState; pub fn routes() -> Vec<(&'static str, &'static str)> { vec![ ("GET", "/.well-known/jwks.json"), ("POST", "/tenants/register"), ("POST", "/auth/register"), ("POST", "/auth/login"), ("POST", "/auth/login-code"), ("POST", "/auth/refresh"), ("POST", "/auth/logout"), ("POST", "/auth/code2token"), ("POST", "/internal/auth/code2token"), ("GET", "/me/permissions"), ("POST", "/authorize/check"), ("GET", "/users"), ("GET", "/users/{id}"), ("PATCH", "/users/{id}"), ("DELETE", "/users/{id}"), ("POST", "/users/me/password/reset"), ("POST", "/users/{id}/password/reset"), ("GET", "/users/{id}/roles"), ("PUT", "/users/{id}/roles"), ("GET", "/permissions"), ("GET", "/roles"), ("POST", "/roles"), ("GET", "/roles/{id}"), ("PATCH", "/roles/{id}"), ("DELETE", "/roles/{id}"), ("POST", "/roles/{id}/permissions/grant"), ("POST", "/roles/{id}/permissions/revoke"), ("POST", "/roles/{id}/users/grant"), ("POST", "/roles/{id}/users/revoke"), ("GET", "/platform/tenants/{tenant_id}/enabled-apps"), ("PUT", "/platform/tenants/{tenant_id}/enabled-apps"), ("GET", "/platform/clients"), ("POST", "/platform/clients"), ("PUT", "/platform/clients/{client_id}/redirect-uris"), ("POST", "/platform/clients/{client_id}/rotate-secret"), ("GET", "/platform/apps"), ("POST", "/platform/apps"), ("GET", "/platform/apps/{app_id}"), ("PATCH", "/platform/apps/{app_id}"), ("DELETE", "/platform/apps/{app_id}"), ("POST", "/platform/apps/{app_id}/status-change-requests"), ("GET", "/platform/app-status-change-requests"), ( "POST", "/platform/app-status-change-requests/{request_id}/approve", ), ( "POST", "/platform/app-status-change-requests/{request_id}/reject", ), ] } pub fn build_app(state: AppState) -> Router { let tenant_required_auth = Router::new() .route( "/register", post(auth::register_handler) .layer(core_middleware::rate_limit::register_rate_limiter()) .layer(axum::middleware::from_fn( core_middleware::rate_limit::log_rate_limit_register, )), ) .route( "/login", post(auth::login_handler) .layer(core_middleware::rate_limit::login_rate_limiter()) .layer(axum::middleware::from_fn( core_middleware::rate_limit::log_rate_limit_login, )), ) .route( "/login-code", post(sso::login_code_handler) .layer(core_middleware::rate_limit::login_rate_limiter()) .layer(axum::middleware::from_fn( core_middleware::rate_limit::log_rate_limit_login, )), ) .route("/code2token", post(sso::code2token_handler)) .layer(axum::middleware::from_fn( core_middleware::auth_tenant::validate_auth_tenant, )); let no_tenant_auth = Router::new().route( "/refresh", post(auth::refresh_handler) .layer(core_middleware::rate_limit::login_rate_limiter()) .layer(axum::middleware::from_fn( core_middleware::rate_limit::log_rate_limit_login, )), ); let public_v1 = Router::new() .route("/.well-known/jwks.json", get(jwks::jwks_handler)) .route("/tenants/register", post(tenant::create_tenant_handler)) .route( "/internal/auth/code2token", post(sso::internal_code2token_handler), ) .nest( "/auth", Router::new() .merge(tenant_required_auth) .merge(no_tenant_auth), ) .with_state(state.clone()); let auth_cfg = core_middleware::auth::AuthMiddlewareConfig { skip_exact_paths: vec![], skip_path_prefixes: vec![], jwt: auth_kit::jwt::JwtVerifyConfig::rs256_from_pem( "iam-service", &crate::utils::keys::get_keys().public_pem, ) .expect("invalid JWT_PUBLIC_KEY_PEM"), }; let tenant_cfg = core_middleware::TenantMiddlewareConfig { skip_exact_paths: vec![], skip_path_prefixes: vec![], }; let protected_v1 = Router::new() .route("/auth/logout", post(auth::logout_handler)) .route("/me/permissions", get(authorization::my_permissions_handler)) .route( "/authorize/check", post(authorization::authorization_check_handler), ) .route("/users", get(user::list_users_handler)) .route( "/users/me/password/reset", post(user::reset_my_password_handler), ) .route("/permissions", get(permission::list_permissions_handler)) .route( "/users/{id}", get(user::get_user_handler) .patch(user::update_user_handler) .delete(user::delete_user_handler), ) .route( "/users/{id}/password/reset", post(user::reset_user_password_handler), ) .route( "/users/{id}/roles", get(user::list_user_roles_handler).put(user::set_user_roles_handler), ) .route( "/roles", get(role::list_roles_handler).post(role::create_role_handler), ) .route( "/roles/{id}", get(role::get_role_handler) .patch(role::update_role_handler) .delete(role::delete_role_handler), ) .route( "/roles/{id}/permissions/grant", post(role::grant_role_permissions_handler), ) .route( "/roles/{id}/permissions/revoke", post(role::revoke_role_permissions_handler), ) .route("/roles/{id}/users/grant", post(role::grant_role_users_handler)) .route( "/roles/{id}/users/revoke", post(role::revoke_role_users_handler), ) .route( "/tenants/me", get(tenant::get_tenant_handler) .patch(tenant::update_tenant_handler) .delete(tenant::delete_tenant_handler), ) .route("/tenants/me/status", post(tenant::update_tenant_status_handler)) .layer(axum::middleware::from_fn_with_state( auth_cfg.clone(), core_middleware::auth::authenticate_with_config, )) .layer(axum::middleware::from_fn_with_state( tenant_cfg.clone(), core_middleware::resolve_tenant_with_config, )) .with_state(state.clone()); let platform_v1 = Router::new() .route( "/platform/tenants/{tenant_id}/enabled-apps", get(platform::get_tenant_enabled_apps_handler) .put(platform::set_tenant_enabled_apps_handler), ) .route( "/platform/clients", get(client::list_clients_handler).post(client::create_client_handler), ) .route( "/platform/clients/{client_id}/rotate-secret", post(client::rotate_client_secret_handler), ) .route( "/platform/clients/{client_id}/redirect-uris", put(client::update_client_redirect_uris_handler), ) .route("/platform/apps", get(app::list_apps_handler).post(app::create_app_handler)) .route( "/platform/apps/{app_id}", get(app::get_app_handler) .patch(app::update_app_handler) .delete(app::delete_app_handler), ) .route( "/platform/apps/{app_id}/status-change-requests", post(app::request_app_status_change_handler), ) .route( "/platform/app-status-change-requests", get(app::list_app_status_change_requests_handler), ) .route( "/platform/app-status-change-requests/{request_id}/approve", post(app::approve_app_status_change_handler), ) .route( "/platform/app-status-change-requests/{request_id}/reject", post(app::reject_app_status_change_handler), ) .layer(axum::middleware::from_fn_with_state( auth_cfg, core_middleware::auth::authenticate_with_config, )) .with_state(state.clone()); let v1 = Router::new() .merge(public_v1) .merge(protected_v1) .merge(platform_v1) .layer(axum::middleware::from_fn( common_telemetry::axum_middleware::trace_http_request, )); Router::new() .route( "/favicon.ico", get(|| async { axum::http::StatusCode::NO_CONTENT }), ) .merge(Scalar::with_url("/scalar", ApiDoc::openapi())) .nest(CANONICAL_BASE, v1) .with_state(state) }