Merge 327555dddf
into be65a8ec01
commit
ae11df6b0c
|
@ -0,0 +1 @@
|
|||
Enabled login_via_existing_session by default.
|
|
@ -2743,14 +2743,11 @@ ui_auth:
|
|||
Matrix supports the ability of an existing session to mint a login token for
|
||||
another client.
|
||||
|
||||
Synapse disables this by default as it has security ramifications -- a malicious
|
||||
client could use the mechanism to spawn more than one session.
|
||||
|
||||
The duration of time the generated token is valid for can be configured with the
|
||||
`token_timeout` sub-option.
|
||||
|
||||
User-interactive authentication is required when this is enabled unless the
|
||||
`require_ui_auth` sub-option is set to `False`.
|
||||
To protect against malicious clients abusing this capability, user-interactive authentication
|
||||
is required unless the `require_ui_auth` sub-option is set to `False`.
|
||||
|
||||
Example configuration:
|
||||
```yaml
|
||||
|
|
|
@ -63,7 +63,7 @@ class AuthConfig(Config):
|
|||
|
||||
# Logging in with an existing session.
|
||||
login_via_existing = config.get("login_via_existing_session", {})
|
||||
self.login_via_existing_enabled = login_via_existing.get("enabled", False)
|
||||
self.login_via_existing_enabled = login_via_existing.get("enabled", True)
|
||||
self.login_via_existing_require_ui_auth = login_via_existing.get(
|
||||
"require_ui_auth", True
|
||||
)
|
||||
|
|
|
@ -54,6 +54,9 @@ class MSC3861OAuthDelegation(TestCase):
|
|||
**default_config("test"),
|
||||
"public_baseurl": BASE_URL,
|
||||
"enable_registration": False,
|
||||
"login_via_existing_session": {
|
||||
"enabled": False,
|
||||
},
|
||||
"experimental_features": {
|
||||
"msc3861": {
|
||||
"enabled": True,
|
||||
|
|
|
@ -113,6 +113,9 @@ class MSC3861OAuthDelegation(HomeserverTestCase):
|
|||
config = super().default_config()
|
||||
config["public_baseurl"] = BASE_URL
|
||||
config["disable_registration"] = True
|
||||
config["login_via_existing_session"] = {
|
||||
"enabled": False,
|
||||
}
|
||||
config["experimental_features"] = {
|
||||
"msc3861": {
|
||||
"enabled": True,
|
||||
|
|
|
@ -35,8 +35,9 @@ from tests.server import FakeChannel
|
|||
from tests.unittest import override_config
|
||||
|
||||
# Login flows we expect to appear in the list after the normal ones.
|
||||
ADDITIONAL_LOGIN_FLOWS = [
|
||||
ADDITIONAL_LOGIN_FLOWS: List[Dict] = [
|
||||
{"type": "m.login.application_service"},
|
||||
{"type": "m.login.token", "get_login_token": True},
|
||||
]
|
||||
|
||||
# a mock instance which the dummy auth providers delegate to, so we can see what's going
|
||||
|
@ -44,6 +45,10 @@ ADDITIONAL_LOGIN_FLOWS = [
|
|||
mock_password_provider = Mock()
|
||||
|
||||
|
||||
def sort_flows(flows: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
return sorted(flows, key=lambda f: f["type"])
|
||||
|
||||
|
||||
class LegacyPasswordOnlyAuthProvider:
|
||||
"""A legacy password_provider which only implements `check_password`."""
|
||||
|
||||
|
@ -183,7 +188,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
def password_only_auth_provider_login_test_body(self) -> None:
|
||||
# login flows should only have m.login.password
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, [{"type": "m.login.password"}] + ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(
|
||||
sort_flows(flows),
|
||||
sort_flows([{"type": "m.login.password"}] + ADDITIONAL_LOGIN_FLOWS),
|
||||
)
|
||||
|
||||
# check_password must return an awaitable
|
||||
mock_password_provider.check_password = AsyncMock(return_value=True)
|
||||
|
@ -364,7 +372,7 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
"""password auth doesn't work if it's disabled across the board"""
|
||||
# login flows should be empty
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(sort_flows(flows), sort_flows(ADDITIONAL_LOGIN_FLOWS))
|
||||
|
||||
# login shouldn't work and should be rejected with a 400 ("unknown login type")
|
||||
channel = self._send_password_login("u", "p")
|
||||
|
@ -385,9 +393,11 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
# (password must come first, because reasons)
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(
|
||||
flows,
|
||||
[{"type": "m.login.password"}, {"type": "test.login_type"}]
|
||||
+ ADDITIONAL_LOGIN_FLOWS,
|
||||
sort_flows(flows),
|
||||
sort_flows(
|
||||
[{"type": "m.login.password"}, {"type": "test.login_type"}]
|
||||
+ ADDITIONAL_LOGIN_FLOWS
|
||||
),
|
||||
)
|
||||
|
||||
# login with missing param should be rejected
|
||||
|
@ -514,7 +524,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
self.register_user("localuser", "localpass")
|
||||
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(
|
||||
sort_flows(flows),
|
||||
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
|
||||
)
|
||||
|
||||
# login shouldn't work and should be rejected with a 400 ("unknown login type")
|
||||
channel = self._send_password_login("localuser", "localpass")
|
||||
|
@ -549,7 +562,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
self.register_user("localuser", "localpass")
|
||||
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(
|
||||
sort_flows(flows),
|
||||
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
|
||||
)
|
||||
|
||||
# login shouldn't work and should be rejected with a 400 ("unknown login type")
|
||||
channel = self._send_password_login("localuser", "localpass")
|
||||
|
@ -580,7 +596,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
self.register_user("localuser", "localpass")
|
||||
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(
|
||||
sort_flows(flows),
|
||||
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
|
||||
)
|
||||
|
||||
# login shouldn't work and should be rejected with a 400 ("unknown login type")
|
||||
channel = self._send_password_login("localuser", "localpass")
|
||||
|
@ -685,7 +704,10 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
self.register_user("localuser", "localpass")
|
||||
|
||||
flows = self._get_login_flows()
|
||||
self.assertEqual(flows, [{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS)
|
||||
self.assertEqual(
|
||||
sort_flows(flows),
|
||||
sort_flows([{"type": "test.login_type"}] + ADDITIONAL_LOGIN_FLOWS),
|
||||
)
|
||||
|
||||
# password login shouldn't work and should be rejected with a 400
|
||||
# ("unknown login type")
|
||||
|
@ -923,7 +945,7 @@ class PasswordAuthProviderTests(unittest.HomeserverTestCase):
|
|||
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
|
||||
return channel.json_body
|
||||
|
||||
def _get_login_flows(self) -> JsonDict:
|
||||
def _get_login_flows(self) -> List[JsonDict]:
|
||||
channel = self.make_request("GET", "/_matrix/client/r0/login")
|
||||
self.assertEqual(channel.code, HTTPStatus.OK, channel.result)
|
||||
return channel.json_body["flows"]
|
||||
|
|
|
@ -39,6 +39,7 @@ class JWKSTestCase(HomeserverTestCase):
|
|||
@override_config(
|
||||
{
|
||||
"disable_registration": True,
|
||||
"login_via_existing_session": {"enabled": False},
|
||||
"experimental_features": {
|
||||
"msc3861": {
|
||||
"enabled": True,
|
||||
|
@ -59,6 +60,7 @@ class JWKSTestCase(HomeserverTestCase):
|
|||
@override_config(
|
||||
{
|
||||
"disable_registration": True,
|
||||
"login_via_existing_session": {"enabled": False},
|
||||
"experimental_features": {
|
||||
"msc3861": {
|
||||
"enabled": True,
|
||||
|
|
|
@ -187,6 +187,7 @@ class CapabilitiesTestCase(unittest.HomeserverTestCase):
|
|||
for room_version in details["support"]:
|
||||
self.assertTrue(room_version in KNOWN_ROOM_VERSIONS, str(room_version))
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": False}})
|
||||
def test_get_get_token_login_fields_when_disabled(self) -> None:
|
||||
"""By default login via an existing session is disabled."""
|
||||
access_token = self.get_success(
|
||||
|
@ -201,7 +202,6 @@ class CapabilitiesTestCase(unittest.HomeserverTestCase):
|
|||
self.assertEqual(channel.code, HTTPStatus.OK)
|
||||
self.assertFalse(capabilities["m.get_login_token"]["enabled"])
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": True}})
|
||||
def test_get_get_token_login_fields_when_enabled(self) -> None:
|
||||
access_token = self.get_success(
|
||||
self.auth_handler.create_access_token_for_user_id(
|
||||
|
|
|
@ -510,6 +510,7 @@ class LoginRestServletTestCase(unittest.HomeserverTestCase):
|
|||
ApprovalNoticeMedium.NONE, channel.json_body["approval_notice_medium"]
|
||||
)
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": False}})
|
||||
def test_get_login_flows_with_login_via_existing_disabled(self) -> None:
|
||||
"""GET /login should return m.login.token without get_login_token"""
|
||||
channel = self.make_request("GET", "/_matrix/client/r0/login")
|
||||
|
@ -518,7 +519,6 @@ class LoginRestServletTestCase(unittest.HomeserverTestCase):
|
|||
flows = {flow["type"]: flow for flow in channel.json_body["flows"]}
|
||||
self.assertNotIn("m.login.token", flows)
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": True}})
|
||||
def test_get_login_flows_with_login_via_existing_enabled(self) -> None:
|
||||
"""GET /login should return m.login.token with get_login_token true"""
|
||||
channel = self.make_request("GET", "/_matrix/client/r0/login")
|
||||
|
|
|
@ -46,6 +46,7 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
|
|||
self.user = "user123"
|
||||
self.password = "password"
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": False}})
|
||||
def test_disabled(self) -> None:
|
||||
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=None)
|
||||
self.assertEqual(channel.code, 404)
|
||||
|
@ -56,12 +57,10 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
|
|||
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=token)
|
||||
self.assertEqual(channel.code, 404)
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": True}})
|
||||
def test_require_auth(self) -> None:
|
||||
channel = self.make_request("POST", GET_TOKEN_ENDPOINT, {}, access_token=None)
|
||||
self.assertEqual(channel.code, 401)
|
||||
|
||||
@override_config({"login_via_existing_session": {"enabled": True}})
|
||||
def test_uia_on(self) -> None:
|
||||
user_id = self.register_user(self.user, self.password)
|
||||
token = self.login(self.user, self.password)
|
||||
|
@ -95,9 +94,7 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
|
|||
self.assertEqual(channel.code, 200, channel.result)
|
||||
self.assertEqual(channel.json_body["user_id"], user_id)
|
||||
|
||||
@override_config(
|
||||
{"login_via_existing_session": {"enabled": True, "require_ui_auth": False}}
|
||||
)
|
||||
@override_config({"login_via_existing_session": {"require_ui_auth": False}})
|
||||
def test_uia_off(self) -> None:
|
||||
user_id = self.register_user(self.user, self.password)
|
||||
token = self.login(self.user, self.password)
|
||||
|
@ -119,7 +116,6 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
|
|||
@override_config(
|
||||
{
|
||||
"login_via_existing_session": {
|
||||
"enabled": True,
|
||||
"require_ui_auth": False,
|
||||
"token_timeout": "15s",
|
||||
}
|
||||
|
@ -136,7 +132,6 @@ class LoginTokenRequestServletTestCase(unittest.HomeserverTestCase):
|
|||
@override_config(
|
||||
{
|
||||
"login_via_existing_session": {
|
||||
"enabled": True,
|
||||
"require_ui_auth": False,
|
||||
"token_timeout": "15s",
|
||||
}
|
||||
|
|
|
@ -113,6 +113,7 @@ class WellKnownTests(unittest.HomeserverTestCase):
|
|||
},
|
||||
},
|
||||
"disable_registration": True,
|
||||
"login_via_existing_session": {"enabled": False},
|
||||
}
|
||||
)
|
||||
def test_client_well_known_msc3861_oauth_delegation(self) -> None:
|
||||
|
|
Loading…
Reference in New Issue