secret-vault 1.19.0

Library provides a secure vault to store securely application secrets in memory from Google/AWS/K8S and environment variables
Documentation
use crate::errors::*;
use crate::*;
use async_trait::*;
use ring::rand::{SecureRandom, SystemRandom};
use rsb_derive::*;
use secret_vault_value::SecretValue;
use std::collections::HashMap;
use tracing::*;

#[derive(Debug, Clone, Eq, PartialEq, Builder)]
pub struct TempSecretOptions {
    pub key_len: usize,

    #[default = "false"]
    pub regenerate_on_refresh: bool,

    #[default = "true"]
    pub printable: bool,
}

#[derive(Debug, Clone)]
pub struct TempSecretGenSourceOptions {
    registered_secrets: HashMap<SecretVaultKey, TempSecretOptions>,
}

impl TempSecretGenSourceOptions {
    pub fn new() -> Self {
        Self {
            registered_secrets: HashMap::new(),
        }
    }

    pub fn add_secret_generator(
        mut self,
        key: &SecretVaultKey,
        options: TempSecretOptions,
    ) -> Self {
        self.registered_secrets.insert(key.clone(), options);
        self
    }
}

#[derive(Debug)]
pub struct TempSecretGenSource {
    options: TempSecretGenSourceOptions,
    secure_rand: SystemRandom,
    generated_secrets: HashMap<SecretVaultKey, SecretValue>,
}

impl TempSecretGenSource {
    pub fn with_options(options: TempSecretGenSourceOptions) -> SecretVaultResult<Self> {
        let secure_rand = SystemRandom::new();

        let secrets_to_pregen: Vec<(&SecretVaultKey, &TempSecretOptions)> = options
            .registered_secrets
            .iter()
            .filter(|(_, options)| !options.regenerate_on_refresh)
            .collect();

        let mut generated_secrets = HashMap::new();

        for (key, options) in secrets_to_pregen {
            debug!("Pre-generating a new secret value for {:?}", key);
            generated_secrets.insert(
                key.clone(),
                generate_secret_value(&secure_rand, options.key_len, options.printable)?,
            );
        }

        Ok(Self {
            options,
            secure_rand,
            generated_secrets,
        })
    }
}

pub fn generate_secret_value(
    secure_rand: &ring::rand::SystemRandom,
    key_len: usize,
    printable: bool,
) -> SecretVaultResult<SecretValue> {
    let effective_key_len = if printable { key_len / 2 } else { key_len };

    let mut rand_key_data: Vec<u8> = vec![0; effective_key_len];
    secure_rand.fill(&mut rand_key_data).map_err(|e| {
        SecretVaultError::SecretsSourceError(
            SecretsSourceError::new(
                SecretVaultErrorPublicGenericDetails::new(format!(
                    "Unable to initialise random key: {e:?}"
                )),
                format!("Unable to initialise random key: {e}"),
            )
            .with_root_cause(Box::new(e)),
        )
    })?;

    if printable {
        Ok(SecretValue::from(hex::encode(rand_key_data)))
    } else {
        Ok(SecretValue::from(rand_key_data))
    }
}

#[async_trait]
impl SecretsSource for TempSecretGenSource {
    fn name(&self) -> String {
        "TempSecretGenSource".to_string()
    }

    async fn get_secrets(
        &self,
        references: &[SecretVaultRef],
    ) -> SecretVaultResult<HashMap<SecretVaultRef, Secret>> {
        let mut result_map: HashMap<SecretVaultRef, Secret> = HashMap::new();

        for secret_ref in references {
            match self.options.registered_secrets.get(&secret_ref.key) {
                Some(secret_options) => {
                    let secret_value = match self.generated_secrets.get(&secret_ref.key) {
                        Some(secret_value) => secret_value.clone(),
                        None => {
                            debug!("Generating a new secret value for {:?}", secret_ref.key);
                            generate_secret_value(
                                &self.secure_rand,
                                secret_options.key_len,
                                secret_options.printable,
                            )?
                        }
                    };

                    result_map.insert(
                        secret_ref.clone(),
                        Secret::new(secret_value, SecretMetadata::create_from_ref(secret_ref)),
                    );
                }
                None if secret_ref.required => {
                    return Err(SecretVaultError::DataNotFoundError(
                        SecretVaultDataNotFoundError::new(
                            SecretVaultErrorPublicGenericDetails::new("SECRET_NOT_FOUND".into()),
                            format!(
                                "Secret is required but not found in registered secrets {:?}",
                                &secret_ref.key
                            ),
                        ),
                    ))
                }
                None => {
                    debug!("Secret or secret version {:?} doesn't exist and since it is not required it is skipped",secret_ref.key);
                }
            }
        }

        Ok(result_map)
    }
}