mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-14 20:56:49 +00:00
refactor: Split remote and deactivated users into their own columns
This commit is contained in:
32 files changed
+391
-278
No files matched your search
@@ -48,7 +48,7 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
||||
|
||||
// Create a user for the server
|
||||
let server_user = services.globals.server_user.as_ref();
|
||||
services.users.create(server_user, None)?;
|
||||
services.users.create_shadow_account(server_user).await?;
|
||||
|
||||
let mut create_content = if room_version_rules.authorization.use_room_create_sender {
|
||||
RoomCreateEventContent::new_v1(server_user.into())
|
||||
|
||||
@@ -108,17 +108,17 @@ impl Service {
|
||||
self.services.globals.server_name(),
|
||||
)?;
|
||||
|
||||
if !self.services.users.exists(&appservice_user_id).await {
|
||||
self.services.users.create(&appservice_user_id, None)?;
|
||||
} else if self
|
||||
if !self
|
||||
.services
|
||||
.users
|
||||
.is_deactivated(&appservice_user_id)
|
||||
.status(&appservice_user_id)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
.is_found()
|
||||
{
|
||||
// Reactivate the appservice user if it was accidentally deactivated
|
||||
self.services.users.set_password(&appservice_user_id, None);
|
||||
self.services
|
||||
.users
|
||||
.create_shadow_account(&appservice_user_id)
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.registration_info
|
||||
|
||||
@@ -54,15 +54,22 @@ impl Service {
|
||||
async fn set_emergency_access(&self) -> Result {
|
||||
let server_user = &self.services.globals.server_user;
|
||||
|
||||
self.services.users.set_password(
|
||||
server_user,
|
||||
self.services
|
||||
.config
|
||||
.emergency_password
|
||||
.as_deref()
|
||||
.map(HashedPassword::new)
|
||||
.transpose()?,
|
||||
);
|
||||
match &self.services.config.emergency_password {
|
||||
| Some(emergency_password) => {
|
||||
let emergency_password = HashedPassword::new(emergency_password)?;
|
||||
|
||||
self.services
|
||||
.users
|
||||
.convert_to_local_account(server_user, emergency_password)
|
||||
.await?;
|
||||
},
|
||||
| None => {
|
||||
self.services
|
||||
.users
|
||||
.convert_to_shadow_account(server_user)
|
||||
.await?;
|
||||
},
|
||||
}
|
||||
|
||||
let (ruleset, pwd_set) = match self.services.config.emergency_password {
|
||||
| Some(_) => (Ruleset::server_default(server_user), true),
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) async fn migrations(services: &Services) -> Result<()> {
|
||||
// requires recreating the database from scratch.
|
||||
if users_count > 0 {
|
||||
let server_user = &services.globals.server_user;
|
||||
if !services.users.exists(server_user).await {
|
||||
if !services.users.status(server_user).await.is_found() {
|
||||
error!("The {server_user} server user does not exist, and the database is not new.");
|
||||
return Err!(Database(
|
||||
"Cannot reuse an existing database after changing the server name, please \
|
||||
@@ -228,6 +228,18 @@ async fn migrate(services: &Services) -> Result<()> {
|
||||
.map_err(|e| err!("Failed to run 'fix_local_invite_state' migration': {e}"))?;
|
||||
}
|
||||
|
||||
if services.globals.db.database_version().await < 18 {
|
||||
services.globals.db.bump_database_version(18);
|
||||
info!("Migration: Bumped database version to 18");
|
||||
}
|
||||
|
||||
if db["global"].get(SPLIT_USERID_PASSWORD).await.is_not_found() {
|
||||
info!("Running migration 'split_userid_password'");
|
||||
split_userid_password(services)
|
||||
.await
|
||||
.map_err(|e| err!("Failed to run 'split_userid_password' migration': {e}"))?;
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
services.globals.db.database_version().await,
|
||||
DATABASE_VERSION,
|
||||
@@ -242,9 +254,9 @@ async fn migrate(services: &Services) -> Result<()> {
|
||||
if !patterns.is_empty() {
|
||||
services
|
||||
.users
|
||||
.stream()
|
||||
.stream_local_users()
|
||||
.filter_map(async |user_id| {
|
||||
if services.users.is_active_local(&user_id).await {
|
||||
if services.users.status(&user_id).await.is_found() {
|
||||
Some(user_id)
|
||||
} else {
|
||||
None
|
||||
@@ -774,7 +786,7 @@ async fn fix_local_invite_state(services: &Services) -> Result {
|
||||
|
||||
let db = &services.db;
|
||||
let cork = db.cork_and_sync();
|
||||
let userroomid_invitestate = services.db["userroomid_invitestate"].clone();
|
||||
let userroomid_invitestate = db["userroomid_invitestate"].clone();
|
||||
|
||||
// for each user invited to a room
|
||||
let fixed = userroomid_invitestate.stream()
|
||||
@@ -818,3 +830,44 @@ async fn fix_local_invite_state(services: &Services) -> Result {
|
||||
db.db.sort()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const SPLIT_USERID_PASSWORD: &str = "split_userid_password";
|
||||
async fn split_userid_password(services: &Services) -> Result {
|
||||
// Split remote and deactivated users out from the `userid_password` table
|
||||
|
||||
let db = &services.db;
|
||||
let cork = db.cork_and_sync();
|
||||
let userid_password = db["userid_password"].clone();
|
||||
let remoteuserid_remoteuser = db["remoteuserid_remoteuser"].clone();
|
||||
let userid_deactivated = db["userid_deactivated"].clone();
|
||||
|
||||
let remote_users = userid_password
|
||||
.stream::<OwnedUserId, String>()
|
||||
.ignore_err()
|
||||
.fold(0_usize, async |mut remote_users, (user_id, hash)| {
|
||||
if !services.globals.user_is_local(&user_id) {
|
||||
assert!(hash.is_empty(), "non-empty hash {hash} for remote user {user_id}");
|
||||
|
||||
remoteuserid_remoteuser.insert(&user_id, "");
|
||||
userid_password.remove(&user_id);
|
||||
remote_users = remote_users.saturating_add(1);
|
||||
} else if hash.is_empty() {
|
||||
if !(services.appservice.is_exclusive_user_id(&user_id).await
|
||||
|| user_id == services.globals.server_user)
|
||||
{
|
||||
info!("Marking {user_id} as deactivated");
|
||||
userid_deactivated.insert(&user_id, "");
|
||||
}
|
||||
}
|
||||
|
||||
remote_users
|
||||
})
|
||||
.await;
|
||||
|
||||
drop(cork);
|
||||
info!(?remote_users, "Split userid_password.");
|
||||
|
||||
db["global"].insert(FIXED_LOCAL_INVITE_STATE_MARKER, []);
|
||||
db.db.sort()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -248,7 +248,7 @@ impl Service {
|
||||
) -> impl Stream<Item = OwnedUserId> + Send + 'a {
|
||||
self.local_users_in_room(room_id)
|
||||
.filter_map(async |user_id| {
|
||||
if self.services.users.is_active(&user_id).await {
|
||||
if self.services.users.status(&user_id).await.is_active() {
|
||||
Some(user_id)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -30,15 +30,8 @@ impl super::Service {
|
||||
) -> Result {
|
||||
let membership = pdu.get_content::<RoomMemberEventContent>()?;
|
||||
|
||||
// Keep track what remote users exist by adding them as "deactivated" users
|
||||
//
|
||||
// TODO: use futures to update remote profiles without blocking the membership
|
||||
// update
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if !self.services.globals.user_is_local(user_id)
|
||||
&& !self.services.users.exists(user_id).await
|
||||
{
|
||||
self.services.users.create(user_id, None)?;
|
||||
if !self.services.globals.user_is_local(user_id) {
|
||||
self.services.users.record_remote_user(user_id);
|
||||
}
|
||||
|
||||
match &membership.membership {
|
||||
|
||||
@@ -255,7 +255,13 @@ impl super::Service {
|
||||
if let Some(state_key) = pdu.state_key() {
|
||||
match UserId::parse(state_key) {
|
||||
| Ok(target_user_id) => {
|
||||
if self.services.users.is_active_local(&target_user_id).await {
|
||||
if self
|
||||
.services
|
||||
.users
|
||||
.status(&target_user_id)
|
||||
.await
|
||||
.is_active()
|
||||
{
|
||||
push_targets.insert(target_user_id.clone());
|
||||
}
|
||||
},
|
||||
|
||||
+111
-61
@@ -6,7 +6,7 @@ use conduwuit::{
|
||||
warn,
|
||||
};
|
||||
use database::{Deserialized, Json};
|
||||
use futures::{FutureExt, Stream, StreamExt, TryFutureExt};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use lettre::Address;
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, UserId,
|
||||
@@ -29,6 +29,28 @@ pub enum AccessTokenStatus {
|
||||
Expired,
|
||||
}
|
||||
|
||||
/// The status of a user account.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AccountStatus {
|
||||
NotFound,
|
||||
Active,
|
||||
Deactivated,
|
||||
}
|
||||
|
||||
impl AccountStatus {
|
||||
pub fn is_found(&self) -> bool { !matches!(self, Self::NotFound) }
|
||||
|
||||
pub fn is_active(&self) -> bool { matches!(self, Self::Active) }
|
||||
|
||||
pub fn ensure_active(&self) -> Result<()> {
|
||||
match self {
|
||||
| Self::Active => Ok(()),
|
||||
| Self::Deactivated => Err!(Request(UserDeactivated("This account is deactivated."))),
|
||||
| Self::NotFound => Err!(Request(NotFound("This account does not exist."))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Service {
|
||||
/// Returns true/false based on whether the recipient/receiving user has
|
||||
/// ignored the sender.
|
||||
@@ -89,16 +111,16 @@ impl super::Service {
|
||||
self.services.admin.user_is_admin(user_id).await
|
||||
}
|
||||
|
||||
/// Create a new user account on this homeserver. Set the password to `None`
|
||||
/// to create a non-local user. Non-local users with a password will return
|
||||
/// an error.
|
||||
#[inline]
|
||||
pub fn create(&self, user_id: &UserId, password: Option<HashedPassword>) -> Result<()> {
|
||||
if !self.services.globals.user_is_local(user_id) && password.is_some() {
|
||||
return Err!("Cannot create a nonlocal user with a set password");
|
||||
/// Create a new shadow user account on this server. Shadow accounts
|
||||
/// have no password and cannot be logged into.
|
||||
pub async fn create_shadow_account(&self, user_id: &UserId) -> Result<()> {
|
||||
assert!(self.services.globals.user_is_local(user_id), "user id must be local");
|
||||
|
||||
if self.status(user_id).await.is_found() {
|
||||
return Err!(Request(UserInUse("An account with this user ID already exists.")));
|
||||
}
|
||||
|
||||
self.set_password(user_id, password);
|
||||
self.db.userid_password.insert(user_id, "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -109,9 +131,9 @@ impl super::Service {
|
||||
user_id: &UserId,
|
||||
password: HashedPassword,
|
||||
email: Option<Address>,
|
||||
) {
|
||||
self.create(user_id, Some(password))
|
||||
.expect("should be able to save a new local user. what happened?");
|
||||
) -> Result<()> {
|
||||
self.create_shadow_account(user_id).await?;
|
||||
self.convert_to_local_account(user_id, password).await?;
|
||||
|
||||
// Set an initial display name
|
||||
{
|
||||
@@ -230,6 +252,8 @@ impl super::Service {
|
||||
}
|
||||
|
||||
info!("Created new user account for {user_id}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn determine_registration_user_id(
|
||||
@@ -297,7 +321,7 @@ impl super::Service {
|
||||
},
|
||||
};
|
||||
|
||||
if self.exists(&user_id).await {
|
||||
if self.status(&user_id).await.is_found() {
|
||||
return Err!(Request(UserInUse("User ID is not available.")));
|
||||
}
|
||||
|
||||
@@ -329,26 +353,24 @@ impl super::Service {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if !self.exists(&user_id).await {
|
||||
if !self.status(&user_id).await.is_found() {
|
||||
break Ok(user_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deactivates an account, removing all of their device IDs and unsetting
|
||||
/// their password.
|
||||
/// Deactivates an account, removing all of their device IDs.
|
||||
pub async fn deactivate_account(&self, user_id: &UserId) -> Result<()> {
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
// Remove all associated devices
|
||||
self.all_device_ids(user_id)
|
||||
.for_each(async |device_id| self.remove_device(user_id, &device_id).await)
|
||||
.await;
|
||||
|
||||
// Set the password to "" to indicate a deactivated account. Hashes will never
|
||||
// result in an empty string, so the user will not be able to log in again.
|
||||
// Systems like changing the password without logging in should check if the
|
||||
// account is deactivated.
|
||||
self.set_password(user_id, None);
|
||||
// Mark user as deactivated
|
||||
self.db.userid_deactivated.insert(user_id, "");
|
||||
|
||||
// TODO: Unhook 3PID
|
||||
Ok(())
|
||||
@@ -393,25 +415,6 @@ impl super::Service {
|
||||
/// Unlocks an account, allowing the user to log in and use it again.
|
||||
pub async fn unlock_account(&self, user_id: &UserId) { self.db.userid_lock.remove(user_id); }
|
||||
|
||||
/// Check if the provided user ID belongs to an existing (possibly
|
||||
/// deactivated) account on this homeserver.
|
||||
#[inline]
|
||||
pub async fn exists(&self, user_id: &UserId) -> bool {
|
||||
self.services.globals.user_is_local(user_id)
|
||||
&& self.db.userid_password.get(user_id).await.is_ok()
|
||||
}
|
||||
|
||||
/// Check if account is deactivated (has an empty password). Returns a
|
||||
/// NotFound error if the user does not exist.
|
||||
pub async fn is_deactivated(&self, user_id: &UserId) -> Result<bool> {
|
||||
self.db
|
||||
.userid_password
|
||||
.get(user_id)
|
||||
.map_ok(|val| val.is_empty())
|
||||
.map_err(|_| err!(Request(NotFound("User does not exist."))))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Check if account is suspended. Returns false if the user does not exist.
|
||||
pub async fn is_suspended(&self, user_id: &UserId) -> Result<bool> {
|
||||
match self
|
||||
@@ -469,18 +472,33 @@ impl super::Service {
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Check if account is active (not deactivated)
|
||||
pub async fn is_active(&self, user_id: &UserId) -> bool {
|
||||
!self.is_deactivated(user_id).await.unwrap_or(true)
|
||||
/// Checks the activation status of the provided user ID's account.
|
||||
pub async fn status(&self, user_id: &UserId) -> AccountStatus {
|
||||
if !self.services.globals.user_is_local(user_id) {
|
||||
AccountStatus::NotFound
|
||||
} else if self.db.userid_password.exists(user_id).await.is_ok() {
|
||||
if self.db.userid_deactivated.exists(user_id).await.is_ok() {
|
||||
AccountStatus::Deactivated
|
||||
} else {
|
||||
AccountStatus::Active
|
||||
}
|
||||
} else {
|
||||
AccountStatus::NotFound
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if account is a local user, and is active (not deactivated)
|
||||
pub async fn is_active_local(&self, user_id: &UserId) -> bool {
|
||||
self.services.globals.user_is_local(user_id) && self.is_active(user_id).await
|
||||
/// Checks if a user is a shadow.
|
||||
pub async fn is_shadow(&self, user_id: &UserId) -> bool {
|
||||
self.db
|
||||
.userid_password
|
||||
.get(user_id)
|
||||
.await
|
||||
.deserialized::<String>()
|
||||
.is_ok_and(|hash| hash.is_empty())
|
||||
}
|
||||
|
||||
/// Returns the number of users registered on this server, including
|
||||
/// deactivated users.
|
||||
/// deactivated and shadow users.
|
||||
#[inline]
|
||||
pub async fn count(&self) -> usize { self.db.userid_password.count().await }
|
||||
|
||||
@@ -522,8 +540,8 @@ impl super::Service {
|
||||
Some((user_id, device_id, AccessTokenStatus::Valid))
|
||||
}
|
||||
|
||||
/// Returns an iterator over all users on this homeserver.
|
||||
pub fn stream(&self) -> impl Stream<Item = OwnedUserId> + Send {
|
||||
/// Returns an iterator over all local users on this homeserver.
|
||||
pub fn stream_local_users(&self) -> impl Stream<Item = OwnedUserId> + Send {
|
||||
self.db.userid_password.keys().ignore_err()
|
||||
}
|
||||
|
||||
@@ -540,16 +558,49 @@ impl super::Service {
|
||||
}
|
||||
|
||||
/// Set a user's password.
|
||||
pub fn set_password(&self, user_id: &UserId, password: Option<HashedPassword>) {
|
||||
if let Some(hash) = password {
|
||||
self.db.userid_password.insert(user_id, hash.0);
|
||||
} else {
|
||||
self.db.userid_password.insert(user_id, b"");
|
||||
pub async fn set_password(&self, user_id: &UserId, password: HashedPassword) -> Result {
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
self.db.userid_password.insert(user_id, password.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert an existing user to an active local account. This will turn
|
||||
/// shadow accounts into normal accounts and will clear the deactivation
|
||||
/// flag on the user if it is set.
|
||||
pub async fn convert_to_local_account(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
password: HashedPassword,
|
||||
) -> Result {
|
||||
if !self.status(user_id).await.is_found() {
|
||||
return Err!(Request(NotFound("This account does not exist.")));
|
||||
}
|
||||
|
||||
self.db.userid_deactivated.remove(user_id);
|
||||
self.db.userid_password.insert(user_id, password.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert an existing user to a shadow account. This will clear their
|
||||
/// password and reactivate them if they are deactivated.
|
||||
pub async fn convert_to_shadow_account(&self, user_id: &UserId) -> Result {
|
||||
if !self.status(user_id).await.is_found() {
|
||||
return Err!(Request(NotFound("This account does not exist.")));
|
||||
}
|
||||
|
||||
self.db.userid_deactivated.remove(user_id);
|
||||
self.db.userid_password.insert(user_id, "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check a user's password.
|
||||
pub async fn check_password(&self, user_id: &UserId, password: &str) -> Result<OwnedUserId> {
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
let (hash, user_id): (String, OwnedUserId) =
|
||||
if let Ok(hash) = self.db.userid_password.get(user_id).await.deserialized() {
|
||||
(hash, user_id.to_owned())
|
||||
@@ -558,21 +609,20 @@ impl super::Service {
|
||||
// better
|
||||
let lowercase_user_id = UserId::parse(user_id.as_str().to_lowercase()).unwrap();
|
||||
|
||||
if let Ok(hash) = self
|
||||
let hash = self
|
||||
.db
|
||||
.userid_password
|
||||
.get(lowercase_user_id.as_str())
|
||||
.await
|
||||
.deserialized()
|
||||
{
|
||||
(hash, lowercase_user_id)
|
||||
} else {
|
||||
return Err!(Request(Forbidden("This user cannot log in with a password.")));
|
||||
}
|
||||
.expect("user should exist");
|
||||
|
||||
(hash, lowercase_user_id)
|
||||
};
|
||||
|
||||
if hash.is_empty() {
|
||||
return Err!(Request(UserDeactivated("This user is deactivated")));
|
||||
// Cannot log into shadow users
|
||||
return Err!(Request(Forbidden("This user cannot log in with a password.")));
|
||||
}
|
||||
|
||||
utils::hash::verify_password(password, &hash)
|
||||
|
||||
@@ -21,10 +21,7 @@ pub struct DehydratedDevice {
|
||||
impl super::Service {
|
||||
/// Creates or recreates the user's dehydrated device.
|
||||
pub async fn set_dehydrated_device(&self, user_id: &UserId, request: Request) -> Result {
|
||||
assert!(
|
||||
self.exists(user_id).await,
|
||||
"Tried to create dehydrated device for non-existent user"
|
||||
);
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
let existing_id = self.get_dehydrated_device_id(user_id).await;
|
||||
|
||||
|
||||
+12
-21
@@ -4,8 +4,8 @@ use std::{
|
||||
};
|
||||
|
||||
use conduwuit::{
|
||||
Err, utils,
|
||||
utils::{ReadyExt, stream::TryIgnore},
|
||||
Err, Result,
|
||||
utils::{self, ReadyExt, stream::TryIgnore},
|
||||
};
|
||||
use database::{Deserialized, Ignore, Interfix, Json};
|
||||
use futures::{Stream, StreamExt};
|
||||
@@ -18,8 +18,7 @@ use serde_json::json;
|
||||
use crate::users::increment;
|
||||
|
||||
impl super::Service {
|
||||
/// Adds a new device to a user. The user must exist, otherwise InvalidParam
|
||||
/// is returned.
|
||||
/// Adds a new device to a user.
|
||||
pub async fn create_device(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
@@ -28,12 +27,8 @@ impl super::Service {
|
||||
token_max_age: Option<Duration>,
|
||||
initial_device_display_name: Option<String>,
|
||||
client_ip: Option<String>,
|
||||
) -> conduwuit::Result<()> {
|
||||
if !self.exists(user_id).await {
|
||||
return Err!(Request(InvalidParam(error!(
|
||||
"Called create_device for non-existent user {user_id}"
|
||||
))));
|
||||
}
|
||||
) -> Result<()> {
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
let key = (user_id, device_id);
|
||||
let mut device = Device::new(device_id.into());
|
||||
@@ -50,7 +45,7 @@ impl super::Service {
|
||||
/// Removes a device from a user.
|
||||
pub async fn remove_device(&self, user_id: &UserId, device_id: &DeviceId) {
|
||||
// Remove dehydrated device if this is the dehydrated device
|
||||
let _: conduwuit::Result<_> = self
|
||||
let _ = self
|
||||
.remove_dehydrated_device(user_id, Some(device_id))
|
||||
.await;
|
||||
|
||||
@@ -97,11 +92,7 @@ impl super::Service {
|
||||
}
|
||||
|
||||
/// Gets the access token associated with a device.
|
||||
pub async fn get_token(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
) -> conduwuit::Result<String> {
|
||||
pub async fn get_token(&self, user_id: &UserId, device_id: &DeviceId) -> Result<String> {
|
||||
let key = (user_id, device_id);
|
||||
self.db.userdeviceid_token.qry(&key).await.deserialized()
|
||||
}
|
||||
@@ -131,7 +122,7 @@ impl super::Service {
|
||||
device_id: &DeviceId,
|
||||
token: &str,
|
||||
token_max_age: Option<Duration>,
|
||||
) -> conduwuit::Result<()> {
|
||||
) -> Result<()> {
|
||||
let key = (user_id, device_id);
|
||||
if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
|
||||
return Err!(Database(error!(
|
||||
@@ -259,7 +250,7 @@ impl super::Service {
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
device: &Device,
|
||||
) -> conduwuit::Result<()> {
|
||||
) -> Result<()> {
|
||||
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
|
||||
self.update_device_metadata_no_increment(user_id, device_id, device)
|
||||
}
|
||||
@@ -273,7 +264,7 @@ impl super::Service {
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
device: &Device,
|
||||
) -> conduwuit::Result<()> {
|
||||
) -> Result<()> {
|
||||
let key = (user_id, device_id);
|
||||
self.db.userdeviceid_metadata.put(key, Json(device));
|
||||
|
||||
@@ -312,7 +303,7 @@ impl super::Service {
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
) -> conduwuit::Result<Device> {
|
||||
) -> Result<Device> {
|
||||
self.db
|
||||
.userdeviceid_metadata
|
||||
.qry(&(user_id, device_id))
|
||||
@@ -321,7 +312,7 @@ impl super::Service {
|
||||
}
|
||||
|
||||
/// Gets the most recent device list version for a user.
|
||||
pub async fn get_devicelist_version(&self, user_id: &UserId) -> conduwuit::Result<u64> {
|
||||
pub async fn get_devicelist_version(&self, user_id: &UserId) -> Result<u64> {
|
||||
self.db
|
||||
.userid_devicelistversion
|
||||
.get(user_id)
|
||||
|
||||
@@ -4,10 +4,11 @@ pub(super) mod device;
|
||||
pub(super) mod filters;
|
||||
pub(super) mod keys;
|
||||
pub(super) mod profile;
|
||||
pub(super) mod remote;
|
||||
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
pub use account::AccessTokenStatus;
|
||||
pub use account::{AccessTokenStatus, AccountStatus};
|
||||
use conduwuit::{
|
||||
Err, Error, Result, err,
|
||||
utils::{self},
|
||||
@@ -75,11 +76,13 @@ struct Data {
|
||||
logintoken_expiresatuserid: Arc<Map>,
|
||||
todeviceid_events: Arc<Map>,
|
||||
token_userdeviceid: Arc<Map>,
|
||||
remoteuserid_remoteuser: Arc<Map>,
|
||||
userdeviceid_tokenexpires: Arc<Map>,
|
||||
userdeviceid_metadata: Arc<Map>,
|
||||
userdeviceid_token: Arc<Map>,
|
||||
userfilterid_filter: Arc<Map>,
|
||||
userid_avatarurl: Arc<Map>,
|
||||
userid_deactivated: Arc<Map>,
|
||||
userid_dehydrateddevice: Arc<Map>,
|
||||
userid_devicelistversion: Arc<Map>,
|
||||
userid_displayname: Arc<Map>,
|
||||
@@ -121,10 +124,12 @@ impl crate::Service for Service {
|
||||
logintoken_expiresatuserid: args.db["logintoken_expiresatuserid"].clone(),
|
||||
todeviceid_events: args.db["todeviceid_events"].clone(),
|
||||
token_userdeviceid: args.db["token_userdeviceid"].clone(),
|
||||
remoteuserid_remoteuser: args.db["remoteuserid_remoteuser"].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_deactivated: args.db["userid_deactivated"].clone(),
|
||||
userid_dehydrateddevice: args.db["userid_dehydrateddevice"].clone(),
|
||||
userid_devicelistversion: args.db["userid_devicelistversion"].clone(),
|
||||
userid_displayname: args.db["userid_displayname"].clone(),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use conduwuit::utils::stream::TryIgnore;
|
||||
use futures::Stream;
|
||||
use ruma::{OwnedUserId, UserId};
|
||||
|
||||
impl super::Service {
|
||||
/// Record the existence of a remote user.
|
||||
pub fn record_remote_user(&self, user_id: &UserId) {
|
||||
assert!(self.services.globals.user_is_local(user_id), "user is not remote");
|
||||
|
||||
self.db.remoteuserid_remoteuser.insert(user_id, "");
|
||||
}
|
||||
|
||||
/// Returns a stream over all remote users this server has ever seen.
|
||||
pub fn stream_remote_users(&self) -> impl Stream<Item = OwnedUserId> + Send {
|
||||
self.db.remoteuserid_remoteuser.keys().ignore_err()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user