diff --git a/room_access_rules/__init__.py b/room_access_rules/__init__.py index 1009f16..b094d95 100644 --- a/room_access_rules/__init__.py +++ b/room_access_rules/__init__.py @@ -62,6 +62,7 @@ class AccessRules: @attr.s(frozen=True, auto_attribs=True) class RoomAccessRulesConfig: id_server: str + bypass_for_users: List[str] = [] domains_forbidden_when_restricted: List[str] = [] fix_admins_for_dm_power_levels: bool = False add_live_location_power_levels: bool = False @@ -306,6 +307,9 @@ async def on_create_room( access_rule = None join_rule = None + if is_requester_admin or requester.user.to_string() in self.config.bypass_for_users: + return True + # If there's a rules event in the initial state, check if it complies with the # spec for im.vector.room.access_rules and deny the request if not. for event in config.get("initial_state", []): @@ -486,6 +490,14 @@ async def check_threepid_can_be_invited( return True + async def _user_can_bypass_rules(self, user_id: str) -> bool: + if ( + user_id in self.config.bypass_for_users + or await self.module_api.is_user_admin(user_id) + ): + return True + return False + async def check_event_allowed( self, event: EventBase, @@ -504,6 +516,9 @@ async def check_event_allowed( allowed, False if it should be rejected. The second entry is always None because this module doesn't replace event contents. """ + if await self._user_can_bypass_rules(event.sender): + return True, None + # We check the rules when altering the state of the room, so only go further if # the event is a state event. if event.is_state(): @@ -643,6 +658,15 @@ async def _on_membership_or_invite( Returns: A boolean indicating whether the event is allowed. """ + + # Let's ignore rules if the user is accepting an invite coming from + # an user in the bypass list or an admin + if event.type == EventTypes.Member and event.membership == Membership.JOIN: + previous_membership = state_events.get((EventTypes.Member, event.state_key)) + if previous_membership and previous_membership.membership == Membership.INVITE: + if await self._user_can_bypass_rules(previous_membership.sender): + return True + if rule == AccessRules.RESTRICTED: ret = self._on_membership_or_invite_restricted(event) elif rule == AccessRules.UNRESTRICTED: diff --git a/tests/__init__.py b/tests/__init__.py index 92fb28b..228ca16 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Any, Dict, Optional -from unittest.mock import Mock +from unittest.mock import AsyncMock, Mock import attr from synapse.module_api import ModuleApi, UserID @@ -87,6 +87,7 @@ def create_module( module_api.http_client = MockHttpClient() module_api.public_room_list_manager = MockPublicRoomListManager() module_api._hs = MockHomeserver() + module_api.is_user_admin = AsyncMock(return_value=False) if config_override is None: config_override = {}