Skip to content

Commit 6c55c57

Browse files
committed
Parametrize the tests for disabled endpoints when delegating auth
1 parent 6dd6fd1 commit 6c55c57

1 file changed

Lines changed: 171 additions & 128 deletions

File tree

tests/handlers/test_oauth_delegation.py

Lines changed: 171 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from unittest.mock import ANY, AsyncMock, Mock
2727
from urllib.parse import parse_qs
2828

29+
from parameterized import parameterized_class
2930
from signedjson.key import (
3031
encode_verify_key_base64,
3132
generate_signing_key,
@@ -48,7 +49,7 @@
4849
from synapse.rest import admin
4950
from synapse.rest.client import account, devices, keys, login, logout, register
5051
from synapse.server import HomeServer
51-
from synapse.types import JsonDict, UserID
52+
from synapse.types import JsonDict, UserID, create_requester
5253
from synapse.util import Clock
5354

5455
from tests.server import FakeChannel
@@ -109,12 +110,7 @@ async def get_json(url: str) -> JsonDict:
109110
class MSC3861OAuthDelegation(HomeserverTestCase):
110111
servlets = [
111112
account.register_servlets,
112-
devices.register_servlets,
113113
keys.register_servlets,
114-
register.register_servlets,
115-
login.register_servlets,
116-
logout.register_servlets,
117-
admin.register_servlets,
118114
]
119115

120116
def default_config(self) -> Dict[str, Any]:
@@ -635,6 +631,170 @@ def test_cross_signing(self) -> None:
635631

636632
self.assertEqual(channel.code, HTTPStatus.UNAUTHORIZED, channel.json_body)
637633

634+
def test_admin_token(self) -> None:
635+
"""The handler should return a requester with admin rights when admin_token is used."""
636+
self._set_introspection_returnvalue({"active": False})
637+
638+
request = Mock(args={})
639+
request.args[b"access_token"] = [b"admin_token_value"]
640+
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
641+
requester = self.get_success(self.auth.get_user_by_req(request))
642+
self.assertEqual(
643+
requester.user.to_string(),
644+
OIDC_ADMIN_USERID,
645+
)
646+
self.assertEqual(requester.is_guest, False)
647+
self.assertEqual(requester.device_id, None)
648+
self.assertEqual(
649+
get_awaitable_result(self.auth.is_server_admin(requester)), True
650+
)
651+
652+
# There should be no call to the introspection endpoint
653+
self._rust_client.post.assert_not_called()
654+
655+
@override_config({"mau_stats_only": True})
656+
def test_request_tracking(self) -> None:
657+
"""Using an access token should update the client_ips and MAU tables."""
658+
# To start, there are no MAU users.
659+
store = self.hs.get_datastores().main
660+
mau = self.get_success(store.get_monthly_active_count())
661+
self.assertEqual(mau, 0)
662+
663+
known_token = "token-token-GOOD-:)"
664+
665+
async def mock_http_client_request(
666+
url: str, request_body: str, **kwargs: Any
667+
) -> bytes:
668+
"""Mocked auth provider response."""
669+
token = parse_qs(request_body)["token"][0]
670+
if token == known_token:
671+
return json.dumps(
672+
{
673+
"active": True,
674+
"scope": MATRIX_USER_SCOPE,
675+
"sub": SUBJECT,
676+
"username": USERNAME,
677+
},
678+
).encode("utf-8")
679+
680+
return json.dumps({"active": False}).encode("utf-8")
681+
682+
self._rust_client.post = mock_http_client_request
683+
684+
EXAMPLE_IPV4_ADDR = "123.123.123.123"
685+
EXAMPLE_USER_AGENT = "httprettygood"
686+
687+
# First test a known access token
688+
channel = FakeChannel(self.site, self.reactor)
689+
# type-ignore: FakeChannel is a mock of an HTTPChannel, not a proper HTTPChannel
690+
req = SynapseRequest(channel, self.site, self.hs.hostname) # type: ignore[arg-type]
691+
req.client.host = EXAMPLE_IPV4_ADDR
692+
req.requestHeaders.addRawHeader("Authorization", f"Bearer {known_token}")
693+
req.requestHeaders.addRawHeader("User-Agent", EXAMPLE_USER_AGENT)
694+
req.content = BytesIO(b"")
695+
req.requestReceived(
696+
b"GET",
697+
b"/_matrix/client/v3/account/whoami",
698+
b"1.1",
699+
)
700+
channel.await_result()
701+
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
702+
self.assertEqual(channel.json_body["user_id"], USER_ID, channel.json_body)
703+
704+
# Expect to see one MAU entry, from the first request
705+
mau = self.get_success(store.get_monthly_active_count())
706+
self.assertEqual(mau, 1)
707+
708+
conn_infos = self.get_success(
709+
store.get_user_ip_and_agents(UserID.from_string(USER_ID))
710+
)
711+
self.assertEqual(len(conn_infos), 1, conn_infos)
712+
conn_info = conn_infos[0]
713+
self.assertEqual(conn_info["access_token"], known_token)
714+
self.assertEqual(conn_info["ip"], EXAMPLE_IPV4_ADDR)
715+
self.assertEqual(conn_info["user_agent"], EXAMPLE_USER_AGENT)
716+
717+
# Now test MAS making a request using the special __oidc_admin token
718+
MAS_IPV4_ADDR = "127.0.0.1"
719+
MAS_USER_AGENT = "masmasmas"
720+
721+
channel = FakeChannel(self.site, self.reactor)
722+
req = SynapseRequest(channel, self.site, self.hs.hostname) # type: ignore[arg-type]
723+
req.client.host = MAS_IPV4_ADDR
724+
req.requestHeaders.addRawHeader(
725+
"Authorization", f"Bearer {self.auth._admin_token()}"
726+
)
727+
req.requestHeaders.addRawHeader("User-Agent", MAS_USER_AGENT)
728+
req.content = BytesIO(b"")
729+
req.requestReceived(
730+
b"GET",
731+
b"/_matrix/client/v3/account/whoami",
732+
b"1.1",
733+
)
734+
channel.await_result()
735+
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
736+
self.assertEqual(
737+
channel.json_body["user_id"], OIDC_ADMIN_USERID, channel.json_body
738+
)
739+
740+
# Still expect to see one MAU entry, from the first request
741+
mau = self.get_success(store.get_monthly_active_count())
742+
self.assertEqual(mau, 1)
743+
744+
conn_infos = self.get_success(
745+
store.get_user_ip_and_agents(UserID.from_string(OIDC_ADMIN_USERID))
746+
)
747+
self.assertEqual(conn_infos, [])
748+
749+
750+
@parameterized_class(
751+
("config",),
752+
[
753+
(
754+
{
755+
"experimental_features": {
756+
"msc3861": {
757+
"enabled": True,
758+
"issuer": ISSUER,
759+
"client_id": CLIENT_ID,
760+
"client_auth_method": "client_secret_post",
761+
"client_secret": CLIENT_SECRET,
762+
"admin_token": "admin_token_value",
763+
}
764+
}
765+
},
766+
),
767+
(
768+
{
769+
"matrix_authentication_service": {
770+
"enabled": True,
771+
"endpoint": "http://localhost:1234/",
772+
"secret": "secret",
773+
},
774+
},
775+
),
776+
],
777+
)
778+
class DisabledEndpointsTestCase(HomeserverTestCase):
779+
servlets = [
780+
account.register_servlets,
781+
devices.register_servlets,
782+
keys.register_servlets,
783+
register.register_servlets,
784+
login.register_servlets,
785+
logout.register_servlets,
786+
admin.register_servlets,
787+
]
788+
789+
config: Dict[str, Any]
790+
791+
def default_config(self) -> Dict[str, Any]:
792+
config = super().default_config()
793+
config["public_baseurl"] = BASE_URL
794+
config["disable_registration"] = True
795+
config.update(self.config)
796+
return config
797+
638798
def expect_unauthorized(
639799
self, method: str, path: str, content: Union[bytes, str, JsonDict] = ""
640800
) -> None:
@@ -774,13 +934,11 @@ def test_device_management_endpoints_removed(self) -> None:
774934

775935
# Because we still support those endpoints with ASes, it checks the
776936
# access token before returning 404
777-
self._set_introspection_returnvalue(
778-
{
779-
"active": True,
780-
"sub": SUBJECT,
781-
"scope": " ".join([MATRIX_USER_SCOPE, MATRIX_DEVICE_SCOPE]),
782-
"username": USERNAME,
783-
},
937+
self.hs.get_auth().get_user_by_req = AsyncMock( # type: ignore[method-assign]
938+
return_value=create_requester(
939+
user_id=USER_ID,
940+
device_id=DEVICE,
941+
)
784942
)
785943

786944
self.expect_unrecognized("POST", "/_matrix/client/v3/delete_devices", auth=True)
@@ -810,118 +968,3 @@ def test_admin_api_endpoints_removed(self) -> None:
810968
self.expect_unrecognized("GET", "/_synapse/admin/v1/users/foo/admin")
811969
self.expect_unrecognized("PUT", "/_synapse/admin/v1/users/foo/admin")
812970
self.expect_unrecognized("POST", "/_synapse/admin/v1/account_validity/validity")
813-
814-
def test_admin_token(self) -> None:
815-
"""The handler should return a requester with admin rights when admin_token is used."""
816-
self._set_introspection_returnvalue({"active": False})
817-
818-
request = Mock(args={})
819-
request.args[b"access_token"] = [b"admin_token_value"]
820-
request.requestHeaders.getRawHeaders = mock_getRawHeaders()
821-
requester = self.get_success(self.auth.get_user_by_req(request))
822-
self.assertEqual(
823-
requester.user.to_string(),
824-
OIDC_ADMIN_USERID,
825-
)
826-
self.assertEqual(requester.is_guest, False)
827-
self.assertEqual(requester.device_id, None)
828-
self.assertEqual(
829-
get_awaitable_result(self.auth.is_server_admin(requester)), True
830-
)
831-
832-
# There should be no call to the introspection endpoint
833-
self._rust_client.post.assert_not_called()
834-
835-
@override_config({"mau_stats_only": True})
836-
def test_request_tracking(self) -> None:
837-
"""Using an access token should update the client_ips and MAU tables."""
838-
# To start, there are no MAU users.
839-
store = self.hs.get_datastores().main
840-
mau = self.get_success(store.get_monthly_active_count())
841-
self.assertEqual(mau, 0)
842-
843-
known_token = "token-token-GOOD-:)"
844-
845-
async def mock_http_client_request(
846-
url: str, request_body: str, **kwargs: Any
847-
) -> bytes:
848-
"""Mocked auth provider response."""
849-
token = parse_qs(request_body)["token"][0]
850-
if token == known_token:
851-
return json.dumps(
852-
{
853-
"active": True,
854-
"scope": MATRIX_USER_SCOPE,
855-
"sub": SUBJECT,
856-
"username": USERNAME,
857-
},
858-
).encode("utf-8")
859-
860-
return json.dumps({"active": False}).encode("utf-8")
861-
862-
self._rust_client.post = mock_http_client_request
863-
864-
EXAMPLE_IPV4_ADDR = "123.123.123.123"
865-
EXAMPLE_USER_AGENT = "httprettygood"
866-
867-
# First test a known access token
868-
channel = FakeChannel(self.site, self.reactor)
869-
# type-ignore: FakeChannel is a mock of an HTTPChannel, not a proper HTTPChannel
870-
req = SynapseRequest(channel, self.site, self.hs.hostname) # type: ignore[arg-type]
871-
req.client.host = EXAMPLE_IPV4_ADDR
872-
req.requestHeaders.addRawHeader("Authorization", f"Bearer {known_token}")
873-
req.requestHeaders.addRawHeader("User-Agent", EXAMPLE_USER_AGENT)
874-
req.content = BytesIO(b"")
875-
req.requestReceived(
876-
b"GET",
877-
b"/_matrix/client/v3/account/whoami",
878-
b"1.1",
879-
)
880-
channel.await_result()
881-
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
882-
self.assertEqual(channel.json_body["user_id"], USER_ID, channel.json_body)
883-
884-
# Expect to see one MAU entry, from the first request
885-
mau = self.get_success(store.get_monthly_active_count())
886-
self.assertEqual(mau, 1)
887-
888-
conn_infos = self.get_success(
889-
store.get_user_ip_and_agents(UserID.from_string(USER_ID))
890-
)
891-
self.assertEqual(len(conn_infos), 1, conn_infos)
892-
conn_info = conn_infos[0]
893-
self.assertEqual(conn_info["access_token"], known_token)
894-
self.assertEqual(conn_info["ip"], EXAMPLE_IPV4_ADDR)
895-
self.assertEqual(conn_info["user_agent"], EXAMPLE_USER_AGENT)
896-
897-
# Now test MAS making a request using the special __oidc_admin token
898-
MAS_IPV4_ADDR = "127.0.0.1"
899-
MAS_USER_AGENT = "masmasmas"
900-
901-
channel = FakeChannel(self.site, self.reactor)
902-
req = SynapseRequest(channel, self.site, self.hs.hostname) # type: ignore[arg-type]
903-
req.client.host = MAS_IPV4_ADDR
904-
req.requestHeaders.addRawHeader(
905-
"Authorization", f"Bearer {self.auth._admin_token()}"
906-
)
907-
req.requestHeaders.addRawHeader("User-Agent", MAS_USER_AGENT)
908-
req.content = BytesIO(b"")
909-
req.requestReceived(
910-
b"GET",
911-
b"/_matrix/client/v3/account/whoami",
912-
b"1.1",
913-
)
914-
channel.await_result()
915-
self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)
916-
self.assertEqual(
917-
channel.json_body["user_id"], OIDC_ADMIN_USERID, channel.json_body
918-
)
919-
920-
# Still expect to see one MAU entry, from the first request
921-
mau = self.get_success(store.get_monthly_active_count())
922-
self.assertEqual(mau, 1)
923-
924-
conn_infos = self.get_success(
925-
store.get_user_ip_and_agents(UserID.from_string(OIDC_ADMIN_USERID))
926-
)
927-
self.assertEqual(conn_infos, [])

0 commit comments

Comments
 (0)