nethsm_config/
credentials.rs1use std::{collections::HashSet, fmt::Display, str::FromStr};
2
3use nethsm::{Credentials, Passphrase, UserId, UserRole};
4use serde::{Deserialize, Serialize};
5use ssh_key::{PublicKey, authorized_keys::Entry};
6use zeroize::Zeroize;
7
8#[derive(Debug, thiserror::Error)]
10pub enum Error {
11 #[error("The SSH authorized key is used multiple times: {ssh_authorized_key}")]
13 DuplicateAuthorizedKeys {
14 ssh_authorized_key: AuthorizedKeyEntry,
15 },
16
17 #[error("Invalid system user name: {0}")]
19 InvalidSystemUserName(String),
20
21 #[error("The SSH authorized key is not valid: {entry}")]
23 InvalidAuthorizedKeyEntry { entry: String },
24
25 #[error("No SSH authorized key provided!")]
27 NoAuthorizedKeys,
28
29 #[error("SSH key error: {0}")]
31 SshKey(#[from] ssh_key::Error),
32
33 #[error("The system-wide User ID has a namespace: {0}")]
35 SystemWideUserIdWithNamespace(UserId),
36
37 #[error("NetHSM user error: {0}")]
39 NetHsmUser(#[from] nethsm::UserError),
40}
41
42#[derive(Clone, Debug, Deserialize, Hash, PartialEq, Eq, Serialize, Zeroize)]
46pub struct ConfigCredentials {
47 #[zeroize(skip)]
48 role: UserRole,
49 #[zeroize(skip)]
50 name: UserId,
51 passphrase: Option<String>,
52}
53
54impl ConfigCredentials {
55 pub fn new(role: UserRole, name: UserId, passphrase: Option<String>) -> Self {
77 Self {
78 role,
79 name,
80 passphrase,
81 }
82 }
83
84 pub fn get_name(&self) -> UserId {
86 self.name.clone()
87 }
88
89 pub fn get_role(&self) -> UserRole {
91 self.role
92 }
93
94 pub fn get_passphrase(&self) -> Option<&str> {
96 self.passphrase.as_deref()
97 }
98
99 pub fn set_passphrase(&mut self, passphrase: String) {
101 self.passphrase = Some(passphrase)
102 }
103
104 pub fn has_passphrase(&self) -> bool {
106 self.passphrase.is_some()
107 }
108}
109
110impl From<ConfigCredentials> for Credentials {
111 fn from(value: ConfigCredentials) -> Self {
112 Self::new(value.name, value.passphrase.map(Passphrase::new))
113 }
114}
115
116#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize, Zeroize)]
121#[serde(into = "String", try_from = "String")]
122pub struct SystemUserId(String);
123
124impl SystemUserId {
125 pub fn new(user: String) -> Result<Self, Error> {
144 if user.is_empty()
145 || !(user
146 .chars()
147 .all(|char| char.is_alphanumeric() || char == '_' || char == '-'))
148 {
149 return Err(Error::InvalidSystemUserName(user));
150 }
151 Ok(Self(user))
152 }
153}
154
155impl AsRef<str> for SystemUserId {
156 fn as_ref(&self) -> &str {
157 &self.0
158 }
159}
160
161impl Display for SystemUserId {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 self.0.fmt(f)
164 }
165}
166
167impl From<SystemUserId> for String {
168 fn from(value: SystemUserId) -> Self {
169 value.to_string()
170 }
171}
172
173impl FromStr for SystemUserId {
174 type Err = Error;
175
176 fn from_str(s: &str) -> Result<Self, Self::Err> {
177 Self::new(s.to_string())
178 }
179}
180
181impl TryFrom<String> for SystemUserId {
182 type Error = Error;
183
184 fn try_from(value: String) -> Result<Self, Self::Error> {
185 Self::new(value)
186 }
187}
188
189#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize, Zeroize)]
195#[serde(into = "String", try_from = "String")]
196pub struct AuthorizedKeyEntry(String);
197
198impl AuthorizedKeyEntry {
199 pub fn new(entry: String) -> Result<Self, Error> {
220 if Entry::from_str(&entry).is_err() {
221 return Err(Error::InvalidAuthorizedKeyEntry { entry });
222 }
223
224 Ok(Self(entry))
225 }
226}
227
228impl AsRef<str> for AuthorizedKeyEntry {
229 fn as_ref(&self) -> &str {
230 &self.0
231 }
232}
233
234impl Display for AuthorizedKeyEntry {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 self.0.fmt(f)
237 }
238}
239
240impl From<AuthorizedKeyEntry> for String {
241 fn from(value: AuthorizedKeyEntry) -> Self {
242 value.to_string()
243 }
244}
245
246impl FromStr for AuthorizedKeyEntry {
247 type Err = Error;
248
249 fn from_str(s: &str) -> Result<Self, Self::Err> {
250 Self::new(s.to_string())
251 }
252}
253
254impl TryFrom<&AuthorizedKeyEntry> for Entry {
255 type Error = Error;
256
257 fn try_from(value: &AuthorizedKeyEntry) -> Result<Self, Error> {
258 Entry::from_str(&value.0).map_err(Error::SshKey)
259 }
260}
261
262impl TryFrom<String> for AuthorizedKeyEntry {
263 type Error = Error;
264
265 fn try_from(value: String) -> Result<Self, Error> {
266 Self::new(value)
267 }
268}
269
270#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
275#[serde(into = "Vec<String>", try_from = "Vec<String>")]
276pub struct AuthorizedKeyEntryList(Vec<AuthorizedKeyEntry>);
277
278impl AuthorizedKeyEntryList {
279 pub fn new(ssh_authorized_keys: Vec<AuthorizedKeyEntry>) -> Result<Self, Error> {
303 if ssh_authorized_keys.is_empty() {
304 return Err(Error::NoAuthorizedKeys);
305 }
306
307 let mut set = HashSet::new();
308 for (ssh_authorized_key, pub_key) in ssh_authorized_keys
309 .iter()
310 .filter_map(|ssh_authorized_key| {
311 if let Ok(entry) = Entry::try_from(ssh_authorized_key) {
312 Some((ssh_authorized_key.clone(), entry.public_key().clone()))
313 } else {
314 None
315 }
316 })
317 .collect::<Vec<(AuthorizedKeyEntry, PublicKey)>>()
318 {
319 if !set.insert(pub_key) {
320 return Err(Error::DuplicateAuthorizedKeys { ssh_authorized_key });
321 }
322 }
323
324 Ok(Self(ssh_authorized_keys))
325 }
326}
327
328impl AsRef<[AuthorizedKeyEntry]> for AuthorizedKeyEntryList {
329 fn as_ref(&self) -> &[AuthorizedKeyEntry] {
330 &self.0
331 }
332}
333
334impl From<AuthorizedKeyEntryList> for Vec<String> {
335 fn from(value: AuthorizedKeyEntryList) -> Self {
336 value
337 .0
338 .iter()
339 .map(|authorized_key| authorized_key.to_string())
340 .collect()
341 }
342}
343
344impl From<&AuthorizedKeyEntryList> for Vec<AuthorizedKeyEntry> {
345 fn from(value: &AuthorizedKeyEntryList) -> Self {
346 value.0.to_vec()
347 }
348}
349
350impl TryFrom<Vec<String>> for AuthorizedKeyEntryList {
351 type Error = Error;
352
353 fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
354 let authorized_keys = {
355 let mut authorized_keys: Vec<AuthorizedKeyEntry> = vec![];
356 for authorized_key in value {
357 authorized_keys.push(AuthorizedKeyEntry::new(authorized_key)?)
358 }
359 authorized_keys
360 };
361
362 Self::new(authorized_keys)
363 }
364}
365
366#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
368#[serde(into = "String", try_from = "String")]
369pub struct SystemWideUserId(UserId);
370
371impl SystemWideUserId {
372 pub fn new(user_id: String) -> Result<Self, Error> {
392 let user_id = UserId::new(user_id)?;
393 if user_id.is_namespaced() {
394 return Err(Error::SystemWideUserIdWithNamespace(user_id));
395 }
396 Ok(Self(user_id))
397 }
398}
399
400impl Display for SystemWideUserId {
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 self.0.fmt(f)
403 }
404}
405
406impl FromStr for SystemWideUserId {
407 type Err = Error;
408 fn from_str(s: &str) -> Result<Self, Self::Err> {
409 Self::new(s.to_string())
410 }
411}
412
413impl From<SystemWideUserId> for String {
414 fn from(value: SystemWideUserId) -> Self {
415 value.to_string()
416 }
417}
418
419impl From<SystemWideUserId> for UserId {
420 fn from(value: SystemWideUserId) -> Self {
421 value.0
422 }
423}
424
425impl TryFrom<String> for SystemWideUserId {
426 type Error = Error;
427
428 fn try_from(value: String) -> Result<Self, Self::Error> {
429 Self::new(value)
430 }
431}