use crate::commands::TIDPLOY_DEFAULT;
use crate::secret_store::{get_password, set_password};
use crate::state::State;
use keyring::Error as KeyringError;
use rpassword::prompt_password;
use std::io::Error as IOError;
use thiserror::Error as ThisError;
use tracing::{debug, span, Level};
#[derive(ThisError, Debug)]
#[error("{msg} {source}")]
pub(crate) struct AuthError {
pub(crate) msg: String,
pub(crate) source: AuthErrorKind,
}
#[derive(ThisError, Debug)]
pub(crate) enum AuthErrorKind {
#[error("Failed to get password from prompt! {0}")]
Prompt(#[from] IOError),
#[error("No password saved.")]
NoPassword,
#[error("Internal keyring failure. {0}")]
Keyring(#[from] KeyringError),
}
pub(crate) fn secret_command(state: &State, key: String) -> Result<(), AuthError> {
let password = prompt_password("Enter secret:\n").map_err(|e| AuthError {
msg: "Failed to create password prompt!".to_owned(),
source: e.into(),
})?;
let path_str = if state.deploy_path.as_str().is_empty() {
"".to_owned()
} else {
format!("{{{}}}", state.deploy_path)
};
let mut store_key = format!("{}:{}", key, state.repo.name);
let has_path = if !path_str.is_empty() && path_str != TIDPLOY_DEFAULT {
store_key.push('/');
store_key.push_str(&path_str);
true
} else {
false
};
if !state.commit_sha.is_empty() && state.commit_sha != TIDPLOY_DEFAULT {
if !has_path {
store_key.push('/');
}
store_key.push('/');
store_key.push_str(&state.commit_sha);
}
set_password(&password, &store_key).map_err(|e| {
let msg = format!(
"Could not set password in auth command with store_key {}!",
store_key
);
AuthError {
msg,
source: e.into(),
}
})?;
Ok(println!("Set password with store_key {}!", &store_key))
}
pub(crate) fn get_secret(state: &State, key: &str) -> Result<String, AuthErrorKind> {
let secret_span = span!(Level::DEBUG, "get_secret");
let _enter = secret_span.enter();
debug!("Getting secret with key {}", key);
let path_str = format!("{{{}}}", state.deploy_path);
let store_key: String = format!(
"{}:{}/{}/{}",
key, state.repo.name, path_str, state.commit_sha
);
if let Some(password) = get_password(&store_key)? {
return Ok(password);
}
let store_key_no_commit = format!("{}:{}/{}", key, state.repo.name, path_str);
if let Some(password) = get_password(&store_key_no_commit)? {
return Ok(password);
}
let store_key_only_repo = format!("{}:{}", key, state.repo.name);
if let Some(password) = get_password(&store_key_only_repo)? {
return Ok(password);
}
let store_key_default = format!("{}:{}", key, TIDPLOY_DEFAULT);
match get_password(&store_key_default)? {
Some(password) => Ok(password),
None => Err(AuthErrorKind::NoPassword),
}
}