From a0b89f886e314bfe00fbd16f21b956bd8733cac8 Mon Sep 17 00:00:00 2001 From: Thomas Forbes Date: Thu, 31 Oct 2024 17:17:47 +0000 Subject: [PATCH] Add kv1::list_with_http_get and kv2::list_with_http_get methods --- src/api/kv1/requests.rs | 25 +++++++++++++++++++++++++ src/api/kv2/requests.rs | 25 +++++++++++++++++++++++++ src/kv1.rs | 21 ++++++++++++++++++++- src/kv2.rs | 23 ++++++++++++++++++++--- vaultrs-tests/tests/api_tests/kv1.rs | 4 +++- vaultrs-tests/tests/api_tests/kv2.rs | 9 +++++++++ 6 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/api/kv1/requests.rs b/src/api/kv1/requests.rs index 7651334..6c86091 100644 --- a/src/api/kv1/requests.rs +++ b/src/api/kv1/requests.rs @@ -70,6 +70,31 @@ pub struct ListSecretRequest { pub path: String, } +/// ## List secret keys with HTTP GET +/// This endpoint list secrets at given location +/// Some servers and middleware may not support custom HTTP methods such as `LIST`. This +/// endpoint provides an alternative way to list secrets using HTTP GET. +/// +/// * Path: {self.mount}/{self.path}?list=true +/// * Method: GET +/// * Response: ListSecretResponse +/// * Reference: +#[derive(Builder, Debug, Endpoint)] +#[endpoint( + path = "{self.mount}/{self.path}", + builder = "true", + response = "ListSecretResponse" +)] +#[builder(setter(into))] +pub struct ListSecretUsingGetRequest { + #[endpoint(skip)] + pub mount: String, + #[endpoint(skip)] + pub path: String, + #[endpoint(query)] + pub list: bool, +} + /// ## Delete secret /// This endpoint delete a secret at given location /// diff --git a/src/api/kv2/requests.rs b/src/api/kv2/requests.rs index 8fa7e57..2a7c79c 100644 --- a/src/api/kv2/requests.rs +++ b/src/api/kv2/requests.rs @@ -212,6 +212,31 @@ pub struct ListSecretsRequest { pub path: String, } +/// ## List Secrets with HTTP GET +/// This endpoint returns a list of key names at the specified location. +/// Some servers and middleware may not support custom HTTP methods such as `LIST`. This +/// endpoint provides an alternative way to list secrets using HTTP GET. +/// +/// * Path: {self.mount}/metadata/{self.path}?list=true +/// * Method: GET +/// * Response: N/A +/// * Reference: +#[derive(Builder, Debug, Endpoint)] +#[endpoint( + path = "{self.mount}/metadata/{self.path}", + response = "ListSecretsResponse", + builder = "true" +)] +#[builder(setter(into))] +pub struct ListSecretsUsingGetRequest { + #[endpoint(skip)] + pub mount: String, + #[endpoint(skip)] + pub path: String, + #[endpoint(query)] + pub list: bool, +} + /// ## Read Secret Metadata /// This endpoint retrieves the metadata and versions for the secret at the /// specified path. diff --git a/src/kv1.rs b/src/kv1.rs index 77cc07f..de20ba6 100644 --- a/src/kv1.rs +++ b/src/kv1.rs @@ -3,7 +3,8 @@ use crate::{ self, kv1::{ requests::{ - DeleteSecretRequest, GetSecretRequest, ListSecretRequest, SetSecretRequest, + DeleteSecretRequest, GetSecretRequest, ListSecretRequest, + ListSecretUsingGetRequest, SetSecretRequest, }, responses::{GetSecretResponse, ListSecretResponse}, }, @@ -95,6 +96,24 @@ pub async fn list( api::exec_with_no_result(client, endpoint).await } +/// List secret keys at given location, using the HTTP GET method, returning raw server response +/// +/// See [ListSecretUsingGetRequest] +pub async fn list_with_http_get( + client: &impl Client, + mount: &str, + path: &str, +) -> Result { + let endpoint = ListSecretUsingGetRequest::builder() + .mount(mount) + .path(path) + .list(true) + .build() + .unwrap(); + + api::exec_with_no_result(client, endpoint).await +} + /// Delete secret at given location /// /// See [DeleteSecretRequest] diff --git a/src/kv2.rs b/src/kv2.rs index 5a59bcb..c4dead4 100644 --- a/src/kv2.rs +++ b/src/kv2.rs @@ -5,9 +5,9 @@ use crate::{ requests::{ DeleteLatestSecretVersionRequest, DeleteSecretMetadataRequest, DeleteSecretVersionsRequest, DestroySecretVersionsRequest, ListSecretsRequest, - ReadSecretMetadataRequest, ReadSecretRequest, SetSecretMetadataRequest, - SetSecretMetadataRequestBuilder, SetSecretRequest, SetSecretRequestOptions, - UndeleteSecretVersionsRequest, + ListSecretsUsingGetRequest, ReadSecretMetadataRequest, ReadSecretRequest, + SetSecretMetadataRequest, SetSecretMetadataRequestBuilder, SetSecretRequest, + SetSecretRequestOptions, UndeleteSecretVersionsRequest, }, responses::{ReadSecretMetadataResponse, SecretVersionMetadata}, }, @@ -101,6 +101,23 @@ pub async fn list( Ok(api::exec_with_result(client, endpoint).await?.keys) } +/// Lists all secret keys at the given path +/// +/// See [ListSecretsUsingGetRequest] +pub async fn list_with_http_get( + client: &impl Client, + mount: &str, + path: &str, +) -> Result, ClientError> { + let endpoint = ListSecretsUsingGetRequest::builder() + .mount(mount) + .path(path) + .list(true) + .build() + .unwrap(); + Ok(api::exec_with_result(client, endpoint).await?.keys) +} + /// Reads the value of the secret at the given path /// /// See [ReadSecretRequest] diff --git a/vaultrs-tests/tests/api_tests/kv1.rs b/vaultrs-tests/tests/api_tests/kv1.rs index 6de554c..6b43fdd 100644 --- a/vaultrs-tests/tests/api_tests/kv1.rs +++ b/vaultrs-tests/tests/api_tests/kv1.rs @@ -49,9 +49,11 @@ async fn test_kv1() { // List secret keys let list_secret = kv1::list(client, mount, "mysecret").await.unwrap(); - println!("{:?}", list_secret); + assert_eq!(list_secret.data.keys, vec!["foo"]); + let list_secret = kv1::list_with_http_get(client, mount, "mysecret").await.unwrap(); + println!("{:?}", list_secret); assert_eq!(list_secret.data.keys, vec!["foo"]); // Delete secret and read again and expect 404 to check deletion diff --git a/vaultrs-tests/tests/api_tests/kv2.rs b/vaultrs-tests/tests/api_tests/kv2.rs index 8c7efa8..431ca6c 100644 --- a/vaultrs-tests/tests/api_tests/kv2.rs +++ b/vaultrs-tests/tests/api_tests/kv2.rs @@ -71,6 +71,12 @@ async fn test_kv2_url_encoding(client: &impl Client) { assert_eq!(secrets.len(), 1); assert_eq!(secrets.first().unwrap(), "password name with whitespace"); + let secrets = kv2::list_with_http_get(client, path, "path/to/some secret/") + .await + .unwrap(); + assert_eq!(secrets.len(), 1); + assert_eq!(secrets.first().unwrap(), "password name with whitespace"); + let res: Result = kv2::read(client, path, name).await; assert!(res.is_ok()); assert_eq!(res.unwrap().key, endpoint.secret.key); @@ -112,6 +118,9 @@ async fn test_list(client: &impl Client, endpoint: &SecretEndpoint) { let res = kv2::list(client, endpoint.path.as_str(), "").await; assert!(res.is_ok()); assert!(!res.unwrap().is_empty()); + let res = kv2::list_with_http_get(client, endpoint.path.as_str(), "").await; + assert!(res.is_ok()); + assert!(!res.unwrap().is_empty()); } async fn test_read(client: &impl Client, endpoint: &SecretEndpoint) {