use nethsm::{Passphrase, UserId, UserRole};
use rpassword::prompt_password;
use rprompt::prompt_reply;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Unable to get backup passphrase")]
Backup,
#[error("Unable to get current backup passphrase")]
CurrentBackup,
#[error("Unable to get current unlock passphrase")]
CurrentUnlock,
#[error("Unable to get new backup passphrase")]
NewBackup,
#[error("Unable to get new unlock passphrase")]
NewUnlock,
#[error("Unable to get new passphrase for user {0}")]
NewUser(String),
#[error("Unable to get unlock passphrase")]
Unlock,
#[error("Unable to get passphrase for user {user_id:?} ({real_name:?}) ")]
User {
user_id: Option<UserId>,
real_name: Option<String>,
},
#[error("Unable to get username")]
UserName,
#[error("User data is invalid: {0}")]
NetHsmUser(#[from] nethsm::UserError),
}
pub enum PassphrasePrompt {
Backup,
CurrentBackup,
CurrentUnlock,
NewBackup,
NewUnlock,
NewUser(UserId),
Unlock,
User {
user_id: Option<UserId>,
real_name: Option<String>,
},
}
impl PassphrasePrompt {
pub fn prompt(&self) -> Result<Passphrase, Error> {
let reason = match self {
Self::Backup => "Backup passphrase: ".to_string(),
Self::CurrentBackup => "Current backup passphrase: ".to_string(),
Self::CurrentUnlock => "Current unlock passphrase: ".to_string(),
Self::NewBackup => "New backup passphrase: ".to_string(),
Self::NewUnlock => "New unlock passphrase: ".to_string(),
Self::NewUser(user) => format!("New passphrase for user \"{user}\": "),
Self::Unlock => "Unlock passphrase: ".to_string(),
Self::User { user_id, real_name } => match (user_id, real_name) {
(Some(user_id), Some(real_name)) => {
format!("Passphrase for user \"{user_id}\" (\"{real_name}\"): ")
}
(None, Some(real_name)) => format!("Passphrase for user \"{real_name}\": "),
(None, None) => "Passphrase for unknown user: ".to_string(),
(Some(user_id), None) => format!("Passphrase for unknown user \"{user_id}\": "),
},
};
Ok(Passphrase::new(prompt_password(reason).map_err(
|_| match self {
Self::Backup => Error::Backup,
Self::CurrentBackup => Error::CurrentBackup,
Self::CurrentUnlock => Error::CurrentUnlock,
Self::NewBackup => Error::NewBackup,
Self::NewUnlock => Error::NewUnlock,
Self::NewUser(user) => Error::NewUser(user.to_string()),
Self::Unlock => Error::Unlock,
Self::User { user_id, real_name } => Error::User {
user_id: user_id.to_owned(),
real_name: real_name.to_owned(),
},
},
)?))
}
}
pub struct UserPrompt(UserRole);
impl UserPrompt {
pub fn new(role: UserRole) -> Self {
Self(role)
}
pub fn prompt(&self) -> Result<UserId, Error> {
let reason = match self.0 {
UserRole::Administrator => "Name of a user in the \"Administrator\" role: ".to_string(),
UserRole::Backup => "Name of a user in the \"Backup\" role: ".to_string(),
UserRole::Metrics => "Name of a user in the \"Metrics\" role: ".to_string(),
UserRole::Operator => "Name of a user in the \"Operator\" role: ".to_string(),
};
let name = UserId::new(prompt_reply(reason).map_err(|_| Error::UserName)?)?;
Ok(name)
}
}