-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Add validation of format for 3pid and add validation of 3pid in admin api #7022
Changes from 7 commits
ea2a8de
a801e8d
4648933
59e4428
7c9da15
8ff3522
131255d
dbaf0a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add validation of format for 3pid. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Add validation of 3pid in admin api ``PUT /_synapse/admin/v2/users/<user_id>``. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,7 @@ class Codes(object): | |
| THREEPID_IN_USE = "M_THREEPID_IN_USE" | ||
| THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND" | ||
| THREEPID_DENIED = "M_THREEPID_DENIED" | ||
| INVALID_THREEPID = "M_INVALID_THREEPID" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is unclear to me if this needs a spec change or not. @matrix-org/synapse-core any opinion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not find an spec for these errors. Only a documenttion https://github.com/matrix-org/matrix-doc/blob/master/specification/client_server_api.rst There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can see the list of error codes at https://matrix.org/docs/spec/client_server/r0.6.0#api-standards, this would need to get added as a separate one following the process at https://matrix.org/docs/spec/proposals |
||
| INVALID_USERNAME = "M_INVALID_USERNAME" | ||
| SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED" | ||
| CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| historical_admin_path_patterns, | ||
| ) | ||
| from synapse.types import UserID | ||
| from synapse.util.threepids import check_3pid_allowed, check_3pid_valid_format | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -195,8 +196,35 @@ async def on_PUT(self, request, user_id): | |
| # add new threepids to user | ||
| current_time = self.hs.get_clock().time_msec() | ||
| for threepid in body["threepids"]: | ||
| # For emails, transform the address to lowercase. | ||
| # We store all email addreses as lowercase in the DB. | ||
| # (See add_threepid in synapse/handlers/auth.py) | ||
| address = threepid["address"].lower() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noting that this seems related to #7021. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change needs to wait on the result of that conversation then. |
||
| if not check_3pid_valid_format(threepid["medium"], address): | ||
| raise SynapseError( | ||
| 400, | ||
| "Third party identifier has not a valid format", | ||
| Codes.INVALID_THREEPID, | ||
| ) | ||
|
|
||
| if not check_3pid_allowed(self.hs, threepid["medium"], address): | ||
| raise SynapseError( | ||
| 403, | ||
| "Your email domain or account phone number is not authorized on this server", | ||
| Codes.THREEPID_DENIED, | ||
| ) | ||
|
|
||
| existing_user_id = await self.store.get_user_id_by_threepid( | ||
| threepid["medium"], address | ||
| ) | ||
|
|
||
| if existing_user_id is not None: | ||
| raise SynapseError( | ||
| 400, "Threepid is already in use", Codes.THREEPID_IN_USE | ||
| ) | ||
|
|
||
| await self.auth_handler.add_threepid( | ||
| user_id, threepid["medium"], threepid["address"], current_time | ||
| user_id, threepid["medium"], address, current_time | ||
| ) | ||
|
|
||
| if "avatar_url" in body: | ||
|
|
@@ -271,8 +299,35 @@ async def on_PUT(self, request, user_id): | |
|
|
||
| current_time = self.hs.get_clock().time_msec() | ||
| for threepid in body["threepids"]: | ||
| # For emails, transform the address to lowercase. | ||
| # We store all email addreses as lowercase in the DB. | ||
| # (See add_threepid in synapse/handlers/auth.py) | ||
| address = threepid["address"].lower() | ||
| if not check_3pid_valid_format(threepid["medium"], address): | ||
| raise SynapseError( | ||
| 400, | ||
| "Third party identifier has not a valid format", | ||
dklimpel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Codes.INVALID_THREEPID, | ||
| ) | ||
|
|
||
| if not check_3pid_allowed(self.hs, threepid["medium"], address): | ||
| raise SynapseError( | ||
| 403, | ||
| "Your email domain or account phone number is not authorized on this server", | ||
| Codes.THREEPID_DENIED, | ||
| ) | ||
|
|
||
| existing_user_id = await self.store.get_user_id_by_threepid( | ||
| threepid["medium"], address | ||
| ) | ||
|
|
||
| if existing_user_id is not None: | ||
| raise SynapseError( | ||
| 400, "Threepid is already in use", Codes.THREEPID_IN_USE | ||
| ) | ||
|
|
||
| await self.auth_handler.add_threepid( | ||
| user_id, threepid["medium"], threepid["address"], current_time | ||
| user_id, threepid["medium"], address, current_time | ||
| ) | ||
|
|
||
| if "avatar_url" in body: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,3 +48,25 @@ def check_3pid_allowed(hs, medium, address): | |
| return True | ||
|
|
||
| return False | ||
|
|
||
|
|
||
| def check_3pid_valid_format(medium, address): | ||
| """Checks whether 3pid has a valid format | ||
|
|
||
| Args: | ||
| medium (str): 3pid medium - e.g. email, msisdn | ||
| address (str): 3pid address to check (e.g. "[email protected]") | ||
dklimpel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Returns: | ||
| bool: whether the email address has a valid format | ||
| """ | ||
|
|
||
| if medium == "email": | ||
| regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" | ||
|
||
| if re.search(regex, address): | ||
| return True | ||
| else: | ||
| return False | ||
dklimpel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| elif medium == "msisdn": | ||
| return True | ||
| else: | ||
| return False | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment above this saying that any other medium is not understood and is thus invalid? Thanks! |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -599,6 +599,83 @@ def test_set_threepid(self): | |
| self.assertEqual("email", channel.json_body["threepids"][0]["medium"]) | ||
| self.assertEqual("[email protected]", channel.json_body["threepids"][0]["address"]) | ||
|
|
||
| def test_set_duplicate_threepid(self): | ||
| """ | ||
| Test setting duplicate threepid for different user. | ||
| """ | ||
| self.hs.config.registration_shared_secret = None | ||
|
|
||
| # Add duplicate threepid with different notations | ||
| body = json.dumps( | ||
| { | ||
| "threepids": [ | ||
| {"medium": "email", "address": "[email protected]"}, | ||
| {"medium": "email", "address": "[email protected]"}, | ||
| ] | ||
| } | ||
| ) | ||
|
|
||
| request, channel = self.make_request( | ||
| "PUT", | ||
| self.url_other_user, | ||
| access_token=self.admin_user_tok, | ||
| content=body.encode(encoding="utf_8"), | ||
| ) | ||
| self.render(request) | ||
|
|
||
| self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) | ||
| self.assertEqual("Threepid is already in use", channel.json_body["error"]) | ||
|
|
||
| def test_set_invalid_threepid(self): | ||
| """ | ||
| Test setting invalid threepid for an other user. | ||
| """ | ||
| self.hs.config.registration_shared_secret = None | ||
|
|
||
| # Add threepid to user | ||
| body = json.dumps({"threepids": [{"medium": "email", "address": "bob3@bob"}]}) | ||
|
|
||
| request, channel = self.make_request( | ||
| "PUT", | ||
| self.url_other_user, | ||
| access_token=self.admin_user_tok, | ||
| content=body.encode(encoding="utf_8"), | ||
| ) | ||
| self.render(request) | ||
|
|
||
| self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) | ||
| self.assertEqual( | ||
| "Third party identifier has not a valid format", channel.json_body["error"] | ||
| ) | ||
|
|
||
| def test_set_not_allowed_threepid(self): | ||
| """ | ||
| Test setting not allowed threepid for an other user. | ||
| """ | ||
| self.hs.config.registration_shared_secret = None | ||
| self.hs.config.allowed_local_3pids = [ | ||
| {"medium": "email", "pattern": r".*@matrix\.org"} | ||
| ] | ||
|
|
||
| # Add threepid to user | ||
| body = json.dumps( | ||
| {"threepids": [{"medium": "email", "address": "[email protected]"}]} | ||
| ) | ||
|
|
||
| request, channel = self.make_request( | ||
| "PUT", | ||
| self.url_other_user, | ||
| access_token=self.admin_user_tok, | ||
| content=body.encode(encoding="utf_8"), | ||
| ) | ||
| self.render(request) | ||
|
|
||
| self.assertEqual(403, int(channel.result["code"]), msg=channel.result["body"]) | ||
| self.assertEqual( | ||
| "Your email domain or account phone number is not authorized on this server", | ||
| channel.json_body["error"], | ||
| ) | ||
|
|
||
| def test_deactivate_user(self): | ||
| """ | ||
| Test deactivating another user. | ||
|
|
@@ -693,7 +770,7 @@ def test_accidental_deactivation_prevention(self): | |
| self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) | ||
| self.assertEqual("@bob:test", channel.json_body["name"]) | ||
| self.assertEqual("bob", channel.json_body["displayname"]) | ||
| self.assertEqual(0, channel.json_body["deactivated"]) | ||
| self.assertEqual(False, channel.json_body["deactivated"]) | ||
|
|
||
| # Change password (and use a str for deactivate instead of a bool) | ||
| body = json.dumps({"password": "abc123", "deactivated": "false"}) # oops! | ||
|
|
@@ -719,4 +796,4 @@ def test_accidental_deactivation_prevention(self): | |
| self.assertEqual("bob", channel.json_body["displayname"]) | ||
|
|
||
| # Ensure they're still alive | ||
| self.assertEqual(0, channel.json_body["deactivated"]) | ||
| self.assertEqual(False, channel.json_body["deactivated"]) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contrary to how towncrier is normally used, we've been putting in newsfragments just for the pull request number, so this file can be removed.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please delete this file.