pub mod kv; use axum::{ body::Body, extract::{Request, State}, http::{StatusCode, Uri}, response::{IntoResponse, Response}, Extension, Router, }; use log::*; use tower::Service; use crate::{common::HttpError, storage::DatabaseDriver}; #[derive(Clone)] /// State to be used to store the database pool /// and the routers for each engine struct EngineMapperState { pool: DatabaseDriver, kv_v2: Router, } /// Secret engine router pub fn secrets_router(pool: DatabaseDriver) -> Router { // State containing the pool and engine routers let state = EngineMapperState { pool: pool.clone(), kv_v2: kv::kv_router(pool.clone()), }; Router::new().fallback(engine_handler).with_state(state) } /// Map the request to the appropriate engine and call the router async fn engine_handler( // State(pool): State, State(engines): State, req: Request, ) -> Response { if let Some((mount_path, engine_type)) = map_mount_points(req.uri(), &engines.pool).await { info!("Found mount point {} of type {}", mount_path, engine_type); // Match the engine type to the appropriate router match engine_type.as_str() { "kv_v2" => call_router(engines.kv_v2, mount_path, req).await, // Mount point exists but the type is unknown _ => unknown_engine(engine_type).into_response(), } } else { // Otherwise, the mount path could not be found HttpError::simple(StatusCode::NOT_FOUND, "Secret engine mount path not found") } } /// Helper function to call the appropriate router with the request async fn call_router(engine: Router, mount_path: String, mut req: Request) -> Response { let rui = req.uri().path().replace(&mount_path, "").parse().unwrap(); *req.uri_mut() = rui; engine .layer(Extension(mount_path)) .call(req) .await .into_response() } /// HTTP error response for unknown engine types /// Occurs when the mount path is found in the database /// but the registered is unknown fn unknown_engine(engine_type: String) -> impl IntoResponse { error!("Engine type {} not implemented", engine_type); HttpError::simple( StatusCode::INTERNAL_SERVER_ERROR, format!("Engine type {} not implemented", engine_type), ) } /// Returns the mount path and engine type for the request, /// if the mount path is registed at the database async fn map_mount_points(req: &Uri, pool: &DatabaseDriver) -> Option<(String, String)> { let mut mount_path_fragments: Vec<&str> = req.path().split('/').collect(); // Find longest matching existing mount path for the request for _ in 1..mount_path_fragments.len() { let path_str = mount_path_fragments.join("/"); let record = sqlx::query!( "SELECT engine_type FROM secret_engines WHERE mount_point = $1", path_str ) .fetch_optional(pool) .await; // Path found if let Ok(Some(row)) = record { trace!( "Mount path {} found with {:?} engine for route request: {}", mount_path_fragments.join("/"), row.engine_type, req.path() ); return Some((mount_path_fragments.join("/"), row.engine_type)); } else { // Shorten the mount path to find a shorter match mount_path_fragments.pop(); } } // If no mount path is found, return None None }