core_crypto/mls/client/
user_id.rs

1use crate::{CryptoError, CryptoResult};
2
3/// Unique identifier of a User (human person holding some devices).
4/// This contradicts the initial design requirements of this project since it was supposed to be
5/// agnostic from Wire.
6/// End-to-end Identity re-shuffled that... But we still want to keep this isolated from the rest
7/// of the crate that's why this should remain here and be used cautiously, having the context quoted
8/// above in mind.
9/// For example in `wireapp://LcksJb74Tm6N12cDjFy7lQ!8e6424430d3b28be@wire.com` the [UserId] is `LcksJb74Tm6N12cDjFy7lQ`
10#[derive(Debug, Clone, Copy, Eq, PartialEq, derive_more::Deref)]
11pub struct UserId<'a>(&'a [u8]);
12
13impl UserId<'_> {
14    const USER_ID_DELIMITER: u8 = 58; // the char ':'
15}
16
17impl<'a> TryFrom<&'a str> for UserId<'a> {
18    type Error = CryptoError;
19
20    fn try_from(client_id: &'a str) -> CryptoResult<Self> {
21        client_id.as_bytes().try_into()
22    }
23}
24
25impl<'a> TryFrom<&'a [u8]> for UserId<'a> {
26    type Error = CryptoError;
27
28    fn try_from(id: &'a [u8]) -> CryptoResult<Self> {
29        let found = id
30            .splitn(2, |&b| b == Self::USER_ID_DELIMITER)
31            .next()
32            .ok_or(CryptoError::InvalidClientId)?;
33        if found.len() == id.len() {
34            return Err(CryptoError::InvalidClientId);
35        }
36        Ok(Self(found))
37    }
38}
39
40impl TryFrom<UserId<'_>> for String {
41    type Error = CryptoError;
42
43    fn try_from(uid: UserId<'_>) -> CryptoResult<Self> {
44        Ok(std::str::from_utf8(&uid)?.to_string())
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use wasm_bindgen_test::*;
52
53    wasm_bindgen_test_configure!(run_in_browser);
54
55    #[async_std::test]
56    #[wasm_bindgen_test]
57    async fn should_parse_client_id() {
58        let user_id = "LcksJb74Tm6N12cDjFy7lQ:8e6424430d3b28be@world.com";
59        let user_id = UserId::try_from(user_id).unwrap();
60        assert_eq!(user_id, UserId("LcksJb74Tm6N12cDjFy7lQ".as_bytes()));
61    }
62
63    #[async_std::test]
64    #[wasm_bindgen_test]
65    async fn should_fail_when_invalid() {
66        let user_id = "LcksJb74Tm6N12cDjFy7lQ/8e6424430d3b28be@world.com";
67        let user_id = UserId::try_from(user_id);
68        assert!(matches!(user_id.unwrap_err(), CryptoError::InvalidClientId));
69    }
70}