signstar_config/config/
credentials.rs1use std::{fmt::Display, str::FromStr};
4
5use nix::unistd::User;
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, Ord, PartialEq, PartialOrd, 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
87impl TryFrom<User> for SystemUserId {
88 type Error = crate::Error;
89
90 fn try_from(value: User) -> Result<Self, Self::Error> {
91 Self::new(value.name)
92 }
93}
94
95#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
101#[serde(into = "String", try_from = "String")]
102pub struct AuthorizedKeyEntry(Entry);
103
104impl AuthorizedKeyEntry {
105 pub fn new(entry: String) -> Result<Self, crate::Error> {
127 Ok(Self(Entry::from_str(&entry).map_err(|_source| {
128 ConfigError::InvalidAuthorizedKeyEntry { entry }
129 })?))
130 }
131}
132
133impl AsRef<Entry> for AuthorizedKeyEntry {
134 fn as_ref(&self) -> &Entry {
135 &self.0
136 }
137}
138
139impl Display for AuthorizedKeyEntry {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(f, "{}", self.0.to_string())
142 }
143}
144
145impl From<AuthorizedKeyEntry> for String {
146 fn from(value: AuthorizedKeyEntry) -> Self {
147 value.to_string()
148 }
149}
150
151impl FromStr for AuthorizedKeyEntry {
152 type Err = crate::Error;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 Self::new(s.to_string())
156 }
157}
158
159impl From<&AuthorizedKeyEntry> for Entry {
160 fn from(value: &AuthorizedKeyEntry) -> Self {
161 value.0.clone()
162 }
163}
164
165impl TryFrom<String> for AuthorizedKeyEntry {
166 type Error = crate::Error;
167
168 fn try_from(value: String) -> Result<Self, crate::Error> {
169 Self::new(value)
170 }
171}
172
173impl std::hash::Hash for AuthorizedKeyEntry {
174 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
175 self.0.to_string().hash(state);
176 }
177}
178
179impl Ord for AuthorizedKeyEntry {
180 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
181 self.0.to_string().cmp(&other.0.to_string())
182 }
183}
184
185impl PartialOrd for AuthorizedKeyEntry {
186 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
187 Some(self.cmp(other))
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use testresult::TestResult;
194
195 use super::*;
196
197 #[test]
198 fn system_user_id_new_fails() {
199 assert!(SystemUserId::new("üser".to_string()).is_err());
200 }
201
202 #[test]
203 fn authorized_key_entry_new_fails() {
204 assert!(AuthorizedKeyEntry::new("foo".to_string()).is_err());
205 }
206
207 #[test]
208 fn authorized_key_to_string() -> TestResult {
209 let entry = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host";
210 let authorized_key = AuthorizedKeyEntry::new(entry.to_string())?;
211
212 assert_eq!(authorized_key.to_string(), entry);
213 Ok(())
214 }
215}