Skip to content
Snippets Groups Projects
membership.rs 49.6 KiB
Newer Older
use std::{
	collections::{hash_map::Entry, BTreeMap, HashMap, HashSet},
	time::{Duration, Instant},
};

use ruma::{
	api::{
		client::{
			error::ErrorKind,
			membership::{
				ban_user, forget_room, get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias,
				joined_members, joined_rooms, kick_user, leave_room, unban_user, ThirdPartySigned,
			},
		},
		federation::{self, membership::create_invite},
	},
	canonical_json::to_canonical_value,
	events::{
		room::{
			join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
			member::{MembershipState, RoomMemberEventContent},
			message::RoomMessageEventContent,
		},
		StateEventType, TimelineEventType,
	},
	serde::Base64,
	state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
	OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
Jonas Platte's avatar
Jonas Platte committed
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use tokio::sync::{MutexGuard, RwLock};
use tracing::{debug, error, info, trace, warn};
use super::get_alias_helper;
Timo Kösters's avatar
Timo Kösters committed
use crate::{
Jason Volk's avatar
Jason Volk committed
	service::{
		pdu::{gen_event_id_canonical_json, PduBuilder},
		server_is_ours, user_is_local,
	},
Jason Volk's avatar
Jason Volk committed
	utils::{self},
	Error, PduEvent, Result, Ruma,
Timo Kösters's avatar
Timo Kösters committed
};
/// 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;
					if let Err(e) = services().users.deactivate_account(user_id) {
						warn!(%e, "Failed to deactivate account");
					}
				}

				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;
					if let Err(e) = services().users.deactivate_account(user_id) {
						warn!(%e, "Failed to deactivate account");
					}
				}

				return Err(Error::BadRequest(
					ErrorKind::forbidden(),
					"This remote server is banned on this homeserver.",
				));
			}
		}
	}

	Ok(())
}

/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
///
/// Tries to join the sender user into a room.
///
/// - If the server knowns about this room: creates the join event and does auth
///   rules locally
/// - If the server does not know about the room: asks other servers over
///   federation
pub(crate) async fn join_room_by_id_route(
	body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
	let sender_user = body.sender_user.as_ref().expect("user is authenticated");

	banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name()).await?;
🥺's avatar
🥺 committed
	// There is no body.server_name for /roomId/join
	let mut servers = services()
		.rooms
		.state_cache
🥺's avatar
🥺 committed
		.servers_invite_via(&body.room_id)?
		.unwrap_or(
			services()
				.rooms
				.state_cache
				.invite_state(sender_user, &body.room_id)?
				.unwrap_or_default()
				.iter()
				.filter_map(|event| serde_json::from_str(event.json().get()).ok())
				.filter_map(|event: serde_json::Value| event.get("sender").cloned())
				.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
				.filter_map(|sender| UserId::parse(sender).ok())
				.map(|user| user.server_name().to_owned())
				.collect::<Vec<_>>(),
		);

	if let Some(server) = body.room_id.server_name() {
		servers.push(server.into());
	}

	join_room_by_id_helper(
		body.sender_user.as_deref(),
		&body.room_id,
		body.reason.clone(),
		&servers,
		body.third_party_signed.as_ref(),
	)
	.await
/// # `POST /_matrix/client/r0/join/{roomIdOrAlias}`
///
/// Tries to join the sender user into a room.
///
/// - If the server knowns about this room: creates the join event and does auth
///   rules locally
/// - If the server does not know about the room: use the server name query
///   param if specified. if not specified, asks other servers over federation
///   via room alias server name and room ID server name
pub(crate) async fn join_room_by_id_or_alias_route(
	body: Ruma<join_room_by_id_or_alias::v3::Request>,
Jonathan de Jong's avatar
Jonathan de Jong committed
) -> Result<join_room_by_id_or_alias::v3::Response> {
	let sender_user = body.sender_user.as_deref().expect("user is authenticated");
	let body = body.body;

	let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
Loading
Loading full blame...