simplify getting join_authorized_via_users_server for make/send_join, remove unnecessary async

Co-authored-by: Matthias Ahouansou <matthias@ahouansou.cz>
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-07-02 15:32:40 -04:00
parent 339a1fc4e8
commit bb27f21ac1
4 changed files with 104 additions and 101 deletions

View file

@ -1055,7 +1055,7 @@ async fn join_room_by_id_helper_local(
.filter(|user| user_is_local(user))
.collect::<Vec<OwnedUserId>>();
let mut authorized_user: Option<OwnedUserId> = None;
let mut join_authorized_via_users_server: Option<OwnedUserId> = None;
if restriction_rooms.iter().any(|restriction_room_id| {
services()
@ -1069,10 +1069,9 @@ async fn join_room_by_id_helper_local(
.rooms
.state_accessor
.user_can_invite(room_id, &user, sender_user, &state_lock)
.await
.unwrap_or(false)
{
authorized_user = Some(user);
join_authorized_via_users_server = Some(user);
break;
}
}
@ -1086,7 +1085,7 @@ async fn join_room_by_id_helper_local(
third_party_invite: None,
blurhash: services().users.blurhash(sender_user)?,
reason: reason.clone(),
join_authorized_via_users_server: authorized_user,
join_authorized_via_users_server,
};
// Try normal join first

View file

@ -7,15 +7,12 @@ use ruma::{
},
StateEventType, TimelineEventType,
},
RoomVersionId,
RoomId, RoomVersionId, UserId,
};
use serde_json::value::to_raw_value;
use tracing::warn;
use crate::{
service::{pdu::PduBuilder, user_is_local},
services, Error, Result, Ruma,
};
use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma};
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
///
@ -72,95 +69,42 @@ pub(crate) async fn create_join_event_template_route(
}
}
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
let state_lock = services()
.globals
.roomid_mutex_state
.lock(&body.room_id)
.await;
let join_rules_event =
services()
let join_authorized_via_users_server = if (services()
.rooms
.state_cache
.is_left(&body.user_id, &body.room_id)
.unwrap_or(true))
&& user_can_perform_restricted_join(&body.user_id, &body.room_id, &room_version_id)?
{
let auth_user = services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomJoinRules, "")?;
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
.as_ref()
.map(|join_rules_event| {
serde_json::from_str(join_rules_event.content.get())
.map_err(|_| Error::bad_database("Invalid join rules event in db."))
})
.transpose()?;
let join_authorized_via_users_server = if let Some(join_rules_event_content) = join_rules_event_content {
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rules_event_content.join_rule {
if r.allow
.iter()
.filter_map(|rule| {
if let AllowRule::RoomMembership(membership) = rule {
Some(membership)
} else {
None
}
})
.any(|m| {
services()
.rooms
.state_cache
.is_joined(&body.user_id, &m.room_id)
.unwrap_or(false)
}) {
if services()
.state_cache
.room_members(&body.room_id)
.filter_map(Result::ok)
.filter(|user| user.server_name() == services().globals.server_name())
.find(|user| {
services()
.rooms
.state_cache
.is_left(&body.user_id, &body.room_id)
.unwrap_or(true)
{
let members: Vec<_> = services()
.rooms
.state_cache
.room_members(&body.room_id)
.filter_map(Result::ok)
.filter(|user| user_is_local(user))
.collect();
.state_accessor
.user_can_invite(&body.room_id, user, &body.user_id, &state_lock)
.unwrap_or(false)
});
let mut auth_user = None;
for user in members {
if services()
.rooms
.state_accessor
.user_can_invite(&body.room_id, &user, &body.user_id, &state_lock)
.await
.unwrap_or(false)
{
auth_user = Some(user);
break;
}
}
if auth_user.is_some() {
auth_user
} else {
return Err(Error::BadRequest(
ErrorKind::UnableToGrantJoin,
"No user on this server is able to assist in joining.",
));
}
} else {
// If the user has any state other than leave, either:
// - the auth_check will deny them (ban, knock - (until/unless MSC4123 is
// merged))
// - they are able to join via other methods (invite)
// - they are already in the room (join)
None
}
} else {
return Err(Error::BadRequest(
ErrorKind::UnableToAuthorizeJoin,
"User is not known to be in any required room.",
));
}
if auth_user.is_some() {
auth_user
} else {
None
return Err(Error::BadRequest(
ErrorKind::UnableToGrantJoin,
"No user on this server is able to assist in joining.",
));
}
} else {
None
@ -231,3 +175,71 @@ pub(crate) async fn create_join_event_template_route(
event: to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"),
})
}
/// Checks whether the given user can join the given room via a restricted join.
/// This doesn't check the current user's membership. This should be done
/// externally, either by using the state cache or attempting to authorize the
/// event.
pub(crate) fn user_can_perform_restricted_join(
user_id: &UserId, room_id: &RoomId, room_version_id: &RoomVersionId,
) -> Result<bool> {
let join_rules_event =
services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomJoinRules, "")?;
let Some(join_rules_event_content) = join_rules_event
.as_ref()
.map(|join_rules_event| {
serde_json::from_str::<RoomJoinRulesEventContent>(join_rules_event.content.get()).map_err(|e| {
warn!("Invalid join rules event in database: {e}");
Error::bad_database("Invalid join rules event in database")
})
})
.transpose()?
else {
return Ok(false);
};
if matches!(
room_version_id,
RoomVersionId::V1
| RoomVersionId::V2
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7
) {
return Ok(false);
}
let (JoinRule::Restricted(r) | JoinRule::KnockRestricted(r)) = join_rules_event_content.join_rule else {
return Ok(false);
};
if r.allow
.iter()
.filter_map(|rule| {
if let AllowRule::RoomMembership(membership) = rule {
Some(membership)
} else {
None
}
})
.any(|m| {
services()
.rooms
.state_cache
.is_joined(user_id, &m.room_id)
.unwrap_or(false)
}) {
Ok(true)
} else {
Err(Error::BadRequest(
ErrorKind::UnableToAuthorizeJoin,
"User is not known to be in any required room.",
))
}
}

View file

@ -8,7 +8,7 @@ use ruma::{
room::member::{MembershipState, RoomMemberEventContent},
StateEventType,
},
CanonicalJsonValue, OwnedServerName, OwnedUserId, RoomId, RoomVersionId, ServerName,
CanonicalJsonValue, OwnedServerName, OwnedUserId, RoomId, ServerName,
};
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use service::user_is_local;
@ -125,16 +125,8 @@ async fn create_join_event(
if content
.join_authorized_via_users_server
.is_some_and(|user| user_is_local(&user))
&& !matches!(
room_version_id,
RoomVersionId::V1
| RoomVersionId::V2
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7
) {
&& super::user_can_perform_restricted_join(&sender, room_id, &room_version_id).unwrap_or_default()
{
ruma::signatures::hash_and_sign_event(
services().globals.server_name().as_str(),
services().globals.keypair(),

View file

@ -299,7 +299,7 @@ impl Service {
})
}
pub async fn user_can_invite(
pub fn user_can_invite(
&self, room_id: &RoomId, sender: &UserId, target_user: &UserId, state_lock: &mutex_map::Guard<()>,
) -> Result<bool> {
let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))