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

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

229 lines
7.7 KiB
Rust
Raw Normal View History

pub(super) mod account;
2025-10-27 07:20:23 +00:00
pub(super) mod dehydrated_device;
pub(super) mod device;
pub(super) mod filters;
pub(super) mod keys;
pub(super) mod profile;
2025-10-27 07:20:23 +00:00
use std::{mem, sync::Arc};
2024-06-28 22:51:39 +00:00
pub use account::AccessTokenStatus;
2024-12-14 21:58:01 -05:00
use conduwuit::{
Err, Error, Result, err,
utils::{self},
2023-07-23 21:57:11 +02:00
};
use database::Map;
use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw};
use serde::{Deserialize, Serialize};
2022-09-06 23:15:09 +02:00
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<Self> {
Ok(Self(utils::hash::password(password).map_err(|e| {
err!(Request(InvalidParam("Password does not meet the requirements: {e}")))
})?))
}
}
2024-05-09 15:59:08 -07:00
pub struct Service {
services: Services,
2024-08-08 17:18:30 +00:00
db: Data,
}
2024-07-18 06:37:47 +00:00
struct Services {
account_data: Dep<account_data::Service>,
2024-07-18 06:37:47 +00:00
admin: Dep<admin::Service>,
alias: Dep<alias::Service>,
appservice: Dep<appservice::Service>,
config: Dep<config::Service>,
firstrun: Dep<firstrun::Service>,
2024-08-08 17:18:30 +00:00
globals: Dep<globals::Service>,
membership: Dep<membership::Service>,
oauth: Dep<oauth::Service>,
2024-08-08 17:18:30 +00:00
state_accessor: Dep<rooms::state_accessor::Service>,
2024-07-18 06:37:47 +00:00
state_cache: Dep<rooms::state_cache::Service>,
threepid: Dep<threepid::Service>,
2024-07-18 06:37:47 +00:00
}
2024-08-08 17:18:30 +00:00
struct Data {
keychangeid_userid: Arc<Map>,
keyid_key: Arc<Map>,
onetimekeyid_onetimekeys: Arc<Map>,
2026-04-25 16:49:47 +00:00
fallbackkeyid_fallbackkey: Arc<Map>,
2024-08-08 17:18:30 +00:00
openidtoken_expiresatuserid: Arc<Map>,
2025-01-11 18:49:21 +00:00
logintoken_expiresatuserid: Arc<Map>,
2024-08-08 17:18:30 +00:00
todeviceid_events: Arc<Map>,
token_userdeviceid: Arc<Map>,
userdeviceid_tokenexpires: Arc<Map>,
2024-08-08 17:18:30 +00:00
userdeviceid_metadata: Arc<Map>,
userdeviceid_token: Arc<Map>,
userfilterid_filter: Arc<Map>,
userid_avatarurl: Arc<Map>,
2025-10-27 07:20:23 +00:00
userid_dehydrateddevice: Arc<Map>,
2024-08-08 17:18:30 +00:00
userid_devicelistversion: Arc<Map>,
userid_displayname: Arc<Map>,
userid_lastonetimekeyupdate: Arc<Map>,
userid_masterkeyid: Arc<Map>,
userid_password: Arc<Map>,
userid_suspension: Arc<Map>,
userid_lock: Arc<Map>,
userid_logindisabled: Arc<Map>,
2024-08-08 17:18:30 +00:00
userid_selfsigningkeyid: Arc<Map>,
userid_usersigningkeyid: Arc<Map>,
useridprofilekey_value: Arc<Map>,
}
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-07-18 06:37:47 +00:00
services: Services {
account_data: args.depend::<account_data::Service>("account_data"),
2024-07-18 06:37:47 +00:00
admin: args.depend::<admin::Service>("admin"),
alias: args.depend::<alias::Service>("alias"),
appservice: args.depend::<appservice::Service>("appservice"),
config: args.depend::<config::Service>("config"),
firstrun: args.depend::<firstrun::Service>("firstrun"),
2024-08-08 17:18:30 +00:00
globals: args.depend::<globals::Service>("globals"),
membership: args.depend::<membership::Service>("membership"),
oauth: args.depend::<oauth::Service>("oauth"),
state_accessor: args
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
2024-07-18 06:37:47 +00:00
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
threepid: args.depend::<threepid::Service>("threepid"),
2024-07-18 06:37:47 +00:00
},
2024-08-08 17:18:30 +00:00
db: Data {
keychangeid_userid: args.db["keychangeid_userid"].clone(),
keyid_key: args.db["keyid_key"].clone(),
onetimekeyid_onetimekeys: args.db["onetimekeyid_onetimekeys"].clone(),
2026-04-25 16:49:47 +00:00
fallbackkeyid_fallbackkey: args.db["fallbackkeyid_fallbackkey"].clone(),
2024-08-08 17:18:30 +00:00
openidtoken_expiresatuserid: args.db["openidtoken_expiresatuserid"].clone(),
2025-01-11 18:49:21 +00:00
logintoken_expiresatuserid: args.db["logintoken_expiresatuserid"].clone(),
2024-08-08 17:18:30 +00:00
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(),
2025-10-27 07:20:23 +00:00
userid_dehydrateddevice: args.db["userid_dehydrateddevice"].clone(),
2024-08-08 17:18:30 +00:00
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(),
2024-08-08 17:18:30 +00:00
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(),
2024-08-08 17:18:30 +00:00
},
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!()) }
}
pub fn parse_master_key(
user_id: &UserId,
master_key: &Raw<CrossSigningKey>,
) -> Result<(Vec<u8>, CrossSigningKey)> {
2024-08-08 17:18:30 +00:00
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))
}
2021-12-22 19:41:33 +01:00
pub fn parse_user_signing_key(user_signing_key: &Raw<CrossSigningKey>) -> Result<String> {
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)
}
2021-12-22 19:41:33 +01:00
/// Ensure that a user only sees signatures from themselves and the target user
2024-08-08 17:18:30 +00:00
fn clean_signatures<F>(
mut cross_signing_key: serde_json::Value,
sender_user: Option<&UserId>,
user_id: &UserId,
allowed_signatures: &F,
2024-08-08 17:18:30 +00:00
) -> Result<serde_json::Value>
where
F: Fn(&UserId) -> bool + Send + Sync,
{
2024-03-25 17:05:11 -04:00
if let Some(signatures) = cross_signing_key
.get_mut("signatures")
.and_then(|v| v.as_object_mut())
{
2021-12-22 19:41:33 +01:00
// 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."))?;
2023-07-16 16:50:03 +02:00
if sender_user == Some(user_id) || sid == user_id || allowed_signatures(sid) {
2021-12-22 19:41:33 +01:00
signatures.insert(user, signature);
2024-03-05 19:48:54 -05:00
}
2021-12-22 19:41:33 +01:00
}
}
2024-08-08 17:18:30 +00:00
Ok(cross_signing_key)
}
//TODO: this is an ABA
fn increment(db: &Arc<Map>, key: &[u8]) {
2024-09-29 07:37:43 +00:00
let old = db.get_blocking(key);
2024-08-08 17:18:30 +00:00
let new = utils::increment(old.ok().as_deref());
2024-10-07 17:54:27 +00:00
db.insert(key, new);
}