Skip to content

Commit

Permalink
Add support for consul secret engine
Browse files Browse the repository at this point in the history
  • Loading branch information
Haennetz committed Feb 23, 2024
1 parent c7ac4f7 commit aed566a
Show file tree
Hide file tree
Showing 8 changed files with 426 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod auth;
pub mod consul;
pub mod database;
pub mod kv1;
pub mod kv2;
Expand Down
2 changes: 2 additions & 0 deletions src/api/consul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod requests;
pub mod responses;
146 changes: 146 additions & 0 deletions src/api/consul/requests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use super::responses::{
GenerateConsulCredsResponse, ListRolesResponse,
ReadRoleResponse,
};
use rustify_derive::Endpoint;

/// ## Create/Update access Config
/// This endpoint creates or updates a consul secret engines access configuration.
///
/// * Path: {self.mount}/config/access
/// * Method: POST
/// * Response: N/A
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#configure-access
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/config/access",
method = "POST",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct SetAccessConfigRequest {
#[endpoint(skip)]
pub mount: String,
pub address: String,
pub schema: Option<String>,
pub token: Option<String>,
pub ca_cert: Option<String>,
pub client_cert: Option<String>,
pub client_key: Option<String>,
}

/// ## Create Role
/// This endpoint creates or updates a named role.
///
/// * Path: {self.mount}/roles/{self.name}
/// * Method: POST
/// * Response: N/A
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#configure-access
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/roles/{self.name}",
method = "POST",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct SetRoleRequest {
#[endpoint(skip)]
pub mount: String,
pub name: String,
pub token_type: Option<String>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub partition: Option<String>,
pub node_identities: Option<Vec<String>>,
pub consul_namespace: Option<String>,
pub service_identities: Option<Vec<String>>,
pub consul_roles: Option<Vec<String>>,
pub consul_policies: Option<Vec<String>>,
pub policy: Option<String>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub policies: Option<Vec<String>>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub local: Option<bool>,
pub max_ttl: Option<String>,
pub ttl: Option<String>,
}

/// ## Read Role
/// This endpoint queries a named role.
///
/// * Path: {self.mount}/roles/{self.name}
/// * Method: GET
/// * Response: [ReadRoleResponse]
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#read-role
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/roles/{self.name}",
response = "ReadRoleResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct ReadRoleRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
}

/// ## List Roles
/// This endpoint returns a list of available roles.
///
/// * Path: {self.mount}/roles
/// * Method: LIST
/// * Response: [ListRolesResponse]
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#list-roles
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/roles",
method = "LIST",
response = "ListRolesResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct ListRolesRequest {
#[endpoint(skip)]
pub mount: String,
}

/// ## Delete Role
/// This endpoint deletes a named role.
///
/// * Path: {self.mount}/roles/{self.name}
/// * Method: DELETE
/// * Response: N/A
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#delete-role
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/roles/{self.name}",
method = "DELETE",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct DeleteRoleRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
}

/// ## Generate Consul Credentials
/// This endpoint creates credentials with the parameters defined in the given role.
///
/// * Path: {self.mount}/creds/{self.name}
/// * Method: POST
/// * Response: [GenerateConsulCredsResponse]
/// * Reference: https://www.vaultproject.io/api-docs/secret/consul#generate-credential
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "{self.mount}/creds/{self.name}",
method = "POST",
response = "GenerateConsulCredsResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct GenerateConsulCredsRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
}
33 changes: 33 additions & 0 deletions src/api/consul/responses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};

/// Response from executing
/// [ReadRoleRequest][crate::api::consul::requests::ReadRoleRequest]
#[derive(Deserialize, Debug, Serialize)]
pub struct ReadRoleResponse {
pub token_type: Option<String>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub partition: Option<String>,
pub node_identities: Option<Vec<String>>,
pub consul_namespace: Option<String>,
pub service_identities: Option<Vec<String>>,
pub consul_roles: Option<Vec<String>>,
pub policy: Option<String>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub policies: Option<Vec<String>>, // DEPRECATED since consul version 1.4 and removed in 1.11
pub consul_policies: Option<Vec<String>>,
pub local: bool,
pub max_ttl: u64,
pub ttl: u64,
}

/// Response from executing
/// [ListRolesRequest][crate::api::consul::requests::ListRolesRequest]
#[derive(Deserialize, Debug, Serialize)]
pub struct ListRolesResponse {
pub keys: Vec<String>,
}

/// Response from executing
/// [GenerateConsulCredsRequest][crate::api::consul::requests::GenerateConsulCredsRequest]
#[derive(Deserialize, Debug, Serialize)]
pub struct GenerateConsulCredsResponse {
pub token: String,
}
119 changes: 119 additions & 0 deletions src/consul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::api;
use crate::api::consul::requests::GenerateConsulCredsRequest;
use crate::api::consul::responses::GenerateConsulCredsResponse;
use crate::client::Client;
use crate::error::ClientError;

/// Generates Consul credentials for the given role
///
/// See [GenerateConsulCredsRequest]
#[instrument(skip(client), err)]
pub async fn generate(
client: &impl Client,
mount: &str,
name: &str,
) -> Result<GenerateConsulCredsResponse, ClientError> {
let endpoint = GenerateConsulCredsRequest::builder()
.mount(mount)
.name(name)
.build()
.unwrap();
api::exec_with_result(client,endpoint).await
}

pub mod config {
use crate::api;
use crate::api::consul::requests::{SetAccessConfigRequest, SetAccessConfigRequestBuilder};
use crate::client::Client;
use crate::error::ClientError;

/// Creates or updates Consul access config
///
/// See [SetAccessConfigRequest]
#[instrument(skip(client, opts), err)]
pub async fn set(
client: &impl Client,
mount: &str,
opts: Option<&mut SetAccessConfigRequestBuilder>
) -> Result<(), ClientError> {
let mut t = SetAccessConfigRequest::builder();
let endpoint = opts
.unwrap_or(&mut t)
.mount(mount)
.build()
.unwrap();
api::exec_with_empty(client, endpoint).await
}
}

pub mod role {
use crate::api;
use crate::api::consul::{
requests::{
DeleteRoleRequest, ListRolesRequest, ReadRoleRequest, SetRoleRequest,
SetRoleRequestBuilder,
},
responses::{ListRolesResponse, ReadRoleResponse},
};
use crate::client::Client;
use crate::error::ClientError;

/// Deletes a role
///
/// See [DeleteRoleRequest]
#[instrument(skip(client), err)]
pub async fn delete(client: &impl Client, mount: &str, name: &str) -> Result<(), ClientError> {
let endpoint = DeleteRoleRequest::builder()
.mount(mount)
.name(name)
.build()
.unwrap();
api::exec_with_empty(client, endpoint).await
}

/// Lists all roles
///
/// See [ListRolesRequest]
#[instrument(skip(client), err)]
pub async fn list(client: &impl Client, mount: &str) -> Result<ListRolesResponse, ClientError> {
let endpoint = ListRolesRequest::builder().mount(mount).build().unwrap();
api::exec_with_result(client, endpoint).await
}

/// Reads a role
///
/// See [ReadRoleRequest]
#[instrument(skip(client), err)]
pub async fn read(
client: &impl Client,
mount: &str,
name: &str,
) -> Result<ReadRoleResponse, ClientError> {
let endpoint = ReadRoleRequest::builder()
.mount(mount)
.name(name)
.build()
.unwrap();
api::exec_with_result(client, endpoint).await
}

/// Creates or updates a role
///
/// See [SetRoleRequest]
#[instrument(skip(client, opts), err)]
pub async fn set(
client: &impl Client,
mount: &str,
name: &str,
opts: Option<&mut SetRoleRequestBuilder>,
) -> Result<(), ClientError> {
let mut t = SetRoleRequest::builder();
let endpoint = opts
.unwrap_or(&mut t)
.mount(mount)
.name(name)
.build()
.unwrap();
api::exec_with_empty(client, endpoint).await
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! * [Token](https://www.vaultproject.io/docs/auth/token)
//! * [Userpass](https://www.vaultproject.io/docs/auth/userpass)
//! * Secrets
//! * [Consul](https://www.vaultproject.io/api-docs/secret/consul)
//! * [Databases](https://www.vaultproject.io/api-docs/secret/databases)
//! * [KV v2](https://www.vaultproject.io/docs/secrets/kv/kv-v2)
//! * [PKI](https://www.vaultproject.io/docs/secrets/pki)
Expand Down Expand Up @@ -199,6 +200,7 @@ extern crate tracing;
pub mod api;
pub mod auth;
pub mod client;
pub mod consul;
pub mod database;
pub mod error;
pub mod kv1;
Expand Down
23 changes: 22 additions & 1 deletion tests/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use async_trait::async_trait;
pub use dockertest_server::servers::cloud::localstack::{LocalStackServer, LocalStackServerConfig};

Check warning on line 2 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `LocalStackServer`

Check warning on line 2 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `LocalStackServer`
pub use dockertest_server::servers::database::postgres::{PostgresServer, PostgresServerConfig};

Check warning on line 3 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `PostgresServer`

Check warning on line 3 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `PostgresServer`

Check warning on line 3 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `PostgresServer`

Check warning on line 3 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `PostgresServer`
pub use dockertest_server::servers::hashi::{VaultServer, VaultServerConfig};
pub use dockertest_server::servers::hashi::{VaultServer, VaultServerConfig, ConsulServer,ConsulServerConfig};

Check warning on line 4 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `ConsulServer`

Check warning on line 4 in tests/common.rs

View workflow job for this annotation

GitHub Actions / Run cargo test for vaultrs

unused import: `ConsulServer`
use dockertest_server::servers::webserver::nginx::{
ManagedContent, NginxServerConfig, WebserverContent,
};
Expand Down Expand Up @@ -151,6 +151,27 @@ pub fn new_test() -> Test {
test
}

// Sets up a new consul test.
#[allow(dead_code)]
pub fn new_consul_test() -> Test {
let mut test = new_test();
let consul_config = ConsulServerConfig::builder()
.port(8500)
// .args(vec![
// "consul".to_string(),
// "agent".to_string(),
// "-dev".to_string(),
// "-hcl".to_string(),
// "\"acl { enabled = true }\"".to_string()]
// )
.version("1.15".to_string())
.token("test".to_string())
.build()
.unwrap();
test.register(consul_config);
test
}

// Sets up a new database test.
#[allow(dead_code)]
pub fn new_db_test() -> Test {
Expand Down
Loading

0 comments on commit aed566a

Please sign in to comment.