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

View file

@ -7,15 +7,12 @@ use ruma::{
}, },
StateEventType, TimelineEventType, StateEventType, TimelineEventType,
}, },
RoomVersionId, RoomId, RoomVersionId, UserId,
}; };
use serde_json::value::to_raw_value; use serde_json::value::to_raw_value;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma};
service::{pdu::PduBuilder, user_is_local},
services, Error, Result, Ruma,
};
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}` /// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
/// ///
@ -72,71 +69,35 @@ 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() let state_lock = services()
.globals .globals
.roomid_mutex_state .roomid_mutex_state
.lock(&body.room_id) .lock(&body.room_id)
.await; .await;
let join_rules_event =
services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomJoinRules, "")?;
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event let join_authorized_via_users_server = if (services()
.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()
.rooms .rooms
.state_cache .state_cache
.is_left(&body.user_id, &body.room_id) .is_left(&body.user_id, &body.room_id)
.unwrap_or(true) .unwrap_or(true))
&& user_can_perform_restricted_join(&body.user_id, &body.room_id, &room_version_id)?
{ {
let members: Vec<_> = services() let auth_user = services()
.rooms .rooms
.state_cache .state_cache
.room_members(&body.room_id) .room_members(&body.room_id)
.filter_map(Result::ok) .filter_map(Result::ok)
.filter(|user| user_is_local(user)) .filter(|user| user.server_name() == services().globals.server_name())
.collect(); .find(|user| {
services()
let mut auth_user = None;
for user in members {
if services()
.rooms .rooms
.state_accessor .state_accessor
.user_can_invite(&body.room_id, &user, &body.user_id, &state_lock) .user_can_invite(&body.room_id, user, &body.user_id, &state_lock)
.await
.unwrap_or(false) .unwrap_or(false)
{ });
auth_user = Some(user);
break;
}
}
if auth_user.is_some() { if auth_user.is_some() {
auth_user auth_user
} else { } else {
@ -145,23 +106,6 @@ pub(crate) async fn create_join_event_template_route(
"No user on this server is able to assist in joining.", "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.",
));
}
} else {
None
}
} else { } else {
None 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"), 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}, room::member::{MembershipState, RoomMemberEventContent},
StateEventType, StateEventType,
}, },
CanonicalJsonValue, OwnedServerName, OwnedUserId, RoomId, RoomVersionId, ServerName, CanonicalJsonValue, OwnedServerName, OwnedUserId, RoomId, ServerName,
}; };
use serde_json::value::{to_raw_value, RawValue as RawJsonValue}; use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use service::user_is_local; use service::user_is_local;
@ -125,16 +125,8 @@ async fn create_join_event(
if content if content
.join_authorized_via_users_server .join_authorized_via_users_server
.is_some_and(|user| user_is_local(&user)) .is_some_and(|user| user_is_local(&user))
&& !matches!( && super::user_can_perform_restricted_join(&sender, room_id, &room_version_id).unwrap_or_default()
room_version_id, {
RoomVersionId::V1
| RoomVersionId::V2
| RoomVersionId::V3
| RoomVersionId::V4
| RoomVersionId::V5
| RoomVersionId::V6
| RoomVersionId::V7
) {
ruma::signatures::hash_and_sign_event( ruma::signatures::hash_and_sign_event(
services().globals.server_name().as_str(), services().globals.server_name().as_str(),
services().globals.keypair(), 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<()>, &self, room_id: &RoomId, sender: &UserId, target_user: &UserId, state_lock: &mutex_map::Guard<()>,
) -> Result<bool> { ) -> Result<bool> {
let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite)) let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))