This repository has been archived by the owner on Apr 12, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #124 from matrix-org/babolivier/3pid_callback
Add a callback to allow modules to deny 3PID (#11854)
- Loading branch information
Showing
10 changed files
with
161 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add a callback to allow modules to allow or forbid a 3PID (email address, phone number) from being associated to a local account. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,43 +32,28 @@ | |
MAX_EMAIL_ADDRESS_LENGTH = 500 | ||
|
||
|
||
async def check_3pid_allowed(hs: "HomeServer", medium: str, address: str) -> bool: | ||
async def check_3pid_allowed( | ||
hs: "HomeServer", | ||
medium: str, | ||
address: str, | ||
registration: bool = False, | ||
) -> bool: | ||
"""Checks whether a given format of 3PID is allowed to be used on this HS | ||
Args: | ||
hs: server | ||
medium: 3pid medium - e.g. email, msisdn | ||
address: address within that medium (e.g. "[email protected]") | ||
msisdns need to first have been canonicalised | ||
registration: whether we want to bind the 3PID as part of registering a new user. | ||
Returns: | ||
bool: whether the 3PID medium/address is allowed to be added to this HS | ||
""" | ||
if hs.config.registration.check_is_for_allowed_local_3pids: | ||
data = await hs.get_simple_http_client().get_json( | ||
"https://%s%s" | ||
% ( | ||
hs.config.registration.check_is_for_allowed_local_3pids, | ||
"/_matrix/identity/api/v1/internal-info", | ||
), | ||
{"medium": medium, "address": address}, | ||
) | ||
|
||
# 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.server_name | ||
and data.get("shadow_hs") != hs.config.server.server_name | ||
): | ||
return False | ||
|
||
if data.get("requires_invite", False) and not data.get("invited", False): | ||
# Requires an invite but hasn't been invited | ||
return False | ||
|
||
return True | ||
if not await hs.get_password_auth_provider().is_3pid_allowed( | ||
medium, address, registration | ||
): | ||
return False | ||
|
||
if hs.config.registration.allowed_local_3pids: | ||
for constraint in hs.config.registration.allowed_local_3pids: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,13 +21,15 @@ | |
|
||
import synapse | ||
from synapse.api.constants import LoginType | ||
from synapse.api.errors import Codes | ||
from synapse.handlers.auth import load_legacy_password_auth_providers | ||
from synapse.module_api import ModuleApi | ||
from synapse.rest.client import devices, login, logout, register | ||
from synapse.rest.client import account, devices, login, logout, register | ||
from synapse.types import JsonDict, UserID | ||
|
||
from tests import unittest | ||
from tests.server import FakeChannel | ||
from tests.test_utils import make_awaitable | ||
from tests.unittest import override_config | ||
|
||
# (possibly experimental) login flows we expect to appear in the list after the normal | ||
|
@@ -158,6 +160,7 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase): | |
devices.register_servlets, | ||
logout.register_servlets, | ||
register.register_servlets, | ||
account.register_servlets, | ||
] | ||
|
||
def setUp(self): | ||
|
@@ -803,6 +806,77 @@ def test_username_uia(self): | |
# Check that the callback has been called. | ||
m.assert_called_once() | ||
|
||
# Set some email configuration so the test doesn't fail because of its absence. | ||
@override_config({"email": {"notif_from": "noreply@test"}}) | ||
def test_3pid_allowed(self): | ||
"""Tests that an is_3pid_allowed_callbacks forbidding a 3PID makes Synapse refuse | ||
to bind the new 3PID, and that one allowing a 3PID makes Synapse accept to bind | ||
the 3PID. Also checks that the module is passed a boolean indicating whether the | ||
user to bind this 3PID to is currently registering. | ||
""" | ||
self._test_3pid_allowed("rin", False) | ||
self._test_3pid_allowed("kitay", True) | ||
|
||
def _test_3pid_allowed(self, username: str, registration: bool): | ||
"""Tests that the "is_3pid_allowed" module callback is called correctly, using | ||
either /register or /account URLs depending on the arguments. | ||
Args: | ||
username: The username to use for the test. | ||
registration: Whether to test with registration URLs. | ||
""" | ||
self.hs.get_identity_handler().send_threepid_validation = Mock( | ||
return_value=make_awaitable(0), | ||
) | ||
|
||
m = Mock(return_value=make_awaitable(False)) | ||
self.hs.get_password_auth_provider().is_3pid_allowed_callbacks = [m] | ||
|
||
self.register_user(username, "password") | ||
tok = self.login(username, "password") | ||
|
||
if registration: | ||
url = "/register/email/requestToken" | ||
else: | ||
url = "/account/3pid/email/requestToken" | ||
|
||
channel = self.make_request( | ||
"POST", | ||
url, | ||
{ | ||
"client_secret": "foo", | ||
"email": "[email protected]", | ||
"send_attempt": 0, | ||
}, | ||
access_token=tok, | ||
) | ||
self.assertEqual(channel.code, 403, channel.result) | ||
self.assertEqual( | ||
channel.json_body["errcode"], | ||
Codes.THREEPID_DENIED, | ||
channel.json_body, | ||
) | ||
|
||
m.assert_called_once_with("email", "[email protected]", registration) | ||
|
||
m = Mock(return_value=make_awaitable(True)) | ||
self.hs.get_password_auth_provider().is_3pid_allowed_callbacks = [m] | ||
|
||
channel = self.make_request( | ||
"POST", | ||
url, | ||
{ | ||
"client_secret": "foo", | ||
"email": "[email protected]", | ||
"send_attempt": 0, | ||
}, | ||
access_token=tok, | ||
) | ||
self.assertEqual(channel.code, 200, channel.result) | ||
self.assertIn("sid", channel.json_body) | ||
|
||
m.assert_called_once_with("email", "[email protected]", registration) | ||
|
||
def _setup_get_username_for_registration(self) -> Mock: | ||
"""Registers a get_username_for_registration callback that appends "-foo" to the | ||
username the client is trying to register. | ||
|