nethsm_config/
mapping.rs

1use std::collections::HashSet;
2
3#[cfg(doc)]
4use nethsm::NetHsm;
5use nethsm::{Connection, KeyId, SigningKeySetup, UserId};
6use serde::{Deserialize, Serialize};
7
8use crate::{
9    AdministrativeSecretHandling,
10    AuthorizedKeyEntry,
11    AuthorizedKeyEntryList,
12    HermeticParallelConfig,
13    NonAdministrativeSecretHandling,
14    SystemUserId,
15    SystemWideUserId,
16};
17
18/// Errors related to mapping
19#[derive(Debug, thiserror::Error)]
20pub enum Error {
21    /// A duplicate top-level [`KeyId`]
22    #[error("Duplicate top-level NetHsm key {key}")]
23    DuplicateKey { key: KeyId },
24
25    /// A duplicate namespaced [`KeyId`]
26    #[error("Duplicate NetHsm key {key} in namespace {namespace}")]
27    DuplicateKeyInNamespace { namespace: String, key: KeyId },
28
29    /// A duplicate [`UserId`]
30    #[error("Duplicate NetHsm user {nethsm_user}")]
31    DuplicateNetHsmUser { nethsm_user: UserId },
32
33    /// A [`UserId`] is used both for a user in the [`Metrics`][`nethsm::UserRole::Metrics`] and
34    /// [`Operator`][`nethsm::UserRole::Operator`] role
35    #[error("The NetHsm user {metrics_user} is both in the Metrics and Operator role!")]
36    MetricsAlsoOperator { metrics_user: SystemWideUserId },
37}
38
39/// A set of users with unique [`UserId`]s, used for metrics retrieval
40///
41/// This struct tracks a user that is intended for the use in the
42/// [`Metrics`][`nethsm::UserRole::Metrics`] role and a list of users, that are intended to be used
43/// in the [`Operator`][`nethsm::UserRole::Operator`] role.
44#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
45pub struct NetHsmMetricsUsers {
46    metrics_user: SystemWideUserId,
47    operator_users: Vec<UserId>,
48}
49
50impl NetHsmMetricsUsers {
51    /// Creates a new [`NetHsmMetricsUsers`]
52    ///
53    /// # Error
54    ///
55    /// Returns an error, if the provided [`UserId`] of the `metrics_user` is duplicated in the
56    /// provided `operator_users`.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// use nethsm_config::NetHsmMetricsUsers;
62    ///
63    /// # fn main() -> testresult::TestResult {
64    /// NetHsmMetricsUsers::new(
65    ///     "metrics1".parse()?,
66    ///     vec!["user1".parse()?, "user2".parse()?],
67    /// )?;
68    ///
69    /// // this fails because there are duplicate UserIds
70    /// assert!(
71    ///     NetHsmMetricsUsers::new(
72    ///         "metrics1".parse()?,
73    ///         vec!["metrics1".parse()?, "user2".parse()?,],
74    ///     )
75    ///     .is_err()
76    /// );
77    /// # Ok(())
78    /// # }
79    /// ```
80    pub fn new(metrics_user: SystemWideUserId, operator_users: Vec<UserId>) -> Result<Self, Error> {
81        // prevent duplicate metrics and operator users
82        if operator_users.contains(&metrics_user.clone().into()) {
83            return Err(Error::MetricsAlsoOperator { metrics_user });
84        }
85
86        Ok(Self {
87            metrics_user,
88            operator_users,
89        })
90    }
91
92    /// Returns all tracked [`UserId`]s of the [`NetHsmMetricsUsers`]
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use nethsm::UserId;
98    /// use nethsm_config::NetHsmMetricsUsers;
99    ///
100    /// # fn main() -> testresult::TestResult {
101    /// let nethsm_metrics_users = NetHsmMetricsUsers::new(
102    ///     "metrics1".parse()?,
103    ///     vec!["user1".parse()?, "user2".parse()?],
104    /// )?;
105    ///
106    /// assert_eq!(
107    ///     nethsm_metrics_users.get_users(),
108    ///     vec![
109    ///         UserId::new("metrics1".to_string())?,
110    ///         UserId::new("user1".to_string())?,
111    ///         UserId::new("user2".to_string())?
112    ///     ]
113    /// );
114    /// # Ok(())
115    /// # }
116    /// ```
117    pub fn get_users(&self) -> Vec<UserId> {
118        [
119            vec![self.metrics_user.clone().into()],
120            self.operator_users.clone(),
121        ]
122        .concat()
123    }
124}
125
126/// User mapping between system users and [`NetHsm`][`nethsm::NetHsm`] users
127#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
128pub enum UserMapping {
129    /// A NetHsm user in the Administrator role, without a system user mapped to it
130    #[serde(rename = "nethsm_only_admin")]
131    NetHsmOnlyAdmin(UserId),
132
133    /// A system user, with SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`] user
134    /// in the Backup role
135    #[serde(rename = "system_nethsm_backup")]
136    SystemNetHsmBackup {
137        nethsm_user: SystemWideUserId,
138        ssh_authorized_key: AuthorizedKeyEntry,
139        system_user: SystemUserId,
140    },
141
142    /// A system user, with SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`] user
143    /// in the Metrics role and `n` users in the Operator role with read-only access to zero or
144    /// more keys
145    #[serde(rename = "system_nethsm_metrics")]
146    SystemNetHsmMetrics {
147        nethsm_users: NetHsmMetricsUsers,
148        ssh_authorized_key: AuthorizedKeyEntry,
149        system_user: SystemUserId,
150    },
151
152    /// A system user, with SSH access, mapped to a [`NetHsm`][`nethsm::NetHsm`] user in the
153    /// Operator role with access to a single signing key.
154    ///
155    /// Signing key and NetHSM user are mapped using a tag.
156    #[serde(rename = "system_nethsm_operator_signing")]
157    SystemNetHsmOperatorSigning {
158        nethsm_user: UserId,
159        nethsm_key_setup: SigningKeySetup,
160        ssh_authorized_key: AuthorizedKeyEntry,
161        system_user: SystemUserId,
162        tag: String,
163    },
164
165    /// A system user, without SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`]
166    /// user in the Metrics role and one or more NetHsm users in the Operator role with
167    /// read-only access to zero or more keys
168    #[serde(rename = "hermetic_system_nethsm_metrics")]
169    HermeticSystemNetHsmMetrics {
170        nethsm_users: NetHsmMetricsUsers,
171        system_user: SystemUserId,
172    },
173
174    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
175    /// used for downloading shares of a shared secret
176    #[serde(rename = "system_only_share_download")]
177    SystemOnlyShareDownload {
178        system_user: SystemUserId,
179        ssh_authorized_keys: AuthorizedKeyEntryList,
180    },
181
182    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
183    /// used for uploading shares of a shared secret
184    #[serde(rename = "system_only_share_upload")]
185    SystemOnlyShareUpload {
186        system_user: SystemUserId,
187        ssh_authorized_keys: AuthorizedKeyEntryList,
188    },
189
190    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
191    /// used for downloading WireGuard configuration
192    #[serde(rename = "system_only_wireguard_download")]
193    SystemOnlyWireGuardDownload {
194        system_user: SystemUserId,
195        ssh_authorized_keys: AuthorizedKeyEntryList,
196    },
197}
198
199impl UserMapping {
200    /// Returns the optional system user of the mapping
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use nethsm_config::{AuthorizedKeyEntryList, SystemUserId, UserMapping};
206    ///
207    /// # fn main() -> testresult::TestResult {
208    /// let mapping = UserMapping::SystemOnlyShareDownload {
209    ///     system_user: "user1".parse()?,
210    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
211    /// };
212    /// assert_eq!(mapping.get_system_user(), Some(&SystemUserId::new("user1".to_string())?));
213    ///
214    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
215    /// assert_eq!(mapping.get_system_user(), None);
216    /// # Ok(())
217    /// # }
218    /// ```
219    pub fn get_system_user(&self) -> Option<&SystemUserId> {
220        match self {
221            UserMapping::NetHsmOnlyAdmin(_) => None,
222            UserMapping::SystemNetHsmBackup {
223                nethsm_user: _,
224                ssh_authorized_key: _,
225                system_user,
226            }
227            | UserMapping::SystemNetHsmOperatorSigning {
228                nethsm_user: _,
229                nethsm_key_setup: _,
230                ssh_authorized_key: _,
231                system_user,
232                tag: _,
233            }
234            | UserMapping::SystemNetHsmMetrics {
235                nethsm_users: _,
236                ssh_authorized_key: _,
237                system_user,
238            }
239            | UserMapping::HermeticSystemNetHsmMetrics {
240                nethsm_users: _,
241                system_user,
242            }
243            | UserMapping::SystemOnlyShareDownload {
244                system_user,
245                ssh_authorized_keys: _,
246            }
247            | UserMapping::SystemOnlyShareUpload {
248                system_user,
249                ssh_authorized_keys: _,
250            }
251            | UserMapping::SystemOnlyWireGuardDownload {
252                system_user,
253                ssh_authorized_keys: _,
254            } => Some(system_user),
255        }
256    }
257
258    /// Returns the NetHsm users of the mapping
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use nethsm::UserId;
264    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
265    ///
266    /// # fn main() -> testresult::TestResult {
267    /// let mapping = UserMapping::SystemOnlyShareDownload {
268    ///     system_user: "user1".parse()?,
269    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
270    /// };
271    /// assert!(mapping.get_nethsm_users().is_empty());
272    ///
273    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
274    /// assert_eq!(mapping.get_nethsm_users(), vec![UserId::new("user1".to_string())?]);
275    /// # Ok(())
276    /// # }
277    /// ```
278    pub fn get_nethsm_users(&self) -> Vec<UserId> {
279        match self {
280            UserMapping::SystemNetHsmBackup {
281                nethsm_user,
282                system_user: _,
283                ssh_authorized_key: _,
284            } => vec![nethsm_user.clone().into()],
285            UserMapping::NetHsmOnlyAdmin(nethsm_user)
286            | UserMapping::SystemNetHsmOperatorSigning {
287                nethsm_user,
288                nethsm_key_setup: _,
289                system_user: _,
290                ssh_authorized_key: _,
291                tag: _,
292            } => vec![nethsm_user.clone()],
293            UserMapping::SystemNetHsmMetrics {
294                nethsm_users,
295                system_user: _,
296                ssh_authorized_key: _,
297            }
298            | UserMapping::HermeticSystemNetHsmMetrics {
299                nethsm_users,
300                system_user: _,
301            } => nethsm_users.get_users(),
302            UserMapping::SystemOnlyShareDownload {
303                system_user: _,
304                ssh_authorized_keys: _,
305            }
306            | UserMapping::SystemOnlyShareUpload {
307                system_user: _,
308                ssh_authorized_keys: _,
309            }
310            | UserMapping::SystemOnlyWireGuardDownload {
311                system_user: _,
312                ssh_authorized_keys: _,
313            } => vec![],
314        }
315    }
316
317    /// Returns the SSH authorized keys of the mapping
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use nethsm_config::{AuthorizedKeyEntry, AuthorizedKeyEntryList, UserMapping};
323    ///
324    /// # fn main() -> testresult::TestResult {
325    /// let mapping = UserMapping::SystemOnlyShareDownload {
326    ///     system_user: "user1".parse()?,
327    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
328    /// };
329    /// assert_eq!(mapping.get_ssh_authorized_keys(), vec![AuthorizedKeyEntry::new("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".to_string())?]);
330    ///
331    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
332    /// assert_eq!(mapping.get_ssh_authorized_keys(), vec![]);
333    /// # Ok(())
334    /// # }
335    /// ```
336    pub fn get_ssh_authorized_keys(&self) -> Vec<AuthorizedKeyEntry> {
337        match self {
338            UserMapping::NetHsmOnlyAdmin(_) => vec![],
339            UserMapping::SystemNetHsmBackup {
340                nethsm_user: _,
341                system_user: _,
342                ssh_authorized_key,
343            }
344            | UserMapping::SystemNetHsmOperatorSigning {
345                nethsm_user: _,
346                nethsm_key_setup: _,
347                system_user: _,
348                ssh_authorized_key,
349                tag: _,
350            } => vec![ssh_authorized_key.clone()],
351            UserMapping::SystemNetHsmMetrics {
352                nethsm_users: _,
353                system_user: _,
354                ssh_authorized_key,
355            } => vec![ssh_authorized_key.clone()],
356            UserMapping::HermeticSystemNetHsmMetrics {
357                nethsm_users: _,
358                system_user: _,
359            } => vec![],
360            UserMapping::SystemOnlyShareDownload {
361                system_user: _,
362                ssh_authorized_keys,
363            }
364            | UserMapping::SystemOnlyShareUpload {
365                system_user: _,
366                ssh_authorized_keys,
367            }
368            | UserMapping::SystemOnlyWireGuardDownload {
369                system_user: _,
370                ssh_authorized_keys,
371            } => ssh_authorized_keys.into(),
372        }
373    }
374
375    /// Returns all used [`KeyId`]s of the mapping
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// use nethsm::{CryptographicKeyContext, KeyId, OpenPgpUserIdList, SigningKeySetup};
381    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
382    ///
383    /// # fn main() -> testresult::TestResult {
384    /// let mapping = UserMapping::SystemNetHsmOperatorSigning {
385    ///     nethsm_user: "user1".parse()?,
386    ///     nethsm_key_setup: SigningKeySetup::new(
387    ///         "key1".parse()?,
388    ///         "Curve25519".parse()?,
389    ///         vec!["EdDsaSignature".parse()?],
390    ///         None,
391    ///         "EdDsa".parse()?,
392    ///         CryptographicKeyContext::OpenPgp{
393    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
394    ///             version: "v4".parse()?,
395    ///         },
396    ///     )?,
397    ///     system_user: "ssh-user1".parse()?,
398    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
399    ///     tag: "tag1".to_string(),
400    /// };
401    /// assert_eq!(mapping.get_key_ids(None), vec![KeyId::new("key1".to_string())?]);
402    ///
403    /// let mapping = UserMapping::SystemOnlyShareDownload {
404    ///     system_user: "user1".parse()?,
405    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
406    /// };
407    /// assert_eq!(mapping.get_key_ids(None), vec![]);
408    /// # Ok(())
409    /// # }
410    /// ```
411    pub fn get_key_ids(&self, namespace: Option<&str>) -> Vec<KeyId> {
412        match self {
413            UserMapping::SystemNetHsmOperatorSigning {
414                nethsm_user,
415                nethsm_key_setup,
416                system_user: _,
417                ssh_authorized_key: _,
418                tag: _,
419            } => {
420                if nethsm_user.namespace().as_deref() == namespace {
421                    vec![nethsm_key_setup.get_key_id()]
422                } else {
423                    vec![]
424                }
425            }
426            UserMapping::SystemNetHsmMetrics {
427                nethsm_users: _,
428                system_user: _,
429                ssh_authorized_key: _,
430            }
431            | UserMapping::NetHsmOnlyAdmin(_)
432            | UserMapping::HermeticSystemNetHsmMetrics {
433                nethsm_users: _,
434                system_user: _,
435            }
436            | UserMapping::SystemNetHsmBackup {
437                nethsm_user: _,
438                system_user: _,
439                ssh_authorized_key: _,
440            }
441            | UserMapping::SystemOnlyShareDownload {
442                system_user: _,
443                ssh_authorized_keys: _,
444            }
445            | UserMapping::SystemOnlyShareUpload {
446                system_user: _,
447                ssh_authorized_keys: _,
448            }
449            | UserMapping::SystemOnlyWireGuardDownload {
450                system_user: _,
451                ssh_authorized_keys: _,
452            } => vec![],
453        }
454    }
455
456    /// Returns tags for keys and users
457    ///
458    /// Tags can be filtered by [namespace] by providing [`Some`] `namespace`.
459    /// Providing [`None`] implies that the context is system-wide.
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// use nethsm::{CryptographicKeyContext, OpenPgpUserIdList, SigningKeySetup};
465    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
466    ///
467    /// # fn main() -> testresult::TestResult {
468    /// let mapping = UserMapping::SystemOnlyShareDownload {
469    ///     system_user: "user1".parse()?,
470    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
471    /// };
472    /// assert!(mapping.get_tags(None).is_empty());
473    ///
474    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
475    /// assert!(mapping.get_tags(None).is_empty());
476    ///
477    /// let mapping = UserMapping::SystemNetHsmOperatorSigning{
478    ///     nethsm_user: "ns1~user1".parse()?,
479    ///     nethsm_key_setup: SigningKeySetup::new(
480    ///         "key1".parse()?,
481    ///         "Curve25519".parse()?,
482    ///         vec!["EdDsaSignature".parse()?],
483    ///         None,
484    ///         "EdDsa".parse()?,
485    ///         CryptographicKeyContext::OpenPgp{
486    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
487    ///             version: "4".parse()?,
488    ///     })?,
489    ///     system_user: "user1".parse()?,
490    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
491    ///     tag: "tag1".to_string(),
492    /// };
493    /// assert!(mapping.get_tags(None).is_empty());
494    /// assert_eq!(mapping.get_tags(Some("ns1")), vec!["tag1"]);
495    /// # Ok(())
496    /// # }
497    /// ```
498    /// [namespace]: https://docs.nitrokey.com/nethsm/administration#namespaces
499    pub fn get_tags(&self, namespace: Option<&str>) -> Vec<&str> {
500        match self {
501            UserMapping::SystemNetHsmOperatorSigning {
502                nethsm_user,
503                nethsm_key_setup: _,
504                system_user: _,
505                ssh_authorized_key: _,
506                tag,
507            } => {
508                if nethsm_user.namespace().as_deref() == namespace {
509                    vec![tag.as_str()]
510                } else {
511                    vec![]
512                }
513            }
514            UserMapping::SystemNetHsmMetrics {
515                nethsm_users: _,
516                system_user: _,
517                ssh_authorized_key: _,
518            }
519            | UserMapping::NetHsmOnlyAdmin(_)
520            | UserMapping::HermeticSystemNetHsmMetrics {
521                nethsm_users: _,
522                system_user: _,
523            }
524            | UserMapping::SystemNetHsmBackup {
525                nethsm_user: _,
526                system_user: _,
527                ssh_authorized_key: _,
528            }
529            | UserMapping::SystemOnlyShareDownload {
530                system_user: _,
531                ssh_authorized_keys: _,
532            }
533            | UserMapping::SystemOnlyShareUpload {
534                system_user: _,
535                ssh_authorized_keys: _,
536            }
537            | UserMapping::SystemOnlyWireGuardDownload {
538                system_user: _,
539                ssh_authorized_keys: _,
540            } => vec![],
541        }
542    }
543
544    /// Returns all [`NetHsm`][`nethsm::NetHsm`] [namespaces] of the mapping
545    ///
546    /// # Examples
547    ///
548    /// ```
549    /// use nethsm::{CryptographicKeyContext, OpenPgpUserIdList, SigningKeySetup};
550    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
551    ///
552    /// # fn main() -> testresult::TestResult {
553    /// let mapping = UserMapping::SystemOnlyShareDownload {
554    ///     system_user: "user1".parse()?,
555    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
556    /// };
557    /// assert!(mapping.get_namespaces().is_empty());
558    ///
559    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
560    /// assert!(mapping.get_namespaces().is_empty());
561    ///
562    /// let mapping = UserMapping::SystemNetHsmOperatorSigning{
563    ///     nethsm_user: "ns1~user1".parse()?,
564    ///     nethsm_key_setup: SigningKeySetup::new(
565    ///         "key1".parse()?,
566    ///         "Curve25519".parse()?,
567    ///         vec!["EdDsaSignature".parse()?],
568    ///         None,
569    ///         "EdDsa".parse()?,
570    ///         CryptographicKeyContext::OpenPgp{
571    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
572    ///             version: "4".parse()?,
573    ///     })?,
574    ///     system_user: "user1".parse()?,
575    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
576    ///     tag: "tag1".to_string(),
577    /// };
578    /// assert_eq!(mapping.get_namespaces(), vec!["ns1".to_string()]);
579    /// # Ok(())
580    /// # }
581    /// ```
582    /// [namespaces]: https://docs.nitrokey.com/nethsm/administration#namespaces
583    pub fn get_namespaces(&self) -> Vec<String> {
584        match self {
585            UserMapping::NetHsmOnlyAdmin(nethsm_user)
586            | UserMapping::SystemNetHsmOperatorSigning {
587                nethsm_user,
588                nethsm_key_setup: _,
589                system_user: _,
590                ssh_authorized_key: _,
591                tag: _,
592            } => {
593                if let Some(namespace) = nethsm_user.namespace() {
594                    vec![namespace]
595                } else {
596                    vec![]
597                }
598            }
599            UserMapping::HermeticSystemNetHsmMetrics {
600                nethsm_users,
601                system_user: _,
602            }
603            | UserMapping::SystemNetHsmMetrics {
604                nethsm_users,
605                system_user: _,
606                ssh_authorized_key: _,
607            } => nethsm_users
608                .get_users()
609                .iter()
610                .filter_map(|user_id| user_id.namespace())
611                .collect(),
612            UserMapping::SystemOnlyShareDownload {
613                system_user: _,
614                ssh_authorized_keys: _,
615            }
616            | UserMapping::SystemNetHsmBackup {
617                nethsm_user: _,
618                system_user: _,
619                ssh_authorized_key: _,
620            }
621            | UserMapping::SystemOnlyShareUpload {
622                system_user: _,
623                ssh_authorized_keys: _,
624            }
625            | UserMapping::SystemOnlyWireGuardDownload {
626                system_user: _,
627                ssh_authorized_keys: _,
628            } => vec![],
629        }
630    }
631
632    /// Returns whether the mapping has both system and [`NetHsm`] users.
633    ///
634    /// Returns `true` if the `self` has at least one system and one [`NetHsm`] user, and `false`
635    /// otherwise.
636    pub fn has_system_and_nethsm_user(&self) -> bool {
637        match self {
638            UserMapping::SystemNetHsmOperatorSigning {
639                nethsm_user: _,
640                nethsm_key_setup: _,
641                system_user: _,
642                ssh_authorized_key: _,
643                tag: _,
644            }
645            | UserMapping::HermeticSystemNetHsmMetrics {
646                nethsm_users: _,
647                system_user: _,
648            }
649            | UserMapping::SystemNetHsmMetrics {
650                nethsm_users: _,
651                system_user: _,
652                ssh_authorized_key: _,
653            }
654            | UserMapping::SystemNetHsmBackup {
655                nethsm_user: _,
656                system_user: _,
657                ssh_authorized_key: _,
658            } => true,
659            UserMapping::SystemOnlyShareDownload {
660                system_user: _,
661                ssh_authorized_keys: _,
662            }
663            | UserMapping::SystemOnlyShareUpload {
664                system_user: _,
665                ssh_authorized_keys: _,
666            }
667            | UserMapping::SystemOnlyWireGuardDownload {
668                system_user: _,
669                ssh_authorized_keys: _,
670            }
671            | UserMapping::NetHsmOnlyAdmin(_) => false,
672        }
673    }
674}
675
676/// A [`UserMapping`] centric view of a [`HermeticParallelConfig`].
677///
678/// Wraps a single [`UserMapping`], as well as the system-wide [`AdministrativeSecretHandling`],
679/// [`NonAdministrativeSecretHandling`] and [`Connection`]s.
680#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
681pub struct ExtendedUserMapping {
682    admin_secret_handling: AdministrativeSecretHandling,
683    non_admin_secret_handling: NonAdministrativeSecretHandling,
684    connections: HashSet<Connection>,
685    user_mapping: UserMapping,
686}
687
688impl ExtendedUserMapping {
689    /// Creates a new [`ExtendedUserMapping`].
690    pub fn new(
691        admin_secret_handling: AdministrativeSecretHandling,
692        non_admin_secret_handling: NonAdministrativeSecretHandling,
693        connections: HashSet<Connection>,
694        user_mapping: UserMapping,
695    ) -> Self {
696        Self {
697            admin_secret_handling,
698            non_admin_secret_handling,
699            connections,
700            user_mapping,
701        }
702    }
703
704    /// Returns the [`AdministrativeSecretHandling`].
705    pub fn get_admin_secret_handling(&self) -> AdministrativeSecretHandling {
706        self.admin_secret_handling
707    }
708
709    /// Returns the [`Connection`]s.
710    pub fn get_connections(&self) -> HashSet<Connection> {
711        self.connections.clone()
712    }
713
714    /// Returns the [`NonAdministrativeSecretHandling`].
715    pub fn get_non_admin_secret_handling(&self) -> NonAdministrativeSecretHandling {
716        self.non_admin_secret_handling
717    }
718
719    /// Returns the [`UserMapping`].
720    pub fn get_user_mapping(&self) -> &UserMapping {
721        &self.user_mapping
722    }
723}
724
725impl From<HermeticParallelConfig> for Vec<ExtendedUserMapping> {
726    /// Creates a `Vec` of [`ExtendedUserMapping`] from a [`HermeticParallelConfig`].
727    ///
728    /// A [`UserMapping`] can not be aware of credentials if it does not track at least one
729    /// [`SystemUserId`] and one [`UserId`]. Therefore only those [`UserMapping`]s for which
730    /// [`has_system_and_nethsm_user`](UserMapping::has_system_and_nethsm_user) returns `true` are
731    /// returned.
732    fn from(value: HermeticParallelConfig) -> Self {
733        value
734            .iter_user_mappings()
735            .filter_map(|mapping| {
736                if mapping.has_system_and_nethsm_user() {
737                    Some(ExtendedUserMapping {
738                        admin_secret_handling: value.get_administrative_secret_handling(),
739                        non_admin_secret_handling: value.get_non_administrative_secret_handling(),
740                        connections: value.iter_connections().cloned().collect(),
741                        user_mapping: mapping.clone(),
742                    })
743                } else {
744                    None
745                }
746            })
747            .collect()
748    }
749}