1mod tests;
8
9use std::collections::{BTreeMap, HashMap};
10
11use arminius_lib_db::{Database, DatabaseError, ResultExt as _};
12use arminius_lib_model::user_keys::{OneTimeKeyMap, OneTimeKeyRow};
13use ruma::{
14 DeviceId, KeyId, OneTimeKeyAlgorithm, OneTimeKeyName, OwnedDeviceId, OwnedOneTimeKeyId,
15 OwnedOneTimeKeyName, OwnedUserId, UInt, UserId, encryption::OneTimeKey,
16};
17
18#[rust_analyzer::prefer_underscore_import]
21pub trait OneTimeKeysQueries: Database {
22 #[tracing::instrument(skip(self))]
26 async fn try_insert_one_time_key(
27 &mut self,
28 key_algorithm: &OneTimeKeyAlgorithm,
29 key_name: &OneTimeKeyName,
30 user_id: &UserId,
31 device_id: &DeviceId,
32 key: &OneTimeKey,
33 ) -> Result<(), DatabaseError> {
34 let key_object =
35 serde_json::to_value(key).invalid_data("Failed to serialize one-time key")?;
36 sqlx::query!(
37 "INSERT INTO one_time_keys (key_algorithm, key_name, device_nid, key_object)
38 VALUES (
39 $1,
40 $2,
41 (
42 SELECT devices.nid
43 FROM devices
44 JOIN users ON devices.user_nid = users.nid
45 WHERE user_id = $3 AND device_id = $4
46 ),
47 $5)
48 ON CONFLICT DO NOTHING",
49 key_algorithm.as_str(),
50 key_name.as_str(),
51 user_id.as_str(),
52 device_id.as_str(),
53 key_object,
54 )
55 .execute(self.connection().await?)
56 .await?;
57
58 Ok(())
59 }
60
61 #[tracing::instrument(skip(self))]
67 async fn import_one_time_key(
68 &mut self,
69 key_algorithm: &OneTimeKeyAlgorithm,
70 key_name: &OneTimeKeyName,
71 user_id: &UserId,
72 device_id: &DeviceId,
73 key: &OneTimeKey,
74 ) -> Result<bool, DatabaseError> {
75 let key_object =
76 serde_json::to_value(key).invalid_data("Failed to serialize one-time key")?;
77 let result = sqlx::query!(
78 r#"
79 INSERT INTO one_time_keys (key_algorithm, key_name, device_nid, key_object)
80 SELECT
81 $1,
82 $2,
83 devices.nid,
84 $5
85 FROM
86 devices
87 JOIN users ON devices.user_nid = users.nid
88 WHERE
89 user_id = $3
90 AND device_id = $4
91 ON CONFLICT
92 DO NOTHING
93 "#,
94 key_algorithm.as_str(),
95 key_name.as_str(),
96 user_id.as_str(),
97 device_id.as_str(),
98 key_object,
99 )
100 .execute(self.connection().await?)
101 .await?;
102 Ok(result.rows_affected() > 0)
103 }
104
105 #[tracing::instrument(skip(self))]
108 async fn fetch_one_time_keys_counts(
109 &mut self,
110 user_id: &UserId,
111 device_id: &DeviceId,
112 ) -> Result<BTreeMap<OneTimeKeyAlgorithm, UInt>, DatabaseError> {
113 Ok(sqlx::query!(
114 "SELECT
115 key_algorithm,
116 COUNT(*) AS count
117 FROM
118 one_time_keys
119 JOIN devices ON one_time_keys.device_nid = devices.nid
120 JOIN users ON devices.user_nid = users.nid
121 WHERE
122 user_id = $1
123 AND device_id = $2
124 GROUP BY
125 key_algorithm
126 ",
127 user_id.as_str(),
128 device_id.as_str(),
129 )
130 .fetch_all(self.connection().await?)
131 .await?
132 .into_iter()
133 .map(|row| {
134 (
135 OneTimeKeyAlgorithm::from(row.key_algorithm),
136 UInt::try_from(row.count.unwrap_or(0)).unwrap_or(UInt::MAX),
138 )
139 })
140 .collect())
141 }
142
143 #[tracing::instrument(skip(self))]
146 async fn fetch_one_time_keys(
147 &mut self,
148 one_time_keys: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, OneTimeKeyAlgorithm>>,
149 ) -> Result<OneTimeKeyMap, DatabaseError> {
150 let mut users_ids = Vec::new();
151 let mut devices_ids = Vec::new();
152 let mut otks_algorithms = Vec::new();
153
154 for (user_id, devices) in one_time_keys {
155 for (device_id, otk_algorithm) in devices {
156 users_ids.push(user_id.to_string());
157 devices_ids.push(device_id.to_string());
158 otks_algorithms.push(otk_algorithm.to_string());
159 }
160 }
161
162 sqlx::query!(
163 "SELECT
164 otks.nid,
165 users.user_id,
166 devices.device_id,
167 otks.key_algorithm,
168 otks.key_name,
169 otks.key_object
170 FROM
171 users
172 JOIN devices ON devices.user_nid = users.nid
173 CROSS JOIN LATERAL (
174 SELECT
175 nid, key_algorithm, key_name, key_object
176 FROM
177 one_time_keys
178 WHERE
179 one_time_keys.device_nid = devices.nid
180 AND key_algorithm = ANY($3::text[])
181 ORDER BY
182 upload_revision ASC
183 LIMIT 1
184 ) AS otks
185 WHERE
186 (user_id, device_id) IN
187 (SELECT * FROM UNNEST($1::text[], $2::text[]))
188 ",
189 &users_ids,
190 &devices_ids,
191 &otks_algorithms
192 )
193 .fetch_all(self.connection().await?)
194 .await?
195 .into_iter()
196 .try_fold(
197 HashMap::new(),
198 |mut result: HashMap<
199 OwnedUserId,
200 HashMap<OwnedDeviceId, HashMap<OwnedOneTimeKeyId, OneTimeKeyRow>>,
201 >,
202 row| {
203 let user_id = row.user_id.parse().invalid_data("Invalid user ID in database")?;
204 let key_id = KeyId::from_parts(
205 OneTimeKeyAlgorithm::from(row.key_algorithm),
206 OwnedOneTimeKeyName::from(row.key_name).as_ref(),
207 );
208 let key = serde_json::from_value(row.key_object)
209 .invalid_data("Invalid one-time key in database")?;
210
211 result
212 .entry(user_id)
213 .or_default()
214 .entry(row.device_id.into())
215 .or_default()
216 .entry(key_id)
217 .or_insert(OneTimeKeyRow { nid: row.nid, key, used: None });
218
219 Ok(result)
220 },
221 )
222 }
223
224 #[tracing::instrument(skip(self))]
228 async fn delete_one_time_keys(&mut self, nids: &[i32]) -> Result<(), DatabaseError> {
229 if sqlx::query!("DELETE FROM one_time_keys WHERE nid = ANY($1::int[])", nids)
230 .execute(self.connection().await?)
231 .await?
232 .rows_affected() as usize
233 != nids.len()
234 {
235 return Err(DatabaseError::SqlxError(sqlx::Error::RowNotFound));
236 }
237
238 Ok(())
239 }
240}
241
242impl<T> OneTimeKeysQueries for T where T: Database {}