// 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, Path(path): Path, ) -> Result { 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 = 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, Path(path): Path, extract::Json(payload): extract::Json, ) -> &'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, Extension(mount_path): Extension, Json(body): Json, ) -> Json { 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, 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") }