rvault/src/engines/kv.rs
2024-05-30 15:26:12 +02:00

205 lines
6 KiB
Rust

// TODO: Remove
#![allow(dead_code)]
// pub mod logic; // TODO: Remove or correct errors
pub mod http_structs;
pub mod db_structs;
// #[cfg(test)]
// mod tests;
use std::{collections::HashMap, convert::Infallible};
use serde_json;
use crate::{
engines::kv::http_structs::*,
storage::DatabaseDriver,
};
use axum::{
extract::{self, Path, State}, http::StatusCode, response::IntoResponse, routing::*, Json, Router
};
use chrono::{DateTime, Utc};
use log::{info, error};
use sqlx::{error, Row};
pub fn kv_router(pool: DatabaseDriver) -> Router {
Router::new()
.route("/config", get(get_config))
.route("/config", post(post_config))
.route("/data/*path", get(get_data))
// .route("/:mount_path/data/*path/", get(get_data))
.route("/data/*path", post(post_data))
.route("/data/*path", delete(delete_data))
.route("/delete/*path", post(delete_path))
.route("/destroy/*path", post(destroy_path))
.route("/metadata/*path", get(get_meta))
// .route("/:mount_path/metadata/*path/", get(get_meta))
.route("/metadata/*path", post(post_meta))
.route("/metadata/*path", delete(delete_meta))
.route("/subkeys/*path", get(get_subkeys))
.route("/undelete/*path", post(post_undelete))
.with_state(pool)
}
async fn get_config() -> &'static str {
todo!("not implemented")
}
async fn post_config() -> &'static str {
todo!("not implemented")
}
async fn get_data(
State(pool): State<DatabaseDriver>,
Path(path): Path<String>,
) -> Result<impl IntoResponse, Infallible> {
match sqlx::query("SELECT * FROM secret_versions WHERE secret_path = $1")
.bind(path)
.fetch_one(&pool)
.await
{
Ok(v) => {
let version: i64 = v.get("version_number");
let secret_content: HashMap<String, String> = HashMap::from([
// TODO: use sqlx to parse the row to a struct, do not do it manually
("secret_data".to_string(), v.get("secret_data")),
("created_time".to_string(), v.get("created_time")),
("deletion_time".to_string(), v.get("deletion_time")),
("version_number".to_string(), version.to_string()),
("secret_path".to_string(), v.get("secret_path")),
]);
let return_secret = KvSecretRes::new(KvSecretResData {
created_time: DateTime::parse_from_rfc3339(secret_content.get("created_time").unwrap_or(&"".to_string())).unwrap_or_default().to_utc(), // TODO
custom_metadata: None,
deletion_time: None, // TODO
destroyed: false,
version: version,
});
info!("{:?}", return_secret);
Ok((StatusCode::OK, Json(return_secret)).into_response())
}
Err(e) => match e {
sqlx::Error::RowNotFound => {
error!("{:?}", e);
let error_struct : ErrorStruct = ErrorStruct{err: e.to_string() };
error!("{:?}", error_struct.err);
Ok(error_struct.into_response()) // TODO: API doesn't specify return value in case of error. Error struct correct? Else send empty secret back?
// let error_secret = KvSecretRes{data: None, options: None};
// Ok(Json())
},
_ => panic!("{:?}", e),
},
}
}
async fn post_data(
State(pool): State<DatabaseDriver>,
Path(path): Path<String>,
extract::Json(payload): extract::Json<KvSecretReq>,
) -> &'static str {
// Insert Metadata first -> Else: Error because of foreign key constraint
log::debug!(
"Secret: {}, Content: {:?}, Version: {:?}, path: {}",
path,
payload.data,
payload.options,
path
);
let data = serde_json::to_string(&payload.data).unwrap();
log::debug!("Received data: {:?}", data);
let created_time = Utc::now().to_string();
let deletion_time = "12-12-2024 12:00:00"; // TODO:
let version = "1";
match sqlx::query(
"INSERT INTO secret_versions VALUES ($1, $2, $3, $4, $5)",
)
.bind(data)
.bind(created_time)
.bind (deletion_time)
.bind(version)
.bind(path)
.execute(&pool)
.await
{
Ok(v) => {
info!("{:?}", v);
}
Err(e) => {
error!("{:?}", e);
}
}
todo!("not implemented")
}
/* mock for return
async fn post_data(
Path(kv_path): Path<String>,
Extension(mount_path): Extension<String>,
Json(body): Json<KvSecretReq>,
) -> Json<KvSecretRes> {
trace!(
"Secret: {}, Content: {:#?}, path: {}",
kv_path,
body.data,
// body.version.unwrap_or(0),
mount_path,
);
let res = KvSecretRes {
data: KvSecretResData {
created_time: chrono::Utc::now(),
custom_metadata: None,
deletion_time: None,
destroyed: false,
version: 1,
},
};
Json(res)
} */
/// TODO: soft delete the secret version at path. can be undone with undelete_secret
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret
// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions
async fn delete_data() -> &'static str {
todo!("not implemented")
}
async fn delete_path() -> &'static str {
todo!("not implemented")
}
async fn destroy_path() -> &'static str {
todo!("not implemented")
}
async fn get_meta() -> &'static str {
todo!("not implemented")
}
async fn post_meta(
State(pool): State<DatabaseDriver>,
Path((mount_path, kv_path)): Path<(String, String)>,
body: String,
) -> &'static str {
// let mut body_json = body_to_json(body);
// let meta_data: SecretMeta = Default::default();
todo!("not implemented")
}
async fn delete_meta() -> &'static str {
todo!("not implemented")
}
async fn get_subkeys() -> &'static str {
todo!("not implemented")
}
async fn post_undelete() -> &'static str {
todo!("not implemented")
}