From 620df86c06eee58032117aa92253515f14b9f859 Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 2 Jun 2024 11:11:53 -0700 Subject: [PATCH] + add post_meta + add destroy_secret --- go_client/tests/secret_test.go | 49 +++++++++++++-- src/engines/kv.rs | 108 +++++++++++++++++++++++++++++---- src/engines/kv/http_structs.rs | 29 ++++++++- 3 files changed, 168 insertions(+), 18 deletions(-) diff --git a/go_client/tests/secret_test.go b/go_client/tests/secret_test.go index 14be686..9d4446a 100644 --- a/go_client/tests/secret_test.go +++ b/go_client/tests/secret_test.go @@ -13,6 +13,7 @@ import ( var client *vault.Client var ctx context.Context + // Apparently used as a default if mountpath is an empty string (client library) var mountpath = "/kv-v2" var mountpath2 = "/some" @@ -106,14 +107,54 @@ func TestDeleteSecret(t *testing.T) { func TestReadSecret(t *testing.T) { _, err := client.Secrets.KvV2Read(ctx, "bar") + if err != nil { log.Fatal("kv2: Failed to read secret:\n\t", err) } } -func TestReadSecret2(t *testing.T) { - _, err := client.Secrets.KvV2Read(ctx, "ba") +func TestReadMeta(t *testing.T) { + _, err := client.Secrets.KvV2ReadMetadata(ctx, "bar") if err != nil { - log.Fatal("kv2: Failed to read secret:\n\t", err) + log.Fatal("kv2: Failed to read metadata:\n\t", err) } -} \ No newline at end of file +} + +func TestWriteAndReadMeta(t *testing.T) { + meta := schema.KvV2WriteMetadataRequest{ + MaxVersions: 5, + CasRequired: false, + DeleteVersionAfter: "3h25m19s", + CustomMetadata: map[string]interface{}{ + "foo": "abc", + "bar": "123", + "baz": "5c07d823-3810-48f6-a147-4c06b5219e84", + }, + } + _, err := client.Secrets.KvV2WriteMetadata(ctx, "newMeta", meta) + if err != nil { + log.Fatal("kv2: Failed to write metadata:\n\t", err) + } + + // read the metadata + _, err2 := client.Secrets.KvV2ReadMetadata(ctx, "newMeta") + if err2 != nil { + log.Fatal("kv2: Failed to read metadata:\n\t", err) + } +} + +// does NOT revert destruction +func TestDestroySecret(t *testing.T) { + _, err := client.Secrets.KvV2DestroyVersions(ctx, "bar", schema.KvV2DestroyVersionsRequest{Versions: []int32{1}}) + if err != nil { + log.Fatal("kv2: Failed to destroy secret:\n\t", err) + } +} + +// does NOT revert destruction +func TestDestroySecret2(t *testing.T) { + _, err := client.Secrets.KvV2DestroyVersions(ctx, "bar", schema.KvV2DestroyVersionsRequest{Versions: []int32{1, 2}}) + if err != nil { + log.Fatal("kv2: Failed to destroy secret:\n\t", err) + } +} diff --git a/src/engines/kv.rs b/src/engines/kv.rs index 1dc1014..16b9fd2 100644 --- a/src/engines/kv.rs +++ b/src/engines/kv.rs @@ -8,10 +8,7 @@ pub mod http_structs; // #[cfg(test)] // mod tests; -use crate::{ - engines::kv::{self, http_structs::*}, - storage::DatabaseDriver, -}; +use crate::{engines::kv::http_structs::*, storage::DatabaseDriver}; use axum::{ extract::{self, Path, State}, http::StatusCode, @@ -24,7 +21,7 @@ use chrono::{DateTime, Utc}; use db_structs::*; use log::{error, info}; use serde_json; -use sqlx::{error, Row}; +use sqlx::{Row, Sqlite}; use std::{collections::HashMap, convert::Infallible}; pub fn kv_router(pool: DatabaseDriver) -> Router { @@ -182,8 +179,48 @@ async fn delete_path() -> &'static str { todo!("not implemented") } -async fn destroy_path() -> &'static str { - todo!("not implemented") +async fn destroy_path( + State(pool): State, + Path(kv_path): Path, + Json(body): Json, +) -> Result { + let versions = body.versions; + + for ver in versions { + let res_wrapper = sqlx::query::( + "DELETE FROM secret_versions where secret_path = $1 AND version_number = $2", + ) + .bind(&kv_path) + .bind(ver) + .execute(&pool) + .await; + + match res_wrapper { + Ok(result) => { + if result.rows_affected() == 0 { + log::debug!( + "No rows were deleted for version {} at path {}", + ver, + kv_path + ); + } else { + log::debug!("Deleted version {} at path {}", ver, kv_path); + } + } + Err(e) => { + log::error!( + "Failed to delete version {} at path {}: {}", + ver, + kv_path, + e + ); + } + } + } + + // check if all are gone + + Ok(StatusCode::NO_CONTENT) } async fn get_meta( @@ -278,14 +315,59 @@ async fn get_meta( Ok((StatusCode::OK, Json(metadata_res)).into_response()) } +// currently only writes the metadata - No case if already exists 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") + Path(kv_path): Path, + Json(body): Json, + // extract::Json(body): extract::Json, +) -> Result { + let now = Utc::now(); + let custom_metadata: String = match serde_json::to_string(&body.custom_metadata) { + Ok(data) => data, + Err(_) => { + log::error!( + "could not serialize custom_metadata: {:?}\ndropping data", + body.custom_metadata + ); + String::new() + } + }; + + let new_metadata = DbSecretMeta { + cas_required: body.cas_required.unwrap_or(false), + secret_path: kv_path, + created_time: now, + delete_version_after: Some("30d".to_string()), + max_versions: body.max_versions, + updated_time: now, + + custom_data: Some(custom_metadata), + }; + + match sqlx::query("INSERT INTO metadata (cas_required, secret_path, created_time, delete_version_after, max_versions, updated_time, custom_data) VALUES ($1, $2, $3, $4, $5, $6, $7)") + .bind(new_metadata.cas_required) + .bind(new_metadata.secret_path) + .bind(new_metadata.created_time) + .bind(new_metadata.delete_version_after.unwrap()) + .bind(new_metadata.max_versions) + .bind(new_metadata.updated_time) + .bind(new_metadata.custom_data.unwrap()) + .execute(&pool) + .await + { + Ok(result) => { + info!("{:?}", result); + if result.rows_affected() == 0 { + log::error!("Failed to insert metadata"); + } + } + Err(e) => { + error!("{:?}", e); + } + }; + + Ok(StatusCode::NO_CONTENT) } async fn delete_meta() -> &'static str { diff --git a/src/engines/kv/http_structs.rs b/src/engines/kv/http_structs.rs index 3f3e24f..d288afe 100644 --- a/src/engines/kv/http_structs.rs +++ b/src/engines/kv/http_structs.rs @@ -59,6 +59,12 @@ impl ErrorStruct { } } +#[derive(Deserialize)] +/// HTTP Request to destroy secret versions +pub struct KvSecretDestrReq { + pub versions: Vec, +} + #[derive(Serialize, Debug)] /// HTTP Response to Reading a Secret metadata /// Container of [`KvMetaResData`] @@ -105,8 +111,29 @@ pub struct KvMetaResData { pub max_versions: i64, pub oldest_version: i64, pub updated_time: DateTime, - // pub custom_metadata: Option, // TODO as hashmap pub custom_metadata: Option>, pub versions: HashMap, // the key to a version is the version number } + +// Example +// { +// "max_versions": 5, +// "cas_required": false, +// "delete_version_after": "3h25m19s", +// "custom_metadata": { +// "foo": "abc", +// "bar": "123", +// "baz": "5c07d823-3810-48f6-a147-4c06b5219e84" +// } +// } +#[derive(Serialize, Debug, Deserialize)] +/// HTTP Request to post metadatas +pub struct KvMetaReq { + pub cas_required: Option, + // pub cas_required: bool, + pub delete_version_after: Option, + pub max_versions: i64, + // pub updated_time: Option>, + pub custom_metadata: Option>, +}