nethsm_config/
mapping.rs

1use std::collections::HashSet;
2
3#[cfg(doc)]
4use nethsm::NetHsm;
5use nethsm::{Connection, KeyId, NamespaceId, SigningKeySetup, UserId, UserRole};
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 filter for retrieving information about users and keys.
40#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
41pub enum FilterUserKeys {
42    /// Consider both system-wide and namespaced users and keys.
43    All,
44
45    /// Only consider users and keys that are in a namespace.
46    Namespaced,
47
48    /// Only consider users and keys that match a specific [`NamespaceId`].
49    Namespace(NamespaceId),
50
51    /// Only consider system-wide users and keys.
52    SystemWide,
53
54    /// Only consider users and keys that match a specific tag.
55    Tag(String),
56}
57
58/// A set of users with unique [`UserId`]s, used for metrics retrieval
59///
60/// This struct tracks a user that is intended for the use in the
61/// [`Metrics`][`nethsm::UserRole::Metrics`] role and a list of users, that are intended to be used
62/// in the [`Operator`][`nethsm::UserRole::Operator`] role.
63#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
64pub struct NetHsmMetricsUsers {
65    metrics_user: SystemWideUserId,
66    operator_users: Vec<UserId>,
67}
68
69impl NetHsmMetricsUsers {
70    /// Creates a new [`NetHsmMetricsUsers`]
71    ///
72    /// # Error
73    ///
74    /// Returns an error, if the provided [`UserId`] of the `metrics_user` is duplicated in the
75    /// provided `operator_users`.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use nethsm_config::NetHsmMetricsUsers;
81    ///
82    /// # fn main() -> testresult::TestResult {
83    /// NetHsmMetricsUsers::new(
84    ///     "metrics1".parse()?,
85    ///     vec!["user1".parse()?, "user2".parse()?],
86    /// )?;
87    ///
88    /// // this fails because there are duplicate UserIds
89    /// assert!(
90    ///     NetHsmMetricsUsers::new(
91    ///         "metrics1".parse()?,
92    ///         vec!["metrics1".parse()?, "user2".parse()?,],
93    ///     )
94    ///     .is_err()
95    /// );
96    /// # Ok(())
97    /// # }
98    /// ```
99    pub fn new(metrics_user: SystemWideUserId, operator_users: Vec<UserId>) -> Result<Self, Error> {
100        // prevent duplicate metrics and operator users
101        if operator_users.contains(&metrics_user.clone().into()) {
102            return Err(Error::MetricsAlsoOperator { metrics_user });
103        }
104
105        Ok(Self {
106            metrics_user,
107            operator_users,
108        })
109    }
110
111    /// Returns all tracked [`UserId`]s of the [`NetHsmMetricsUsers`]
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// use nethsm::UserId;
117    /// use nethsm_config::NetHsmMetricsUsers;
118    ///
119    /// # fn main() -> testresult::TestResult {
120    /// let nethsm_metrics_users = NetHsmMetricsUsers::new(
121    ///     "metrics1".parse()?,
122    ///     vec!["user1".parse()?, "user2".parse()?],
123    /// )?;
124    ///
125    /// assert_eq!(
126    ///     nethsm_metrics_users.get_users(),
127    ///     vec![
128    ///         UserId::new("metrics1".to_string())?,
129    ///         UserId::new("user1".to_string())?,
130    ///         UserId::new("user2".to_string())?
131    ///     ]
132    /// );
133    /// # Ok(())
134    /// # }
135    /// ```
136    pub fn get_users(&self) -> Vec<UserId> {
137        [
138            vec![self.metrics_user.clone().into()],
139            self.operator_users.clone(),
140        ]
141        .concat()
142    }
143
144    /// Returns all tracked [`UserId`]s and their respective [`UserRole`].
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use nethsm::{UserId, UserRole};
150    /// use nethsm_config::NetHsmMetricsUsers;
151    ///
152    /// # fn main() -> testresult::TestResult {
153    /// let nethsm_metrics_users = NetHsmMetricsUsers::new(
154    ///     "metrics1".parse()?,
155    ///     vec!["user1".parse()?, "user2".parse()?],
156    /// )?;
157    ///
158    /// assert_eq!(
159    ///     nethsm_metrics_users.get_users_and_roles(),
160    ///     vec![
161    ///         (UserId::new("metrics1".to_string())?, UserRole::Metrics),
162    ///         (UserId::new("user1".to_string())?, UserRole::Operator),
163    ///         (UserId::new("user2".to_string())?, UserRole::Operator)
164    ///     ]
165    /// );
166    /// # Ok(())
167    /// # }
168    /// ```
169    pub fn get_users_and_roles(&self) -> Vec<(UserId, UserRole)> {
170        [
171            vec![(self.metrics_user.clone().into(), UserRole::Metrics)],
172            self.operator_users
173                .iter()
174                .map(|user| (user.clone(), UserRole::Operator))
175                .collect(),
176        ]
177        .concat()
178    }
179}
180
181/// User mapping between system users and [`NetHsm`][`nethsm::NetHsm`] users
182#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
183pub enum UserMapping {
184    /// A NetHsm user in the Administrator role, without a system user mapped to it
185    #[serde(rename = "nethsm_only_admin")]
186    NetHsmOnlyAdmin(UserId),
187
188    /// A system user, with SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`] user
189    /// in the Backup role
190    #[serde(rename = "system_nethsm_backup")]
191    SystemNetHsmBackup {
192        nethsm_user: SystemWideUserId,
193        ssh_authorized_key: AuthorizedKeyEntry,
194        system_user: SystemUserId,
195    },
196
197    /// A system user, with SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`] user
198    /// in the Metrics role and `n` users in the Operator role with read-only access to zero or
199    /// more keys
200    #[serde(rename = "system_nethsm_metrics")]
201    SystemNetHsmMetrics {
202        nethsm_users: NetHsmMetricsUsers,
203        ssh_authorized_key: AuthorizedKeyEntry,
204        system_user: SystemUserId,
205    },
206
207    /// A system user, with SSH access, mapped to a [`NetHsm`][`nethsm::NetHsm`] user in the
208    /// Operator role with access to a single signing key.
209    ///
210    /// Signing key and NetHSM user are mapped using a tag.
211    #[serde(rename = "system_nethsm_operator_signing")]
212    SystemNetHsmOperatorSigning {
213        nethsm_user: UserId,
214        nethsm_key_setup: SigningKeySetup,
215        ssh_authorized_key: AuthorizedKeyEntry,
216        system_user: SystemUserId,
217        tag: String,
218    },
219
220    /// A system user, without SSH access, mapped to a system-wide [`NetHsm`][`nethsm::NetHsm`]
221    /// user in the Metrics role and one or more NetHsm users in the Operator role with
222    /// read-only access to zero or more keys
223    #[serde(rename = "hermetic_system_nethsm_metrics")]
224    HermeticSystemNetHsmMetrics {
225        nethsm_users: NetHsmMetricsUsers,
226        system_user: SystemUserId,
227    },
228
229    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
230    /// used for downloading shares of a shared secret
231    #[serde(rename = "system_only_share_download")]
232    SystemOnlyShareDownload {
233        system_user: SystemUserId,
234        ssh_authorized_keys: AuthorizedKeyEntryList,
235    },
236
237    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
238    /// used for uploading shares of a shared secret
239    #[serde(rename = "system_only_share_upload")]
240    SystemOnlyShareUpload {
241        system_user: SystemUserId,
242        ssh_authorized_keys: AuthorizedKeyEntryList,
243    },
244
245    /// A system user, with SSH access for one or more SSH keys, not mapped to any NetHsm user,
246    /// used for downloading WireGuard configuration
247    #[serde(rename = "system_only_wireguard_download")]
248    SystemOnlyWireGuardDownload {
249        system_user: SystemUserId,
250        ssh_authorized_keys: AuthorizedKeyEntryList,
251    },
252}
253
254impl UserMapping {
255    /// Returns the optional system user of the mapping
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// use nethsm_config::{AuthorizedKeyEntryList, SystemUserId, UserMapping};
261    ///
262    /// # fn main() -> testresult::TestResult {
263    /// let mapping = UserMapping::SystemOnlyShareDownload {
264    ///     system_user: "user1".parse()?,
265    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
266    /// };
267    /// assert_eq!(mapping.get_system_user(), Some(&SystemUserId::new("user1".to_string())?));
268    ///
269    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
270    /// assert_eq!(mapping.get_system_user(), None);
271    /// # Ok(())
272    /// # }
273    /// ```
274    pub fn get_system_user(&self) -> Option<&SystemUserId> {
275        match self {
276            UserMapping::NetHsmOnlyAdmin(_) => None,
277            UserMapping::SystemNetHsmBackup {
278                nethsm_user: _,
279                ssh_authorized_key: _,
280                system_user,
281            }
282            | UserMapping::SystemNetHsmOperatorSigning {
283                nethsm_user: _,
284                nethsm_key_setup: _,
285                ssh_authorized_key: _,
286                system_user,
287                tag: _,
288            }
289            | UserMapping::SystemNetHsmMetrics {
290                nethsm_users: _,
291                ssh_authorized_key: _,
292                system_user,
293            }
294            | UserMapping::HermeticSystemNetHsmMetrics {
295                nethsm_users: _,
296                system_user,
297            }
298            | UserMapping::SystemOnlyShareDownload {
299                system_user,
300                ssh_authorized_keys: _,
301            }
302            | UserMapping::SystemOnlyShareUpload {
303                system_user,
304                ssh_authorized_keys: _,
305            }
306            | UserMapping::SystemOnlyWireGuardDownload {
307                system_user,
308                ssh_authorized_keys: _,
309            } => Some(system_user),
310        }
311    }
312
313    /// Returns the NetHsm users of the mapping
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use nethsm::UserId;
319    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
320    ///
321    /// # fn main() -> testresult::TestResult {
322    /// let mapping = UserMapping::SystemOnlyShareDownload {
323    ///     system_user: "user1".parse()?,
324    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
325    /// };
326    /// assert!(mapping.get_nethsm_users().is_empty());
327    ///
328    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
329    /// assert_eq!(mapping.get_nethsm_users(), vec![UserId::new("user1".to_string())?]);
330    /// # Ok(())
331    /// # }
332    /// ```
333    pub fn get_nethsm_users(&self) -> Vec<UserId> {
334        match self {
335            UserMapping::SystemNetHsmBackup {
336                nethsm_user,
337                system_user: _,
338                ssh_authorized_key: _,
339            } => vec![nethsm_user.clone().into()],
340            UserMapping::NetHsmOnlyAdmin(nethsm_user)
341            | UserMapping::SystemNetHsmOperatorSigning {
342                nethsm_user,
343                nethsm_key_setup: _,
344                system_user: _,
345                ssh_authorized_key: _,
346                tag: _,
347            } => vec![nethsm_user.clone()],
348            UserMapping::SystemNetHsmMetrics {
349                nethsm_users,
350                system_user: _,
351                ssh_authorized_key: _,
352            }
353            | UserMapping::HermeticSystemNetHsmMetrics {
354                nethsm_users,
355                system_user: _,
356            } => nethsm_users.get_users(),
357            UserMapping::SystemOnlyShareDownload {
358                system_user: _,
359                ssh_authorized_keys: _,
360            }
361            | UserMapping::SystemOnlyShareUpload {
362                system_user: _,
363                ssh_authorized_keys: _,
364            }
365            | UserMapping::SystemOnlyWireGuardDownload {
366                system_user: _,
367                ssh_authorized_keys: _,
368            } => vec![],
369        }
370    }
371
372    /// Returns the list of all tracked [`UserId`]s and their respective [`UserRole`]s.
373    ///
374    /// # Examples
375    ///
376    /// ```
377    /// use nethsm::{UserId, UserRole};
378    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
379    ///
380    /// # fn main() -> testresult::TestResult {
381    /// let mapping = UserMapping::SystemOnlyShareDownload {
382    ///     system_user: "user1".parse()?,
383    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
384    /// };
385    /// assert!(mapping.get_nethsm_users_and_roles().is_empty());
386    ///
387    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
388    /// assert_eq!(mapping.get_nethsm_users_and_roles(), vec![(UserId::new("user1".to_string())?, UserRole::Administrator)]);
389    /// # Ok(())
390    /// # }
391    /// ```
392    pub fn get_nethsm_users_and_roles(&self) -> Vec<(UserId, UserRole)> {
393        match self {
394            UserMapping::SystemNetHsmBackup {
395                nethsm_user,
396                system_user: _,
397                ssh_authorized_key: _,
398            } => vec![(nethsm_user.clone().into(), UserRole::Backup)],
399            UserMapping::NetHsmOnlyAdmin(nethsm_user) => {
400                vec![(nethsm_user.clone(), UserRole::Administrator)]
401            }
402            UserMapping::SystemNetHsmOperatorSigning {
403                nethsm_user,
404                nethsm_key_setup: _,
405                system_user: _,
406                ssh_authorized_key: _,
407                tag: _,
408            } => vec![(nethsm_user.clone(), UserRole::Operator)],
409            UserMapping::SystemNetHsmMetrics {
410                nethsm_users,
411                system_user: _,
412                ssh_authorized_key: _,
413            }
414            | UserMapping::HermeticSystemNetHsmMetrics {
415                nethsm_users,
416                system_user: _,
417            } => nethsm_users.get_users_and_roles(),
418            UserMapping::SystemOnlyShareDownload {
419                system_user: _,
420                ssh_authorized_keys: _,
421            }
422            | UserMapping::SystemOnlyShareUpload {
423                system_user: _,
424                ssh_authorized_keys: _,
425            }
426            | UserMapping::SystemOnlyWireGuardDownload {
427                system_user: _,
428                ssh_authorized_keys: _,
429            } => vec![],
430        }
431    }
432
433    /// Returns the tracked [`UserId`], its respective [`UserRole`] and tag.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// use nethsm::{UserId, UserRole};
439    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
440    ///
441    /// # fn main() -> testresult::TestResult {
442    /// let mapping = UserMapping::SystemOnlyShareDownload {
443    ///     system_user: "user1".parse()?,
444    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
445    /// };
446    /// assert!(mapping.get_nethsm_user_role_and_tag().is_none());
447    /// # Ok(())
448    /// # }
449    /// ```
450    pub fn get_nethsm_user_role_and_tag(&self) -> Option<(UserId, UserRole, &str)> {
451        match self {
452            UserMapping::SystemNetHsmOperatorSigning {
453                nethsm_user,
454                nethsm_key_setup: _,
455                system_user: _,
456                ssh_authorized_key: _,
457                tag,
458            } => Some((nethsm_user.clone(), UserRole::Operator, tag)),
459            UserMapping::SystemNetHsmBackup { .. }
460            | UserMapping::NetHsmOnlyAdmin(..)
461            | UserMapping::SystemNetHsmMetrics { .. }
462            | UserMapping::HermeticSystemNetHsmMetrics { .. }
463            | UserMapping::SystemOnlyShareDownload { .. }
464            | UserMapping::SystemOnlyShareUpload { .. }
465            | UserMapping::SystemOnlyWireGuardDownload { .. } => None,
466        }
467    }
468
469    /// Returns the SSH authorized keys of the mapping
470    ///
471    /// # Examples
472    ///
473    /// ```
474    /// use nethsm_config::{AuthorizedKeyEntry, AuthorizedKeyEntryList, UserMapping};
475    ///
476    /// # fn main() -> testresult::TestResult {
477    /// let mapping = UserMapping::SystemOnlyShareDownload {
478    ///     system_user: "user1".parse()?,
479    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
480    /// };
481    /// assert_eq!(mapping.get_ssh_authorized_keys(), vec![AuthorizedKeyEntry::new("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".to_string())?]);
482    ///
483    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
484    /// assert_eq!(mapping.get_ssh_authorized_keys(), vec![]);
485    /// # Ok(())
486    /// # }
487    /// ```
488    pub fn get_ssh_authorized_keys(&self) -> Vec<AuthorizedKeyEntry> {
489        match self {
490            UserMapping::NetHsmOnlyAdmin(_) => vec![],
491            UserMapping::SystemNetHsmBackup {
492                nethsm_user: _,
493                system_user: _,
494                ssh_authorized_key,
495            }
496            | UserMapping::SystemNetHsmOperatorSigning {
497                nethsm_user: _,
498                nethsm_key_setup: _,
499                system_user: _,
500                ssh_authorized_key,
501                tag: _,
502            } => vec![ssh_authorized_key.clone()],
503            UserMapping::SystemNetHsmMetrics {
504                nethsm_users: _,
505                system_user: _,
506                ssh_authorized_key,
507            } => vec![ssh_authorized_key.clone()],
508            UserMapping::HermeticSystemNetHsmMetrics {
509                nethsm_users: _,
510                system_user: _,
511            } => vec![],
512            UserMapping::SystemOnlyShareDownload {
513                system_user: _,
514                ssh_authorized_keys,
515            }
516            | UserMapping::SystemOnlyShareUpload {
517                system_user: _,
518                ssh_authorized_keys,
519            }
520            | UserMapping::SystemOnlyWireGuardDownload {
521                system_user: _,
522                ssh_authorized_keys,
523            } => ssh_authorized_keys.into(),
524        }
525    }
526
527    /// Returns all used [`KeyId`]s of the mapping
528    ///
529    /// # Examples
530    ///
531    /// ```
532    /// use nethsm::{CryptographicKeyContext, KeyId, OpenPgpUserIdList, SigningKeySetup};
533    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
534    ///
535    /// # fn main() -> testresult::TestResult {
536    /// let mapping = UserMapping::SystemNetHsmOperatorSigning {
537    ///     nethsm_user: "user1".parse()?,
538    ///     nethsm_key_setup: SigningKeySetup::new(
539    ///         "key1".parse()?,
540    ///         "Curve25519".parse()?,
541    ///         vec!["EdDsaSignature".parse()?],
542    ///         None,
543    ///         "EdDsa".parse()?,
544    ///         CryptographicKeyContext::OpenPgp{
545    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
546    ///             version: "v4".parse()?,
547    ///         },
548    ///     )?,
549    ///     system_user: "ssh-user1".parse()?,
550    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
551    ///     tag: "tag1".to_string(),
552    /// };
553    /// assert_eq!(mapping.get_key_ids(None), vec![KeyId::new("key1".to_string())?]);
554    ///
555    /// let mapping = UserMapping::SystemOnlyShareDownload {
556    ///     system_user: "user1".parse()?,
557    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
558    /// };
559    /// assert_eq!(mapping.get_key_ids(None), vec![]);
560    /// # Ok(())
561    /// # }
562    /// ```
563    pub fn get_key_ids(&self, namespace: Option<&NamespaceId>) -> Vec<KeyId> {
564        match self {
565            UserMapping::SystemNetHsmOperatorSigning {
566                nethsm_user,
567                nethsm_key_setup,
568                system_user: _,
569                ssh_authorized_key: _,
570                tag: _,
571            } => {
572                if nethsm_user.namespace() == namespace {
573                    vec![nethsm_key_setup.get_key_id()]
574                } else {
575                    vec![]
576                }
577            }
578            UserMapping::SystemNetHsmMetrics {
579                nethsm_users: _,
580                system_user: _,
581                ssh_authorized_key: _,
582            }
583            | UserMapping::NetHsmOnlyAdmin(_)
584            | UserMapping::HermeticSystemNetHsmMetrics {
585                nethsm_users: _,
586                system_user: _,
587            }
588            | UserMapping::SystemNetHsmBackup {
589                nethsm_user: _,
590                system_user: _,
591                ssh_authorized_key: _,
592            }
593            | UserMapping::SystemOnlyShareDownload {
594                system_user: _,
595                ssh_authorized_keys: _,
596            }
597            | UserMapping::SystemOnlyShareUpload {
598                system_user: _,
599                ssh_authorized_keys: _,
600            }
601            | UserMapping::SystemOnlyWireGuardDownload {
602                system_user: _,
603                ssh_authorized_keys: _,
604            } => vec![],
605        }
606    }
607
608    /// Returns tags for keys and users
609    ///
610    /// Tags can be filtered by [namespace] by providing [`Some`] `namespace`.
611    /// Providing [`None`] implies that the context is system-wide.
612    ///
613    /// # Examples
614    ///
615    /// ```
616    /// use nethsm::{CryptographicKeyContext, OpenPgpUserIdList, SigningKeySetup};
617    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
618    ///
619    /// # fn main() -> testresult::TestResult {
620    /// let mapping = UserMapping::SystemOnlyShareDownload {
621    ///     system_user: "user1".parse()?,
622    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
623    /// };
624    /// assert!(mapping.get_tags(None).is_empty());
625    ///
626    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
627    /// assert!(mapping.get_tags(None).is_empty());
628    ///
629    /// let mapping = UserMapping::SystemNetHsmOperatorSigning{
630    ///     nethsm_user: "ns1~user1".parse()?,
631    ///     nethsm_key_setup: SigningKeySetup::new(
632    ///         "key1".parse()?,
633    ///         "Curve25519".parse()?,
634    ///         vec!["EdDsaSignature".parse()?],
635    ///         None,
636    ///         "EdDsa".parse()?,
637    ///         CryptographicKeyContext::OpenPgp{
638    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
639    ///             version: "4".parse()?,
640    ///     })?,
641    ///     system_user: "user1".parse()?,
642    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
643    ///     tag: "tag1".to_string(),
644    /// };
645    /// assert!(mapping.get_tags(None).is_empty());
646    /// assert_eq!(mapping.get_tags(Some(&"ns1".parse()?)), vec!["tag1"]);
647    /// # Ok(())
648    /// # }
649    /// ```
650    /// [namespace]: https://docs.nitrokey.com/nethsm/administration#namespaces
651    pub fn get_tags(&self, namespace: Option<&NamespaceId>) -> Vec<&str> {
652        match self {
653            UserMapping::SystemNetHsmOperatorSigning {
654                nethsm_user,
655                nethsm_key_setup: _,
656                system_user: _,
657                ssh_authorized_key: _,
658                tag,
659            } => {
660                if nethsm_user.namespace() == namespace {
661                    vec![tag.as_str()]
662                } else {
663                    vec![]
664                }
665            }
666            UserMapping::SystemNetHsmMetrics {
667                nethsm_users: _,
668                system_user: _,
669                ssh_authorized_key: _,
670            }
671            | UserMapping::NetHsmOnlyAdmin(_)
672            | UserMapping::HermeticSystemNetHsmMetrics {
673                nethsm_users: _,
674                system_user: _,
675            }
676            | UserMapping::SystemNetHsmBackup {
677                nethsm_user: _,
678                system_user: _,
679                ssh_authorized_key: _,
680            }
681            | UserMapping::SystemOnlyShareDownload {
682                system_user: _,
683                ssh_authorized_keys: _,
684            }
685            | UserMapping::SystemOnlyShareUpload {
686                system_user: _,
687                ssh_authorized_keys: _,
688            }
689            | UserMapping::SystemOnlyWireGuardDownload {
690                system_user: _,
691                ssh_authorized_keys: _,
692            } => vec![],
693        }
694    }
695
696    /// Returns a list of tuples of [`UserId`], [`SigningKeySetup`] and tag for the mapping.
697    ///
698    /// Using a `filter` (see [`FilterUserKeys`]) it is possible to have only a subset of the
699    /// available tuples be returned:
700    ///
701    /// - [`FilterUserKeys::All`]: Returns all available tuples.
702    /// - [`FilterUserKeys::Namespaced`]: Returns tuples that match [`UserId`]s with a namespace.
703    /// - [`FilterUserKeys::Namespace`]: Returns tuples that match [`UserId`]s with a specific
704    ///   namespace.
705    /// - [`FilterUserKeys::SystemWide`]: Returns tuples that match [`UserId`]s without a namespace.
706    /// - [`FilterUserKeys::Namespace`]: Returns tuples that match a specific tag.
707    ///
708    /// # Examples
709    ///
710    /// ```
711    /// use nethsm::{CryptographicKeyContext, KeyId, OpenPgpUserIdList, SigningKeySetup, UserId};
712    /// use nethsm_config::{AuthorizedKeyEntryList, FilterUserKeys, UserMapping};
713    ///
714    /// # fn main() -> testresult::TestResult {
715    /// let mapping = UserMapping::SystemNetHsmOperatorSigning {
716    ///     nethsm_user: "user1".parse()?,
717    ///     nethsm_key_setup: SigningKeySetup::new(
718    ///         "key1".parse()?,
719    ///         "Curve25519".parse()?,
720    ///         vec!["EdDsaSignature".parse()?],
721    ///         None,
722    ///         "EdDsa".parse()?,
723    ///         CryptographicKeyContext::OpenPgp{
724    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
725    ///             version: "v4".parse()?,
726    ///         },
727    ///     )?,
728    ///     system_user: "ssh-user1".parse()?,
729    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
730    ///     tag: "tag1".to_string(),
731    /// };
732    /// assert_eq!(
733    ///     mapping.get_nethsm_user_key_and_tag(FilterUserKeys::All),
734    ///     vec![(
735    ///         UserId::new("user1".to_string())?,
736    ///         SigningKeySetup::new(
737    ///             "key1".parse()?,
738    ///             "Curve25519".parse()?,
739    ///             vec!["EdDsaSignature".parse()?],
740    ///             None,
741    ///             "EdDsa".parse()?,
742    ///             CryptographicKeyContext::OpenPgp{
743    ///                 user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
744    ///                 version: "v4".parse()?,
745    ///             },
746    ///         )?,
747    ///         "tag1".to_string(),
748    ///     )]
749    /// );
750    /// assert_eq!(mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Namespace("test".parse()?)), vec![]);
751    /// assert_eq!(mapping.get_nethsm_user_key_and_tag(FilterUserKeys::Tag("tag2".parse()?)), vec![]);
752    ///
753    /// let mapping = UserMapping::SystemOnlyShareDownload {
754    ///     system_user: "user1".parse()?,
755    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
756    /// };
757    /// assert_eq!(mapping.get_nethsm_user_key_and_tag(FilterUserKeys::All), vec![]);
758    /// # Ok(())
759    /// # }
760    /// ```
761    pub fn get_nethsm_user_key_and_tag(
762        &self,
763        filter: FilterUserKeys,
764    ) -> Vec<(UserId, SigningKeySetup, String)> {
765        match self {
766            UserMapping::SystemNetHsmOperatorSigning {
767                nethsm_user,
768                nethsm_key_setup,
769                system_user: _,
770                ssh_authorized_key: _,
771                tag,
772            } => match filter {
773                FilterUserKeys::All => {
774                    vec![(nethsm_user.clone(), nethsm_key_setup.clone(), tag.clone())]
775                }
776                FilterUserKeys::Namespaced => {
777                    if nethsm_user.is_namespaced() {
778                        vec![(nethsm_user.clone(), nethsm_key_setup.clone(), tag.clone())]
779                    } else {
780                        Vec::new()
781                    }
782                }
783                FilterUserKeys::Namespace(namespace) => {
784                    if Some(&namespace) == nethsm_user.namespace() {
785                        vec![(nethsm_user.clone(), nethsm_key_setup.clone(), tag.clone())]
786                    } else {
787                        Vec::new()
788                    }
789                }
790                FilterUserKeys::SystemWide => {
791                    if !nethsm_user.is_namespaced() {
792                        vec![(nethsm_user.clone(), nethsm_key_setup.clone(), tag.clone())]
793                    } else {
794                        Vec::new()
795                    }
796                }
797                FilterUserKeys::Tag(filter_tag) => {
798                    if &filter_tag == tag {
799                        vec![(nethsm_user.clone(), nethsm_key_setup.clone(), tag.clone())]
800                    } else {
801                        Vec::new()
802                    }
803                }
804            },
805            UserMapping::SystemNetHsmMetrics {
806                nethsm_users: _,
807                system_user: _,
808                ssh_authorized_key: _,
809            }
810            | UserMapping::NetHsmOnlyAdmin(_)
811            | UserMapping::HermeticSystemNetHsmMetrics {
812                nethsm_users: _,
813                system_user: _,
814            }
815            | UserMapping::SystemNetHsmBackup {
816                nethsm_user: _,
817                system_user: _,
818                ssh_authorized_key: _,
819            }
820            | UserMapping::SystemOnlyShareDownload {
821                system_user: _,
822                ssh_authorized_keys: _,
823            }
824            | UserMapping::SystemOnlyShareUpload {
825                system_user: _,
826                ssh_authorized_keys: _,
827            }
828            | UserMapping::SystemOnlyWireGuardDownload {
829                system_user: _,
830                ssh_authorized_keys: _,
831            } => vec![],
832        }
833    }
834
835    /// Returns all [`NetHsm`][`nethsm::NetHsm`] [namespaces] of the mapping
836    ///
837    /// # Examples
838    ///
839    /// ```
840    /// use nethsm::{CryptographicKeyContext, OpenPgpUserIdList, SigningKeySetup};
841    /// use nethsm_config::{AuthorizedKeyEntryList, UserMapping};
842    ///
843    /// # fn main() -> testresult::TestResult {
844    /// let mapping = UserMapping::SystemOnlyShareDownload {
845    ///     system_user: "user1".parse()?,
846    ///     ssh_authorized_keys: AuthorizedKeyEntryList::new(vec!["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?])?,
847    /// };
848    /// assert!(mapping.get_namespaces().is_empty());
849    ///
850    /// let mapping = UserMapping::NetHsmOnlyAdmin("user1".parse()?);
851    /// assert!(mapping.get_namespaces().is_empty());
852    ///
853    /// let mapping = UserMapping::SystemNetHsmOperatorSigning{
854    ///     nethsm_user: "ns1~user1".parse()?,
855    ///     nethsm_key_setup: SigningKeySetup::new(
856    ///         "key1".parse()?,
857    ///         "Curve25519".parse()?,
858    ///         vec!["EdDsaSignature".parse()?],
859    ///         None,
860    ///         "EdDsa".parse()?,
861    ///         CryptographicKeyContext::OpenPgp{
862    ///             user_ids: OpenPgpUserIdList::new(vec!["John Doe <john@example.org>".parse()?])?,
863    ///             version: "4".parse()?,
864    ///     })?,
865    ///     system_user: "user1".parse()?,
866    ///     ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3NyNfSqtDxdnWwSVzulZi0k7Lyjw3vBEG+U8y6KsuW user@host".parse()?,
867    ///     tag: "tag1".to_string(),
868    /// };
869    /// assert_eq!(mapping.get_namespaces(), vec!["ns1".parse()?]);
870    /// # Ok(())
871    /// # }
872    /// ```
873    /// [namespaces]: https://docs.nitrokey.com/nethsm/administration#namespaces
874    pub fn get_namespaces(&self) -> Vec<NamespaceId> {
875        match self {
876            UserMapping::NetHsmOnlyAdmin(nethsm_user)
877            | UserMapping::SystemNetHsmOperatorSigning {
878                nethsm_user,
879                nethsm_key_setup: _,
880                system_user: _,
881                ssh_authorized_key: _,
882                tag: _,
883            } => {
884                if let Some(namespace) = nethsm_user.namespace() {
885                    vec![namespace.clone()]
886                } else {
887                    vec![]
888                }
889            }
890            UserMapping::HermeticSystemNetHsmMetrics {
891                nethsm_users,
892                system_user: _,
893            }
894            | UserMapping::SystemNetHsmMetrics {
895                nethsm_users,
896                system_user: _,
897                ssh_authorized_key: _,
898            } => nethsm_users
899                .get_users()
900                .iter()
901                .filter_map(|user_id| user_id.namespace())
902                .cloned()
903                .collect(),
904            UserMapping::SystemOnlyShareDownload {
905                system_user: _,
906                ssh_authorized_keys: _,
907            }
908            | UserMapping::SystemNetHsmBackup {
909                nethsm_user: _,
910                system_user: _,
911                ssh_authorized_key: _,
912            }
913            | UserMapping::SystemOnlyShareUpload {
914                system_user: _,
915                ssh_authorized_keys: _,
916            }
917            | UserMapping::SystemOnlyWireGuardDownload {
918                system_user: _,
919                ssh_authorized_keys: _,
920            } => vec![],
921        }
922    }
923
924    /// Returns whether the mapping has both system and [`NetHsm`] users.
925    ///
926    /// Returns `true` if the `self` has at least one system and one [`NetHsm`] user, and `false`
927    /// otherwise.
928    pub fn has_system_and_nethsm_user(&self) -> bool {
929        match self {
930            UserMapping::SystemNetHsmOperatorSigning {
931                nethsm_user: _,
932                nethsm_key_setup: _,
933                system_user: _,
934                ssh_authorized_key: _,
935                tag: _,
936            }
937            | UserMapping::HermeticSystemNetHsmMetrics {
938                nethsm_users: _,
939                system_user: _,
940            }
941            | UserMapping::SystemNetHsmMetrics {
942                nethsm_users: _,
943                system_user: _,
944                ssh_authorized_key: _,
945            }
946            | UserMapping::SystemNetHsmBackup {
947                nethsm_user: _,
948                system_user: _,
949                ssh_authorized_key: _,
950            } => true,
951            UserMapping::SystemOnlyShareDownload {
952                system_user: _,
953                ssh_authorized_keys: _,
954            }
955            | UserMapping::SystemOnlyShareUpload {
956                system_user: _,
957                ssh_authorized_keys: _,
958            }
959            | UserMapping::SystemOnlyWireGuardDownload {
960                system_user: _,
961                ssh_authorized_keys: _,
962            }
963            | UserMapping::NetHsmOnlyAdmin(_) => false,
964        }
965    }
966}
967
968/// A [`UserMapping`] centric view of a [`HermeticParallelConfig`].
969///
970/// Wraps a single [`UserMapping`], as well as the system-wide [`AdministrativeSecretHandling`],
971/// [`NonAdministrativeSecretHandling`] and [`Connection`]s.
972#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
973pub struct ExtendedUserMapping {
974    admin_secret_handling: AdministrativeSecretHandling,
975    non_admin_secret_handling: NonAdministrativeSecretHandling,
976    connections: HashSet<Connection>,
977    user_mapping: UserMapping,
978}
979
980impl ExtendedUserMapping {
981    /// Creates a new [`ExtendedUserMapping`].
982    pub fn new(
983        admin_secret_handling: AdministrativeSecretHandling,
984        non_admin_secret_handling: NonAdministrativeSecretHandling,
985        connections: HashSet<Connection>,
986        user_mapping: UserMapping,
987    ) -> Self {
988        Self {
989            admin_secret_handling,
990            non_admin_secret_handling,
991            connections,
992            user_mapping,
993        }
994    }
995
996    /// Returns the [`AdministrativeSecretHandling`].
997    pub fn get_admin_secret_handling(&self) -> AdministrativeSecretHandling {
998        self.admin_secret_handling
999    }
1000
1001    /// Returns the [`Connection`]s.
1002    pub fn get_connections(&self) -> HashSet<Connection> {
1003        self.connections.clone()
1004    }
1005
1006    /// Returns the [`NonAdministrativeSecretHandling`].
1007    pub fn get_non_admin_secret_handling(&self) -> NonAdministrativeSecretHandling {
1008        self.non_admin_secret_handling
1009    }
1010
1011    /// Returns the [`UserMapping`].
1012    pub fn get_user_mapping(&self) -> &UserMapping {
1013        &self.user_mapping
1014    }
1015}
1016
1017impl From<HermeticParallelConfig> for Vec<ExtendedUserMapping> {
1018    /// Creates a `Vec` of [`ExtendedUserMapping`] from a [`HermeticParallelConfig`].
1019    ///
1020    /// A [`UserMapping`] can not be aware of credentials if it does not track at least one
1021    /// [`SystemUserId`] and one [`UserId`]. Therefore only those [`UserMapping`]s for which
1022    /// [`has_system_and_nethsm_user`](UserMapping::has_system_and_nethsm_user) returns `true` are
1023    /// returned.
1024    fn from(value: HermeticParallelConfig) -> Self {
1025        value
1026            .iter_user_mappings()
1027            .filter_map(|mapping| {
1028                if mapping.has_system_and_nethsm_user() {
1029                    Some(ExtendedUserMapping {
1030                        admin_secret_handling: value.get_administrative_secret_handling(),
1031                        non_admin_secret_handling: value.get_non_administrative_secret_handling(),
1032                        connections: value.iter_connections().cloned().collect(),
1033                        user_mapping: mapping.clone(),
1034                    })
1035                } else {
1036                    None
1037                }
1038            })
1039            .collect()
1040    }
1041}