phemios_core/
server_profile.rs1use serde_json::Value;
3pub fn parse(text: &str) -> Result<Value, String> {
5 if text.len() > 32768 {
6 return Err("Profil de connexion trop volumineux.".into());
7 }
8 let value: Value =
9 serde_json::from_str(text).map_err(|_| "Profil de connexion Aède illisible.")?;
10 let fields = value
11 .as_object()
12 .ok_or("Profil de connexion Aède invalide.")?;
13 if fields.len() != 5
14 || fields.keys().any(|key| {
15 !["format", "version", "address", "username", "certificate"].contains(&key.as_str())
16 })
17 || value["format"] != "aede-local-server"
18 || value["version"] != 1
19 {
20 return Err("Profil de connexion Aède incompatible ou contenant des champs privés.".into());
21 }
22 let address =
23 crate::network::server_address(value["address"].as_str().ok_or("Adresse absente.")?)?;
24 let uri: ureq::http::Uri = address.parse().map_err(|_| "Adresse locale invalide.")?;
25 if uri.scheme_str() != Some("https")
26 || uri.host() != Some("127.0.0.1")
27 || uri.port_u16().is_none_or(|port| port == 0)
28 {
29 return Err("Ce certificat est réservé à un serveur HTTPS sur cet ordinateur.".into());
30 }
31 let username = value["username"].as_str().ok_or("Compte absent.")?;
32 if username.is_empty()
33 || username.len() > 128
34 || !username
35 .bytes()
36 .all(|b| b.is_ascii_alphanumeric() || b"._-".contains(&b))
37 {
38 return Err("Compte du profil invalide.".into());
39 }
40 let bytes = value["certificate"]
41 .as_array()
42 .filter(|v| !v.is_empty() && v.len() <= 8192)
43 .ok_or("Certificat local invalide.")?
44 .iter()
45 .map(|v| {
46 v.as_u64()
47 .and_then(|n| u8::try_from(n).ok())
48 .ok_or("Certificat local invalide.")
49 })
50 .collect::<Result<Vec<_>, _>>()?;
51 rustls::RootCertStore::empty()
52 .add(rustls::pki_types::CertificateDer::from(bytes))
53 .map_err(|_| "Certificat local illisible.")?;
54 Ok(value)
55}
56#[cfg(test)]
57#[path = "server_profile_tests.rs"]
58mod tests;