secretgarden 0.5.0

tool for generating and securely storing secrets
use anyhow::{anyhow, Context, Result as AHResult};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use clap::Parser;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::io::{self, Read};

use crate::secret_store::SecretStore;
use crate::types::{CommonOpts, ConfigType, WithCommonOpts};

#[derive(Parser, Debug, Serialize, Deserialize, PartialEq)]
/// Get an existing opaque value.
///
/// Opaque values cannot be generated and must be set with `set-opaque`.
pub struct OpaqueOpts {
    #[clap(flatten)]
    #[serde(skip)]
    common: CommonOpts,
}

impl WithCommonOpts for OpaqueOpts {
    fn common_opts(&self) -> &CommonOpts {
        &self.common
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct OpaqueConfig {}
impl ConfigType<'_> for OpaqueConfig {}

pub fn generate_opaque(_: &OpaqueOpts, _: &OpaqueConfig) -> AHResult<String> {
    Err(anyhow!("Cannot generate opaque value"))
}

#[derive(Parser)]
/// Set an opaque value.
pub struct SetOpaqueOpts {
    #[clap(help = "Name of the secret to set")]
    name: String,
    #[clap(help = "The new value of the secret; if not provided, will be read on stdin")]
    value: Option<String>,
    #[clap(long, help = "Decode the secret's value with base64")]
    base64: bool,
}

pub fn run_set_opaque(store: &mut impl SecretStore, s: SetOpaqueOpts) -> AHResult<()> {
    let mut value: String;

    match s.value {
        Some(v) => value = v,
        None => {
            value = String::new();

            io::stdin()
                .read_to_string(&mut value)
                .context("Failed to read value from stdin")?;

            value = value.trim_end().to_string();
        }
    }

    if s.base64 {
        value = STANDARD
            .decode(value)
            .context("Failed to decode provided value as base64")?
            .iter()
            .map(|c| *c as char)
            .collect();
    }

    store.set_opaque(s.name, value)
}