feat(dev) engines: "Dynamic Routing"

This commit is contained in:
Laurenz 2024-05-05 17:52:57 +02:00
parent 55270c0637
commit 6755e61163
3 changed files with 62 additions and 13 deletions

View file

@ -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');

View file

@ -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<String>,
}
/// Secret engine router
pub fn secrets_router(pool: DatabaseDriver) -> Router<DatabaseDriver> {
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<DatabaseDriver>, req: Request) -> Response<Body> {
match map_mount_points(req.uri(), &pool).await {
Some((mount_path, engine_type)) => {
/// Map the request to the appropriate engine and call the router
async fn engine_handler(
// State(pool): State<DatabaseDriver>,
State(engines): State<EngineMapperState>,
req: Request,
) -> Response<Body> {
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);
todo!()
// 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<String>, 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
}

View file

@ -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<DatabaseDriver> {
pub fn kv_router(pool: DatabaseDriver) -> Router<String> {
Router::new()
.route("/:mount_path/config", get(get_config))
.route("/:mount_path/config", post(post_config))