Files
continuwuity/src/service/key_backups/mod.rs
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

286 lines
7.5 KiB
Rust
Raw Normal View History

2024-05-09 15:59:08 -07:00
use std::{collections::BTreeMap, sync::Arc};
2022-09-07 13:25:51 +02:00
2024-08-08 17:18:30 +00:00
use conduit::{
err, implement,
2024-08-08 17:18:30 +00:00
utils::stream::{ReadyExt, TryIgnore},
Err, Result,
2024-08-08 17:18:30 +00:00
};
use database::{Deserialized, Ignore, Interfix, Map};
use futures::StreamExt;
2020-06-16 12:11:38 +02:00
use ruma::{
2022-10-08 13:03:07 +02:00
api::client::backup::{BackupAlgorithm, KeyBackupData, RoomKeyBackup},
2021-12-22 19:41:33 +01:00
serde::Raw,
2022-10-09 17:25:06 +02:00
OwnedRoomId, RoomId, UserId,
2020-06-16 12:11:38 +02:00
};
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
use crate::{globals, Dep};
2024-05-09 15:59:08 -07:00
pub struct Service {
2024-06-28 22:51:39 +00:00
db: Data,
2024-08-08 17:18:30 +00:00
services: Services,
}
struct Data {
backupid_algorithm: Arc<Map>,
backupid_etag: Arc<Map>,
backupkeyid_backup: Arc<Map>,
}
struct Services {
globals: Dep<globals::Service>,
2022-09-07 13:25:51 +02:00
}
2024-07-04 03:26:19 +00:00
impl crate::Service for Service {
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
Ok(Arc::new(Self {
2024-08-08 17:18:30 +00:00
db: Data {
backupid_algorithm: args.db["backupid_algorithm"].clone(),
backupid_etag: args.db["backupid_etag"].clone(),
backupkeyid_backup: args.db["backupkeyid_backup"].clone(),
},
services: Services {
globals: args.depend::<globals::Service>("globals"),
},
2024-07-04 03:26:19 +00:00
}))
2024-05-27 03:17:20 +00:00
}
2024-07-04 03:26:19 +00:00
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
}
2024-08-08 17:18:30 +00:00
#[implement(Service)]
pub fn create_backup(&self, user_id: &UserId, backup_metadata: &Raw<BackupAlgorithm>) -> Result<String> {
let version = self.services.globals.next_count()?.to_string();
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
let mut key = user_id.as_bytes().to_vec();
key.push(0xFF);
key.extend_from_slice(version.as_bytes());
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.db.backupid_algorithm.insert(
&key,
&serde_json::to_vec(backup_metadata).expect("BackupAlgorithm::to_vec always works"),
);
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.db
.backupid_etag
.insert(&key, &self.services.globals.next_count()?.to_be_bytes());
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
Ok(version)
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
#[implement(Service)]
pub async fn delete_backup(&self, user_id: &UserId, version: &str) {
let mut key = user_id.as_bytes().to_vec();
key.push(0xFF);
key.extend_from_slice(version.as_bytes());
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.db.backupid_algorithm.remove(&key);
self.db.backupid_etag.remove(&key);
let key = (user_id, version, Interfix);
self.db
.backupkeyid_backup
.keys_prefix_raw(&key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_for_each(|outdated_key| self.db.backupkeyid_backup.remove(outdated_key))
.await;
}
#[implement(Service)]
pub async fn update_backup(
&self, user_id: &UserId, version: &str, backup_metadata: &Raw<BackupAlgorithm>,
) -> Result<String> {
let key = (user_id, version);
if self.db.backupid_algorithm.qry(&key).await.is_err() {
return Err!(Request(NotFound("Tried to update nonexistent backup.")));
2020-06-16 12:11:38 +02:00
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
let mut key = user_id.as_bytes().to_vec();
key.push(0xFF);
key.extend_from_slice(version.as_bytes());
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
self.db
.backupid_algorithm
.insert(&key, backup_metadata.json().get().as_bytes());
self.db
.backupid_etag
.insert(&key, &self.services.globals.next_count()?.to_be_bytes());
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
Ok(version.to_owned())
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
#[implement(Service)]
pub async fn get_latest_backup_version(&self, user_id: &UserId) -> Result<String> {
type Key<'a> = (&'a UserId, &'a str);
2024-03-05 19:48:54 -05:00
let last_possible_key = (user_id, u64::MAX);
2024-08-08 17:18:30 +00:00
self.db
.backupid_algorithm
.rev_keys_from(&last_possible_key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_take_while(|(user_id_, _): &Key<'_>| *user_id_ == user_id)
.map(|(_, version): Key<'_>| version.to_owned())
2024-08-08 17:18:30 +00:00
.next()
.await
.ok_or_else(|| err!(Request(NotFound("No backup versions found"))))
}
2024-03-05 19:48:54 -05:00
2024-08-08 17:18:30 +00:00
#[implement(Service)]
pub async fn get_latest_backup(&self, user_id: &UserId) -> Result<(String, Raw<BackupAlgorithm>)> {
type Key<'a> = (&'a UserId, &'a str);
type KeyVal<'a> = (Key<'a>, Raw<BackupAlgorithm>);
2024-03-05 19:48:54 -05:00
let last_possible_key = (user_id, u64::MAX);
2024-08-08 17:18:30 +00:00
self.db
.backupid_algorithm
.rev_stream_from(&last_possible_key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_take_while(|((user_id_, _), _): &KeyVal<'_>| *user_id_ == user_id)
.map(|((_, version), algorithm): KeyVal<'_>| (version.to_owned(), algorithm))
2024-08-08 17:18:30 +00:00
.next()
.await
.ok_or_else(|| err!(Request(NotFound("No backup found"))))
}
#[implement(Service)]
pub async fn get_backup(&self, user_id: &UserId, version: &str) -> Result<Raw<BackupAlgorithm>> {
let key = (user_id, version);
2024-09-28 15:14:48 +00:00
self.db.backupid_algorithm.qry(&key).await.deserialized()
2024-08-08 17:18:30 +00:00
}
#[implement(Service)]
pub async fn add_key(
&self, user_id: &UserId, version: &str, room_id: &RoomId, session_id: &str, key_data: &Raw<KeyBackupData>,
) -> Result<()> {
let key = (user_id, version);
if self.db.backupid_algorithm.qry(&key).await.is_err() {
return Err!(Request(NotFound("Tried to update nonexistent backup.")));
}
2024-08-08 17:18:30 +00:00
let mut key = user_id.as_bytes().to_vec();
key.push(0xFF);
key.extend_from_slice(version.as_bytes());
self.db
.backupid_etag
.insert(&key, &self.services.globals.next_count()?.to_be_bytes());
key.push(0xFF);
key.extend_from_slice(room_id.as_bytes());
key.push(0xFF);
key.extend_from_slice(session_id.as_bytes());
self.db
.backupkeyid_backup
.insert(&key, key_data.json().get().as_bytes());
Ok(())
}
#[implement(Service)]
pub async fn count_keys(&self, user_id: &UserId, version: &str) -> usize {
let prefix = (user_id, version);
self.db
.backupkeyid_backup
.keys_prefix_raw(&prefix)
2024-08-08 17:18:30 +00:00
.count()
.await
}
#[implement(Service)]
pub async fn get_etag(&self, user_id: &UserId, version: &str) -> String {
let key = (user_id, version);
self.db
.backupid_etag
.qry(&key)
.await
.deserialized::<u64>()
.as_ref()
.map(ToString::to_string)
.expect("Backup has no etag.")
}
#[implement(Service)]
pub async fn get_all(&self, user_id: &UserId, version: &str) -> BTreeMap<OwnedRoomId, RoomKeyBackup> {
type Key<'a> = (Ignore, Ignore, &'a RoomId, &'a str);
type KeyVal<'a> = (Key<'a>, Raw<KeyBackupData>);
2024-08-08 17:18:30 +00:00
let mut rooms = BTreeMap::<OwnedRoomId, RoomKeyBackup>::new();
let default = || RoomKeyBackup {
sessions: BTreeMap::new(),
};
let prefix = (user_id, version, Interfix);
self.db
.backupkeyid_backup
.stream_prefix(&prefix)
.ignore_err()
.ready_for_each(|((_, _, room_id, session_id), key_backup_data): KeyVal<'_>| {
2024-08-08 17:18:30 +00:00
rooms
.entry(room_id.into())
.or_insert_with(default)
.sessions
.insert(session_id.into(), key_backup_data);
2024-08-08 17:18:30 +00:00
})
.await;
rooms
}
#[implement(Service)]
pub async fn get_room(
&self, user_id: &UserId, version: &str, room_id: &RoomId,
) -> BTreeMap<String, Raw<KeyBackupData>> {
type KeyVal<'a> = ((Ignore, Ignore, Ignore, &'a str), Raw<KeyBackupData>);
2024-08-08 17:18:30 +00:00
let prefix = (user_id, version, room_id, Interfix);
self.db
.backupkeyid_backup
.stream_prefix(&prefix)
.ignore_err()
.map(|((.., session_id), key_backup_data): KeyVal<'_>| (session_id.to_owned(), key_backup_data))
2024-08-08 17:18:30 +00:00
.collect()
.await
}
#[implement(Service)]
pub async fn get_session(
&self, user_id: &UserId, version: &str, room_id: &RoomId, session_id: &str,
) -> Result<Raw<KeyBackupData>> {
let key = (user_id, version, room_id, session_id);
2024-09-28 15:14:48 +00:00
self.db.backupkeyid_backup.qry(&key).await.deserialized()
2024-08-08 17:18:30 +00:00
}
#[implement(Service)]
pub async fn delete_all_keys(&self, user_id: &UserId, version: &str) {
let key = (user_id, version, Interfix);
self.db
.backupkeyid_backup
.keys_prefix_raw(&key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_for_each(|outdated_key| self.db.backupkeyid_backup.remove(outdated_key))
.await;
}
#[implement(Service)]
pub async fn delete_room_keys(&self, user_id: &UserId, version: &str, room_id: &RoomId) {
let key = (user_id, version, room_id, Interfix);
self.db
.backupkeyid_backup
.keys_prefix_raw(&key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_for_each(|outdated_key| self.db.backupkeyid_backup.remove(outdated_key))
.await;
}
#[implement(Service)]
pub async fn delete_room_key(&self, user_id: &UserId, version: &str, room_id: &RoomId, session_id: &str) {
let key = (user_id, version, room_id, session_id);
self.db
.backupkeyid_backup
.keys_prefix_raw(&key)
2024-08-08 17:18:30 +00:00
.ignore_err()
.ready_for_each(|outdated_key| self.db.backupkeyid_backup.remove(outdated_key))
.await;
2020-06-16 12:11:38 +02:00
}