signstar_config/
non_admin_credentials.rs

1//! Non-administrative credentials handling for a NetHSM backend.
2use std::{
3    fmt::{Debug, Display},
4    path::PathBuf,
5};
6
7#[cfg(doc)]
8use nethsm::NetHsm;
9use signstar_common::common::SECRET_FILE_MODE;
10use signstar_crypto::traits::UserWithPassphrase;
11
12use crate::{
13    ExtendedUserMapping,
14    SignstarConfig,
15    SystemUserId,
16    UserMapping,
17    utils::get_current_system_user,
18};
19
20/// An error that may occur when handling non-administrative credentials for a NetHSM backend.
21#[derive(Debug, thiserror::Error)]
22pub enum Error {
23    /// There are one or more errors when loading credentials for a specific system user.
24    #[error("Errors occurred when loading credentials for system user {system_user}:\n{errors}")]
25    CredentialsLoading {
26        /// The system user for which loading of backend user credentials led to errors.
27        system_user: SystemUserId,
28        /// The errors that occurred during loading of backend user credentials for `system_user`.
29        errors: CredentialsLoadingErrors,
30    },
31
32    /// There are no credentials for a specific system user.
33    #[error("There are no credentials for system user {system_user}")]
34    CredentialsMissing {
35        /// The system user for which credentials are missing.
36        system_user: SystemUserId,
37    },
38
39    /// A mapping does not offer a system user.
40    #[error("There is no system user in the mapping.")]
41    NoSystemUser,
42
43    /// A user is not a signing user for the NetHSM backend.
44    #[error("The user is not an operator user in the NetHSM backend used for signing.")]
45    NotSigningUser,
46
47    /// A passphrase directory can not be created.
48    #[error("Passphrase directory {path} for user {system_user} can not be created:\n{source}")]
49    SecretsDirCreate {
50        /// The path to a secrets directory that could not be created.
51        path: PathBuf,
52        /// The system user in whose home directory `path` could not be created.
53        system_user: SystemUserId,
54        /// The source error.
55        source: std::io::Error,
56    },
57
58    /// A secrets file can not be created.
59    #[error("The secrets file {path} can not be created for user {system_user}:\n{source}")]
60    SecretsFileCreate {
61        /// The path to a secrets file that could not be created.
62        path: PathBuf,
63        /// The system user in whose home directory `path` could not be created.
64        system_user: SystemUserId,
65        /// The source error.
66        source: std::io::Error,
67    },
68
69    /// The file metadata of a secrets file cannot be retrieved.
70    #[error("File metadata of secrets file {path} cannot be retrieved")]
71    SecretsFileMetadata {
72        /// The path to a secrets file for which metadata could not be retrieved.
73        path: PathBuf,
74        /// The source error.
75        source: std::io::Error,
76    },
77
78    /// A secrets file does not exist.
79    #[error("Secrets file not found: {path}")]
80    SecretsFileMissing {
81        /// The path to a secrets file that is missing.
82        path: PathBuf,
83    },
84
85    /// A secrets file is not a file.
86    #[error("Secrets file is not a file: {path}")]
87    SecretsFileNotAFile {
88        /// The path to a secrets file that is not a file.
89        path: PathBuf,
90    },
91
92    /// A secrets file does not have the correct permissions.
93    #[error("Secrets file {path} has permissions {mode}, but {SECRET_FILE_MODE} is required")]
94    SecretsFilePermissions {
95        /// The path to a secrets file for which permissions could not be set.
96        path: PathBuf,
97        /// The file mode that should be applied to the file at `path`.
98        mode: u32,
99    },
100
101    /// A secrets file cannot be read.
102    #[error("Failed reading secrets file {path}:\n{source}")]
103    SecretsFileRead {
104        /// The path to a secrets file that could not be read.
105        path: PathBuf,
106        /// The source error.
107        source: std::io::Error,
108    },
109
110    /// A secrets file can not be written to.
111    #[error("The secrets file {path} can not be written to for user {system_user}: {source}")]
112    SecretsFileWrite {
113        /// The path to a secrets file that could not be written to.
114        path: PathBuf,
115        /// The system user in whose home directory `path` resides.
116        system_user: SystemUserId,
117        /// The source error.
118        source: std::io::Error,
119    },
120}
121
122/// An error that may occur when loading credentials for a [`SystemUserId`].
123///
124/// Alongside an [`Error`][`crate::Error`] contains a target user name for which the error occurred.
125#[derive(Debug)]
126pub struct CredentialsLoadingError {
127    user_id: String,
128    error: crate::Error,
129}
130
131impl CredentialsLoadingError {
132    /// Creates a new [`CredentialsLoadingError`].
133    pub fn new(user_id: String, error: crate::Error) -> Self {
134        Self { user_id, error }
135    }
136
137    /// Returns a reference to the user name.
138    pub fn get_user_id(&self) -> &str {
139        &self.user_id
140    }
141
142    /// Returns a reference to the [`Error`][crate::Error].
143    pub fn get_error(&self) -> &crate::Error {
144        &self.error
145    }
146}
147
148impl Display for CredentialsLoadingError {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        write!(f, "{}: {}", self.user_id, self.error)
151    }
152}
153
154/// A wrapper for a list of [`CredentialsLoadingError`]s.
155#[derive(Debug)]
156pub struct CredentialsLoadingErrors {
157    errors: Vec<CredentialsLoadingError>,
158}
159
160impl CredentialsLoadingErrors {
161    /// Creates a new [`CredentialsLoadingError`].
162    pub fn new(errors: Vec<CredentialsLoadingError>) -> Self {
163        Self { errors }
164    }
165
166    /// Returns a reference to the list of [`CredentialsLoadingError`]s.
167    pub fn get_errors(&self) -> &[CredentialsLoadingError] {
168        &self.errors
169    }
170}
171
172impl Display for CredentialsLoadingErrors {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        write!(
175            f,
176            "{}",
177            self.errors
178                .iter()
179                .map(|error| error.to_string())
180                .collect::<Vec<String>>()
181                .join("\n")
182        )
183    }
184}
185
186/// A collection of credentials and credential loading errors for a system user.
187///
188/// Tracks a [`SystemUserId`], zero or more [`UserWithPassphrase`] implementations mapped to it, as
189/// well as zero or more errors related to loading the passphrase for each of those backend users.
190#[derive(Debug)]
191pub struct CredentialsLoading {
192    mapping: ExtendedUserMapping,
193    credentials: Vec<Box<dyn UserWithPassphrase>>,
194    errors: CredentialsLoadingErrors,
195}
196
197impl CredentialsLoading {
198    /// Creates a new [`CredentialsLoading`].
199    pub fn new(
200        mapping: ExtendedUserMapping,
201        credentials: Vec<Box<dyn UserWithPassphrase>>,
202        errors: CredentialsLoadingErrors,
203    ) -> Self {
204        Self {
205            mapping,
206            credentials,
207            errors,
208        }
209    }
210
211    /// Creates a [`CredentialsLoading`] for the calling system user.
212    ///
213    /// Uses the data of the calling system user to derive the specific mapping for it from the
214    /// Signstar configuration (a [`SignstarConfig`]).
215    /// Then continues to retrieve the credentials for all associated [`NetHsm`] users of the
216    /// mapping.
217    ///
218    /// # Errors
219    ///
220    /// Returns an error if
221    /// - it is not possible to derive user data from the calling process,
222    /// - if there is no user data for the calling process,
223    /// - the Signstar configuration file does not exist,
224    /// - it is not possible to load the Signstar configuration,
225    /// - not exactly one user mapping exists for the calling system user,
226    /// - or if credentials loading fails due to a severe error.
227    pub fn from_system_user() -> Result<Self, crate::Error> {
228        let user = get_current_system_user()?;
229
230        let system_config = SignstarConfig::new_from_file(None)?;
231
232        let Some(mapping) = system_config.get_extended_mapping_for_user(&user.name) else {
233            return Err(
234                crate::ConfigError::NoMatchingMappingForSystemUser { name: user.name }.into(),
235            );
236        };
237
238        // get all credentials for the mapping
239        let credentials_loading = mapping.load_credentials()?;
240
241        Ok(credentials_loading)
242    }
243
244    /// Returns the [`ExtendedUserMapping`].
245    pub fn get_mapping(&self) -> &ExtendedUserMapping {
246        &self.mapping
247    }
248
249    /// Returns all [credentials][`UserWithPassphrase`].
250    pub fn get_credentials(&self) -> &[Box<dyn UserWithPassphrase>] {
251        &self.credentials
252    }
253
254    /// Returns a reference to a [`SystemUserId`].
255    ///
256    /// # Errors
257    ///
258    /// Returns an error if there is no system user in the tracked mapping.
259    pub fn get_system_user_id(&self) -> Result<&SystemUserId, crate::Error> {
260        match self.mapping.get_user_mapping().get_system_user() {
261            Some(system_user) => Ok(system_user),
262            None => Err(crate::Error::NonAdminSecretHandling(Error::NoSystemUser)),
263        }
264    }
265
266    /// Indicates whether there are any errors with user names.
267    ///
268    /// Returns `true` if there are errors, `false` otherwise.
269    pub fn has_userid_errors(&self) -> bool {
270        !self.errors.get_errors().is_empty()
271    }
272
273    /// Returns the collected errors for user names.
274    pub fn get_userid_errors(self) -> CredentialsLoadingErrors {
275        self.errors
276    }
277
278    /// Indicates whether the contained [`ExtendedUserMapping`] is that of a signing user.
279    pub fn has_signing_user(&self) -> bool {
280        match self.mapping.get_user_mapping() {
281            UserMapping::NetHsmOnlyAdmin(_)
282            | UserMapping::SystemNetHsmBackup { .. }
283            | UserMapping::SystemNetHsmMetrics { .. }
284            | UserMapping::HermeticSystemNetHsmMetrics { .. }
285            | UserMapping::SystemOnlyShareDownload { .. }
286            | UserMapping::SystemOnlyShareUpload { .. }
287            | UserMapping::SystemOnlyWireGuardDownload { .. } => false,
288            UserMapping::SystemNetHsmOperatorSigning { .. } => true,
289            #[cfg(feature = "yubihsm2")]
290            UserMapping::SystemYubiHsmOperatorSigning { .. } => true,
291        }
292    }
293
294    /// Returns the credentials for a signing user.
295    ///
296    /// # Errors
297    ///
298    /// Returns an error if
299    /// - the tracked user is not a signing user
300    /// - errors occurred when loading the system user's credentials
301    /// - or there are no credentials for the system user.
302    pub fn credentials_for_signing_user(self) -> Result<Box<dyn UserWithPassphrase>, crate::Error> {
303        if !self.has_signing_user() {
304            return Err(crate::Error::NonAdminSecretHandling(Error::NotSigningUser));
305        }
306
307        if !self.errors.get_errors().is_empty() {
308            return Err(crate::Error::NonAdminSecretHandling(
309                Error::CredentialsLoading {
310                    system_user: self.get_system_user_id()?.clone(),
311                    errors: self.errors,
312                },
313            ));
314        }
315
316        let system_user = self.get_system_user_id()?.clone();
317        let mut iterator = self.credentials.into_iter();
318
319        if let Some(credentials) = iterator.next() {
320            Ok(credentials)
321        } else {
322            Err(crate::Error::NonAdminSecretHandling(
323                Error::CredentialsMissing { system_user },
324            ))
325        }
326    }
327}