Skip to content

Commit

Permalink
Add kv1::list_with_http_get and kv2::list_with_http_get methods
Browse files Browse the repository at this point in the history
  • Loading branch information
orf committed Nov 16, 2024
1 parent d12c6cf commit f7674c9
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 19 deletions.
26 changes: 26 additions & 0 deletions src/api/kv1/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ 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
26 changes: 26 additions & 0 deletions src/api/kv2/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,32 @@ 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,7 @@ use crate::{
self,
kv1::{
requests::{
DeleteSecretRequest, GetSecretRequest, ListSecretRequest, SetSecretRequest,
DeleteSecretRequest, GetSecretRequest, ListSecretRequest, ListSecretUsingGetRequest, SetSecretRequest,
},
responses::{GetSecretResponse, ListSecretResponse},
},
Expand Down Expand Up @@ -95,6 +95,25 @@ 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
21 changes: 19 additions & 2 deletions src/kv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
requests::{
DeleteLatestSecretVersionRequest, DeleteSecretMetadataRequest,
DeleteSecretVersionsRequest, DestroySecretVersionsRequest, ListSecretsRequest,
ReadSecretMetadataRequest, ReadSecretRequest, SetSecretMetadataRequest,
SetSecretMetadataRequestBuilder, SetSecretRequest, SetSecretRequestOptions,
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
20 changes: 12 additions & 8 deletions tests/kv1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ 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"]);
for list_secret in [
kv1::list(&client, mount, "mysecret").await.unwrap(),
kv1::list_with_http_get(&client, mount, "mysecret").await.unwrap()
] {
assert_eq!(list_secret.data.keys, vec!["foo"]);
}

// Delete secret and read again and expect 404 to check deletion
kv1::delete(&client, mount, secret_path).await.unwrap();
Expand Down Expand Up @@ -93,9 +94,12 @@ fn test_kv1() {

println!("{:}", read_secrets.get("key1").unwrap()); // value1

let list_secret = kv1::list(&client, mount, "my").await.unwrap();

println!("{:?}", list_secret.data.keys); // [ "secrets" ]
for list_secret in [
kv1::list(&client, mount, "my").await.unwrap(),
kv1::list_with_http_get(&client, mount, "my").await.unwrap()
] {
assert_eq!(list_secret.data.keys, vec!["secrets"]);
}

kv1::delete(&client, mount, "my/secrets").await.unwrap();
});
Expand Down
26 changes: 18 additions & 8 deletions tests/kv2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ async fn test_kv2_url_encoding(server: &VaultServer) {
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 All @@ -101,7 +108,7 @@ async fn test_delete_versions(client: &impl Client, endpoint: &SecretEndpoint) {
endpoint.name.as_str(),
vec![1],
)
.await;
.await;
assert!(res.is_ok());
}

Expand All @@ -112,14 +119,17 @@ async fn test_destroy_versions(client: &impl Client, endpoint: &SecretEndpoint)
endpoint.name.as_str(),
vec![1],
)
.await;
.await;
assert!(res.is_ok());
}

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 Expand Up @@ -156,7 +166,7 @@ async fn test_set_with_compare_and_swap(client: &impl Client, endpoint: &SecretE
&endpoint.secret,
SetSecretRequestOptions { cas: 0 },
)
.await;
.await;
assert!(res.is_ok());
let res = kv2::set_with_options(
client,
Expand All @@ -165,7 +175,7 @@ async fn test_set_with_compare_and_swap(client: &impl Client, endpoint: &SecretE
&endpoint.secret,
SetSecretRequestOptions { cas: 0 },
)
.await;
.await;
assert!(res.is_err());
}

Expand All @@ -183,7 +193,7 @@ async fn test_set_metadata(client: &impl Client, endpoint: &SecretEndpoint) {
])),
),
)
.await;
.await;
assert!(res.is_ok());
}

Expand All @@ -194,7 +204,7 @@ async fn test_undelete_versions(client: &impl Client, endpoint: &SecretEndpoint)
endpoint.name.as_str(),
vec![1],
)
.await;
.await;
assert!(res.is_ok());
}

Expand All @@ -219,7 +229,7 @@ mod config {
.delete_version_after("768h"),
),
)
.await;
.await;

assert!(resp.is_ok());
}
Expand All @@ -246,7 +256,7 @@ async fn create(client: &impl Client, endpoint: &SecretEndpoint) -> Result<(), C
endpoint.name.as_str(),
&endpoint.secret,
)
.await?;
.await?;
Ok(())
}

Expand Down

0 comments on commit f7674c9

Please sign in to comment.