diff --git a/src/auth.rs b/src/auth.rs index d6e033d..f1767e3 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,13 +1,13 @@ use axum::Router; -use crate::storage::DatabaseDriver; +use crate::storage::DbPool; // route prefix: `/auth/token/` // mod token; // use self::token::token_auth_router; -pub fn auth_router(pool: DatabaseDriver) -> Router { +pub fn auth_router(pool: DbPool) -> Router { Router::new().with_state(pool) // .nest("/token", token_auth_router()) } diff --git a/src/common.rs b/src/common.rs index f3a0cee..928ad2c 100644 --- a/src/common.rs +++ b/src/common.rs @@ -21,3 +21,14 @@ impl HttpError { HttpError::new(status_code, vec![error.to_string(); 1]) } } + +/// Custom serialization function for `secret_data` +pub fn serialize_reject_none(value: &Option, serializer: S) -> Result +where + S: serde::Serializer, +{ + match value { + Some(data) => serializer.serialize_str(data), + None => Err(serde::ser::Error::custom("`secret_data` must not be None during serialization!")), + } +} diff --git a/src/engines.rs b/src/engines.rs index e8f8698..f767c7a 100644 --- a/src/engines.rs +++ b/src/engines.rs @@ -10,13 +10,13 @@ use axum::{ use log::*; use tower::Service; -use crate::{common::HttpError, storage::DatabaseDriver}; +use crate::{common::HttpError, storage::DbPool}; #[derive(Clone)] /// State to be used to store the database pool /// and the routers for each engine struct EngineMapperState { - pool: DatabaseDriver, + pool: DbPool, kv_v2: Router, } @@ -24,7 +24,7 @@ struct EngineMapperState { struct EnginePath(String); /// Secret engine router -pub fn secrets_router(pool: DatabaseDriver) -> Router { +pub fn secrets_router(pool: DbPool) -> Router { // State containing the pool and engine routers let state = EngineMapperState { pool: pool.clone(), @@ -81,7 +81,7 @@ fn unknown_engine(engine_type: String) -> impl IntoResponse { /// 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)> { +async fn map_mount_points(req: &Uri, pool: &DbPool) -> Option<(String, String)> { let mut mount_path_fragments: Vec<&str> = req.path().split('/').collect(); // Find longest matching existing mount path for the request diff --git a/src/engines/kv.rs b/src/engines/kv.rs index 18aff42..2d2281e 100644 --- a/src/engines/kv.rs +++ b/src/engines/kv.rs @@ -5,14 +5,13 @@ mod meta; // #[cfg(test)] // mod tests; -use crate::storage::DatabaseDriver; +use crate::storage::DbPool; use axum::{ Router, - extract::{Path, State}, routing::*, }; -pub fn kv_router(pool: DatabaseDriver) -> Router { +pub fn kv_router(pool: DbPool) -> Router { Router::new() .route("/config", get(get_config)) .route("/config", post(post_config)) diff --git a/src/engines/kv/data.rs b/src/engines/kv/data.rs index faf0dd5..31c2449 100644 --- a/src/engines/kv/data.rs +++ b/src/engines/kv/data.rs @@ -1,11 +1,8 @@ use super::structs::KvV2WriteRequest; use crate::{ - DatabaseDriver, - common::HttpError, - engines::{ - EnginePath, - kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, - }, + common::HttpError, engines::{ + kv::structs::{KvSecretData, KvSecretRes, KvV2WriteResponse, Wrapper}, EnginePath + }, DbPool }; use axum::{ Extension, Json, @@ -26,7 +23,7 @@ pub struct GetDataQuery { } pub async fn get_data( - State(pool): State, + State(pool): State, Query(params): Query, Path(path): Path, Extension(EnginePath(engine_path)): Extension, @@ -66,7 +63,7 @@ pub async fn get_data( } Err(e) => match e { sqlx::Error::RowNotFound => { - error!("Row not found {:?}", e); + warn!("Secret not found (could be correct behavior) {:?}", e); Ok(HttpError::simple( StatusCode::NOT_FOUND, "Secret not found within kv2 engine", @@ -78,16 +75,15 @@ pub async fn get_data( } pub async fn post_data( - State(pool): State, + State(pool): State, Path(kv_path): Path, Extension(EnginePath(engine_path)): Extension, Json(secret): Json, ) -> Result { - log::debug!( - "Engine: {}, Secret: {}, Content: {}, Version: {:?}, path: {}", + debug!( + "Engine: {}, Secret: {}, Version: {:?}, path: {}", engine_path, kv_path, - secret.data, secret.version, //.unwrap_or(0), kv_path ); @@ -117,8 +113,6 @@ pub async fn post_data( tx.commit().await.expect("FAILED TO WRITE TX!"); - warn!("test: {res_m:?} {res_r:?} {}", res_r.version_number); - let res = KvV2WriteResponse { created_time: created_time.into(), custom_metadata: None, @@ -134,11 +128,11 @@ pub async fn post_data( // 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 pub async fn delete_data( - State(pool): State, + State(pool): State, Path(path): Path, Extension(EnginePath(engine_path)): Extension, ) -> Result { - log::debug!("Secret: {}, path: {}", path, path); + debug!("Secret: {}, path: {}", path, path); let del_time = UtcDateTime::now().unix_timestamp(); @@ -186,7 +180,7 @@ pub async fn delete_data( error!( "Strange - a version to be deleted has been found but could not be found to set deletion.\n\t{e:?}" ); - // Not commited transactions will be aborted upon drop + // Not committed transactions will be aborted upon drop // tx.rollback().await.unwrap(); return Err(HttpError::simple( StatusCode::INTERNAL_SERVER_ERROR, @@ -204,7 +198,7 @@ pub async fn delete_data( // Not pub async fn patch_data( - State(pool): State, + State(pool): State, Path(kv_path): Path, Extension(EnginePath(engine_path)): Extension, Json(secret): Json, diff --git a/src/engines/kv/meta.rs b/src/engines/kv/meta.rs index caf9844..006ebd9 100644 --- a/src/engines/kv/meta.rs +++ b/src/engines/kv/meta.rs @@ -1,4 +1,4 @@ -use crate::storage::DatabaseDriver; +use crate::storage::DbPool; use axum::extract::{Path, State}; pub async fn delete_path() -> &'static str { @@ -14,7 +14,7 @@ pub async fn get_meta() -> &'static str { } pub async fn post_meta( - State(pool): State, + State(pool): State, Path((mount_path, kv_path)): Path<(String, String)>, body: String, ) -> &'static str { diff --git a/src/engines/kv/structs.rs b/src/engines/kv/structs.rs index 64af36a..b420ec7 100644 --- a/src/engines/kv/structs.rs +++ b/src/engines/kv/structs.rs @@ -15,7 +15,6 @@ use time::{OffsetDateTime, UtcDateTime, serde::rfc3339}; // } #[derive(Serialize, Deserialize, Debug, Clone)] -/// For SQLite support pub struct KvSecretData { pub secret_data: String, #[serde(with = "rfc3339")] @@ -43,7 +42,6 @@ pub struct Wrapper { pub data: T, } - #[derive(Serialize, Deserialize, Debug)] pub struct KvSecretRes { /// Map (required) diff --git a/src/identity.rs b/src/identity.rs index 3bdce76..128e0e3 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -1,7 +1,7 @@ use axum::Router; -use crate::storage::DatabaseDriver; +use crate::storage::DbPool; -pub fn identity_router(pool: DatabaseDriver) -> Router { +pub fn identity_router(pool: DbPool) -> Router { Router::new().with_state(pool) } diff --git a/src/main.rs b/src/main.rs index 575e296..62d5a48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use axum::{ }; use log::*; use std::{env, net::SocketAddr, str::FromStr}; -use storage::DatabaseDriver; +use storage::DbPool; use tokio::{net::TcpListener, signal}; use crate::common::HttpError; @@ -70,7 +70,7 @@ async fn set_default_content_type_json( Ok(next.run(req).await) } -async fn shutdown_signal(pool: DatabaseDriver) { +async fn shutdown_signal(pool: DbPool) { let ctrl_c = async { signal::ctrl_c() .await diff --git a/src/storage.rs b/src/storage.rs index 517737e..546b0e2 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,11 +1,14 @@ +pub mod sealing; + use std::{fs::File, path::Path}; use log::*; use sqlx::{sqlite::SqlitePoolOptions, Pool, Sqlite}; -pub(crate) type DatabaseDriver = Pool; +pub(crate) type DbType = Sqlite; +pub(crate) type DbPool = Pool; -pub async fn create_pool(db_url: String) -> DatabaseDriver { +pub async fn create_pool(db_url: String) -> DbPool { // Create SQLite database file if it does not exist if db_url.starts_with("sqlite:") && db_url != ("sqlite::memory:") { let path = db_url.replace("sqlite:", ""); diff --git a/src/storage/sled.rs b/src/storage/sled.rs deleted file mode 100644 index d7108ea..0000000 --- a/src/storage/sled.rs +++ /dev/null @@ -1,167 +0,0 @@ -#[cfg(test)] -mod tests { - use super::*; - use base::create_mock_meta; - #[test] - fn test_update_secret() { - let db: sled::Db = sled::open("sled_db").unwrap(); - update_secret(&db, "foo", TempSecret{version: -99, content: "cool".to_string()}); - } - #[test] - fn test_get_secret() { - let db: sled::Db = sled::open("sled_db").unwrap(); - get_secret(&db, "foo"); - } - #[test] - fn test_delete_secret(){ - let db: sled::Db = sled::open("sled_db").unwrap(); - delete_secret(&db, "foo"); - } - #[test] - fn test_meta(){ - let db: sled::Db = sled::open("sled_db").unwrap(); - println!("writing metadata:"); - update_secret_meta(&db, "metatest", create_mock_meta()); - println!("getting metadata:"); - get_secretmeta(&db, "metatest"); - } -} - -use sled::Db; -use base::{deserialize_metadata_struct, deserialize_secret_struct, serialize_metadata_json, serialize_secret_json, SecretMeta, TempSecret}; - -/// [TODO] Currently no proper versioning -/// inserts a secret. If there was already a secret in the given path, the version is incremented -fn update_secret(db: &Db, path: &str, mut secret: TempSecret) { - match get_secret(db, path) { - Some(old_secret) => { - // case secret found. TODO save it somewhere for versioning - secret.version = old_secret.version + 1; - #[cfg(test)] - print!("something was found. new version {} \n", secret.version) - } - None => { - // case new secret - secret.version = 1; - } - } - // if let secret_json = serialize_secret_json(&secret) { - // let _res = db.insert(path, secret_json); // maybe this can be handled cleaner - match serialize_secret_json(&secret) { - Ok(secret_json) => { - #[cfg(test)] - println!("String: {:?}", secret_json.clone()); - let as_ivec = sled::IVec::from(secret_json.into_bytes()); // maybe outsource this in a fn later - #[cfg(test)] - println!("ivec: {:?}", as_ivec); - match db.insert(path, as_ivec) { - Ok(_) => println!("Secret inserted"), - Err(e) => eprintln!("Failed to insert secret: {}", e), - } - } - Err(e) => eprintln!("Failed to serialize secret: {}", e), - } -} - -// !TODO eliminate redundancy: refactor get and update functions to accept generic types! - -// read and return a secret from the DB -//if there is no secret, return None -fn get_secret(db: &Db, path: &str) -> Option{ - let raw_secret; - match db.get(path) { - Ok(Some(ivec)) => { - raw_secret = ivec; - } - Err(e) => { - eprintln!("Error on retrieving secret: {}", e); - return None; - } - Ok(None) => { - return None; - } - } - // outsource this in a fn later. TODO maybe deal with unwrap - let as_str = String::from_utf8(raw_secret.to_vec()).unwrap(); - match deserialize_secret_struct(&as_str) { - Ok(secret) => { - #[cfg(test)] - println!("got some secret: {:?}", secret); - return Some(secret); - } - Err(e) => { - eprintln!("error on secret deserialization: {}", e); - return None; - } - - } -} - - -// TODO write abstract get_something fn -// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#read-secret-metadata -fn get_secretmeta(db: &Db, path: &str) -> Option{ - let raw_metadata; - match db.get(path) { - Ok(Some(ivec)) => { - raw_metadata = ivec; - } - Err(e) => { - eprintln!("Error on retrieving metadata: {}", e); - return None; - } - Ok(None) => { - return None; - } - } - let as_str = String::from_utf8(raw_metadata.to_vec()).unwrap(); - match deserialize_metadata_struct(&as_str) { - Ok(meta) => { - #[cfg(test)] - println!("got some metadata: {:?}", meta); - return Some(meta); - } - Err(e) => { - eprintln!("error on secret deserialization: {}", e); - return None; - } - - } -} - -// currently early version (copied from update_secret) -fn update_secret_meta(db: &Db, path: &str, mut meta: SecretMeta) { - match get_secretmeta(db, path) { - Some(meta) => { - // case secret found. TODO save it somewhere for versioning - #[cfg(test)] - print!("something was found. new version {:?} \n", meta) - } - None => { - } - } - match serialize_metadata_json(&meta) { - Ok(meta_json) => { - #[cfg(test)] - println!("String: {:?}", meta_json.clone()); - let as_ivec = sled::IVec::from(meta_json.into_bytes()); // maybe outsource this in a fn later - #[cfg(test)] - println!("ivec: {:?}", as_ivec); - match db.insert(path, as_ivec) { - Ok(_) => println!("Metadata inserted"), - Err(e) => eprintln!("Failed to insert meta: {}", e), - } - } - Err(e) => eprintln!("Failed to serialize meta: {}", e), - } -} - - -/// 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 - -/// hard delete secret at path -fn delete_secret(db: &Db, path: &str) { - let rem = db.remove(path); -} \ No newline at end of file diff --git a/src/sys.rs b/src/sys.rs index 3dd38dd..fb4b994 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,8 +1,8 @@ use axum::Router; -use crate::storage::DatabaseDriver; +use crate::storage::DbPool; /// System routes -pub fn sys_router(pool: DatabaseDriver) -> Router { +pub fn sys_router(pool: DbPool) -> Router { Router::new().with_state(pool) }