sopass 0.5.0

command line password manager using SOP
Documentation
//! The `init` sub-command.

use std::{fs::create_dir_all, path::PathBuf};

use clap::Parser;
use log::{debug, info};

use crate::{
    sop::{Certificate, SopError},
    store::{EncryptedStore, EncryptedStoreError, Store},
    Config, LeafCommand,
};

/// Create and initialize a new password store. Use an existing key
/// for this device and extract the corresponding certificate and
/// store it in the new password store.
#[derive(Debug, Parser)]
pub struct InitCommand {
    /// Name of certificate extracted from the specified key.
    #[clap(long)]
    name: String,

    /// File containing the key to use on this device. This implies
    /// using a SOP implementation that supports software keys only.
    #[clap(long)]
    key: Option<PathBuf>,

    /// File containing the certificate corresponding to the key to
    /// use on this device. This implies using a SOP implementation
    /// that supports OpenPGP cards for decryption.
    #[clap(long)]
    cert: Option<PathBuf>,
}

impl LeafCommand for InitCommand {
    type Error = InitError;

    fn run(&self, config: &Config) -> Result<(), InitError> {
        info!("init password store");
        debug!("{config:#?}");
        let store_dir = config.store();
        if !store_dir.exists() {
            println!("creating {}", store_dir.display());
            create_dir_all(store_dir).map_err(|err| InitError::CreateDir(store_dir.into(), err))?;
        }

        let mut store = Store::default();
        let encrypted = EncryptedStore::new(config.sop(), config.store());

        if let Some(key) = &self.key {
            encrypted.install_key(key)?;
            let cert = encrypted.extract_cert()?;
            store.push_cert(&self.name, &cert);
        } else if let Some(cert) = &self.cert {
            encrypted.install_cert(cert)?;
            let cert = Certificate::load(cert)?;
            store.push_cert(&self.name, &cert);
        } else {
            return Err(InitError::NoKey);
        }

        encrypted.write_store(&store)?;
        Ok(())
    }
}

/// Possible errors from the `init` sub-command.
#[derive(Debug, thiserror::Error)]
pub enum InitError {
    /// Can't create store directory.
    #[error("failed to create store directory {0}")]
    CreateDir(PathBuf, #[source] std::io::Error),

    /// Either --key or --cert must be used.
    #[error("neither --key or --cert has been specified")]
    NoKey,

    /// Error from dealing with the encrypted store.
    #[error(transparent)]
    Encrypted(#[from] EncryptedStoreError),

    /// Error from dealing with the the `sop` module.
    #[error(transparent)]
    Sop(#[from] SopError),
}