nethsm_config/
prompt.rs

1use nethsm::{Passphrase, UserId, UserRole};
2use rpassword::prompt_password;
3use rprompt::prompt_reply;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    /// Getting backup passphrase failed
8    #[error("Unable to get backup passphrase")]
9    Backup,
10
11    /// Getting current backup passphrase failed
12    #[error("Unable to get current backup passphrase")]
13    CurrentBackup,
14
15    /// Getting current unlock passphrase failed
16    #[error("Unable to get current unlock passphrase")]
17    CurrentUnlock,
18
19    /// Getting new backup passphrase failed
20    #[error("Unable to get new backup passphrase")]
21    NewBackup,
22
23    /// Getting new unlock passphrase failed
24    #[error("Unable to get new unlock passphrase")]
25    NewUnlock,
26
27    /// Getting new passphrase for user failed
28    #[error("Unable to get new passphrase for user {0}")]
29    NewUser(String),
30
31    /// Getting unlock passphrase failed
32    #[error("Unable to get unlock passphrase")]
33    Unlock,
34
35    /// Getting current passphrase for user failed
36    #[error("Unable to get passphrase for user {user_id:?} ({real_name:?}) ")]
37    User {
38        user_id: Option<UserId>,
39        real_name: Option<String>,
40    },
41
42    /// Getting username failed
43    #[error("Unable to get username")]
44    UserName,
45
46    /// The user data is not correct
47    #[error("User data is invalid: {0}")]
48    NetHsmUser(#[from] nethsm::UserError),
49}
50
51/// Passphrase prompt
52pub enum PassphrasePrompt {
53    /// Prompt for backup passphrase
54    Backup,
55    /// Prompt for current backup passphrase
56    CurrentBackup,
57    /// Prompt for current unlock passphrase
58    CurrentUnlock,
59    /// Prompt for new backup passphrase
60    NewBackup,
61    /// Prompt for new unlock passphrase
62    NewUnlock,
63    /// Prompt for new user passphrase
64    NewUser(UserId),
65    /// Prompt for unlock passphrase
66    Unlock,
67    /// Prompt for current user passphrase
68    User {
69        user_id: Option<UserId>,
70        real_name: Option<String>,
71    },
72}
73
74impl PassphrasePrompt {
75    /// Prompts for a passphrase
76    pub fn prompt(&self) -> Result<Passphrase, Error> {
77        let reason = match self {
78            Self::Backup => "Backup passphrase: ".to_string(),
79            Self::CurrentBackup => "Current backup passphrase: ".to_string(),
80            Self::CurrentUnlock => "Current unlock passphrase: ".to_string(),
81            Self::NewBackup => "New backup passphrase: ".to_string(),
82            Self::NewUnlock => "New unlock passphrase: ".to_string(),
83            Self::NewUser(user) => format!("New passphrase for user \"{user}\": "),
84            Self::Unlock => "Unlock passphrase: ".to_string(),
85            Self::User { user_id, real_name } => match (user_id, real_name) {
86                (Some(user_id), Some(real_name)) => {
87                    format!("Passphrase for user \"{user_id}\" (\"{real_name}\"): ")
88                }
89                (None, Some(real_name)) => format!("Passphrase for user \"{real_name}\": "),
90                (None, None) => "Passphrase for unknown user: ".to_string(),
91                (Some(user_id), None) => format!("Passphrase for unknown user \"{user_id}\": "),
92            },
93        };
94
95        Ok(Passphrase::new(prompt_password(reason).map_err(
96            |_| match self {
97                Self::Backup => Error::Backup,
98                Self::CurrentBackup => Error::CurrentBackup,
99                Self::CurrentUnlock => Error::CurrentUnlock,
100                Self::NewBackup => Error::NewBackup,
101                Self::NewUnlock => Error::NewUnlock,
102                Self::NewUser(user) => Error::NewUser(user.to_string()),
103                Self::Unlock => Error::Unlock,
104                Self::User { user_id, real_name } => Error::User {
105                    user_id: user_id.to_owned(),
106                    real_name: real_name.to_owned(),
107                },
108            },
109        )?))
110    }
111}
112
113/// Username prompt
114pub struct UserPrompt(UserRole);
115
116impl UserPrompt {
117    /// Creates a new [`UserPrompt`] based on a [`UserRole`]
118    pub fn new(role: UserRole) -> Self {
119        Self(role)
120    }
121
122    /// Prompt for a username
123    pub fn prompt(&self) -> Result<UserId, Error> {
124        let reason = match self.0 {
125            UserRole::Administrator => "Name of a user in the \"Administrator\" role: ".to_string(),
126            UserRole::Backup => "Name of a user in the \"Backup\" role: ".to_string(),
127            UserRole::Metrics => "Name of a user in the \"Metrics\" role: ".to_string(),
128            UserRole::Operator => "Name of a user in the \"Operator\" role: ".to_string(),
129        };
130
131        let name = UserId::new(prompt_reply(reason).map_err(|_| Error::UserName)?)?;
132        Ok(name)
133    }
134}