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 nethsm::{FullCredentials, UserId};
10use signstar_common::common::SECRET_FILE_MODE;
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 [`UserId`] for which the error
125/// occurred.
126#[derive(Debug)]
127pub struct CredentialsLoadingError {
128    user_id: UserId,
129    error: crate::Error,
130}
131
132impl CredentialsLoadingError {
133    /// Creates a new [`CredentialsLoadingError`].
134    pub fn new(user_id: UserId, error: crate::Error) -> Self {
135        Self { user_id, error }
136    }
137
138    /// Returns a reference to the [`UserId`].
139    pub fn get_user_id(&self) -> &UserId {
140        &self.user_id
141    }
142
143    /// Returns a reference to the [`Error`][crate::Error].
144    pub fn get_error(&self) -> &crate::Error {
145        &self.error
146    }
147}
148
149impl Display for CredentialsLoadingError {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "{}: {}", self.user_id, self.error)
152    }
153}
154
155/// A wrapper for a list of [`CredentialsLoadingError`]s.
156#[derive(Debug)]
157pub struct CredentialsLoadingErrors {
158    errors: Vec<CredentialsLoadingError>,
159}
160
161impl CredentialsLoadingErrors {
162    /// Creates a new [`CredentialsLoadingError`].
163    pub fn new(errors: Vec<CredentialsLoadingError>) -> Self {
164        Self { errors }
165    }
166
167    /// Returns a reference to the list of [`CredentialsLoadingError`]s.
168    pub fn get_errors(&self) -> &[CredentialsLoadingError] {
169        &self.errors
170    }
171}
172
173impl Display for CredentialsLoadingErrors {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        write!(
176            f,
177            "{}",
178            self.errors
179                .iter()
180                .map(|error| error.to_string())
181                .collect::<Vec<String>>()
182                .join("\n")
183        )
184    }
185}
186
187/// A collection of credentials and credential loading errors for a system user.
188///
189/// Tracks a [`SystemUserId`], zero or more [`FullCredentials`] mapped to it, as well as zero or
190/// more errors related to loading the passphrase for a [`UserId`].
191#[derive(Debug)]
192pub struct CredentialsLoading {
193    mapping: ExtendedUserMapping,
194    credentials: Vec<FullCredentials>,
195    errors: CredentialsLoadingErrors,
196}
197
198impl CredentialsLoading {
199    /// Creates a new [`CredentialsLoading`].
200    pub fn new(
201        mapping: ExtendedUserMapping,
202        credentials: Vec<FullCredentials>,
203        errors: CredentialsLoadingErrors,
204    ) -> Self {
205        Self {
206            mapping,
207            credentials,
208            errors,
209        }
210    }
211
212    /// Creates a [`CredentialsLoading`] for the calling system user.
213    ///
214    /// Uses the data of the calling system user to derive the specific mapping for it from the
215    /// Signstar configuration (a [`SignstarConfig`]).
216    /// Then continues to retrieve the credentials for all associated [`NetHsm`] users of the
217    /// mapping.
218    ///
219    /// # Errors
220    ///
221    /// Returns an error if
222    /// - it is not possible to derive user data from the calling process,
223    /// - if there is no user data for the calling process,
224    /// - the Signstar configuration file does not exist,
225    /// - it is not possible to load the Signstar configuration,
226    /// - not exactly one user mapping exists for the calling system user,
227    /// - or if credentials loading fails due to a severe error.
228    pub fn from_system_user() -> Result<Self, crate::Error> {
229        let user = get_current_system_user()?;
230
231        let system_config = SignstarConfig::new_from_file(None)?;
232
233        let Some(mapping) = system_config.get_extended_mapping_for_user(&user.name) else {
234            return Err(
235                crate::ConfigError::NoMatchingMappingForSystemUser { name: user.name }.into(),
236            );
237        };
238
239        // get all credentials for the mapping
240        let credentials_loading = mapping.load_credentials()?;
241
242        Ok(credentials_loading)
243    }
244
245    /// Returns the [`ExtendedUserMapping`].
246    pub fn get_mapping(&self) -> &ExtendedUserMapping {
247        &self.mapping
248    }
249
250    /// Returns all [`FullCredentials`].
251    pub fn get_credentials(&self) -> &[FullCredentials] {
252        &self.credentials
253    }
254
255    /// Returns a reference to a [`SystemUserId`].
256    ///
257    /// # Errors
258    ///
259    /// Returns an error if there is no system user in the tracked mapping.
260    pub fn get_system_user_id(&self) -> Result<&SystemUserId, crate::Error> {
261        match self.mapping.get_user_mapping().get_system_user() {
262            Some(system_user) => Ok(system_user),
263            None => Err(crate::Error::NonAdminSecretHandling(Error::NoSystemUser)),
264        }
265    }
266
267    /// Indicates whether there are any errors with [`UserId`]s.
268    ///
269    /// Returns `true` if there are errors, `false` otherwise.
270    pub fn has_userid_errors(&self) -> bool {
271        !self.errors.get_errors().is_empty()
272    }
273
274    /// Returns the collected errors for [`UserId`]s.
275    pub fn get_userid_errors(self) -> CredentialsLoadingErrors {
276        self.errors
277    }
278
279    /// Indicates whether the contained [`ExtendedUserMapping`] is that of a signing user.
280    pub fn has_signing_user(&self) -> bool {
281        matches!(
282            self.mapping.get_user_mapping(),
283            UserMapping::SystemNetHsmOperatorSigning {
284                nethsm_user: _,
285                nethsm_key_setup: _,
286                ssh_authorized_key: _,
287                system_user: _,
288                tag: _,
289            }
290        )
291    }
292
293    /// Returns the credentials for a signing user.
294    ///
295    /// # Errors
296    ///
297    /// Returns an error if
298    /// - the tracked user is not a signing user
299    /// - errors occurred when loading the system user's credentials
300    /// - or there are no credentials for the system user.
301    pub fn credentials_for_signing_user(self) -> Result<FullCredentials, crate::Error> {
302        if !self.has_signing_user() {
303            return Err(crate::Error::NonAdminSecretHandling(Error::NotSigningUser));
304        }
305
306        if !self.errors.get_errors().is_empty() {
307            return Err(crate::Error::NonAdminSecretHandling(
308                Error::CredentialsLoading {
309                    system_user: self.get_system_user_id()?.clone(),
310                    errors: self.errors,
311                },
312            ));
313        }
314
315        if let Some(credentials) = self.credentials.first() {
316            Ok(credentials.clone())
317        } else {
318            return Err(crate::Error::NonAdminSecretHandling(
319                Error::CredentialsMissing {
320                    system_user: self.get_system_user_id()?.clone(),
321                },
322            ));
323        }
324    }
325}