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}