signstar_config/nethsm/
admin_credentials.rs

1//! Administrative credentials for [`NetHsm`] backends.
2
3use nethsm::{FullCredentials, Passphrase};
4#[cfg(doc)]
5use nethsm::{NetHsm, UserId};
6use serde::{Deserialize, Serialize};
7
8use crate::{AdminCredentials, admin_credentials::Error};
9
10/// Administrative credentials.
11///
12/// Tracks the following credentials and passphrases:
13/// - the backup passphrase of the backend,
14/// - the unlock passphrase of the backend,
15/// - the top-level administrator credentials of the backend,
16/// - the namespace administrator credentials of the backend.
17///
18/// # Note
19///
20/// The unlock and backup passphrase must be at least 10 characters long.
21/// The passphrases of top-level and namespace administrator accounts must be at least 10 characters
22/// long.
23/// The list of top-level administrator credentials must include an account with the username
24/// "admin".
25#[derive(Clone, Debug, Default, Deserialize, Serialize)]
26pub struct NetHsmAdminCredentials {
27    iteration: u32,
28    backup_passphrase: Passphrase,
29    unlock_passphrase: Passphrase,
30    administrators: Vec<FullCredentials>,
31    namespace_administrators: Vec<FullCredentials>,
32}
33
34impl NetHsmAdminCredentials {
35    /// Creates a new [`NetHsmAdminCredentials`] instance.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use nethsm::FullCredentials;
41    /// use signstar_config::NetHsmAdminCredentials;
42    ///
43    /// # fn main() -> testresult::TestResult {
44    /// let creds = NetHsmAdminCredentials::new(
45    ///     1,
46    ///     "backup-passphrase".parse()?,
47    ///     "unlock-passphrase".parse()?,
48    ///     vec![FullCredentials::new(
49    ///         "admin".parse()?,
50    ///         "admin-passphrase".parse()?,
51    ///     )],
52    ///     vec![FullCredentials::new(
53    ///         "ns1~admin".parse()?,
54    ///         "ns1-admin-passphrase".parse()?,
55    ///     )],
56    /// )?;
57    /// # // the backup passphrase is too short
58    /// # assert!(NetHsmAdminCredentials::new(
59    /// #     1,
60    /// #     "short".parse()?,
61    /// #     "unlock-passphrase".parse()?,
62    /// #     vec![FullCredentials::new("admin".parse()?, "admin-passphrase".parse()?)],
63    /// #     vec![FullCredentials::new(
64    /// #         "ns1~admin".parse()?,
65    /// #         "ns1-admin-passphrase".parse()?,
66    /// #     )],
67    /// # ).is_err());
68    /// #
69    /// # // the unlock passphrase is too short
70    /// # assert!(NetHsmAdminCredentials::new(
71    /// #     1,
72    /// #     "backup-passphrase".parse()?,
73    /// #     "short".parse()?,
74    /// #     vec![FullCredentials::new("admin".parse()?, "admin-passphrase".parse()?)],
75    /// #     vec![FullCredentials::new(
76    /// #         "ns1~admin".parse()?,
77    /// #         "ns1-admin-passphrase".parse()?,
78    /// #     )],
79    /// # ).is_err());
80    /// #
81    /// # // there is no top-level administrator
82    /// # assert!(NetHsmAdminCredentials::new(
83    /// #     1,
84    /// #     "backup-passphrase".parse()?,
85    /// #     "unlock-passphrase".parse()?,
86    /// #     Vec::new(),
87    /// #     vec![FullCredentials::new(
88    /// #         "ns1~admin".parse()?,
89    /// #         "ns1-admin-passphrase".parse()?,
90    /// #     )],
91    /// # ).is_err());
92    /// #
93    /// # // there is no default top-level administrator
94    /// # assert!(NetHsmAdminCredentials::new(
95    /// #     1,
96    /// #     "backup-passphrase".parse()?,
97    /// #     "unlock-passphrase".parse()?,
98    /// #     vec![FullCredentials::new("some".parse()?, "admin-passphrase".parse()?)],
99    /// #     vec![FullCredentials::new(
100    /// #         "ns1~admin".parse()?,
101    /// #         "ns1-admin-passphrase".parse()?,
102    /// #     )],
103    /// # ).is_err());
104    /// #
105    /// # // a top-level administrator passphrase is too short
106    /// # assert!(NetHsmAdminCredentials::new(
107    /// #     1,
108    /// #     "backup-passphrase".parse()?,
109    /// #     "unlock-passphrase".parse()?,
110    /// #     vec![FullCredentials::new("admin".parse()?, "short".parse()?)],
111    /// #     vec![FullCredentials::new(
112    /// #         "ns1~admin".parse()?,
113    /// #         "ns1-admin-passphrase".parse()?,
114    /// #     )],
115    /// # ).is_err());
116    /// #
117    /// # // a namespace administrator passphrase is too short
118    /// # assert!(NetHsmAdminCredentials::new(
119    /// #     1,
120    /// #     "backup-passphrase".parse()?,
121    /// #     "unlock-passphrase".parse()?,
122    /// #     vec![FullCredentials::new("some".parse()?, "admin-passphrase".parse()?)],
123    /// #     vec![FullCredentials::new(
124    /// #         "ns1~admin".parse()?,
125    /// #         "short".parse()?,
126    /// #     )],
127    /// # ).is_err());
128    /// # Ok(())
129    /// # }
130    /// ```
131    pub fn new(
132        iteration: u32,
133        backup_passphrase: Passphrase,
134        unlock_passphrase: Passphrase,
135        administrators: Vec<FullCredentials>,
136        namespace_administrators: Vec<FullCredentials>,
137    ) -> Result<Self, crate::Error> {
138        let admin_credentials = Self {
139            iteration,
140            backup_passphrase,
141            unlock_passphrase,
142            administrators,
143            namespace_administrators,
144        };
145        admin_credentials.validate()?;
146
147        Ok(admin_credentials)
148    }
149
150    /// Returns the iteration.
151    pub fn get_iteration(&self) -> u32 {
152        self.iteration
153    }
154
155    /// Returns the backup passphrase.
156    pub fn get_backup_passphrase(&self) -> &str {
157        self.backup_passphrase.expose_borrowed()
158    }
159
160    /// Returns the unlock passphrase.
161    pub fn get_unlock_passphrase(&self) -> &str {
162        self.unlock_passphrase.expose_borrowed()
163    }
164
165    /// Returns the list of administrators.
166    pub fn get_administrators(&self) -> &[FullCredentials] {
167        &self.administrators
168    }
169
170    /// Returns the default system-wide administrator "admin".
171    ///
172    /// # Errors
173    ///
174    /// Returns an error if no administrative account with the system-wide [`UserId`] "admin" is
175    /// found.
176    pub fn get_default_administrator(&self) -> Result<&FullCredentials, crate::Error> {
177        let Some(first_admin) = self
178            .administrators
179            .iter()
180            .find(|user| user.name.to_string() == "admin")
181        else {
182            return Err(Error::AdministratorNoDefault.into());
183        };
184        Ok(first_admin)
185    }
186
187    /// Returns the list of namespace administrators.
188    pub fn get_namespace_administrators(&self) -> &[FullCredentials] {
189        &self.namespace_administrators
190    }
191}
192
193impl AdminCredentials for NetHsmAdminCredentials {
194    /// Validates the [`NetHsmAdminCredentials`].
195    ///
196    /// # Errors
197    ///
198    /// Returns an error if
199    /// - there is no top-level administrator user,
200    /// - the default top-level administrator user (with the name "admin") is missing,
201    /// - a user passphrase is too short,
202    /// - the backup passphrase is too short,
203    /// - or the unlock passphrase is too short.
204    fn validate(&self) -> Result<(), crate::Error> {
205        // there is no top-level administrator user
206        if self.get_administrators().is_empty() {
207            return Err(crate::Error::AdminSecretHandling(
208                Error::AdministratorMissing,
209            ));
210        }
211
212        // there is no top-level administrator user with the name "admin"
213        if !self
214            .get_administrators()
215            .iter()
216            .any(|user| user.name.to_string() == "admin")
217        {
218            return Err(crate::Error::AdminSecretHandling(
219                Error::AdministratorNoDefault,
220            ));
221        }
222
223        let minimum_length: usize = 10;
224
225        // a top-level administrator user passphrase is too short
226        for user in self.get_administrators().iter() {
227            if user.passphrase.expose_borrowed().len() < minimum_length {
228                return Err(crate::Error::AdminSecretHandling(
229                    Error::PassphraseTooShort {
230                        context: format!("user {}", user.name),
231                        minimum_length,
232                    },
233                ));
234            }
235        }
236
237        // a namespace administrator user passphrase is too short
238        for user in self.get_namespace_administrators().iter() {
239            if user.passphrase.expose_borrowed().len() < minimum_length {
240                return Err(crate::Error::AdminSecretHandling(
241                    Error::PassphraseTooShort {
242                        context: format!("user {}", user.name),
243                        minimum_length,
244                    },
245                ));
246            }
247        }
248
249        // the backup passphrase is too short
250        if self.get_backup_passphrase().len() < minimum_length {
251            return Err(crate::Error::AdminSecretHandling(
252                Error::PassphraseTooShort {
253                    context: "backups".to_string(),
254                    minimum_length,
255                },
256            ));
257        }
258
259        // the unlock passphrase is too short
260        if self.get_unlock_passphrase().len() < minimum_length {
261            return Err(crate::Error::AdminSecretHandling(
262                Error::PassphraseTooShort {
263                    context: "unlocking".to_string(),
264                    minimum_length,
265                },
266            ));
267        }
268
269        Ok(())
270    }
271}