From 6755e611633da0973efe052813b44ab56fe4b3c9 Mon Sep 17 00:00:00 2001 From: C0ffeeCode Date: Sun, 5 May 2024 17:52:57 +0200 Subject: [PATCH] feat(dev) engines: "Dynamic Routing" --- migrations/20240428160456_init.sql | 2 +- src/engines.rs | 71 +++++++++++++++++++++++++----- src/engines/kv.rs | 2 +- 3 files changed, 62 insertions(+), 13 deletions(-) diff --git a/migrations/20240428160456_init.sql b/migrations/20240428160456_init.sql index 95f17bd..c061eb1 100644 --- a/migrations/20240428160456_init.sql +++ b/migrations/20240428160456_init.sql @@ -5,4 +5,4 @@ CREATE TABLE secret_engines ( engine_type TEXT NOT NULL ); -INSERT INTO secret_engines (mount_point, engine_type) VALUES ('/kv-v2', 'kv'); +INSERT INTO secret_engines (mount_point, engine_type) VALUES ('/kv-v2', 'kv_v2'); diff --git a/src/engines.rs b/src/engines.rs index 6ddc717..ae6fc51 100644 --- a/src/engines.rs +++ b/src/engines.rs @@ -3,30 +3,76 @@ pub mod kv; use axum::{ body::Body, extract::{Request, State}, - http::Uri, - response::Response, + http::{StatusCode, Uri}, + response::{IntoResponse, Response}, Router, }; use log::*; +use tower::Service; use crate::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 { - Router::new().fallback(engine_handler).with_state(pool) + // 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) } -async fn engine_handler(State(pool): State, req: Request) -> Response { - match map_mount_points(req.uri(), &pool).await { - Some((mount_path, engine_type)) => { - info!("Found mount point {} of type {}", mount_path, engine_type); - todo!() +/// 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(), } - None => todo!(), + } else { + // Otherwise, the mount path could not be found + (StatusCode::NOT_FOUND, "Mount path not found").into_response() } - - // Response::new(Body::from("Mount path:")) } +/// Helper function to call the appropriate router with the request +async fn call_router(engine: Router, mount_path: String, req: Request) -> Response { + engine + .with_state(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) -> (StatusCode, String) { + error!("Engine type {} not implemented", engine_type); + ( + 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(); @@ -40,6 +86,7 @@ async fn map_mount_points(req: &Uri, pool: &DatabaseDriver) -> Option<(String, S .fetch_optional(pool) .await; + // Path found if let Ok(Some(row)) = record { trace!( "Mount path {} found with {:?} engine for route request: {}", @@ -50,8 +97,10 @@ async fn map_mount_points(req: &Uri, pool: &DatabaseDriver) -> Option<(String, S 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 } diff --git a/src/engines/kv.rs b/src/engines/kv.rs index 77880d4..ca3e77b 100644 --- a/src/engines/kv.rs +++ b/src/engines/kv.rs @@ -11,7 +11,7 @@ use crate::engines::kv::structs::KvSecret; use crate::{engines::kv::logic::body_to_json, storage::DatabaseDriver}; use axum::{extract::Path, routing::*, Router}; -pub fn kv_router(pool: DatabaseDriver) -> Router { +pub fn kv_router(pool: DatabaseDriver) -> Router { Router::new() .route("/:mount_path/config", get(get_config)) .route("/:mount_path/config", post(post_config))