+ add post_meta
+ add destroy_secret
This commit is contained in:
parent
a6de2133fd
commit
620df86c06
3 changed files with 168 additions and 18 deletions
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
var client *vault.Client
|
var client *vault.Client
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
|
|
||||||
// Apparently used as a default if mountpath is an empty string (client library)
|
// Apparently used as a default if mountpath is an empty string (client library)
|
||||||
var mountpath = "/kv-v2"
|
var mountpath = "/kv-v2"
|
||||||
var mountpath2 = "/some"
|
var mountpath2 = "/some"
|
||||||
|
|
@ -106,14 +107,54 @@ func TestDeleteSecret(t *testing.T) {
|
||||||
|
|
||||||
func TestReadSecret(t *testing.T) {
|
func TestReadSecret(t *testing.T) {
|
||||||
_, err := client.Secrets.KvV2Read(ctx, "bar")
|
_, err := client.Secrets.KvV2Read(ctx, "bar")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("kv2: Failed to read secret:\n\t", err)
|
log.Fatal("kv2: Failed to read secret:\n\t", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadSecret2(t *testing.T) {
|
func TestReadMeta(t *testing.T) {
|
||||||
_, err := client.Secrets.KvV2Read(ctx, "ba")
|
_, err := client.Secrets.KvV2ReadMetadata(ctx, "bar")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("kv2: Failed to read secret:\n\t", err)
|
log.Fatal("kv2: Failed to read metadata:\n\t", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ pub mod http_structs;
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod tests;
|
// mod tests;
|
||||||
|
|
||||||
use crate::{
|
use crate::{engines::kv::http_structs::*, storage::DatabaseDriver};
|
||||||
engines::kv::{self, http_structs::*},
|
|
||||||
storage::DatabaseDriver,
|
|
||||||
};
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{self, Path, State},
|
extract::{self, Path, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
|
@ -24,7 +21,7 @@ use chrono::{DateTime, Utc};
|
||||||
use db_structs::*;
|
use db_structs::*;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use sqlx::{error, Row};
|
use sqlx::{Row, Sqlite};
|
||||||
use std::{collections::HashMap, convert::Infallible};
|
use std::{collections::HashMap, convert::Infallible};
|
||||||
|
|
||||||
pub fn kv_router(pool: DatabaseDriver) -> Router {
|
pub fn kv_router(pool: DatabaseDriver) -> Router {
|
||||||
|
|
@ -182,8 +179,48 @@ async fn delete_path() -> &'static str {
|
||||||
todo!("not implemented")
|
todo!("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn destroy_path() -> &'static str {
|
async fn destroy_path(
|
||||||
todo!("not implemented")
|
State(pool): State<DatabaseDriver>,
|
||||||
|
Path(kv_path): Path<String>,
|
||||||
|
Json(body): Json<KvSecretDestrReq>,
|
||||||
|
) -> Result<impl IntoResponse, Infallible> {
|
||||||
|
let versions = body.versions;
|
||||||
|
|
||||||
|
for ver in versions {
|
||||||
|
let res_wrapper = sqlx::query::<Sqlite>(
|
||||||
|
"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(
|
async fn get_meta(
|
||||||
|
|
@ -278,14 +315,59 @@ async fn get_meta(
|
||||||
Ok((StatusCode::OK, Json(metadata_res)).into_response())
|
Ok((StatusCode::OK, Json(metadata_res)).into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// currently only writes the metadata - No case if already exists
|
||||||
async fn post_meta(
|
async fn post_meta(
|
||||||
State(pool): State<DatabaseDriver>,
|
State(pool): State<DatabaseDriver>,
|
||||||
Path((mount_path, kv_path)): Path<(String, String)>,
|
Path(kv_path): Path<String>,
|
||||||
body: String,
|
Json(body): Json<KvMetaReq>,
|
||||||
) -> &'static str {
|
// extract::Json(body): extract::Json<http_structs::KvMetaReq>,
|
||||||
// let mut body_json = body_to_json(body);
|
) -> Result<impl IntoResponse, Infallible> {
|
||||||
// let meta_data: SecretMeta = Default::default();
|
let now = Utc::now();
|
||||||
todo!("not implemented")
|
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 {
|
async fn delete_meta() -> &'static str {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,12 @@ impl ErrorStruct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
/// HTTP Request to destroy secret versions
|
||||||
|
pub struct KvSecretDestrReq {
|
||||||
|
pub versions: Vec<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
/// HTTP Response to Reading a Secret metadata
|
/// HTTP Response to Reading a Secret metadata
|
||||||
/// Container of [`KvMetaResData`]
|
/// Container of [`KvMetaResData`]
|
||||||
|
|
@ -105,8 +111,29 @@ pub struct KvMetaResData {
|
||||||
pub max_versions: i64,
|
pub max_versions: i64,
|
||||||
pub oldest_version: i64,
|
pub oldest_version: i64,
|
||||||
pub updated_time: DateTime<Utc>,
|
pub updated_time: DateTime<Utc>,
|
||||||
// pub custom_metadata: Option<String>, // TODO as hashmap
|
|
||||||
pub custom_metadata: Option<HashMap<String, String>>,
|
pub custom_metadata: Option<HashMap<String, String>>,
|
||||||
pub versions: HashMap<i64, KvMetaResVersionData>,
|
pub versions: HashMap<i64, KvMetaResVersionData>,
|
||||||
// the key to a version is the version number
|
// 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<bool>,
|
||||||
|
// pub cas_required: bool,
|
||||||
|
pub delete_version_after: Option<String>,
|
||||||
|
pub max_versions: i64,
|
||||||
|
// pub updated_time: Option<DateTime<Utc>>,
|
||||||
|
pub custom_metadata: Option<HashMap<String, String>>,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue