wire_e2e_identity/legacy/
id.rs1use base64::Engine;
2
3use super::{Error, Result};
4
5#[cfg(test)]
6const DOMAIN: &str = "wire.com";
7const COLON: u8 = b':';
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
10pub struct ClientId(Vec<u8>);
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
14pub struct WireQualifiedClientId(ClientId);
15
16impl WireQualifiedClientId {
17 pub fn get_user_id(&self) -> String {
18 let mut split = self.0.split(|b| b == &COLON);
19 let user_id = split.next().unwrap();
20 uuid::Uuid::try_parse_ascii(user_id).unwrap().to_string()
21 }
22}
23
24#[cfg(test)]
25impl WireQualifiedClientId {
26 pub fn generate() -> Self {
27 let user_id = uuid::Uuid::new_v4();
28 Self::generate_from_user_id(&user_id)
29 }
30
31 pub fn generate_from_user_id(user_id: &uuid::Uuid) -> Self {
32 let user_id = user_id.to_string();
33 let device_id = rand::random::<u64>();
34 let client_id = format!("{user_id}:{device_id:x}@{DOMAIN}");
35 Self(client_id.into_bytes().into())
36 }
37}
38
39impl<'a> TryFrom<&'a [u8]> for WireQualifiedClientId {
41 type Error = Error;
42
43 fn try_from(bytes: &'a [u8]) -> Result<Self> {
44 const COLON: u8 = 58;
45 let mut split = bytes.split(|b| b == &COLON);
46 let user_id = split.next().ok_or(Error::InvalidClientId)?;
47
48 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD
49 .decode(user_id)
50 .map_err(|_| Error::InvalidClientId)?;
51
52 let user_id = uuid::Uuid::from_slice(&user_id).map_err(|_| Error::InvalidClientId)?;
53 let mut buf = [0; uuid::fmt::Hyphenated::LENGTH];
54 let user_id = user_id.hyphenated().encode_lower(&mut buf);
55
56 let rest = split.next().ok_or(Error::InvalidClientId)?;
57 if split.next().is_some() {
58 return Err(Error::InvalidClientId);
59 }
60
61 let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
62 Ok(Self(client_id.into()))
63 }
64}
65
66impl std::str::FromStr for WireQualifiedClientId {
67 type Err = Error;
68
69 fn from_str(s: &str) -> Result<Self> {
70 s.as_bytes().try_into()
71 }
72}
73
74impl TryFrom<WireQualifiedClientId> for String {
75 type Error = Error;
76
77 fn try_from(id: WireQualifiedClientId) -> Result<Self> {
78 String::from_utf8(id.to_vec()).map_err(|_| Error::InvalidClientId)
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash, derive_more::From, derive_more::Into, derive_more::Deref)]
84pub struct QualifiedE2eiClientId(ClientId);
85
86#[cfg(test)]
87impl QualifiedE2eiClientId {
88 pub fn generate() -> Self {
89 Self::generate_from_user_id(&uuid::Uuid::new_v4())
90 }
91
92 pub fn generate_with_domain(domain: &str) -> Self {
93 Self::generate_from_user_id_and_domain(&uuid::Uuid::new_v4(), domain)
94 }
95
96 pub fn generate_from_user_id(user_id: &uuid::Uuid) -> Self {
97 Self::generate_from_user_id_and_domain(user_id, DOMAIN)
98 }
99
100 pub fn generate_from_user_id_and_domain(user_id: &uuid::Uuid, domain: &str) -> Self {
101 use base64::Engine as _;
102
103 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());
104
105 let device_id = rand::random::<u64>();
106 let client_id = format!("{user_id}:{device_id:x}@{domain}");
107 Self(client_id.into_bytes().into())
108 }
109
110 pub fn from_str_unchecked(s: &str) -> Self {
111 Self(s.as_bytes().to_owned().into())
112 }
113}
114
115impl<'a> TryFrom<&'a [u8]> for QualifiedE2eiClientId {
117 type Error = Error;
118
119 fn try_from(bytes: &'a [u8]) -> Result<Self> {
120 let mut split = bytes.split(|b| b == &COLON);
121 let user_id = split.next().ok_or(Error::InvalidClientId)?;
122
123 let user_id = std::str::from_utf8(user_id)
124 .map_err(|_| Error::InvalidClientId)?
125 .parse::<uuid::Uuid>()
126 .map_err(|_| Error::InvalidClientId)?;
127
128 let user_id = base64::prelude::BASE64_URL_SAFE_NO_PAD.encode(user_id.as_bytes());
129
130 let rest = split.next().ok_or(Error::InvalidClientId)?;
131 if split.next().is_some() {
132 return Err(Error::InvalidClientId);
133 }
134
135 let client_id = [user_id.as_bytes(), &[COLON], rest].concat();
136 Ok(Self(client_id.into()))
137 }
138}
139
140impl AsRef<[u8]> for QualifiedE2eiClientId {
141 fn as_ref(&self) -> &[u8] {
142 &self.0
143 }
144}
145
146impl std::str::FromStr for QualifiedE2eiClientId {
147 type Err = Error;
148
149 fn from_str(s: &str) -> Result<Self> {
150 s.as_bytes().try_into()
151 }
152}
153
154impl TryFrom<QualifiedE2eiClientId> for String {
155 type Error = Error;
156
157 fn try_from(id: QualifiedE2eiClientId) -> Result<Self> {
158 String::from_utf8(id.to_vec()).map_err(|_| Error::InvalidClientId)
159 }
160}