mod cached;
mod providers;
pub mod encodings;
pub use cached::CachedSecret;
pub use encodings::{DecodingError, SecretDecoder};
pub use providers::EnvVarSecret;
#[cfg(feature = "fs")]
pub use providers::{FileSecret, FileSecretError};
use secrecy::ExposeSecret as _;
use serde::{Deserialize, Serialize};
use crate::platform::{MaybeSend, MaybeSendSync};
#[derive(Debug, Clone)]
pub struct SecretString(secrecy::SecretString);
impl<'de> Deserialize<'de> for SecretString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Self::new(s))
}
}
impl Serialize for SecretString {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.0.expose_secret())
}
}
impl SecretString {
#[must_use]
pub fn new(secret: impl AsRef<str>) -> Self {
SecretString(secret.as_ref().into())
}
#[must_use]
pub fn expose_secret(&self) -> &str {
self.0.expose_secret()
}
}
#[derive(Debug, Clone)]
pub struct SecretBytes(secrecy::SecretBox<[u8]>);
impl SecretBytes {
#[must_use]
pub fn new(secret: Vec<u8>) -> Self {
SecretBytes(secrecy::SecretBox::new(secret.into_boxed_slice()))
}
#[must_use]
pub fn expose_secret(&self) -> &[u8] {
self.0.expose_secret()
}
}
#[derive(Debug, Clone)]
pub struct SecretOutput<T: Clone> {
pub value: T,
pub identity: Option<String>,
}
pub trait Secret: Clone + MaybeSendSync {
type Error: crate::Error;
type Output: Clone + MaybeSendSync;
fn get_secret_value(
&self,
) -> impl Future<Output = Result<SecretOutput<Self::Output>, Self::Error>> + MaybeSend;
}
#[derive(Debug, Clone)]
pub struct WithIdentity<S: Secret> {
inner: S,
identity: String,
}
impl<S: Secret> WithIdentity<S> {
pub fn new(inner: S, identity: impl Into<String>) -> Self {
Self {
inner,
identity: identity.into(),
}
}
}
impl<S: Secret> Secret for WithIdentity<S> {
type Output = S::Output;
type Error = S::Error;
async fn get_secret_value(&self) -> Result<SecretOutput<Self::Output>, Self::Error> {
let mut output = self.inner.get_secret_value().await?;
output.identity = Some(self.identity.clone());
Ok(output)
}
}
#[cfg(test)]
mod tests {
use std::convert::Infallible;
use super::*;
#[derive(Clone)]
struct MockSecret(SecretString);
impl Secret for MockSecret {
type Output = SecretString;
type Error = Infallible;
async fn get_secret_value(&self) -> Result<SecretOutput<Self::Output>, Self::Error> {
Ok(SecretOutput {
value: self.0.clone(),
identity: None,
})
}
}
#[tokio::test]
async fn test_with_identity() {
let secret = MockSecret(SecretString::new("secret"));
let with_id = WithIdentity::new(secret, "my-id");
let output = with_id.get_secret_value().await.unwrap();
assert_eq!(output.value.expose_secret(), "secret");
assert_eq!(output.identity.unwrap(), "my-id");
}
}