Skip to main content

signstar_config/
error.rs

1//! Common, top-level error type for all components of signstar-config.
2
3use std::{
4    path::PathBuf,
5    process::{ExitCode, ExitStatus},
6    string::FromUtf8Error,
7};
8
9/// An error that may occur when using Signstar config.
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12    /// An error specific to administrative secret handling.
13    #[error("Error with administrative secret handling:\n{0}")]
14    AdminSecretHandling(#[from] crate::admin_credentials::Error),
15
16    /// An error specific to Signstar config handling.
17    /// Applying permissions to a file or directory failed.
18    #[error("Unable to apply permissions from mode {mode} to {path}:\n{source}")]
19    ApplyPermissions {
20        /// The path to a file for which permissions can not be applied.
21        path: PathBuf,
22        /// The file mode that should be applied for `path`.
23        mode: u32,
24        /// The source error.
25        source: std::io::Error,
26    },
27
28    /// The ownership of a path can not be changed.
29    #[error("Changing ownership of {path} to user {user} failed:\n{source}")]
30    Chown {
31        /// The path to a file for which ownership can not be changed.
32        path: PathBuf,
33        /// The system user that should be the new owner of `path`.
34        user: String,
35        /// The source error.
36        source: std::io::Error,
37    },
38
39    /// Unable to attach to stdin of a command.
40    #[error("Unable to attach to stdin of command \"{command}\"")]
41    CommandAttachToStdin {
42        /// The command for which attaching to stdin failed.
43        command: String,
44    },
45
46    /// A command exited unsuccessfully.
47    #[error("The command \"{command}\" could not be started in the background:\n{source}")]
48    CommandBackground {
49        /// The command that could not be started in the background.
50        command: String,
51        /// The source error.
52        source: std::io::Error,
53    },
54
55    /// A command could not be executed.
56    #[error("The command \"{command}\" could not be executed:\n{source}")]
57    CommandExec {
58        /// The command that could not be executed.
59        command: String,
60        /// The source error.
61        source: std::io::Error,
62    },
63
64    /// A command exited unsuccessfully.
65    #[error(
66        "The command \"{command}\" exited with non-zero status code \"{exit_status}\":\nstderr:\n{stderr}"
67    )]
68    CommandNonZero {
69        /// The command that exited with a non-zero exit code.
70        command: String,
71        /// The exit status of `command`.
72        exit_status: ExitStatus,
73        /// The stderr of `command`.
74        stderr: String,
75    },
76
77    /// Unable to write to stdin of a command.
78    #[error("Unable to write to stdin of command \"{command}\"")]
79    CommandWriteToStdin {
80        /// The command for which writing to stdin failed.
81        command: String,
82        /// The source error.
83        source: std::io::Error,
84    },
85
86    /// Configuration errors.
87    #[error("Signstar config error:\n{0}")]
88    Config(#[from] crate::ConfigError),
89
90    /// An I/O error occurred for a file.
91    #[error("I/O error for file {path} while {context}: {source}")]
92    IoPath {
93        /// The path to the file for which the error occurred.
94        path: PathBuf,
95        /// The context in which the error occurs.
96        ///
97        /// This is meant to complete the sentence "I/O error for file {path} while ".
98        context: &'static str,
99        /// The error source.
100        source: std::io::Error,
101    },
102
103    /// A NetHSM error.
104    #[cfg(feature = "nethsm")]
105    #[error("NetHSM error:\n{0}")]
106    NetHsm(#[from] nethsm::Error),
107
108    /// A NetHSM backend error
109    ///
110    /// This variant is used when actions for a NetHSM backend fail.
111    #[cfg(feature = "nethsm")]
112    #[error("NetHSM backend error:\n{0}")]
113    NetHsmBackend(#[from] crate::NetHsmBackendError),
114
115    /// Low-level administrative credentials handling in signstar-common failed.
116    #[error("Handling of administrative credentials failed:\n{0}")]
117    SignstarCommonAdminCreds(#[from] signstar_common::admin_credentials::Error),
118
119    /// A [`signstar_crypto::Error`] occurred.
120    #[error(transparent)]
121    SignstarCrypto(#[from] signstar_crypto::Error),
122
123    /// Joining a thread returned an error.
124    #[error("Thread error while {context}")]
125    Thread {
126        /// The context in which the failed thread ran.
127        ///
128        /// Should complete the sentence "Thread error while ".
129        context: String,
130    },
131
132    /// TOML error while reading a file.
133    #[error("TOML read error for file {path} while {context}: {source}")]
134    TomlRead {
135        /// The path to a file that fails to read.
136        path: PathBuf,
137        /// The context in which the error occurs.
138        ///
139        /// This is meant to complete the sentence " error for file {path} while ".
140        context: &'static str,
141        /// The error source.
142        source: Box<toml::de::Error>,
143    },
144
145    /// TOML error while writing a file.
146    #[error("TOML write error for file {path} while {context}: {source}")]
147    TomlWrite {
148        /// The path to a file that fails to read.
149        path: PathBuf,
150        /// The context in which the error occurs.
151        ///
152        /// This is meant to complete the sentence " error for file {path} while ".
153        context: &'static str,
154        /// The error source.
155        source: toml::ser::Error,
156    },
157
158    /// An error occurred when using a Signstar config trait.
159    #[error(transparent)]
160    Traits(#[from] crate::config::TraitsError),
161
162    /// A UTF-8 error occurred when trying to convert a byte vector to a string.
163    #[error("Converting contents of {path} to string failed while {context}:\n{source}")]
164    Utf8String {
165        /// The path to a file for which conversion to UTF-8 string failed.
166        path: PathBuf,
167        /// The context in which the error occurred.
168        ///
169        /// Should complete the sentence "Converting contents of `path` to string failed while "
170        context: String,
171        /// The source error.
172        source: FromUtf8Error,
173    },
174
175    /// A utility function returned an error.
176    #[error("Utility function error: {0}")]
177    Utils(#[from] crate::utils::Error),
178
179    /// A garde validation error occurred.
180    #[error("Validation error while {context}: {source}")]
181    Validation {
182        /// The context in which the error occurred.
183        ///
184        /// This is meant to complete the sentence "Validation error while ".
185        context: String,
186
187        /// The error source.
188        source: garde::Report,
189    },
190
191    /// A NetHSM configuration object error occurred.
192    #[cfg(feature = "nethsm")]
193    #[error("NetHSM configuration object error: {0}")]
194    NetHsmConfig(#[from] crate::nethsm::NetHsmConfigError),
195
196    /// A YubiHSM2 configuration object error occurred.
197    #[cfg(feature = "yubihsm2")]
198    #[error("YubiHSM2 configuration object error: {0}")]
199    YubiHsm2Config(#[from] crate::yubihsm2::YubiHSM2ConfigError),
200}
201
202/// Mapping for relevant [`Error`] variants to an [`ExitCode`].
203#[derive(Clone, Copy, Debug, Eq, num_enum::IntoPrimitive, Ord, PartialEq, PartialOrd)]
204#[repr(u8)]
205pub enum ErrorExitCode {
206    /// Mapping for [`crate::admin_credentials::Error::AdministratorMissing`] wrapped in
207    /// [`Error::AdminSecretHandling`].
208    AdminCredentialsAdministratorMissing = 100,
209
210    /// Mapping for [`crate::admin_credentials::Error::AdministratorNoDefault`] wrapped in
211    /// [`Error::AdminSecretHandling`].
212    AdminCredentialsAdministratorNoDefault = 101,
213
214    /// Mapping for [`crate::admin_credentials::Error::CredsFileCreate`] wrapped in
215    /// [`Error::AdminSecretHandling`].
216    AdminCredentialsCredsFileCreate = 102,
217
218    /// Mapping for [`crate::admin_credentials::Error::CredsFileMissing`] wrapped in
219    /// [`Error::AdminSecretHandling`].
220    AdminCredentialsCredsFileMissing = 103,
221
222    /// Mapping for [`crate::admin_credentials::Error::CredsFileNotAFile`] wrapped in
223    /// [`Error::AdminSecretHandling`].
224    AdminCredentialsCredsFileNotAFile = 104,
225
226    /// Mapping for [`crate::admin_credentials::Error::CredsFileWrite`] wrapped in
227    /// [`Error::AdminSecretHandling`].
228    AdminCredentialsCredsFileWrite = 105,
229
230    /// Mapping for [`crate::admin_credentials::Error::PassphraseTooShort`] wrapped in
231    /// [`Error::AdminSecretHandling`].
232    AdminCredentialsPassphraseTooShort = 106,
233
234    /// Mapping for [`Error::ApplyPermissions`].
235    ApplyPermissions = 10,
236
237    /// Mapping for [`Error::Chown`].
238    Chown = 11,
239
240    /// Mapping for [`Error::CommandAttachToStdin`].
241    CommandAttachToStdin = 12,
242
243    /// Mapping for [`Error::CommandBackground`].
244    CommandBackground = 13,
245
246    /// Mapping for [`Error::CommandExec`].
247    CommandExec = 14,
248
249    /// Mapping for [`Error::CommandNonZero`].
250    CommandNonZero = 15,
251
252    /// Mapping for [`Error::CommandWriteToStdin`].
253    CommandWriteToStdin = 16,
254
255    /// Mapping for [`crate::ConfigError::ConfigIsMissing`] wrapped in [`Error::Config`].
256    ConfigConfigMissing = 120,
257
258    /// Mapping for [`crate::ConfigError::MissingFileExtension`] wrapped in [`Error::Config`].
259    ConfigMissingFileExtension = 180,
260
261    /// Mapping for [`crate::ConfigError::UnsupportedFileExtension`] wrapped in [`Error::Config`].
262    ConfigUnsupportedFileExtension = 181,
263
264    /// Mapping for [`crate::ConfigError::DuplicateNetHsmUserId`] wrapped in
265    /// [`Error::Config`].
266    #[cfg(feature = "nethsm")]
267    ConfigDuplicateNetHsmUserId = 121,
268
269    /// Mapping for [`crate::ConfigError::DuplicateSshPublicKey`] wrapped in
270    /// [`Error::Config`].
271    ConfigDuplicateSshPublicKey = 122,
272
273    /// Mapping for [`crate::ConfigError::DuplicateKeyId`] wrapped in [`Error::Config`].
274    #[cfg(feature = "nethsm")]
275    ConfigDuplicateKeyId = 123,
276
277    /// Mapping for [`crate::ConfigError::DuplicateSystemUserId`] wrapped in
278    /// [`Error::Config`].
279    ConfigDuplicateSystemUserId = 124,
280
281    /// Mapping for [`crate::ConfigError::DuplicateTag`] wrapped in [`Error::Config`].
282    #[cfg(feature = "nethsm")]
283    ConfigDuplicateTag = 125,
284
285    /// Mapping for [`crate::ConfigError::InvalidSystemUserName`] wrapped in
286    /// [`Error::Config`].
287    ConfigInvalidSystemUserName = 126,
288
289    /// Mapping for [`crate::ConfigError::InvalidAuthorizedKeyEntry`] wrapped in
290    /// [`Error::Config`].
291    ConfigInvalidAuthorizedKeyEntry = 127,
292
293    /// Mapping for [`crate::ConfigError::MetricsAlsoOperator`] wrapped in
294    /// [`Error::Config`].
295    #[cfg(feature = "nethsm")]
296    ConfigMetricsAlsoOperator = 128,
297
298    /// Mapping for [`crate::ConfigError::MissingAdministrator`] wrapped in
299    /// [`Error::Config`].
300    #[cfg(feature = "nethsm")]
301    ConfigMissingAdministrator = 129,
302
303    /// Mapping for [`crate::ConfigError::MissingShareDownloadSystemUser`] wrapped in
304    /// [`Error::Config`].
305    ConfigMissingShareDownloadSystemUser = 130,
306
307    /// Mapping for [`crate::ConfigError::MissingShareUploadSystemUser`] wrapped in
308    /// [`Error::Config`].
309    ConfigMissingShareUploadSystemUser = 131,
310
311    /// Mapping for [`crate::ConfigError::NoAuthorizedKeys`] wrapped in [`Error::Config`].
312    ConfigNoAuthorizedKeys = 132,
313
314    /// Mapping for [`crate::ConfigError::NoMatchingMappingForSystemUser`] wrapped in
315    /// [`Error::Config`].
316    ConfigNoMatchingMappingForSystemUser = 133,
317
318    /// Mapping for [`crate::ConfigError::NoSssButShareUsers`] wrapped in [`Error::Config`].
319    ConfigNoSssButShareUsers = 134,
320
321    /// Mapping for [`crate::ConfigError::SshKey`] wrapped in [`Error::Config`].
322    ConfigSshKey = 135,
323
324    /// Mapping for [`crate::ConfigError::User`] wrapped in [`Error::Config`].
325    #[cfg(feature = "nethsm")]
326    ConfigUser = 136,
327
328    /// Mapping for [`crate::ConfigError::YamlDeserialize`] wrapped in [`Error::Config`].
329    ConfigYamlDeserialize = 137,
330
331    /// Mapping for [`crate::ConfigError::YamlSerialize`] wrapped in [`Error::Config`].
332    ConfigYamlSerialize = 138,
333
334    /// Mapping for [`crate::Error::NetHsm`].
335    IoPath = 17,
336
337    /// Mapping for [`crate::Error::NetHsm`].
338    #[cfg(feature = "nethsm")]
339    NetHsm = 18,
340
341    /// Mapping for [`crate::Error::NetHsmBackend`].
342    #[cfg(feature = "nethsm")]
343    NetHsmBackend = 19,
344
345    /// Mapping for [`signstar_common::admin_credentials::Error::ApplyPermissions`] wrapped in
346    /// [`Error::SignstarCommonAdminCreds`].
347    SignstarCommonAdminCredsApplyPermissions = 170,
348
349    /// Mapping for [`signstar_common::admin_credentials::Error::CreateDirectory`] wrapped in
350    /// [`Error::SignstarCommonAdminCreds`].
351    SignstarCommonAdminCredsCreateDirectory = 171,
352
353    /// Mapping for [`signstar_common::admin_credentials::Error::DirChangeOwner`] wrapped in
354    /// [`Error::SignstarCommonAdminCreds`].
355    SignstarCommonAdminCredsDirChangeOwner = 172,
356
357    /// Mapping for [`Error::SignstarCrypto`]
358    SignstarCrypto = 20,
359
360    /// Mapping for [`Error::Thread`].
361    Thread = 21,
362
363    /// Mapping for [`Error::TomlRead`].
364    TomlRead = 22,
365
366    /// Mapping for [`Error::TomlWrite`].
367    TomlWrite = 23,
368
369    /// Mapping for [`Error::Traits`].
370    Traits = 40,
371
372    /// Mapping for [`Error::Utf8String`].
373    Utf8String = 24,
374
375    /// Mapping for [`Error::Validation`].
376    Validation = 25,
377
378    /// Mapping for [`Error::NetHsmConfig`].
379    #[cfg(feature = "nethsm")]
380    NetHsmConfig = 26,
381
382    /// Mapping for [`Error::YubiHsm2Config`].
383    #[cfg(feature = "yubihsm2")]
384    YubiHsm2Config = 27,
385
386    /// Mapping for [`crate::utils::Error::ExecutableNotFound`] wrapped in [`Error::Utils`].
387    UtilsExecutableNotFound = 190,
388
389    /// Mapping for [`crate::utils::Error::SystemUserData`] wrapped in [`Error::Utils`].
390    UtilsSystemUserData = 192,
391
392    /// Mapping for [`crate::utils::Error::SystemUserLookup`] wrapped in [`Error::Utils`].
393    UtilsSystemUserLookup = 193,
394
395    /// Mapping for [`crate::utils::Error::SystemUserMismatch`] wrapped in [`Error::Utils`].
396    UtilsSystemUserMismatch = 194,
397
398    /// Mapping for [`crate::utils::Error::SystemUserNotRoot`] wrapped in [`Error::Utils`].
399    UtilsSystemUserNotRoot = 195,
400}
401
402impl From<Error> for ErrorExitCode {
403    fn from(value: Error) -> Self {
404        match value {
405            // admin credentials related errors and their exit codes
406            Error::AdminSecretHandling(error) => match error {
407                crate::admin_credentials::Error::AdministratorMissing => {
408                    Self::AdminCredentialsAdministratorMissing
409                }
410                crate::admin_credentials::Error::AdministratorNoDefault => {
411                    Self::AdminCredentialsAdministratorNoDefault
412                }
413                crate::admin_credentials::Error::CredsFileCreate { .. } => {
414                    Self::AdminCredentialsCredsFileCreate
415                }
416                crate::admin_credentials::Error::CredsFileMissing { .. } => {
417                    Self::AdminCredentialsCredsFileMissing
418                }
419                crate::admin_credentials::Error::CredsFileNotAFile { .. } => {
420                    Self::AdminCredentialsCredsFileNotAFile
421                }
422                crate::admin_credentials::Error::CredsFileWrite { .. } => {
423                    Self::AdminCredentialsCredsFileWrite
424                }
425                crate::admin_credentials::Error::PassphraseTooShort { .. } => {
426                    Self::AdminCredentialsPassphraseTooShort
427                }
428            },
429            // config related errors
430            Error::Config(error) => match error {
431                crate::ConfigError::ConfigIsMissing => Self::ConfigConfigMissing,
432                crate::ConfigError::MissingFileExtension { .. } => Self::ConfigMissingFileExtension,
433                crate::ConfigError::UnsupportedFileExtension { .. } => {
434                    Self::ConfigUnsupportedFileExtension
435                }
436                #[cfg(feature = "nethsm")]
437                crate::ConfigError::DuplicateNetHsmUserId { .. } => {
438                    Self::ConfigDuplicateNetHsmUserId
439                }
440                crate::ConfigError::DuplicateSshPublicKey { .. } => {
441                    Self::ConfigDuplicateSshPublicKey
442                }
443                #[cfg(feature = "nethsm")]
444                crate::ConfigError::DuplicateKeyId { .. } => Self::ConfigDuplicateKeyId,
445                crate::ConfigError::DuplicateSystemUserId { .. } => {
446                    Self::ConfigDuplicateSystemUserId
447                }
448                #[cfg(feature = "nethsm")]
449                crate::ConfigError::DuplicateTag { .. } => Self::ConfigDuplicateTag,
450                crate::ConfigError::InvalidSystemUserName { .. } => {
451                    Self::ConfigInvalidSystemUserName
452                }
453                crate::ConfigError::InvalidAuthorizedKeyEntry { .. } => {
454                    Self::ConfigInvalidAuthorizedKeyEntry
455                }
456                #[cfg(feature = "nethsm")]
457                crate::ConfigError::MetricsAlsoOperator { .. } => Self::ConfigMetricsAlsoOperator,
458                #[cfg(feature = "nethsm")]
459                crate::ConfigError::MissingAdministrator { .. } => Self::ConfigMissingAdministrator,
460                crate::ConfigError::MissingShareDownloadSystemUser => {
461                    Self::ConfigMissingShareDownloadSystemUser
462                }
463                crate::ConfigError::MissingShareUploadSystemUser => {
464                    Self::ConfigMissingShareUploadSystemUser
465                }
466                crate::ConfigError::NoAuthorizedKeys => Self::ConfigNoAuthorizedKeys,
467                crate::ConfigError::NoMatchingMappingForSystemUser { .. } => {
468                    Self::ConfigNoMatchingMappingForSystemUser
469                }
470                crate::ConfigError::NoSssButShareUsers { .. } => Self::ConfigNoSssButShareUsers,
471                crate::ConfigError::SshKey(_) => Self::ConfigSshKey,
472                #[cfg(feature = "nethsm")]
473                crate::ConfigError::User(_) => Self::ConfigUser,
474                crate::ConfigError::YamlDeserialize { .. } => Self::ConfigYamlDeserialize,
475                crate::ConfigError::YamlSerialize { .. } => Self::ConfigYamlSerialize,
476            },
477            // NetHSM related errors
478            #[cfg(feature = "nethsm")]
479            Error::NetHsm(_) => Self::NetHsm,
480            // NetHSM backend related errors
481            #[cfg(feature = "nethsm")]
482            Error::NetHsmBackend(_) => Self::NetHsmBackend,
483            // signstar-common admin credentials related errors
484            Error::SignstarCommonAdminCreds(error) => match error {
485                signstar_common::admin_credentials::Error::ApplyPermissions { .. } => {
486                    Self::SignstarCommonAdminCredsApplyPermissions
487                }
488                signstar_common::admin_credentials::Error::CreateDirectory { .. } => {
489                    Self::SignstarCommonAdminCredsCreateDirectory
490                }
491                signstar_common::admin_credentials::Error::DirChangeOwner { .. } => {
492                    Self::SignstarCommonAdminCredsDirChangeOwner
493                }
494            },
495            // utils related errors
496            Error::Utils(error) => match error {
497                crate::utils::Error::ExecutableNotFound { .. } => Self::UtilsExecutableNotFound,
498                crate::utils::Error::SystemUserData { .. } => Self::UtilsSystemUserData,
499                crate::utils::Error::SystemUserLookup { .. } => Self::UtilsSystemUserLookup,
500                crate::utils::Error::SystemUserMismatch { .. } => Self::UtilsSystemUserMismatch,
501                crate::utils::Error::SystemUserNotRoot { .. } => Self::UtilsSystemUserNotRoot,
502            },
503            // top-level errors and their exit codes
504            Error::ApplyPermissions { .. } => Self::ApplyPermissions,
505            Error::CommandAttachToStdin { .. } => Self::CommandAttachToStdin,
506            Error::Chown { .. } => Self::Chown,
507            Error::CommandBackground { .. } => Self::CommandBackground,
508            Error::CommandExec { .. } => Self::CommandExec,
509            Error::CommandNonZero { .. } => Self::CommandNonZero,
510            Error::CommandWriteToStdin { .. } => Self::CommandWriteToStdin,
511            Error::IoPath { .. } => Self::IoPath,
512            Error::SignstarCrypto { .. } => Self::SignstarCrypto,
513            Error::Thread { .. } => Self::Thread,
514            Error::TomlRead { .. } => Self::TomlRead,
515            Error::TomlWrite { .. } => Self::TomlWrite,
516            Error::Traits { .. } => Self::Traits,
517            Error::Utf8String { .. } => Self::Utf8String,
518            Error::Validation { .. } => Self::Validation,
519            #[cfg(feature = "nethsm")]
520            Error::NetHsmConfig { .. } => Self::NetHsmConfig,
521            #[cfg(feature = "yubihsm2")]
522            Error::YubiHsm2Config { .. } => Self::YubiHsm2Config,
523        }
524    }
525}
526
527impl From<ErrorExitCode> for ExitCode {
528    fn from(value: ErrorExitCode) -> Self {
529        Self::from(std::convert::Into::<u8>::into(value))
530    }
531}
532
533impl From<ErrorExitCode> for i32 {
534    fn from(value: ErrorExitCode) -> Self {
535        Self::from(std::convert::Into::<u8>::into(value))
536    }
537}