diff options
-rw-r--r-- | docs/sample_config.yaml | 5 | ||||
-rw-r--r-- | synapse/config/registration.py | 8 | ||||
-rw-r--r-- | synapse/groups/groups_server.py | 21 | ||||
-rw-r--r-- | synapse/handlers/groups_local.py | 19 | ||||
-rw-r--r-- | synapse/rest/client/v2_alpha/account.py | 4 | ||||
-rw-r--r-- | synapse/rest/client/v2_alpha/groups.py | 26 | ||||
-rw-r--r-- | synapse/rest/client/v2_alpha/register.py | 10 | ||||
-rw-r--r-- | synapse/storage/databases/main/group_server.py | 10 | ||||
-rw-r--r-- | synapse/util/threepids.py | 49 |
9 files changed, 145 insertions, 7 deletions
diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml index 8a3206e845..e71bd782b0 100644 --- a/docs/sample_config.yaml +++ b/docs/sample_config.yaml @@ -1182,6 +1182,11 @@ account_validity: # Mandate that users are only allowed to associate certain formats of # 3PIDs with accounts on this server. # +# Use an Identity Server to establish which 3PIDs are allowed to register? +# Overrides allowed_local_3pids below. +# +#check_is_for_allowed_local_3pids: matrix.org +# #allowed_local_3pids: # - medium: email # pattern: '.*@matrix\.org' diff --git a/synapse/config/registration.py b/synapse/config/registration.py index d7e3690a32..aeae5bcaea 100644 --- a/synapse/config/registration.py +++ b/synapse/config/registration.py @@ -100,6 +100,9 @@ class RegistrationConfig(Config): self.registrations_require_3pid = config.get("registrations_require_3pid", []) self.allowed_local_3pids = config.get("allowed_local_3pids", []) + self.check_is_for_allowed_local_3pids = config.get( + "check_is_for_allowed_local_3pids", None + ) self.enable_3pid_lookup = config.get("enable_3pid_lookup", True) self.registration_shared_secret = config.get("registration_shared_secret") @@ -299,6 +302,11 @@ class RegistrationConfig(Config): # Mandate that users are only allowed to associate certain formats of # 3PIDs with accounts on this server. # + # Use an Identity Server to establish which 3PIDs are allowed to register? + # Overrides allowed_local_3pids below. + # + #check_is_for_allowed_local_3pids: matrix.org + # #allowed_local_3pids: # - medium: email # pattern: '.*@matrix\\.org' diff --git a/synapse/groups/groups_server.py b/synapse/groups/groups_server.py index e5f85b472d..de4c94cd3a 100644 --- a/synapse/groups/groups_server.py +++ b/synapse/groups/groups_server.py @@ -719,6 +719,27 @@ class GroupsServerHandler(GroupsServerWorkerHandler): raise NotImplementedError() + async def change_user_admin_in_group( + self, group_id, user_id, want_admin, requester_user_id, content + ): + """Promotes or demotes a user in a group. + """ + + await self.check_group_is_ours(group_id, requester_user_id, and_exists=True) + + if requester_user_id == user_id: + raise SynapseError(400, "User cannot target themselves") + + is_admin = await self.store.is_user_admin_in_group( + group_id, requester_user_id + ) + if not is_admin: + raise SynapseError(403, "User is not admin in group") + + await self.store.change_user_admin_in_group(group_id, user_id, want_admin) + + return {} + async def remove_user_from_group( self, group_id, user_id, requester_user_id, content ): diff --git a/synapse/handlers/groups_local.py b/synapse/handlers/groups_local.py index 9684e60fc8..489a7b885d 100644 --- a/synapse/handlers/groups_local.py +++ b/synapse/handlers/groups_local.py @@ -461,6 +461,25 @@ class GroupsLocalHandler(GroupsLocalWorkerHandler): return {"state": "invite", "user_profile": user_profile} + async def change_user_admin_in_group( + self, group_id, user_id, want_admin, requester_user_id, content + ): + """Promotes or demotes a user in a group. + """ + + if not self.is_mine_id(user_id): + raise SynapseError(400, "User not on this server") + + # TODO: We should probably support federation, but this is fine for now + if not self.is_mine_id(group_id): + raise SynapseError(400, "Group not on this server") + + res = await self.groups_server_handler.change_user_admin_in_group( + group_id, user_id, want_admin, requester_user_id, content + ) + + return res + async def remove_user_from_group( self, group_id, user_id, requester_user_id, content ): diff --git a/synapse/rest/client/v2_alpha/account.py b/synapse/rest/client/v2_alpha/account.py index ab5815e7f7..86d3d86fad 100644 --- a/synapse/rest/client/v2_alpha/account.py +++ b/synapse/rest/client/v2_alpha/account.py @@ -366,7 +366,7 @@ class EmailThreepidRequestTokenRestServlet(RestServlet): send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param - if not check_3pid_allowed(self.hs, "email", email): + if not await check_3pid_allowed(self.hs, "email", email): raise SynapseError( 403, "Your email domain is not authorized on this server", @@ -441,7 +441,7 @@ class MsisdnThreepidRequestTokenRestServlet(RestServlet): msisdn = phone_number_to_msisdn(country, phone_number) - if not check_3pid_allowed(self.hs, "msisdn", msisdn): + if not await check_3pid_allowed(self.hs, "msisdn", msisdn): raise SynapseError( 403, "Account phone numbers are not authorized on this server", diff --git a/synapse/rest/client/v2_alpha/groups.py b/synapse/rest/client/v2_alpha/groups.py index a3bb095c2d..75215a3779 100644 --- a/synapse/rest/client/v2_alpha/groups.py +++ b/synapse/rest/client/v2_alpha/groups.py @@ -552,6 +552,31 @@ class GroupAdminUsersKickServlet(RestServlet): return 200, result +class GroupAdminChangeAdminServlet(RestServlet): + """Promote or demote a user in the group + """ + + PATTERNS = client_patterns( + "/groups/(?P<group_id>[^/]*)/admin/users/admins/(?P<user_id>[^/]*)$" + ) + + def __init__(self, hs): + super(GroupAdminChangeAdminServlet, self).__init__() + self.auth = hs.get_auth() + self.clock = hs.get_clock() + self.groups_handler = hs.get_groups_local_handler() + + async def on_POST(self, request, group_id, user_id): + requester = await self.auth.get_user_by_req(request) + requester_user_id = requester.user.to_string() + + content = parse_json_object_from_request(request) + want_admin = content["is_admin"] + result = await self.groups_handler.change_user_admin_in_group( + group_id, user_id, want_admin, requester_user_id, content + ) + + return 200, result class GroupSelfLeaveServlet(RestServlet): """Leave a joined group @@ -726,6 +751,7 @@ def register_servlets(hs, http_server): GroupAdminRoomsConfigServlet(hs).register(http_server) GroupAdminUsersInviteServlet(hs).register(http_server) GroupAdminUsersKickServlet(hs).register(http_server) + GroupAdminChangeAdminServlet(hs).register(http_server) GroupSelfLeaveServlet(hs).register(http_server) GroupSelfJoinServlet(hs).register(http_server) GroupSelfAcceptInviteServlet(hs).register(http_server) diff --git a/synapse/rest/client/v2_alpha/register.py b/synapse/rest/client/v2_alpha/register.py index ffa2dfce42..ec8ef9bf88 100644 --- a/synapse/rest/client/v2_alpha/register.py +++ b/synapse/rest/client/v2_alpha/register.py @@ -117,10 +117,10 @@ class EmailRegisterRequestTokenRestServlet(RestServlet): send_attempt = body["send_attempt"] next_link = body.get("next_link") # Optional param - if not check_3pid_allowed(self.hs, "email", email): + if not await check_3pid_allowed(self.hs, "email", email, during_registration=True): raise SynapseError( 403, - "Your email domain is not authorized to register on this server", + "You currently can't create an account with this email address", Codes.THREEPID_DENIED, ) @@ -192,7 +192,7 @@ class MsisdnRegisterRequestTokenRestServlet(RestServlet): msisdn = phone_number_to_msisdn(country, phone_number) - if not check_3pid_allowed(self.hs, "msisdn", msisdn): + if not await check_3pid_allowed(self.hs, "msisdn", msisdn, during_registration=True): raise SynapseError( 403, "Phone numbers are not authorized to register on this server", @@ -538,7 +538,9 @@ class RegisterRestServlet(RestServlet): medium = auth_result[login_type]["medium"] address = auth_result[login_type]["address"] - if not check_3pid_allowed(self.hs, medium, address): + if not await check_3pid_allowed( + self.hs, medium, address, during_registration=True + ): raise SynapseError( 403, "Third party identifiers (email/phone numbers)" diff --git a/synapse/storage/databases/main/group_server.py b/synapse/storage/databases/main/group_server.py index 7218191965..f724f45494 100644 --- a/synapse/storage/databases/main/group_server.py +++ b/synapse/storage/databases/main/group_server.py @@ -1116,6 +1116,16 @@ class GroupServerStore(GroupServerWorkerStore): "remove_user_from_group", _remove_user_from_group_txn ) + async def change_user_admin_in_group( + self, group_id: str, user_id: str, is_admin: bool + ) -> int: + return await self.db_pool.simple_update( + table="group_users", + keyvalues={"group_id": group_id, "user_id": user_id}, + updatevalues={"is_admin": is_admin}, + desc="change_user_admin_in_group" + ) + async def add_room_to_group( self, group_id: str, room_id: str, is_public: bool ) -> None: diff --git a/synapse/util/threepids.py b/synapse/util/threepids.py index 43c2e0ac23..527d873935 100644 --- a/synapse/util/threepids.py +++ b/synapse/util/threepids.py @@ -19,7 +19,7 @@ import re logger = logging.getLogger(__name__) -def check_3pid_allowed(hs, medium, address): +async def check_3pid_allowed(hs, medium, address, during_registration: bool = False): """Checks whether a given format of 3PID is allowed to be used on this HS Args: @@ -27,10 +27,57 @@ def check_3pid_allowed(hs, medium, address): medium (str): 3pid medium - e.g. email, msisdn address (str): address within that medium (e.g. "wotan@matrix.org") msisdns need to first have been canonicalised + during_registration: Whether this request has been made while registering a new + user. Returns: bool: whether the 3PID medium/address is allowed to be added to this HS """ + if hs.config.check_is_for_allowed_local_3pids and during_registration: + # If this 3pid is being approved as part of registering a new user, + # we'll want to make sure the 3pid has been invited by someone already. + # + # We condition on registration so that user 3pids do not require an invite while + # doing tasks other than registration, such as resetting their password or adding a + # second email to their account. + data = await hs.get_simple_http_client().get_json( + "https://%s%s" % ( + hs.config.check_is_for_allowed_local_3pids, + "/_matrix/identity/api/v1/internal-info" + ), + {'medium': medium, 'address': address} + ) + logger.info( + "Received internal-info data for medium '%s', address '%s': %s", + medium, address, data, + ) + + # Check for invalid response + if 'hs' not in data and 'shadow_hs' not in data: + return False + + # Check if this user is intended to register for this homeserver + if ( + data.get('hs') != hs.config.server_name + and data.get('shadow_hs') != hs.config.server_name + ): + logger.info( + "%s did not match %s or %s did not match %s", + data.get("hs"), hs.config.server_name, + data.get("shadow_hs"), hs.config.server_name, + ) + return False + + if data.get('requires_invite', False) and not data.get('invited', False): + # Requires an invite but hasn't been invited + logger.info( + "3PID check failed due to 'required_invite' = '%s' and 'invited' = '%s'", + data.get('required_invite'), data.get("invited"), + ) + return False + + return True + if hs.config.allowed_local_3pids: for constraint in hs.config.allowed_local_3pids: logger.debug( |