config option to auto-remediate bad users joining bad rooms or servers
also forgets all rooms upon leave_all_rooms Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
6946eead28
commit
d15e461303
5 changed files with 117 additions and 144 deletions
|
@ -269,6 +269,19 @@ url_preview_check_root_domain = false
|
||||||
# Defaults to true
|
# Defaults to true
|
||||||
allow_profile_lookup_federation_requests = true
|
allow_profile_lookup_federation_requests = true
|
||||||
|
|
||||||
|
# Config option to automatically deactivate the account of any user who attempts to join a:
|
||||||
|
# - banned room
|
||||||
|
# - forbidden room alias
|
||||||
|
# - room alias or ID with a forbidden server name
|
||||||
|
#
|
||||||
|
# This may be useful if all your banned lists consist of toxic rooms or servers that no good faith user would ever attempt to join, and
|
||||||
|
# to automatically remediate the problem without any admin user intervention.
|
||||||
|
#
|
||||||
|
# This will also make the user leave all rooms. Federation (e.g. remote room invites) are ignored here.
|
||||||
|
#
|
||||||
|
# Defaults to false as rooms can be banned for non-moderation-related reasons
|
||||||
|
#auto_deactivate_banned_room_attempts = false
|
||||||
|
|
||||||
|
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
|
|
|
@ -533,7 +533,7 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the user leave all rooms before deactivation
|
// Make the user leave all rooms before deactivation
|
||||||
client_server::leave_all_rooms(sender_user).await?;
|
client_server::leave_all_rooms(sender_user).await;
|
||||||
|
|
||||||
// Remove devices and mark account as deactivated
|
// Remove devices and mark account as deactivated
|
||||||
services().users.deactivate_account(sender_user)?;
|
services().users.deactivate_account(sender_user)?;
|
||||||
|
|
|
@ -21,12 +21,13 @@ use ruma::{
|
||||||
room::{
|
room::{
|
||||||
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
|
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
|
||||||
member::{MembershipState, RoomMemberEventContent},
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
|
message::RoomMessageEventContent,
|
||||||
},
|
},
|
||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
serde::Base64,
|
serde::Base64,
|
||||||
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||||
OwnedUserId, RoomId, RoomVersionId, UserId,
|
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
@ -40,6 +41,91 @@ use crate::{
|
||||||
Error, PduEvent, Result, Ruma,
|
Error, PduEvent, Result, Ruma,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Checks if the room is banned in any way possible and the sender user is not
|
||||||
|
/// an admin.
|
||||||
|
///
|
||||||
|
/// Performs automatic deactivation if `auto_deactivate_banned_room_attempts` is
|
||||||
|
/// enabled
|
||||||
|
#[tracing::instrument]
|
||||||
|
async fn banned_room_check(user_id: &UserId, room_id: Option<&RoomId>, server_name: Option<&ServerName>) -> Result<()> {
|
||||||
|
if !services().users.is_admin(user_id)? {
|
||||||
|
if let Some(room_id) = room_id {
|
||||||
|
if services().rooms.metadata.is_banned(room_id)?
|
||||||
|
|| services()
|
||||||
|
.globals
|
||||||
|
.config
|
||||||
|
.forbidden_remote_server_names
|
||||||
|
.contains(&room_id.server_name().unwrap().to_owned())
|
||||||
|
{
|
||||||
|
warn!(
|
||||||
|
"User {user_id} who is not an admin attempted to send an invite for or attempted to join a banned \
|
||||||
|
room or banned room server name: {room_id}."
|
||||||
|
);
|
||||||
|
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.config
|
||||||
|
.auto_deactivate_banned_room_attempts
|
||||||
|
{
|
||||||
|
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
||||||
|
services()
|
||||||
|
.admin
|
||||||
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// ignore errors
|
||||||
|
leave_all_rooms(user_id).await;
|
||||||
|
_ = services().users.deactivate_account(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"This room is banned on this homeserver.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if let Some(server_name) = server_name {
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.config
|
||||||
|
.forbidden_remote_server_names
|
||||||
|
.contains(&server_name.to_owned())
|
||||||
|
{
|
||||||
|
warn!(
|
||||||
|
"User {user_id} who is not an admin tried joining a room which has the server name {server_name} \
|
||||||
|
that is globally forbidden. Rejecting.",
|
||||||
|
);
|
||||||
|
|
||||||
|
if services()
|
||||||
|
.globals
|
||||||
|
.config
|
||||||
|
.auto_deactivate_banned_room_attempts
|
||||||
|
{
|
||||||
|
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
||||||
|
services()
|
||||||
|
.admin
|
||||||
|
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||||
|
)))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// ignore errors
|
||||||
|
leave_all_rooms(user_id).await;
|
||||||
|
_ = services().users.deactivate_account(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::forbidden(),
|
||||||
|
"This remote server is banned on this homeserver.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
||||||
///
|
///
|
||||||
/// Tries to join the sender user into a room.
|
/// Tries to join the sender user into a room.
|
||||||
|
@ -53,32 +139,7 @@ pub(crate) async fn join_room_by_id_route(
|
||||||
) -> Result<join_room_by_id::v3::Response> {
|
) -> Result<join_room_by_id::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
|
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This room is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(server) = body.room_id.server_name() {
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&server.to_owned())
|
|
||||||
&& !services().users.is_admin(sender_user)?
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"User {sender_user} tried joining room ID {} which has a server name that is globally forbidden. \
|
|
||||||
Rejecting.",
|
|
||||||
body.room_id
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This remote server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is no body.server_name for /roomId/join
|
// There is no body.server_name for /roomId/join
|
||||||
let mut servers = services()
|
let mut servers = services()
|
||||||
|
@ -131,31 +192,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
|
|
||||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
||||||
Ok(room_id) => {
|
Ok(room_id) => {
|
||||||
if services().rooms.metadata.is_banned(&room_id)? && !services().users.is_admin(sender_user)? {
|
banned_room_check(sender_user, Some(&room_id), room_id.server_name()).await?;
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This room is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(server) = room_id.server_name() {
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&server.to_owned())
|
|
||||||
&& !services().users.is_admin(sender_user)?
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"User {sender_user} tried joining room ID {room_id} which has a server name that is globally \
|
|
||||||
forbidden. Rejecting.",
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This remote server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut servers = body.server_name.clone();
|
let mut servers = body.server_name.clone();
|
||||||
servers.extend(
|
servers.extend(
|
||||||
|
@ -186,69 +223,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||||
(servers, room_id)
|
(servers, room_id)
|
||||||
},
|
},
|
||||||
Err(room_alias) => {
|
Err(room_alias) => {
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&room_alias.server_name().to_owned())
|
|
||||||
&& !services().users.is_admin(sender_user)?
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"User {sender_user} tried joining room alias {room_alias} which has a server name that is \
|
|
||||||
globally forbidden. Rejecting.",
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This remote server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
||||||
|
|
||||||
if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? {
|
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name())).await?;
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This room is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&room_alias.server_name().to_owned())
|
|
||||||
&& !services().users.is_admin(sender_user)?
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which the alias has a \
|
|
||||||
server name that is globally forbidden. Rejecting.",
|
|
||||||
&response.room_id
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This remote server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(server) = response.room_id.server_name() {
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&server.to_owned())
|
|
||||||
&& !services().users.is_admin(sender_user)?
|
|
||||||
{
|
|
||||||
warn!(
|
|
||||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which has a server \
|
|
||||||
name that is globally forbidden. Rejecting.",
|
|
||||||
&response.room_id
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This remote server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut servers = body.server_name;
|
let mut servers = body.server_name;
|
||||||
servers.extend(response.servers);
|
servers.extend(response.servers);
|
||||||
|
@ -321,30 +298,7 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
|
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
|
||||||
info!(
|
|
||||||
"Local user {} who is not an admin attempted to send an invite for banned room {}.",
|
|
||||||
&sender_user, &body.room_id
|
|
||||||
);
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"This room is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(server) = body.room_id.server_name() {
|
|
||||||
if services()
|
|
||||||
.globals
|
|
||||||
.config
|
|
||||||
.forbidden_remote_server_names
|
|
||||||
.contains(&server.to_owned())
|
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::forbidden(),
|
|
||||||
"Server is banned on this homeserver.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let invite_user::v3::InvitationRecipient::UserId {
|
if let invite_user::v3::InvitationRecipient::UserId {
|
||||||
user_id,
|
user_id,
|
||||||
|
@ -1606,8 +1560,9 @@ pub(crate) async fn invite_helper(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a user leave all their joined rooms
|
// Make a user leave all their joined rooms, forgets all rooms, and ignores
|
||||||
pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
// errors
|
||||||
|
pub(crate) async fn leave_all_rooms(user_id: &UserId) {
|
||||||
let all_rooms = services()
|
let all_rooms = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
|
@ -1627,10 +1582,9 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ignore errors
|
// ignore errors
|
||||||
|
_ = services().rooms.state_cache.forget(&room_id, user_id);
|
||||||
_ = leave_room(user_id, &room_id, None).await;
|
_ = leave_room(user_id, &room_id, None).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||||
|
|
|
@ -207,6 +207,8 @@ pub(crate) struct Config {
|
||||||
|
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
pub(crate) auto_join_rooms: Vec<OwnedRoomId>,
|
pub(crate) auto_join_rooms: Vec<OwnedRoomId>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) auto_deactivate_banned_room_attempts: bool,
|
||||||
|
|
||||||
#[serde(default = "default_rocksdb_log_level")]
|
#[serde(default = "default_rocksdb_log_level")]
|
||||||
pub(crate) rocksdb_log_level: String,
|
pub(crate) rocksdb_log_level: String,
|
||||||
|
@ -612,6 +614,10 @@ impl fmt::Display for Config {
|
||||||
"Allow incoming profile lookup federation requests",
|
"Allow incoming profile lookup federation requests",
|
||||||
&self.allow_profile_lookup_federation_requests.to_string(),
|
&self.allow_profile_lookup_federation_requests.to_string(),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"Auto deactivate banned room join attempts",
|
||||||
|
&self.auto_deactivate_banned_room_attempts.to_string(),
|
||||||
|
),
|
||||||
("Notification push path", &self.notification_push_path),
|
("Notification push path", &self.notification_push_path),
|
||||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||||
(
|
(
|
||||||
|
|
|
@ -167,7 +167,7 @@ pub(crate) async fn deactivate(
|
||||||
services().users.deactivate_account(&user_id)?;
|
services().users.deactivate_account(&user_id)?;
|
||||||
|
|
||||||
if leave_rooms {
|
if leave_rooms {
|
||||||
leave_all_rooms(&user_id).await?;
|
leave_all_rooms(&user_id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain(format!(
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
@ -282,7 +282,7 @@ pub(crate) async fn deactivate_all(body: Vec<&str>, leave_rooms: bool, force: bo
|
||||||
|
|
||||||
if leave_rooms {
|
if leave_rooms {
|
||||||
for &user_id in &user_ids {
|
for &user_id in &user_ids {
|
||||||
_ = leave_all_rooms(user_id).await;
|
leave_all_rooms(user_id).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue