You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-09 04:22:45 +03:00
Add a way to reactivate users on the homeserver
This commit is contained in:
@@ -27,7 +27,9 @@ use mas_matrix::HomeserverConnection;
|
||||
use mas_matrix_synapse::SynapseConnection;
|
||||
use mas_storage::{
|
||||
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
||||
job::{DeactivateUserJob, JobRepositoryExt, ProvisionUserJob, SyncDevicesJob},
|
||||
job::{
|
||||
DeactivateUserJob, JobRepositoryExt, ProvisionUserJob, ReactivateUserJob, SyncDevicesJob,
|
||||
},
|
||||
user::{UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||
Clock, RepositoryAccess, SystemClock,
|
||||
};
|
||||
@@ -488,9 +490,11 @@ impl Options {
|
||||
.await?
|
||||
.context("User not found")?;
|
||||
|
||||
info!(%user.id, "Unlocking user");
|
||||
warn!(%user.id, "User scheduling user reactivation");
|
||||
repo.job()
|
||||
.schedule_job(ReactivateUserJob::new(&user))
|
||||
.await?;
|
||||
|
||||
repo.user().unlock(user).await?;
|
||||
repo.into_inner().commit().await?;
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -131,6 +131,9 @@ struct SynapseUser {
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
external_ids: Option<Vec<ExternalID>>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
deactivated: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -539,6 +542,50 @@ impl HomeserverConnection for SynapseConnection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "homeserver.reactivate_user",
|
||||
skip_all,
|
||||
fields(
|
||||
matrix.homeserver = self.homeserver,
|
||||
matrix.mxid = mxid,
|
||||
),
|
||||
err(Debug),
|
||||
)]
|
||||
async fn reactivate_user(&self, mxid: &str) -> Result<(), anyhow::Error> {
|
||||
let body = SynapseUser {
|
||||
deactivated: Some(false),
|
||||
..SynapseUser::default()
|
||||
};
|
||||
|
||||
let mut client = self
|
||||
.http_client_factory
|
||||
.client("homeserver.reactivate_user")
|
||||
.request_bytes_to_body()
|
||||
.json_request()
|
||||
.response_body_to_bytes()
|
||||
.catch_http_errors(catch_homeserver_error);
|
||||
|
||||
let mxid = urlencoding::encode(mxid);
|
||||
let request = self
|
||||
.put(&format!("_synapse/admin/v2/users/{mxid}"))
|
||||
.body(body)?;
|
||||
|
||||
let response = client
|
||||
.ready()
|
||||
.await?
|
||||
.call(request)
|
||||
.await
|
||||
.context("Failed to provision user in Synapse")?;
|
||||
|
||||
match response.status() {
|
||||
StatusCode::CREATED | StatusCode::OK => Ok(()),
|
||||
code => Err(anyhow::anyhow!(
|
||||
"Failed to provision user in Synapse: {}",
|
||||
code
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "homeserver.set_displayname",
|
||||
skip_all,
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -288,6 +288,18 @@ pub trait HomeserverConnection: Send + Sync {
|
||||
/// be deleted.
|
||||
async fn delete_user(&self, mxid: &str, erase: bool) -> Result<(), Self::Error>;
|
||||
|
||||
/// Reactivate a user on the homeserver.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `mxid` - The Matrix ID of the user to reactivate.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the homeserver is unreachable or the user could not
|
||||
/// be reactivated.
|
||||
async fn reactivate_user(&self, mxid: &str) -> Result<(), Self::Error>;
|
||||
|
||||
/// Set the displayname of a user on the homeserver.
|
||||
///
|
||||
/// # Parameters
|
||||
@@ -362,6 +374,10 @@ impl<T: HomeserverConnection + Send + Sync + ?Sized> HomeserverConnection for &T
|
||||
(**self).delete_user(mxid, erase).await
|
||||
}
|
||||
|
||||
async fn reactivate_user(&self, mxid: &str) -> Result<(), Self::Error> {
|
||||
(**self).reactivate_user(mxid).await
|
||||
}
|
||||
|
||||
async fn set_displayname(&self, mxid: &str, displayname: &str) -> Result<(), Self::Error> {
|
||||
(**self).set_displayname(mxid, displayname).await
|
||||
}
|
||||
@@ -412,6 +428,10 @@ impl<T: HomeserverConnection + ?Sized> HomeserverConnection for Arc<T> {
|
||||
(**self).delete_user(mxid, erase).await
|
||||
}
|
||||
|
||||
async fn reactivate_user(&self, mxid: &str) -> Result<(), Self::Error> {
|
||||
(**self).reactivate_user(mxid).await
|
||||
}
|
||||
|
||||
async fn set_displayname(&self, mxid: &str, displayname: &str) -> Result<(), Self::Error> {
|
||||
(**self).set_displayname(mxid, displayname).await
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -148,6 +148,10 @@ impl crate::HomeserverConnection for HomeserverConnection {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reactivate_user(&self, _mxid: &str) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_displayname(&self, mxid: &str, displayname: &str) -> Result<(), Self::Error> {
|
||||
let mut users = self.users.write().await;
|
||||
let user = users.get_mut(mxid).context("User not found")?;
|
||||
|
@@ -455,6 +455,34 @@ mod jobs {
|
||||
const NAME: &'static str = "deactivate-user";
|
||||
}
|
||||
|
||||
/// A job to reactivate a user
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ReactivateUserJob {
|
||||
user_id: Ulid,
|
||||
}
|
||||
|
||||
impl ReactivateUserJob {
|
||||
/// Create a new job to reactivate a user
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `user` - The user to reactivate
|
||||
#[must_use]
|
||||
pub fn new(user: &User) -> Self {
|
||||
Self { user_id: user.id }
|
||||
}
|
||||
|
||||
/// The ID of the user to reactivate
|
||||
#[must_use]
|
||||
pub fn user_id(&self) -> Ulid {
|
||||
self.user_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Job for ReactivateUserJob {
|
||||
const NAME: &'static str = "reactivate-user";
|
||||
}
|
||||
|
||||
/// Send account recovery emails
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SendAccountRecoveryEmailsJob {
|
||||
@@ -489,6 +517,6 @@ mod jobs {
|
||||
}
|
||||
|
||||
pub use self::jobs::{
|
||||
DeactivateUserJob, DeleteDeviceJob, ProvisionDeviceJob, ProvisionUserJob,
|
||||
DeactivateUserJob, DeleteDeviceJob, ProvisionDeviceJob, ProvisionUserJob, ReactivateUserJob,
|
||||
SendAccountRecoveryEmailsJob, SyncDevicesJob, VerifyEmailJob,
|
||||
};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
use anyhow::Context;
|
||||
use apalis_core::{context::JobContext, executor::TokioExecutor, monitor::Monitor};
|
||||
use mas_storage::{
|
||||
job::{DeactivateUserJob, JobWithSpanContext},
|
||||
job::{DeactivateUserJob, JobWithSpanContext, ReactivateUserJob},
|
||||
user::UserRepository,
|
||||
RepositoryAccess,
|
||||
};
|
||||
@@ -54,7 +54,8 @@ async fn deactivate_user(
|
||||
|
||||
// TODO: delete the sessions & access tokens
|
||||
|
||||
// Before calling back to the homeserver, commit the changes to the database
|
||||
// Before calling back to the homeserver, commit the changes to the database, as
|
||||
// we want the user to be locked out as soon as possible
|
||||
repo.save().await?;
|
||||
|
||||
let mxid = matrix.mxid(&user.username);
|
||||
@@ -64,6 +65,39 @@ async fn deactivate_user(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Job to reactivate a user, both locally and on the Matrix homeserver.
|
||||
#[tracing::instrument(
|
||||
name = "job.reactivate_user",
|
||||
fields(user.id = %job.user_id()),
|
||||
skip_all,
|
||||
err(Debug),
|
||||
)]
|
||||
pub async fn reactivate_user(
|
||||
job: JobWithSpanContext<ReactivateUserJob>,
|
||||
ctx: JobContext,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let state = ctx.state();
|
||||
let matrix = state.matrix_connection();
|
||||
let mut repo = state.repository().await?;
|
||||
|
||||
let user = repo
|
||||
.user()
|
||||
.lookup(job.user_id())
|
||||
.await?
|
||||
.context("User not found")?;
|
||||
|
||||
let mxid = matrix.mxid(&user.username);
|
||||
info!("Reactivating user {} on homeserver", mxid);
|
||||
matrix.reactivate_user(&mxid).await?;
|
||||
|
||||
// We want to unlock the user from our side only once it has been reactivated on
|
||||
// the homeserver
|
||||
let _user = repo.user().unlock(user).await?;
|
||||
repo.save().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn register(
|
||||
suffix: &str,
|
||||
monitor: Monitor<TokioExecutor>,
|
||||
@@ -73,5 +107,10 @@ pub(crate) fn register(
|
||||
let deactivate_user_worker =
|
||||
crate::build!(DeactivateUserJob => deactivate_user, suffix, state, storage_factory);
|
||||
|
||||
monitor.register(deactivate_user_worker)
|
||||
let reactivate_user_worker =
|
||||
crate::build!(ReactivateUserJob => reactivate_user, suffix, state, storage_factory);
|
||||
|
||||
monitor
|
||||
.register(deactivate_user_worker)
|
||||
.register(reactivate_user_worker)
|
||||
}
|
||||
|
Reference in New Issue
Block a user