Skip to content

Commit

Permalink
Merge pull request #76 from avandecreme/certificate_login
Browse files Browse the repository at this point in the history
Add support for login via certificate
  • Loading branch information
Haennetz authored Mar 1, 2024
2 parents 34e3874 + 6b5e4f4 commit 4068c18
Show file tree
Hide file tree
Showing 10 changed files with 627 additions and 3 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
components: rustfmt, clippy
- name: Use cache
- name: Use cache
uses: Swatinem/rust-cache@v1
with:
working-directory: vaultrs-login/
Expand Down Expand Up @@ -87,13 +87,19 @@ jobs:
with:
command: test
args: --no-run
- name: Run cargo test
- name: Run cargo test --all-features
uses: actions-rs/cargo@v1
env:
RUST_TEST_THREADS: 1
with:
command: test
args: --all-features
- name: Run cargo test
uses: actions-rs/cargo@v1
env:
RUST_TEST_THREADS: 1
with:
command: test
test-vaultrs-login:
name: Run cargo test for vaultrs-login
runs-on: ubuntu-latest
Expand Down Expand Up @@ -136,4 +142,4 @@ jobs:
override: true
- uses: katyo/publish-crates@v1
with:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features =
tracing-test = "0.2.4"
test-log = { version = "0.2.11", features = ["trace"] }
env_logger = "0.10.0"
dockertest = "0.3.0"
dockertest-server = { version = "0.1.7", features = ["hashi", "database", "webserver", "cloud"] }
jwt = "0.16.0"
sha2 = "0.10.6"
hmac = "0.12.1"
serial_test = "1.0.0"
rcgen = "0.12.1"
tempfile = "3.10.1"
1 change: 1 addition & 0 deletions src/api/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod approle;
pub mod aws;
pub mod cert;
pub mod kubernetes;
pub mod oidc;
pub mod userpass;
2 changes: 2 additions & 0 deletions src/api/auth/cert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod requests;
pub mod responses;
126 changes: 126 additions & 0 deletions src/api/auth/cert/requests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use rustify_derive::Endpoint;

use super::responses::{ListCaCertificateRoleResponse, ReadCaCertificateRoleResponse};

/// ## Create/Update CA certificate role
/// Create or update a CA certificate role.
///
/// * Path: /auth/{self.mount}/certs/{self.name}
/// * Method: POST
/// * Response: N/A
/// * Reference: https://developer.hashicorp.com/vault/api-docs/auth/cert#create-ca-certificate-role
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "/auth/{self.mount}/certs/{self.name}",
method = "POST",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct CreateCaCertificateRoleRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
pub certificate: String,
pub allowed_common_names: Option<Vec<String>>,
pub allowed_dns_sans: Option<Vec<String>>,
pub allowed_email_sans: Option<Vec<String>>,
pub allowed_uri_sans: Option<Vec<String>>,
pub allowed_organizational_units: Option<Vec<String>>,
pub required_extensions: Option<Vec<String>>,
pub allowed_metadata_extensions: Option<Vec<String>>,
pub ocsp_enabled: Option<bool>,
pub ocsp_ca_certificates: Option<String>,
pub ocsp_servers_override: Option<Vec<String>>,
pub ocsp_fail_open: Option<bool>,
pub ocsp_query_all_servers: Option<bool>,
pub display_name: Option<String>,
pub token_ttl: Option<String>,
pub token_max_ttl: Option<String>,
pub token_policies: Option<Vec<String>>,
pub token_bound_cidrs: Option<Vec<String>>,
pub token_explicit_max_ttl: Option<String>,
pub token_no_default_policy: Option<bool>,
pub token_num_uses: Option<u64>,
pub token_period: Option<String>,
pub token_type: Option<String>,
}

/// ## Read CA certificate role
/// Reads the properties of an existing CA certificate role.
///
/// * Path: /auth/{self.mount}/certs/{self.name}
/// * Method: GET
/// * Response: [ReadCaCertificateRoleResponse]
/// * Reference: https://developer.hashicorp.com/vault/api-docs/auth/cert#read-ca-certificate-role
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "/auth/{self.mount}/certs/{self.name}",
response = "ReadCaCertificateRoleResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct ReadCaCertificateRoleRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
}

/// ## Delete CA certificate role
/// This endpoint deletes the CA certificate role.
///
/// * Path: /auth/{self.mount}/certs/{self.name}
/// * Method: DELETE
/// * Response: N/A
/// * Reference: https://developer.hashicorp.com/vault/api-docs/auth/cert#delete-certificate-role
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "/auth/{self.mount}/certs/{self.name}",
method = "DELETE",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct DeleteCaCertificateRoleRequest {
#[endpoint(skip)]
pub mount: String,
#[endpoint(skip)]
pub name: String,
}

/// ## List CA certificate role
/// List available CA certificate roles.
///
/// * Path: /auth/{self.mount}/certs
/// * Method: LIST
/// * Response: [ListCaCertificateRoleResponse]
/// * Reference: https://developer.hashicorp.com/vault/api-docs/auth/cert#list-certificate-roles
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(
path = "/auth/{self.mount}/certs",
method = "LIST",
response = "ListCaCertificateRoleResponse",
builder = "true"
)]
#[builder(setter(into, strip_option), default)]
pub struct ListCaCertificateRoleRequest {
#[endpoint(skip)]
pub mount: String,
}

/// ## Login
/// Login with the TLS certificate method and authenticate against only the named
/// certificate role.
///
/// * Path: /auth/{self.mount}/login
/// * Method: POST
/// * Response: N/A
/// * Reference: https://developer.hashicorp.com/vault/api-docs/auth/cert#login-with-tls-certificate-method
#[derive(Builder, Debug, Default, Endpoint)]
#[endpoint(path = "/auth/{self.mount}/login", method = "POST", builder = "true")]
#[builder(setter(into, strip_option), default)]
pub struct LoginRequest {
#[endpoint(skip)]
pub mount: String,
pub cert_name: String,
}
32 changes: 32 additions & 0 deletions src/api/auth/cert/responses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};

/// Response from executing
/// [ReadCaCertificateRoleRequest][crate::api::auth::cert::requests::ReadCaCertificateRoleRequest]
#[derive(Deserialize, Debug, Serialize)]
pub struct ReadCaCertificateRoleResponse {
pub allowed_common_names: Option<Vec<String>>,
pub allowed_dns_sans: Option<Vec<String>>,
pub allowed_email_sans: Option<Vec<String>>,
pub allowed_metadata_extensions: Option<Vec<String>>,
pub allowed_organizational_units: Option<Vec<String>>,
pub allowed_uri_sans: Option<Vec<String>>,
pub certificate: String,
pub display_name: String,
pub required_extensions: Option<Vec<String>>,
pub token_bound_cidrs: Vec<String>,
pub token_explicit_max_ttl: u64,
pub token_max_ttl: u64,
pub token_no_default_policy: bool,
pub token_num_uses: u64,
pub token_period: u64,
pub token_policies: Vec<String>,
pub token_ttl: u64,
pub token_type: String,
}

/// Response from executing
/// [ListCaCertificateRoleRequest][crate::api::auth::cert::requests::ListCaCertificateRoleRequest]
#[derive(Deserialize, Debug, Serialize)]
pub struct ListCaCertificateRoleResponse {
pub keys: Vec<String>,
}
1 change: 1 addition & 0 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod approle;
pub mod aws;
pub mod cert;
pub mod kubernetes;
pub mod oidc;
pub mod userpass;
107 changes: 107 additions & 0 deletions src/auth/cert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use crate::{
api::{self, auth::cert::requests::LoginRequest, AuthInfo},
client::Client,
error::ClientError,
};

// Fetch a token with policies corresponding to the certificate.
//
// See [LoginRequest]
#[instrument(skip(client), err)]
pub async fn login(
client: &impl Client,
mount: &str,
cert_name: &str,
) -> Result<AuthInfo, ClientError> {
let endpoint = LoginRequest::builder()
.mount(mount)
.cert_name(cert_name)
.build()
.unwrap();
api::auth(client, endpoint).await
}

pub mod ca_cert_role {
use crate::{
api::{
self,
auth::cert::{
requests::{
CreateCaCertificateRoleRequest, CreateCaCertificateRoleRequestBuilder,
DeleteCaCertificateRoleRequest, ListCaCertificateRoleRequest,
ReadCaCertificateRoleRequest,
},
responses::{ListCaCertificateRoleResponse, ReadCaCertificateRoleResponse},
},
},
client::Client,
error::ClientError,
};

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

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

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

/// Creates a new CA certificate role
///
/// See [CreateCaCertificateRoleRequest]
#[instrument(skip(client, opts), err)]
pub async fn set(
client: &impl Client,
mount: &str,
name: &str,
certificate: &str,
opts: Option<&mut CreateCaCertificateRoleRequestBuilder>,
) -> Result<(), ClientError> {
let mut t = CreateCaCertificateRoleRequest::builder();
let endpoint = opts
.unwrap_or(&mut t)
.mount(mount)
.name(name)
.certificate(certificate)
.build()
.unwrap();
api::exec_with_empty(client, endpoint).await
}
}
Loading

0 comments on commit 4068c18

Please sign in to comment.