@@ -823,8 +823,14 @@ async def update_membership_locked(
823
823
824
824
latest_event_ids = await self .store .get_prev_events_for_room (room_id )
825
825
826
+ # There are 2 reasons that mandate to have the full state:
827
+ # - to calculate `is_host_in_room`, however we now approximate that we consider the server
828
+ # in the room if we are still in the middle of a fast join.
829
+ # - to calculate if another user trying to join is allowed to. In this case we now
830
+ # make another `make/send_join` to a resident server to validate that. We could just accept
831
+ # it but it would mean that we would potentially leaked the history to a banned user.
826
832
state_before_join = await self .state_handler .compute_state_after_events (
827
- room_id , latest_event_ids
833
+ room_id , latest_event_ids , await_full_state = False
828
834
)
829
835
830
836
# TODO: Refactor into dictionary of explicitly allowed transitions
@@ -881,7 +887,10 @@ async def update_membership_locked(
881
887
if action == "kick" :
882
888
raise AuthError (403 , "The target user is not in the room" )
883
889
884
- is_host_in_room = await self ._is_host_in_room (state_before_join )
890
+ is_partial_state_room = await self .store .is_partial_state_room (room_id )
891
+ is_host_in_room = await self ._is_host_in_room (
892
+ is_partial_state_room , state_before_join
893
+ )
885
894
886
895
if effective_membership_state == Membership .JOIN :
887
896
if requester .is_guest :
@@ -927,6 +936,7 @@ async def update_membership_locked(
927
936
room_id ,
928
937
remote_room_hosts ,
929
938
content ,
939
+ is_partial_state_room ,
930
940
is_host_in_room ,
931
941
state_before_join ,
932
942
)
@@ -1073,6 +1083,7 @@ async def _should_perform_remote_join(
1073
1083
room_id : str ,
1074
1084
remote_room_hosts : List [str ],
1075
1085
content : JsonDict ,
1086
+ is_partial_state_room : bool ,
1076
1087
is_host_in_room : bool ,
1077
1088
state_before_join : StateMap [str ],
1078
1089
) -> Tuple [bool , List [str ]]:
@@ -1093,6 +1104,8 @@ async def _should_perform_remote_join(
1093
1104
remote_room_hosts: A list of remote room hosts.
1094
1105
content: The content to use as the event body of the join. This may
1095
1106
be modified.
1107
+ is_partial_state_room: True if the server currently doesn't hold the fully
1108
+ state of the room.
1096
1109
is_host_in_room: True if the host is in the room.
1097
1110
state_before_join: The state before the join event (i.e. the resolution of
1098
1111
the states after its parent events).
@@ -1109,6 +1122,20 @@ async def _should_perform_remote_join(
1109
1122
if not is_host_in_room :
1110
1123
return True , remote_room_hosts
1111
1124
1125
+ prev_member_event_id = state_before_join .get ((EventTypes .Member , user_id ), None )
1126
+ previous_membership = None
1127
+ if prev_member_event_id :
1128
+ prev_member_event = await self .store .get_event (prev_member_event_id )
1129
+ previous_membership = prev_member_event .membership
1130
+
1131
+ # If we are not fully join yet, and the target is not already in the room,
1132
+ # let's do a remote join so another server with the full state can validate
1133
+ # that the user has not been ban for example.
1134
+ # We could just accept the join and wait for state-res to resolve that later on
1135
+ # but we would then leak room history to this person until then, which is pretty bad.
1136
+ if is_partial_state_room and previous_membership != Membership .JOIN :
1137
+ return True , remote_room_hosts
1138
+
1112
1139
# If the host is in the room, but not one of the authorised hosts
1113
1140
# for restricted join rules, a remote join must be used.
1114
1141
room_version = await self .store .get_room_version (room_id )
@@ -1122,15 +1149,8 @@ async def _should_perform_remote_join(
1122
1149
1123
1150
# If the user is invited to the room or already joined, the join
1124
1151
# event can always be issued locally.
1125
- prev_member_event_id = state_before_join .get ((EventTypes .Member , user_id ), None )
1126
- prev_member_event = None
1127
- if prev_member_event_id :
1128
- prev_member_event = await self .store .get_event (prev_member_event_id )
1129
- if prev_member_event .membership in (
1130
- Membership .JOIN ,
1131
- Membership .INVITE ,
1132
- ):
1133
- return False , []
1152
+ if previous_membership in (Membership .JOIN , Membership .INVITE ):
1153
+ return False , []
1134
1154
1135
1155
# If the local host has a user who can issue invites, then a local
1136
1156
# join can be done.
@@ -1154,7 +1174,7 @@ async def _should_perform_remote_join(
1154
1174
1155
1175
# Ensure the member should be allowed access via membership in a room.
1156
1176
await self .event_auth_handler .check_restricted_join_rules (
1157
- state_before_join , room_version , user_id , prev_member_event
1177
+ state_before_join , room_version , user_id , previous_membership
1158
1178
)
1159
1179
1160
1180
# If this is going to be a local join, additional information must
@@ -1634,7 +1654,13 @@ async def _make_and_store_3pid_invite(
1634
1654
)
1635
1655
return event , stream_id
1636
1656
1637
- async def _is_host_in_room (self , current_state_ids : StateMap [str ]) -> bool :
1657
+ async def _is_host_in_room (
1658
+ self , is_partial_state_room : bool , current_state_ids : StateMap [str ]
1659
+ ) -> bool :
1660
+ # When we only have a partial state, let's assume we are still in the room
1661
+ if is_partial_state_room :
1662
+ return True
1663
+
1638
1664
# Have we just created the room, and is this about to be the very
1639
1665
# first member event?
1640
1666
create_event_id = current_state_ids .get (("m.room.create" , "" ))
0 commit comments