mirror of
https://gitlab.com/famedly/conduit.git
synced 2024-11-04 17:29:14 +01:00
Merge branch 'deactivate-user-command' into 'next'
Deactivate user command See merge request famedly/conduit!337
This commit is contained in:
commit
9ee199b0c3
3 changed files with 156 additions and 60 deletions
|
@ -4,7 +4,7 @@ use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{admin::make_user_admin, DatabaseGuard},
|
database::{admin::make_user_admin, DatabaseGuard},
|
||||||
pdu::PduBuilder,
|
pdu::PduBuilder,
|
||||||
utils, Error, Result, Ruma,
|
utils, Database, Error, Result, Ruma,
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
|
@ -398,55 +398,8 @@ pub async fn deactivate_route(
|
||||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave all joined rooms and reject all invitations
|
// Make the user leave all rooms before deactivation
|
||||||
// TODO: work over federation invites
|
db.rooms.leave_all_rooms(&sender_user, &db).await?;
|
||||||
let all_rooms = db
|
|
||||||
.rooms
|
|
||||||
.rooms_joined(sender_user)
|
|
||||||
.chain(
|
|
||||||
db.rooms
|
|
||||||
.rooms_invited(sender_user)
|
|
||||||
.map(|t| t.map(|(r, _)| r)),
|
|
||||||
)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for room_id in all_rooms {
|
|
||||||
let room_id = room_id?;
|
|
||||||
let event = RoomMemberEventContent {
|
|
||||||
membership: MembershipState::Leave,
|
|
||||||
displayname: None,
|
|
||||||
avatar_url: None,
|
|
||||||
is_direct: None,
|
|
||||||
third_party_invite: None,
|
|
||||||
blurhash: None,
|
|
||||||
reason: None,
|
|
||||||
join_authorized_via_users_server: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mutex_state = Arc::clone(
|
|
||||||
db.globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.entry(room_id.clone())
|
|
||||||
.or_default(),
|
|
||||||
);
|
|
||||||
let state_lock = mutex_state.lock().await;
|
|
||||||
|
|
||||||
db.rooms.build_and_append_pdu(
|
|
||||||
PduBuilder {
|
|
||||||
event_type: RoomEventType::RoomMember,
|
|
||||||
content: to_raw_value(&event).expect("event is valid, we just created it"),
|
|
||||||
unsigned: None,
|
|
||||||
state_key: Some(sender_user.to_string()),
|
|
||||||
redacts: None,
|
|
||||||
},
|
|
||||||
sender_user,
|
|
||||||
&room_id,
|
|
||||||
&db,
|
|
||||||
&state_lock,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove devices and mark account as deactivated
|
// Remove devices and mark account as deactivated
|
||||||
db.users.deactivate_account(sender_user)?;
|
db.users.deactivate_account(sender_user)?;
|
||||||
|
|
|
@ -101,6 +101,12 @@ impl Admin {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
Some(event) = receiver.recv() => {
|
Some(event) = receiver.recv() => {
|
||||||
let guard = db.read().await;
|
let guard = db.read().await;
|
||||||
|
|
||||||
|
let message_content = match event {
|
||||||
|
AdminRoomEvent::SendMessage(content) => content,
|
||||||
|
AdminRoomEvent::ProcessMessage(room_message) => process_admin_message(&*guard, room_message).await
|
||||||
|
};
|
||||||
|
|
||||||
let mutex_state = Arc::clone(
|
let mutex_state = Arc::clone(
|
||||||
guard.globals
|
guard.globals
|
||||||
.roomid_mutex_state
|
.roomid_mutex_state
|
||||||
|
@ -109,18 +115,10 @@ impl Admin {
|
||||||
.entry(conduit_room.clone())
|
.entry(conduit_room.clone())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
match event {
|
send_message(message_content, guard, &state_lock);
|
||||||
AdminRoomEvent::SendMessage(content) => {
|
|
||||||
send_message(content, guard, &state_lock);
|
|
||||||
}
|
|
||||||
AdminRoomEvent::ProcessMessage(room_message) => {
|
|
||||||
let reply_message = process_admin_message(&*guard, room_message).await;
|
|
||||||
|
|
||||||
send_message(reply_message, guard, &state_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
}
|
}
|
||||||
|
@ -240,6 +238,39 @@ enum AdminCommand {
|
||||||
/// List all rooms we are currently handling an incoming pdu from
|
/// List all rooms we are currently handling an incoming pdu from
|
||||||
IncomingFederation,
|
IncomingFederation,
|
||||||
|
|
||||||
|
/// Deactivate a user
|
||||||
|
///
|
||||||
|
/// User will not be removed from all rooms by default.
|
||||||
|
/// Use --leave-rooms to force the user to leave all rooms
|
||||||
|
DeactivateUser {
|
||||||
|
#[clap(short, long)]
|
||||||
|
leave_rooms: bool,
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[clap(verbatim_doc_comment)]
|
||||||
|
/// Deactivate a list of users
|
||||||
|
///
|
||||||
|
/// Recommended to use in conjunction with list-local-users.
|
||||||
|
///
|
||||||
|
/// Users will not be removed from joined rooms by default.
|
||||||
|
/// Can be overridden with --leave-rooms flag.
|
||||||
|
/// Removing a mass amount of users from a room may cause a significant amount of leave events.
|
||||||
|
/// The time to leave rooms may depend significantly on joined rooms and servers.
|
||||||
|
///
|
||||||
|
/// [commandbody]
|
||||||
|
/// # ```
|
||||||
|
/// # User list here
|
||||||
|
/// # ```
|
||||||
|
DeactivateAll {
|
||||||
|
#[clap(short, long)]
|
||||||
|
/// Remove users from their joined rooms
|
||||||
|
leave_rooms: bool,
|
||||||
|
#[clap(short, long)]
|
||||||
|
/// Also deactivate admin accounts
|
||||||
|
force: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// Get the auth_chain of a PDU
|
/// Get the auth_chain of a PDU
|
||||||
GetAuthChain {
|
GetAuthChain {
|
||||||
/// An event ID (the $ character followed by the base64 reference hash)
|
/// An event ID (the $ character followed by the base64 reference hash)
|
||||||
|
@ -603,6 +634,97 @@ async fn process_admin_command(
|
||||||
db.rooms.disabledroomids.remove(room_id.as_bytes())?;
|
db.rooms.disabledroomids.remove(room_id.as_bytes())?;
|
||||||
RoomMessageEventContent::text_plain("Room enabled.")
|
RoomMessageEventContent::text_plain("Room enabled.")
|
||||||
}
|
}
|
||||||
|
AdminCommand::DeactivateUser {
|
||||||
|
leave_rooms,
|
||||||
|
user_id,
|
||||||
|
} => {
|
||||||
|
let user_id = Arc::<UserId>::from(user_id);
|
||||||
|
if db.users.exists(&user_id)? {
|
||||||
|
RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Making {} leave all rooms before deactivation...",
|
||||||
|
user_id
|
||||||
|
));
|
||||||
|
|
||||||
|
db.users.deactivate_account(&user_id)?;
|
||||||
|
|
||||||
|
if leave_rooms {
|
||||||
|
db.rooms.leave_all_rooms(&user_id, &db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomMessageEventContent::text_plain(format!(
|
||||||
|
"User {} has been deactivated",
|
||||||
|
user_id
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
RoomMessageEventContent::text_plain(format!(
|
||||||
|
"User {} doesn't exist on this server",
|
||||||
|
user_id
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdminCommand::DeactivateAll { leave_rooms, force } => {
|
||||||
|
if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```" {
|
||||||
|
let usernames = body.clone().drain(1..body.len() - 1).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut user_ids: Vec<&UserId> = Vec::new();
|
||||||
|
|
||||||
|
for &username in &usernames {
|
||||||
|
match <&UserId>::try_from(username) {
|
||||||
|
Ok(user_id) => user_ids.push(user_id),
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"{} is not a valid username",
|
||||||
|
username
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut deactivation_count = 0;
|
||||||
|
let mut admins = Vec::new();
|
||||||
|
|
||||||
|
if !force {
|
||||||
|
user_ids.retain(|&user_id| {
|
||||||
|
match db.users.is_admin(user_id, &db.rooms, &db.globals) {
|
||||||
|
Ok(is_admin) => match is_admin {
|
||||||
|
true => {
|
||||||
|
admins.push(user_id.localpart());
|
||||||
|
false
|
||||||
|
}
|
||||||
|
false => true,
|
||||||
|
},
|
||||||
|
Err(_) => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for &user_id in &user_ids {
|
||||||
|
match db.users.deactivate_account(user_id) {
|
||||||
|
Ok(_) => deactivation_count += 1,
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if leave_rooms {
|
||||||
|
for &user_id in &user_ids {
|
||||||
|
let _ = db.rooms.leave_all_rooms(user_id, &db).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if admins.is_empty() {
|
||||||
|
RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Deactivated {} accounts.",
|
||||||
|
deactivation_count
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
RoomMessageEventContent::text_plain(format!("Deactivated {} accounts.\nSkipped admin accounts: {:?}. Use --force to deactivate admin accounts", deactivation_count, admins.join(", ")))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RoomMessageEventContent::text_plain(
|
||||||
|
"Expected code block in command body. Add --help for details.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(reply_message_content)
|
Ok(reply_message_content)
|
||||||
|
|
|
@ -2569,6 +2569,27 @@ impl Rooms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make a user leave all their joined rooms
|
||||||
|
#[tracing::instrument(skip(self, db))]
|
||||||
|
pub async fn leave_all_rooms(&self, user_id: &UserId, db: &Database) -> Result<()> {
|
||||||
|
let all_rooms = db
|
||||||
|
.rooms
|
||||||
|
.rooms_joined(user_id)
|
||||||
|
.chain(db.rooms.rooms_invited(user_id).map(|t| t.map(|(r, _)| r)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for room_id in all_rooms {
|
||||||
|
let room_id = match room_id {
|
||||||
|
Ok(room_id) => room_id,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = self.leave_room(user_id, &room_id, db).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, db))]
|
#[tracing::instrument(skip(self, db))]
|
||||||
pub async fn leave_room(
|
pub async fn leave_room(
|
||||||
&self,
|
&self,
|
||||||
|
|
Loading…
Reference in a new issue