signstar_config/
non_admin_credentials.rs1use std::{
3 fmt::{Debug, Display},
4 path::PathBuf,
5};
6
7use signstar_common::common::SECRET_FILE_MODE;
8use signstar_crypto::traits::UserWithPassphrase;
9
10use crate::{
11 ExtendedUserMapping,
12 SignstarConfig,
13 SystemUserId,
14 UserMapping,
15 utils::get_current_system_user,
16};
17
18#[derive(Debug, thiserror::Error)]
20pub enum Error {
21 #[error("Errors occurred when loading credentials for system user {system_user}:\n{errors}")]
23 CredentialsLoading {
24 system_user: SystemUserId,
26 errors: CredentialsLoadingErrors,
28 },
29
30 #[error("There are no credentials for system user {system_user}")]
32 CredentialsMissing {
33 system_user: SystemUserId,
35 },
36
37 #[error("There is no system user in the mapping.")]
39 NoSystemUser,
40
41 #[error("The user is not an operator user in the HSM backend used for signing.")]
43 NotSigningUser,
44
45 #[error("Passphrase directory {path} for user {system_user} can not be created:\n{source}")]
47 SecretsDirCreate {
48 path: PathBuf,
50 system_user: SystemUserId,
52 source: std::io::Error,
54 },
55
56 #[error("The secrets file {path} can not be created for user {system_user}:\n{source}")]
58 SecretsFileCreate {
59 path: PathBuf,
61 system_user: SystemUserId,
63 source: std::io::Error,
65 },
66
67 #[error("File metadata of secrets file {path} cannot be retrieved")]
69 SecretsFileMetadata {
70 path: PathBuf,
72 source: std::io::Error,
74 },
75
76 #[error("Secrets file not found: {path}")]
78 SecretsFileMissing {
79 path: PathBuf,
81 },
82
83 #[error("Secrets file is not a file: {path}")]
85 SecretsFileNotAFile {
86 path: PathBuf,
88 },
89
90 #[error("Secrets file {path} has permissions {mode}, but {SECRET_FILE_MODE} is required")]
92 SecretsFilePermissions {
93 path: PathBuf,
95 mode: u32,
97 },
98
99 #[error("Failed reading secrets file {path}:\n{source}")]
101 SecretsFileRead {
102 path: PathBuf,
104 source: std::io::Error,
106 },
107
108 #[error("The secrets file {path} can not be written to for user {system_user}: {source}")]
110 SecretsFileWrite {
111 path: PathBuf,
113 system_user: SystemUserId,
115 source: std::io::Error,
117 },
118}
119
120#[derive(Debug)]
124pub struct CredentialsLoadingError {
125 user_id: String,
126 error: crate::Error,
127}
128
129impl CredentialsLoadingError {
130 pub fn new(user_id: String, error: crate::Error) -> Self {
132 Self { user_id, error }
133 }
134
135 pub fn get_user_id(&self) -> &str {
137 &self.user_id
138 }
139
140 pub fn get_error(&self) -> &crate::Error {
142 &self.error
143 }
144}
145
146impl Display for CredentialsLoadingError {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 write!(f, "{}: {}", self.user_id, self.error)
149 }
150}
151
152#[derive(Debug)]
154pub struct CredentialsLoadingErrors {
155 errors: Vec<CredentialsLoadingError>,
156}
157
158impl CredentialsLoadingErrors {
159 pub fn new(errors: Vec<CredentialsLoadingError>) -> Self {
161 Self { errors }
162 }
163
164 pub fn get_errors(&self) -> &[CredentialsLoadingError] {
166 &self.errors
167 }
168}
169
170impl Display for CredentialsLoadingErrors {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 write!(
173 f,
174 "{}",
175 self.errors
176 .iter()
177 .map(|error| error.to_string())
178 .collect::<Vec<String>>()
179 .join("\n")
180 )
181 }
182}
183
184#[derive(Debug)]
189pub struct CredentialsLoading {
190 mapping: ExtendedUserMapping,
191 credentials: Vec<Box<dyn UserWithPassphrase>>,
192 errors: CredentialsLoadingErrors,
193}
194
195impl CredentialsLoading {
196 pub fn new(
198 mapping: ExtendedUserMapping,
199 credentials: Vec<Box<dyn UserWithPassphrase>>,
200 errors: CredentialsLoadingErrors,
201 ) -> Self {
202 Self {
203 mapping,
204 credentials,
205 errors,
206 }
207 }
208
209 pub fn from_system_user() -> Result<Self, crate::Error> {
225 let user = get_current_system_user()?;
226
227 let system_config = SignstarConfig::new_from_file(None)?;
228
229 let Some(mapping) = system_config.get_extended_mapping_for_user(&user.name) else {
230 return Err(
231 crate::ConfigError::NoMatchingMappingForSystemUser { name: user.name }.into(),
232 );
233 };
234
235 let credentials_loading = mapping.load_credentials()?;
237
238 Ok(credentials_loading)
239 }
240
241 pub fn get_mapping(&self) -> &ExtendedUserMapping {
243 &self.mapping
244 }
245
246 pub fn get_credentials(&self) -> &[Box<dyn UserWithPassphrase>] {
248 &self.credentials
249 }
250
251 pub fn get_system_user_id(&self) -> Result<&SystemUserId, crate::Error> {
257 match self.mapping.get_user_mapping().get_system_user() {
258 Some(system_user) => Ok(system_user),
259 None => Err(crate::Error::NonAdminSecretHandling(Error::NoSystemUser)),
260 }
261 }
262
263 pub fn has_userid_errors(&self) -> bool {
267 !self.errors.get_errors().is_empty()
268 }
269
270 pub fn get_userid_errors(self) -> CredentialsLoadingErrors {
272 self.errors
273 }
274
275 pub fn has_signing_user(&self) -> bool {
277 match self.mapping.get_user_mapping() {
278 UserMapping::NetHsmOnlyAdmin(_)
279 | UserMapping::SystemNetHsmBackup { .. }
280 | UserMapping::SystemNetHsmMetrics { .. }
281 | UserMapping::HermeticSystemNetHsmMetrics { .. }
282 | UserMapping::SystemOnlyShareDownload { .. }
283 | UserMapping::SystemOnlyShareUpload { .. }
284 | UserMapping::SystemOnlyWireGuardDownload { .. } => false,
285 UserMapping::SystemNetHsmOperatorSigning { .. } => true,
286 #[cfg(feature = "yubihsm2")]
287 UserMapping::YubiHsmOnlyAdmin { .. } => false,
288 #[cfg(feature = "yubihsm2")]
289 UserMapping::SystemYubiHsmOperatorSigning { .. } => true,
290 }
291 }
292
293 pub fn credentials_for_signing_user(self) -> Result<Box<dyn UserWithPassphrase>, 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 let system_user = self.get_system_user_id()?.clone();
316 let mut iterator = self.credentials.into_iter();
317
318 if let Some(credentials) = iterator.next() {
319 Ok(credentials)
320 } else {
321 Err(crate::Error::NonAdminSecretHandling(
322 Error::CredentialsMissing { system_user },
323 ))
324 }
325 }
326}