pub(super) mod account; pub(super) mod dehydrated_device; pub(super) mod device; pub(super) mod filters; pub(super) mod keys; pub(super) mod profile; use std::{mem, sync::Arc}; pub use account::AccessTokenStatus; use conduwuit::{ Err, Error, Result, err, utils::{self}, }; use database::Map; use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw}; use serde::{Deserialize, Serialize}; use crate::{ Dep, account_data, admin, appservice, config, firstrun, globals, oauth, rooms::{self, alias, membership}, threepid, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserSuspension { /// Whether the user is currently suspended pub suspended: bool, /// When the user was suspended (Unix timestamp in milliseconds) pub suspended_at: u64, /// User ID of who suspended this user pub suspended_by: String, } /// A password hash. This is only for use when setting a user's password, /// if the hash needs to be kept around for a while without keeping the password /// in memory. #[derive(Serialize, Deserialize)] pub struct HashedPassword(String); impl HashedPassword { pub fn new(password: &str) -> Result { Ok(Self(utils::hash::password(password).map_err(|e| { err!(Request(InvalidParam("Password does not meet the requirements: {e}"))) })?)) } } pub struct Service { services: Services, db: Data, } struct Services { account_data: Dep, admin: Dep, alias: Dep, appservice: Dep, config: Dep, firstrun: Dep, globals: Dep, membership: Dep, oauth: Dep, state_accessor: Dep, state_cache: Dep, threepid: Dep, } struct Data { keychangeid_userid: Arc, keyid_key: Arc, onetimekeyid_onetimekeys: Arc, fallbackkeyid_fallbackkey: Arc, openidtoken_expiresatuserid: Arc, logintoken_expiresatuserid: Arc, todeviceid_events: Arc, token_userdeviceid: Arc, userdeviceid_tokenexpires: Arc, userdeviceid_metadata: Arc, userdeviceid_token: Arc, userfilterid_filter: Arc, userid_avatarurl: Arc, userid_dehydrateddevice: Arc, userid_devicelistversion: Arc, userid_displayname: Arc, userid_lastonetimekeyupdate: Arc, userid_masterkeyid: Arc, userid_password: Arc, userid_suspension: Arc, userid_lock: Arc, userid_logindisabled: Arc, userid_selfsigningkeyid: Arc, userid_usersigningkeyid: Arc, useridprofilekey_value: Arc, } impl crate::Service for Service { fn build(args: crate::Args<'_>) -> Result> { Ok(Arc::new(Self { services: Services { account_data: args.depend::("account_data"), admin: args.depend::("admin"), alias: args.depend::("alias"), appservice: args.depend::("appservice"), config: args.depend::("config"), firstrun: args.depend::("firstrun"), globals: args.depend::("globals"), membership: args.depend::("membership"), oauth: args.depend::("oauth"), state_accessor: args .depend::("rooms::state_accessor"), state_cache: args.depend::("rooms::state_cache"), threepid: args.depend::("threepid"), }, db: Data { keychangeid_userid: args.db["keychangeid_userid"].clone(), keyid_key: args.db["keyid_key"].clone(), onetimekeyid_onetimekeys: args.db["onetimekeyid_onetimekeys"].clone(), fallbackkeyid_fallbackkey: args.db["fallbackkeyid_fallbackkey"].clone(), openidtoken_expiresatuserid: args.db["openidtoken_expiresatuserid"].clone(), logintoken_expiresatuserid: args.db["logintoken_expiresatuserid"].clone(), todeviceid_events: args.db["todeviceid_events"].clone(), token_userdeviceid: args.db["token_userdeviceid"].clone(), userdeviceid_metadata: args.db["userdeviceid_metadata"].clone(), userdeviceid_token: args.db["userdeviceid_token"].clone(), userfilterid_filter: args.db["userfilterid_filter"].clone(), userid_avatarurl: args.db["userid_avatarurl"].clone(), userid_dehydrateddevice: args.db["userid_dehydrateddevice"].clone(), userid_devicelistversion: args.db["userid_devicelistversion"].clone(), userid_displayname: args.db["userid_displayname"].clone(), userid_lastonetimekeyupdate: args.db["userid_lastonetimekeyupdate"].clone(), userid_masterkeyid: args.db["userid_masterkeyid"].clone(), userid_password: args.db["userid_password"].clone(), userid_suspension: args.db["userid_suspension"].clone(), userid_lock: args.db["userid_lock"].clone(), userid_logindisabled: args.db["userid_logindisabled"].clone(), userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(), userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(), useridprofilekey_value: args.db["useridprofilekey_value"].clone(), userdeviceid_tokenexpires: args.db["userdeviceid_tokenexpires"].clone(), }, })) } fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } } pub fn parse_master_key( user_id: &UserId, master_key: &Raw, ) -> Result<(Vec, CrossSigningKey)> { let mut prefix = user_id.as_bytes().to_vec(); prefix.push(0xFF); let master_key = master_key .deserialize() .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key"))?; let mut master_key_ids = master_key.keys.values(); let master_key_id = master_key_ids .next() .ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Master key contained no key."))?; if master_key_ids.next().is_some() { return Err(Error::BadRequest( ErrorKind::InvalidParam, "Master key contained more than one key.", )); } let mut master_key_key = prefix.clone(); master_key_key.extend_from_slice(master_key_id.as_bytes()); Ok((master_key_key, master_key)) } pub fn parse_user_signing_key(user_signing_key: &Raw) -> Result { let mut user_signing_key_ids = user_signing_key .deserialize() .map_err(|_| err!(Request(InvalidParam("Invalid user signing key"))))? .keys .into_values(); let user_signing_key_id = user_signing_key_ids .next() .ok_or(err!(Request(InvalidParam("User signing key contained no key."))))?; if user_signing_key_ids.next().is_some() { return Err!(Request(InvalidParam("User signing key contained more than one key."))); } Ok(user_signing_key_id) } /// Ensure that a user only sees signatures from themselves and the target user fn clean_signatures( mut cross_signing_key: serde_json::Value, sender_user: Option<&UserId>, user_id: &UserId, allowed_signatures: &F, ) -> Result where F: Fn(&UserId) -> bool + Send + Sync, { if let Some(signatures) = cross_signing_key .get_mut("signatures") .and_then(|v| v.as_object_mut()) { // Don't allocate for the full size of the current signatures, but require // at most one resize if nothing is dropped let new_capacity = signatures.len() / 2; for (user, signature) in mem::replace(signatures, serde_json::Map::with_capacity(new_capacity)) { let sid = <&UserId>::try_from(user.as_str()) .map_err(|_| Error::bad_database("Invalid user ID in database."))?; if sender_user == Some(user_id) || sid == user_id || allowed_signatures(sid) { signatures.insert(user, signature); } } } Ok(cross_signing_key) } //TODO: this is an ABA fn increment(db: &Arc, key: &[u8]) { let old = db.get_blocking(key); let new = utils::increment(old.ok().as_deref()); db.insert(key, new); }