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, utils::get_current_system_user};
11
12#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Zeroize)]
17#[serde(into = "String", try_from = "String")]
18pub struct SystemUserId(String);
19
20impl SystemUserId {
21 pub fn new(user: String) -> Result<Self, crate::Error> {
40 if user.is_empty()
41 || !(user
42 .chars()
43 .all(|char| char.is_ascii_alphanumeric() || char == '_' || char == '-'))
44 {
45 return Err(ConfigError::InvalidSystemUserName { name: user }.into());
46 }
47 Ok(Self(user))
48 }
49
50 pub fn from_current_unix_user() -> Result<Self, crate::Error> {
60 let current_unix_user = get_current_system_user()?;
61 Self::try_from(current_unix_user)
62 }
63}
64
65impl AsRef<str> for SystemUserId {
66 fn as_ref(&self) -> &str {
67 &self.0
68 }
69}
70
71impl Display for SystemUserId {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 self.0.fmt(f)
74 }
75}
76
77impl From<SystemUserId> for String {
78 fn from(value: SystemUserId) -> Self {
79 value.0
80 }
81}
82
83impl FromStr for SystemUserId {
84 type Err = crate::Error;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 Self::new(s.to_string())
88 }
89}
90
91impl TryFrom<String> for SystemUserId {
92 type Error = crate::Error;
93
94 fn try_from(value: String) -> Result<Self, Self::Error> {
95 Self::new(value)
96 }
97}
98
99impl TryFrom<User> for SystemUserId {
100 type Error = crate::Error;
101
102 fn try_from(value: User) -> Result<Self, Self::Error> {
103 Self::new(value.name)
104 }
105}
106
107#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
113#[serde(into = "String", try_from = "String")]
114pub struct AuthorizedKeyEntry(Entry);
115
116impl AuthorizedKeyEntry {
117 pub fn new(entry: String) -> Result<Self, crate::Error> {
139 Ok(Self(Entry::from_str(&entry).map_err(|_source| {
140 ConfigError::InvalidAuthorizedKeyEntry { entry }
141 })?))
142 }
143}
144
145impl AsRef<Entry> for AuthorizedKeyEntry {
146 fn as_ref(&self) -> &Entry {
147 &self.0
148 }
149}
150
151impl Display for AuthorizedKeyEntry {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 write!(f, "{}", self.0.to_string())
154 }
155}
156
157impl From<AuthorizedKeyEntry> for String {
158 fn from(value: AuthorizedKeyEntry) -> Self {
159 value.to_string()
160 }
161}
162
163impl FromStr for AuthorizedKeyEntry {
164 type Err = crate::Error;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 Self::new(s.to_string())
168 }
169}
170
171impl From<&AuthorizedKeyEntry> for Entry {
172 fn from(value: &AuthorizedKeyEntry) -> Self {
173 value.0.clone()
174 }
175}
176
177impl TryFrom<String> for AuthorizedKeyEntry {
178 type Error = crate::Error;
179
180 fn try_from(value: String) -> Result<Self, crate::Error> {
181 Self::new(value)
182 }
183}
184
185impl std::hash::Hash for AuthorizedKeyEntry {
186 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
187 self.0.to_string().hash(state);
188 }
189}
190
191impl Ord for AuthorizedKeyEntry {
192 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
193 self.0.to_string().cmp(&other.0.to_string())
194 }
195}
196
197impl PartialOrd for AuthorizedKeyEntry {
198 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
199 Some(self.cmp(other))
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use testresult::TestResult;
206
207 use super::*;
208
209 #[test]
210 fn system_user_id_new_fails() {
211 assert!(SystemUserId::new("üser".to_string()).is_err());
212 }
213
214 #[test]
215 fn authorized_key_entry_new_fails() {
216 assert!(AuthorizedKeyEntry::new("foo".to_string()).is_err());
217 }
218
219 #[test]
220 fn authorized_key_to_string() -> TestResult {
221 let entry = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPkpXKiNhy39A3bZ1u19a5d4sFwYMBkWQyCbzgUfdKBm user@host";
222 let authorized_key = AuthorizedKeyEntry::new(entry.to_string())?;
223
224 assert_eq!(authorized_key.to_string(), entry);
225 Ok(())
226 }
227}