Skip to main content

phemios_core/
server_profile.rs

1//! Explicitly imported public identity for a separate Aède HTTPS loopback application.
2use serde_json::Value;
3/// Parse a bounded public connection profile. Private material and remote trust overrides are refused.
4pub 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;