signstar_config/config/
credentials.rs1use std::{fmt::Display, str::FromStr};
4
5use nethsm::UserId;
6use serde::{Deserialize, Serialize};
7use ssh_key::authorized_keys::Entry;
8use zeroize::Zeroize;
9
10use crate::ConfigError;
11#[cfg(doc)]
12use crate::SignstarConfig;
13
14#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Zeroize)]
19#[serde(into = "String", try_from = "String")]
20pub struct SystemUserId(String);
21
22impl SystemUserId {
23 pub fn new(user: String) -> Result<Self, crate::Error> {
42 if user.is_empty()
43 || !(user
44 .chars()
45 .all(|char| char.is_ascii_alphanumeric() || char == '_' || char == '-'))
46 {
47 return Err(ConfigError::InvalidSystemUserName { name: user }.into());
48 }
49 Ok(Self(user))
50 }
51}
52
53impl AsRef<str> for SystemUserId {
54 fn as_ref(&self) -> &str {
55 &self.0
56 }
57}
58
59impl Display for SystemUserId {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 self.0.fmt(f)
62 }
63}
64
65impl From<SystemUserId> for String {
66 fn from(value: SystemUserId) -> Self {
67 value.0
68 }
69}
70
71impl FromStr for SystemUserId {
72 type Err = crate::Error;
73
74 fn from_str(s: &str) -> Result<Self, Self::Err> {
75 Self::new(s.to_string())
76 }
77}
78
79impl TryFrom<String> for SystemUserId {
80 type Error = crate::Error;
81
82 fn try_from(value: String) -> Result<Self, Self::Error> {
83 Self::new(value)
84 }
85}
86
87#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Zeroize)]
93#[serde(into = "String", try_from = "String")]
94pub struct AuthorizedKeyEntry(String);
95
96impl AuthorizedKeyEntry {
97 pub fn new(entry: String) -> Result<Self, crate::Error> {
118 if Entry::from_str(&entry).is_err() {
119 return Err(ConfigError::InvalidAuthorizedKeyEntry { entry }.into());
120 }
121
122 Ok(Self(entry))
123 }
124}
125
126impl AsRef<str> for AuthorizedKeyEntry {
127 fn as_ref(&self) -> &str {
128 &self.0
129 }
130}
131
132impl Display for AuthorizedKeyEntry {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 self.0.fmt(f)
135 }
136}
137
138impl From<AuthorizedKeyEntry> for String {
139 fn from(value: AuthorizedKeyEntry) -> Self {
140 value.to_string()
141 }
142}
143
144impl FromStr for AuthorizedKeyEntry {
145 type Err = crate::Error;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 Self::new(s.to_string())
149 }
150}
151
152impl TryFrom<&AuthorizedKeyEntry> for Entry {
153 type Error = crate::Error;
154
155 fn try_from(value: &AuthorizedKeyEntry) -> Result<Self, crate::Error> {
156 Entry::from_str(&value.0)
157 .map_err(|source| crate::Error::Config(ConfigError::SshKey(source)))
158 }
159}
160
161impl TryFrom<String> for AuthorizedKeyEntry {
162 type Error = crate::Error;
163
164 fn try_from(value: String) -> Result<Self, crate::Error> {
165 Self::new(value)
166 }
167}
168
169#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
171#[serde(into = "String", try_from = "String")]
172pub struct SystemWideUserId(UserId);
173
174impl SystemWideUserId {
175 pub fn new(user_id: String) -> Result<Self, crate::Error> {
195 let user_id = UserId::new(user_id)
196 .map_err(|source| crate::Error::Config(ConfigError::User(source)))?;
197 if user_id.is_namespaced() {
198 return Err(ConfigError::SystemWideUserIdWithNamespace(user_id).into());
199 }
200 Ok(Self(user_id))
201 }
202}
203
204impl Display for SystemWideUserId {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 self.0.fmt(f)
207 }
208}
209
210impl FromStr for SystemWideUserId {
211 type Err = crate::Error;
212 fn from_str(s: &str) -> Result<Self, Self::Err> {
213 Self::new(s.to_string())
214 }
215}
216
217impl From<SystemWideUserId> for String {
218 fn from(value: SystemWideUserId) -> Self {
219 value.to_string()
220 }
221}
222
223impl From<SystemWideUserId> for UserId {
224 fn from(value: SystemWideUserId) -> Self {
225 value.0
226 }
227}
228
229impl TryFrom<String> for SystemWideUserId {
230 type Error = crate::Error;
231
232 fn try_from(value: String) -> Result<Self, Self::Error> {
233 Self::new(value)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use testresult::TestResult;
240
241 use super::*;
242
243 #[test]
244 fn system_user_id_new_fails() {
245 assert!(SystemUserId::new("üser".to_string()).is_err());
246 }
247
248 #[test]
249 fn authorized_key_entry_new_fails() {
250 assert!(AuthorizedKeyEntry::new("foo".to_string()).is_err());
251 }
252
253 #[test]
254 fn authorized_key_as_ref() -> TestResult {
255 let entry = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host";
256 let authorized_key = AuthorizedKeyEntry::new(entry.to_string())?;
257
258 assert_eq!(authorized_key.as_ref(), entry);
259 Ok(())
260 }
261
262 #[test]
263 fn system_wide_user_id_new_fails() -> TestResult {
264 assert!(SystemWideUserId::new("ns1~test".to_string()).is_err());
265 Ok(())
266 }
267
268 #[test]
269 fn system_wide_user_id_from_str() -> TestResult {
270 assert!(SystemWideUserId::from_str("ns1~test").is_err());
271 assert!(SystemWideUserId::from_str("test").is_ok());
272 Ok(())
273 }
274}