fix: Apply spam checker to local restricted joins

This commit is contained in:
timedout
2026-01-05 20:10:29 +00:00
committed by Jade Ellis
parent 76865e6f91
commit 4b873a1b95
3 changed files with 78 additions and 49 deletions
+64 -45
View File
@@ -33,7 +33,7 @@ use ruma::{
events::{ events::{
StateEventType, StateEventType,
room::{ room::{
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent}, join_rules::{AllowRule, JoinRule},
member::{MembershipState, RoomMemberEventContent}, member::{MembershipState, RoomMemberEventContent},
}, },
}, },
@@ -338,6 +338,17 @@ pub async fn join_room_by_id_helper(
))); )));
} }
if services.antispam.check_all_joins() {
if let Err(e) = services
.antispam
.meowlnir_accept_make_join(room_id.to_owned(), sender_user.to_owned())
.await
{
warn!("Antispam prevented user {} from joining room {}: {}", sender_user, room_id, e);
return Err!(Request(Forbidden("Antispam rejected join request.")));
};
}
if server_in_room { if server_in_room {
join_room_by_id_helper_local( join_room_by_id_helper_local(
services, services,
@@ -736,45 +747,51 @@ async fn join_room_by_id_helper_local(
state_lock: RoomMutexGuard, state_lock: RoomMutexGuard,
) -> Result { ) -> Result {
debug_info!("We can join locally"); debug_info!("We can join locally");
let join_rules = services.rooms.state_accessor.get_join_rules(room_id).await;
let join_rules_event_content = services let mut restricted_join_authorized = None;
.rooms match join_rules {
.state_accessor | JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted) => {
.room_state_get_content::<RoomJoinRulesEventContent>( for restriction in restricted.allow {
room_id, match restriction {
&StateEventType::RoomJoinRules, | AllowRule::RoomMembership(membership) => {
"", if services
)
.await;
let restriction_rooms = match join_rules_event_content {
| Ok(RoomJoinRulesEventContent {
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
}) => restricted
.allow
.into_iter()
.filter_map(|a| match a {
| AllowRule::RoomMembership(r) => Some(r.room_id),
| _ => None,
})
.collect(),
| _ => Vec::new(),
};
let join_authorized_via_users_server: Option<OwnedUserId> = {
if restriction_rooms
.iter()
.stream()
.any(|restriction_room_id| {
trace!("Checking if {sender_user} is joined to {restriction_room_id}");
services
.rooms .rooms
.state_cache .state_cache
.is_joined(sender_user, restriction_room_id) .is_joined(sender_user, &membership.room_id)
})
.await .await
{ {
services restricted_join_authorized = Some(true);
break;
}
},
| AllowRule::UnstableSpamChecker => {
match services
.antispam
.meowlnir_accept_make_join(room_id.to_owned(), sender_user.to_owned())
.await
{
| Ok(()) => {
restricted_join_authorized = Some(true);
break;
},
| Err(_) =>
return Err!(Request(Forbidden(
"Antispam rejected join request."
))),
}
},
| _ => {},
}
}
},
| _ => {},
};
let join_authorized_via_users_server = if restricted_join_authorized.is_none() {
None
} else {
match restricted_join_authorized.unwrap() {
| true => services
.rooms .rooms
.state_cache .state_cache
.local_users_in_room(room_id) .local_users_in_room(room_id)
@@ -790,10 +807,14 @@ async fn join_room_by_id_helper_local(
.boxed() .boxed()
.next() .next()
.await .await
.map(ToOwned::to_owned) .map(ToOwned::to_owned),
} else { | false => {
trace!("No restriction rooms are joined by {sender_user}"); warn!(
None "Join authorization failed for restricted join in room {room_id} for user \
{sender_user}"
);
return Err!(Request(Forbidden("You are not authorized to join this room.")));
},
} }
}; };
@@ -821,16 +842,14 @@ async fn join_room_by_id_helper_local(
return Ok(()); return Ok(());
}; };
if restriction_rooms.is_empty() if servers.is_empty() || servers.len() == 1 && services.globals.server_is_ours(&servers[0]) {
&& (servers.is_empty()
|| servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
{
return Err(error); return Err(error);
} }
warn!( warn!(
"We couldn't do the join locally, maybe federation can help to satisfy the restricted \ ?error,
join requirements" remote_servers = %servers.len()-1,
"Could not join restricted room locally, attempting remote join",
); );
let Ok((make_join_response, remote_server)) = let Ok((make_join_response, remote_server)) =
make_join_request(services, sender_user, room_id, servers).await make_join_request(services, sender_user, room_id, servers).await
+1 -1
View File
@@ -214,7 +214,7 @@ pub(crate) async fn user_can_perform_restricted_join(
.await .await
{ {
| Ok(()) => Ok(true), | Ok(()) => Ok(true),
| Err(e) => Err!(Request(Forbidden("Antispam rejected join request."))), | Err(_) => Err!(Request(Forbidden("Antispam rejected join request."))),
}, },
| _ => { | _ => {
debug_info!( debug_info!(
+10
View File
@@ -169,4 +169,14 @@ impl Service {
Ok(()) Ok(())
} }
} }
/// Returns whether all joins should be checked with Meowlnir.
/// Is always false if Meowlnir is not configured.
pub fn check_all_joins(&self) -> bool {
if let Some(Antispam { meowlnir: Some(cfg), .. }) = &self.services.config.antispam {
cfg.check_all_joins
} else {
false
}
}
} }