mod redact;
mod reference;
mod vault;
use secrecy::{ExposeSecret, SecretString};
pub use redact::{Redactor, redact_line};
pub use reference::{VaultRef, is_reference, parse_reference};
pub use vault::{SecretSource, VaultSettings, VaultSource};
#[derive(Debug, thiserror::Error)]
pub enum SecretError {
#[error("invalid secret reference: {0}")]
InvalidReference(String),
#[error("invalid VAULT_ADDR: {0}")]
InvalidVaultAddress(String),
#[error("VAULT_ADDR is set but no token was found in VAULT_TOKEN or ~/.vault-token")]
MissingVaultToken,
#[error("cannot read {}", path.display())]
VaultTokenFile {
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
#[error("a vault: reference was found but VAULT_ADDR is not set")]
VaultNotConfigured,
#[error("vault request failed")]
Vault(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("vault returned a response that could not be parsed")]
VaultResponse,
#[error("field missing or not a string at {reference}")]
MissingField {
reference: String,
},
}
pub struct Resolver<S> {
source: Option<S>,
}
impl<S: SecretSource> Resolver<S> {
#[must_use]
pub fn new(source: Option<S>) -> Self {
Self { source }
}
pub async fn resolve(&self, value: &SecretString) -> Result<SecretString, SecretError> {
match parse_reference(value.expose_secret())? {
None => Ok(value.clone()),
Some(reference) => match &self.source {
Some(source) => source.fetch(&reference).await,
None => Err(SecretError::VaultNotConfigured),
},
}
}
}