signstar_config/
non_admin_credentials.rs1use 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#[derive(Debug, thiserror::Error)]
22pub enum Error {
23 #[error("Errors occurred when loading credentials for system user {system_user}:\n{errors}")]
25 CredentialsLoading {
26 system_user: SystemUserId,
28 errors: CredentialsLoadingErrors,
30 },
31
32 #[error("There are no credentials for system user {system_user}")]
34 CredentialsMissing {
35 system_user: SystemUserId,
37 },
38
39 #[error("There is no system user in the mapping.")]
41 NoSystemUser,
42
43 #[error("The user is not an operator user in the NetHSM backend used for signing.")]
45 NotSigningUser,
46
47 #[error("Passphrase directory {path} for user {system_user} can not be created:\n{source}")]
49 SecretsDirCreate {
50 path: PathBuf,
52 system_user: SystemUserId,
54 source: std::io::Error,
56 },
57
58 #[error("The secrets file {path} can not be created for user {system_user}:\n{source}")]
60 SecretsFileCreate {
61 path: PathBuf,
63 system_user: SystemUserId,
65 source: std::io::Error,
67 },
68
69 #[error("File metadata of secrets file {path} cannot be retrieved")]
71 SecretsFileMetadata {
72 path: PathBuf,
74 source: std::io::Error,
76 },
77
78 #[error("Secrets file not found: {path}")]
80 SecretsFileMissing {
81 path: PathBuf,
83 },
84
85 #[error("Secrets file is not a file: {path}")]
87 SecretsFileNotAFile {
88 path: PathBuf,
90 },
91
92 #[error("Secrets file {path} has permissions {mode}, but {SECRET_FILE_MODE} is required")]
94 SecretsFilePermissions {
95 path: PathBuf,
97 mode: u32,
99 },
100
101 #[error("Failed reading secrets file {path}:\n{source}")]
103 SecretsFileRead {
104 path: PathBuf,
106 source: std::io::Error,
108 },
109
110 #[error("The secrets file {path} can not be written to for user {system_user}: {source}")]
112 SecretsFileWrite {
113 path: PathBuf,
115 system_user: SystemUserId,
117 source: std::io::Error,
119 },
120}
121
122#[derive(Debug)]
126pub struct CredentialsLoadingError {
127 user_id: String,
128 error: crate::Error,
129}
130
131impl CredentialsLoadingError {
132 pub fn new(user_id: String, error: crate::Error) -> Self {
134 Self { user_id, error }
135 }
136
137 pub fn get_user_id(&self) -> &str {
139 &self.user_id
140 }
141
142 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#[derive(Debug)]
156pub struct CredentialsLoadingErrors {
157 errors: Vec<CredentialsLoadingError>,
158}
159
160impl CredentialsLoadingErrors {
161 pub fn new(errors: Vec<CredentialsLoadingError>) -> Self {
163 Self { errors }
164 }
165
166 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#[derive(Debug)]
191pub struct CredentialsLoading {
192 mapping: ExtendedUserMapping,
193 credentials: Vec<Box<dyn UserWithPassphrase>>,
194 errors: CredentialsLoadingErrors,
195}
196
197impl CredentialsLoading {
198 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 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 let credentials_loading = mapping.load_credentials()?;
240
241 Ok(credentials_loading)
242 }
243
244 pub fn get_mapping(&self) -> &ExtendedUserMapping {
246 &self.mapping
247 }
248
249 pub fn get_credentials(&self) -> &[Box<dyn UserWithPassphrase>] {
251 &self.credentials
252 }
253
254 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 pub fn has_userid_errors(&self) -> bool {
270 !self.errors.get_errors().is_empty()
271 }
272
273 pub fn get_userid_errors(self) -> CredentialsLoadingErrors {
275 self.errors
276 }
277
278 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 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}