Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use HTTP GET for list requests #111

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/api/kv1/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: <https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v1#list-secrets>
#[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
///
Expand Down
25 changes: 25 additions & 0 deletions src/api/kv2/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: <https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#list-secrets>
#[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.
Expand Down
21 changes: 20 additions & 1 deletion src/kv1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use crate::{
self,
kv1::{
requests::{
DeleteSecretRequest, GetSecretRequest, ListSecretRequest, SetSecretRequest,
DeleteSecretRequest, GetSecretRequest, ListSecretRequest,
ListSecretUsingGetRequest, SetSecretRequest,
},
responses::{GetSecretResponse, ListSecretResponse},
},
Expand Down Expand Up @@ -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<ListSecretResponse, ClientError> {
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]
Expand Down
23 changes: 20 additions & 3 deletions src/kv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -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<Vec<String>, 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]
Expand Down
6 changes: 5 additions & 1 deletion vaultrs-tests/tests/api_tests/kv1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ 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
Expand Down
9 changes: 9 additions & 0 deletions vaultrs-tests/tests/api_tests/kv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestSecret, _> = kv2::read(client, path, name).await;
assert!(res.is_ok());
assert_eq!(res.unwrap().key, endpoint.secret.key);
Expand Down Expand Up @@ -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) {
Expand Down
Loading