rvault/src/engines.rs
2024-05-05 18:04:25 +02:00

109 lines
3.4 KiB
Rust

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::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<DatabaseDriver> {
// 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<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);
// 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
(StatusCode::NOT_FOUND, "Mount path not found").into_response()
}
}
/// 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) -> (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();
// 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
}