Skip to main content

nethsm/cli/config/
prompt.rs

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