def test_get_user_by_req_appservice_valid_token_valid_device_id(self): """ Tests that when an application service passes the device_id URL parameter with the ID of a valid device for the user in question, the requester instance tracks that device ID. """ masquerading_user_id = b"@doppelganger:matrix.org" masquerading_device_id = b"DOPPELDEVICE" app_service = Mock(token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None) app_service.is_interested_in_user = Mock(return_value=True) self.store.get_app_service_by_token = Mock(return_value=app_service) # This just needs to return a truth-y value. self.store.get_user_by_id = simple_async_mock({"is_guest": False}) self.store.get_user_by_access_token = simple_async_mock(None) # This also needs to just return a truth-y value self.store.get_device = simple_async_mock({"hidden": False}) request = Mock(args={}) request.getClientIP.return_value = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.args[b"user_id"] = [masquerading_user_id] request.args[b"org.matrix.msc3202.device_id"] = [ masquerading_device_id ] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = self.get_success(self.auth.get_user_by_req(request)) self.assertEquals(requester.user.to_string(), masquerading_user_id.decode("utf8")) self.assertEquals(requester.device_id, masquerading_device_id.decode("utf8"))
def test_blocking_mau__appservice_requester_disallowed_when_tracking_ips( self): self.auth_blocking._max_mau_value = 50 self.auth_blocking._limit_usage_by_mau = True self.auth_blocking._track_appservice_user_ips = True self.store.get_monthly_active_count = simple_async_mock(100) self.store.user_last_seen_monthly_active = simple_async_mock() self.store.is_trial_user = simple_async_mock() appservice = ApplicationService( "abcd", self.hs.config.server.server_name, id="1234", namespaces={ "users": [{ "regex": "@_appservice.*:sender", "exclusive": True }] }, sender="@appservice:sender", ) requester = Requester( user="******", access_token_id=None, device_id="FOOBAR", is_guest=False, shadow_banned=False, app_service=appservice, authenticated_entity="@appservice:server", ) self.get_failure(self.auth.check_auth_blocking(requester=requester), ResourceLimitError)
def test_get_user_by_req_appservice_valid_token_invalid_device_id(self): """ Tests that when an application service passes the device_id URL parameter with an ID that is not a valid device ID for the user in question, the request fails with the appropriate error code. """ masquerading_user_id = b"@doppelganger:matrix.org" masquerading_device_id = b"NOT_A_REAL_DEVICE_ID" app_service = Mock(token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None) app_service.is_interested_in_user = Mock(return_value=True) self.store.get_app_service_by_token = Mock(return_value=app_service) # This just needs to return a truth-y value. self.store.get_user_by_id = simple_async_mock({"is_guest": False}) self.store.get_user_by_access_token = simple_async_mock(None) # This also needs to just return a falsey value self.store.get_device = simple_async_mock(None) request = Mock(args={}) request.getClientIP.return_value = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.args[b"user_id"] = [masquerading_user_id] request.args[b"org.matrix.msc3202.device_id"] = [ masquerading_device_id ] request.requestHeaders.getRawHeaders = mock_getRawHeaders() failure = self.get_failure(self.auth.get_user_by_req(request), AuthError) self.assertEquals(failure.value.code, 400) self.assertEquals(failure.value.errcode, Codes.EXCLUSIVE)
def test_single_service_up_txn_not_sent(self): # Test: The AS is up and the txn is not sent. A Recoverer is made and # started. service = Mock() events = [Mock(), Mock()] txn_id = "foobar" txn = Mock(id=txn_id, service=service, events=events) # mock methods self.store.get_appservice_state = simple_async_mock( ApplicationServiceState.UP) self.store.set_appservice_state = simple_async_mock(True) txn.send = simple_async_mock(False) # fails to send self.store.create_appservice_txn = simple_async_mock(txn) # actual call self.successResultOf( defer.ensureDeferred(self.txnctrl.send(service, events))) self.store.create_appservice_txn.assert_called_once_with( service=service, events=events, ephemeral=[]) self.assertEquals(1, self.recoverer_fn.call_count) # recoverer made self.assertEquals(1, self.recoverer.recover.call_count) # and invoked self.assertEquals(1, len(self.txnctrl.recoverers)) # and stored self.assertEquals(0, txn.complete.call_count) # txn not completed self.store.set_appservice_state.assert_called_once_with( service, ApplicationServiceState.DOWN # service marked as down )
async def _make_callback_with_userinfo( hs: HomeServer, userinfo: dict, client_redirect_url: str = "http://client/redirect") -> None: """Mock up an OIDC callback with the given userinfo dict We'll pull out the OIDC handler from the homeserver, stub out a couple of methods, and poke in the userinfo dict as if it were the response to an OIDC userinfo call. Args: hs: the HomeServer impl to send the callback to. userinfo: the OIDC userinfo dict client_redirect_url: the URL to redirect to on success. """ from synapse.handlers.oidc import OidcSessionData handler = hs.get_oidc_handler() provider = handler._providers["oidc"] provider._exchange_code = simple_async_mock(return_value={}) provider._parse_id_token = simple_async_mock(return_value=userinfo) provider._fetch_userinfo = simple_async_mock(return_value=userinfo) state = "state" session = handler._token_generator.generate_oidc_session_token( state=state, session_data=OidcSessionData( idp_id="oidc", nonce="nonce", client_redirect_url=client_redirect_url, ui_auth_session_id="", ), ) request = _build_callback_request("code", state, session) await handler.handle_oidc_callback(request)
def test_recover_retry_txn(self): txn = Mock() txns = [txn, None] pop_txn = False def take_txn(*args, **kwargs): if pop_txn: return defer.succeed(txns.pop(0)) else: return defer.succeed(txn) self.store.get_oldest_unsent_txn = Mock(side_effect=take_txn) self.recoverer.recover() self.assertEqual(0, self.store.get_oldest_unsent_txn.call_count) txn.send = simple_async_mock(False) txn.complete = simple_async_mock(None) self.clock.advance_time(2) self.assertEqual(1, txn.send.call_count) self.assertEqual(0, txn.complete.call_count) self.assertEqual(0, self.callback.call_count) self.clock.advance_time(4) self.assertEqual(2, txn.send.call_count) self.assertEqual(0, txn.complete.call_count) self.assertEqual(0, self.callback.call_count) self.clock.advance_time(8) self.assertEqual(3, txn.send.call_count) self.assertEqual(0, txn.complete.call_count) self.assertEqual(0, self.callback.call_count) txn.send = simple_async_mock(True) # successfully send the txn pop_txn = True # returns the txn the first time, then no more. self.clock.advance_time(16) self.assertEqual(1, txn.send.call_count) # new mock reset call count self.assertEqual(1, txn.complete.call_count) self.callback.assert_called_once_with(self.recoverer)
def test_single_service_up_txn_sent(self): # Test: The AS is up and the txn is successfully sent. service = Mock() events = [Mock(), Mock()] txn_id = "foobar" txn = Mock(id=txn_id, service=service, events=events) # mock methods self.store.get_appservice_state = simple_async_mock( ApplicationServiceState.UP) txn.send = simple_async_mock(True) txn.complete = simple_async_mock(True) self.store.create_appservice_txn = simple_async_mock(txn) # actual call self.successResultOf( defer.ensureDeferred(self.txnctrl.send(service, events))) self.store.create_appservice_txn.assert_called_once_with( service=service, events=events, ephemeral=[] # txn made and saved ) self.assertEquals(0, len(self.txnctrl.recoverers)) # no recoverer made txn.complete.assert_called_once_with(self.store) # txn completed
def test_single_service_up_txn_sent(self): # Test: The AS is up and the txn is successfully sent. service = Mock() events = [Mock(), Mock()] txn_id = "foobar" txn = Mock(id=txn_id, service=service, events=events) # mock methods self.store.get_appservice_state = simple_async_mock( ApplicationServiceState.UP) txn.send = simple_async_mock(True) txn.complete = simple_async_mock(True) self.store.create_appservice_txn = simple_async_mock(txn) # actual call self.successResultOf( defer.ensureDeferred(self.txnctrl.send(service, events))) self.store.create_appservice_txn.assert_called_once_with( service=service, events=events, ephemeral=[], to_device_messages=[], # txn made and saved one_time_key_counts={}, unused_fallback_keys={}, device_list_summary=DeviceListUpdates(), ) self.assertEqual(0, len(self.txnctrl.recoverers)) # no recoverer made txn.complete.assert_called_once_with(self.store) # txn completed
def test_cannot_use_regular_token_as_guest(self): USER_ID = "@percy:matrix.org" self.store.add_access_token_to_user = simple_async_mock(None) self.store.get_device = simple_async_mock(None) token = self.get_success( self.hs.get_auth_handler().get_access_token_for_user_id( USER_ID, "DEVICE", valid_until_ms=None ) ) self.store.add_access_token_to_user.assert_called_with( user_id=USER_ID, token=token, device_id="DEVICE", valid_until_ms=None, puppets_user_id=None, ) async def get_user(tok): if token != tok: return None return TokenLookupResult( user_id=USER_ID, is_guest=False, token_id=1234, device_id="DEVICE", ) self.store.get_user_by_access_token = get_user self.store.get_user_by_id = simple_async_mock({"is_guest": False}) # check the token works request = Mock(args={}) request.args[b"access_token"] = [token.encode("ascii")] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = self.get_success( self.auth.get_user_by_req(request, allow_guest=True) ) self.assertEqual(UserID.from_string(USER_ID), requester.user) self.assertFalse(requester.is_guest) # add an is_guest caveat mac = pymacaroons.Macaroon.deserialize(token) mac.add_first_party_caveat("guest = true") guest_tok = mac.serialize() # the token should *not* work now request = Mock(args={}) request.args[b"access_token"] = [guest_tok.encode("ascii")] request.requestHeaders.getRawHeaders = mock_getRawHeaders() cm = self.get_failure( self.auth.get_user_by_req(request, allow_guest=True), InvalidClientCredentialsError, ) self.assertEqual(401, cm.value.code) self.assertEqual("Guest access token used for regular user", cm.value.msg) self.store.get_user_by_id.assert_called_with(USER_ID)
def test_single_service_down(self): # Test: The AS is down so it shouldn't push; Recoverers will do it. # It should still make a transaction though. service = Mock() events = [Mock(), Mock()] txn = Mock(id="idhere", service=service, events=events) self.store.get_appservice_state = simple_async_mock( ApplicationServiceState.DOWN) self.store.create_appservice_txn = simple_async_mock(txn) # actual call self.successResultOf( defer.ensureDeferred(self.txnctrl.send(service, events))) self.store.create_appservice_txn.assert_called_once_with( service=service, events=events, ephemeral=[], to_device_messages=[], # txn made and saved one_time_key_counts={}, unused_fallback_keys={}, device_list_summary=DeviceListUpdates(), ) self.assertEqual(0, txn.send.call_count) # txn not sent though self.assertEqual(0, txn.complete.call_count) # or completed
def test_regex_alias_match(self): self.service.namespaces[ApplicationService.NS_ALIASES].append( _regex("#irc_.*:matrix.org")) self.store.get_aliases_for_room = simple_async_mock( ["#irc_foobar:matrix.org", "#athing:matrix.org"]) self.store.get_users_in_room = simple_async_mock([]) self.assertTrue((yield defer.ensureDeferred( self.service.is_interested_in_event(self.event.event_id, self.event, self.store))))
def test_get_user_by_req_user_valid_token(self): user_info = TokenLookupResult(user_id=self.test_user, token_id=5, device_id="device") self.store.get_user_by_access_token = simple_async_mock(user_info) self.store.mark_access_token_as_used = simple_async_mock(None) request = Mock(args={}) request.args[b"access_token"] = [self.test_token] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = self.get_success(self.auth.get_user_by_req(request)) self.assertEquals(requester.user.to_string(), self.test_user)
def test_member_list_match(self): self.service.namespaces[ApplicationService.NS_USERS].append( _regex("@irc_.*")) # Note that @irc_fo:here is the AS user. self.store.get_users_in_room = simple_async_mock( ["@alice:here", "@irc_fo:here", "@bob:here"]) self.store.get_aliases_for_room = simple_async_mock([]) self.event.sender = "@xmpp_foobar:matrix.org" self.assertTrue((yield defer.ensureDeferred( self.service.is_interested_in_event(self.event.event_id, self.event, self.store))))
def test_get_user_by_req__puppeted_token__not_tracking_puppeted_mau(self): self.store.get_user_by_access_token = simple_async_mock( TokenLookupResult( user_id="@baldrick:matrix.org", device_id="device", token_owner="@admin:matrix.org", )) self.store.insert_client_ip = simple_async_mock(None) request = Mock(args={}) request.getClientIP.return_value = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.requestHeaders.getRawHeaders = mock_getRawHeaders() self.get_success(self.auth.get_user_by_req(request)) self.store.insert_client_ip.assert_called_once()
def test_blocking_mau__depending_on_user_type(self): self.auth_blocking._max_mau_value = 50 self.auth_blocking._limit_usage_by_mau = True self.store.get_monthly_active_count = simple_async_mock(100) # Support users allowed self.get_success(self.auth.check_auth_blocking(user_type=UserTypes.SUPPORT)) self.store.get_monthly_active_count = simple_async_mock(100) # Bots not allowed self.get_failure( self.auth.check_auth_blocking(user_type=UserTypes.BOT), ResourceLimitError ) self.store.get_monthly_active_count = simple_async_mock(100) # Real users not allowed self.get_failure(self.auth.check_auth_blocking(), ResourceLimitError)
def test_map_saml_response_to_user(self) -> None: """Ensure that mapping the SAML response returned from a provider to an MXID works properly.""" # stub out the auth handler auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() # send a mocked-up SAML response to the callback saml_response = FakeAuthnResponse({ "uid": "test_user", "username": "******" }) request = _mock_request() self.get_success( self.handler._handle_authn_response(request, saml_response, "redirect_uri")) # check that the auth handler got called as expected auth_handler.complete_sso_login.assert_called_once_with( "@test_user:test", "saml", request, "redirect_uri", None, new_user=True, auth_provider_session_id=None, )
def make_homeserver(self, reactor, clock): # Mock out the calls over federation. fed_transport_client = Mock(spec=["send_transaction"]) fed_transport_client.send_transaction = simple_async_mock({}) return self.setup_test_homeserver( federation_transport_client=fed_transport_client, )
def test_map_saml_response_to_existing_user(self): """Existing users can log in with SAML account.""" store = self.hs.get_datastore() self.get_success( store.register_user(user_id="@test_user:test", password_hash=None)) # stub out the auth handler auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() # Map a user via SSO. saml_response = FakeAuthnResponse({ "uid": "tester", "mxid": ["test_user"], "username": "******" }) request = _mock_request() self.get_success( self.handler._handle_authn_response(request, saml_response, "")) # check that the auth handler got called as expected auth_handler.complete_sso_login.assert_called_once_with( "@test_user:test", request, "", None) # Subsequent calls should map to the same mxid. auth_handler.complete_sso_login.reset_mock() self.get_success( self.handler._handle_authn_response(request, saml_response, "")) auth_handler.complete_sso_login.assert_called_once_with( "@test_user:test", request, "", None)
def test_attribute_requirements(self): """The required attributes must be met from the OIDC userinfo response.""" auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() # userinfo lacking "test": "foobar" attribute should fail. userinfo = { "sub": "tester", "username": "******", } self.get_success(_make_callback_with_userinfo(self.hs, userinfo)) auth_handler.complete_sso_login.assert_not_called() # userinfo with "test": "foobar" attribute should succeed. userinfo = { "sub": "tester", "username": "******", "test": "foobar", } self.get_success(_make_callback_with_userinfo(self.hs, userinfo)) # check that the auth handler got called as expected auth_handler.complete_sso_login.assert_called_once_with("@tester:test", "oidc", ANY, ANY, None, new_user=True)
def test_map_cas_user_to_existing_user(self): """Existing users can log in with CAS account.""" store = self.hs.get_datastore() self.get_success( store.register_user(user_id="@test_user:test", password_hash=None)) # stub out the auth handler auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() # Map a user via SSO. cas_response = CasResponse("test_user", {}) request = _mock_request() self.get_success( self.handler._handle_cas_response(request, cas_response, "redirect_uri", "")) # check that the auth handler got called as expected auth_handler.complete_sso_login.assert_called_once_with( "@test_user:test", request, "redirect_uri", None) # Subsequent calls should map to the same mxid. auth_handler.complete_sso_login.reset_mock() self.get_success( self.handler._handle_cas_response(request, cas_response, "redirect_uri", "")) auth_handler.complete_sso_login.assert_called_once_with( "@test_user:test", request, "redirect_uri", None)
def prepare(self, reactor: "MemoryReactor", clock: Clock, hs: HomeServer): self.scheduler = ApplicationServiceScheduler(hs) self.txn_ctrl = Mock() self.txn_ctrl.send = simple_async_mock() # Replace instantiated _TransactionController instances with our Mock self.scheduler.txn_ctrl = self.txn_ctrl self.scheduler.queuer.txn_ctrl = self.txn_ctrl
def setUp(self): self.service = ApplicationService( id="unique_identifier", sender="@as:test", url="some_url", token="some_token", ) self.event = Mock( event_id="$abc:xyz", type="m.something", room_id="!foo:bar", sender="@someone:somewhere", ) self.store = Mock() self.store.get_aliases_for_room = simple_async_mock([]) self.store.get_users_in_room = simple_async_mock([])
def test_map_saml_response_to_user_retries(self) -> None: """The mapping provider can retry generating an MXID if the MXID is already in use.""" # stub out the auth handler and error renderer auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() sso_handler = self.hs.get_sso_handler() sso_handler.render_error = Mock(return_value=None) # register a user to occupy the first-choice MXID store = self.hs.get_datastores().main self.get_success( store.register_user(user_id="@test_user:test", password_hash=None)) # send the fake SAML response saml_response = FakeAuthnResponse({ "uid": "test", "username": "******" }) request = _mock_request() self.get_success( self.handler._handle_authn_response(request, saml_response, ""), ) # test_user is already taken, so test_user1 gets registered instead. auth_handler.complete_sso_login.assert_called_once_with( "@test_user1:test", "saml", request, "", None, new_user=True, auth_provider_session_id=None, ) auth_handler.complete_sso_login.reset_mock() # Register all of the potential mxids for a particular SAML username. self.get_success( store.register_user(user_id="@tester:test", password_hash=None)) for i in range(1, 3): self.get_success( store.register_user(user_id="@tester%d:test" % i, password_hash=None)) # Now attempt to map to a username, this will fail since all potential usernames are taken. saml_response = FakeAuthnResponse({ "uid": "tester", "username": "******" }) self.get_success( self.handler._handle_authn_response(request, saml_response, ""), ) sso_handler.render_error.assert_called_once_with( request, "mapping_error", "Unable to generate a Matrix ID from the SSO response", ) auth_handler.complete_sso_login.assert_not_called()
def test_get_user_by_req_user_missing_token(self): user_info = TokenLookupResult(user_id=self.test_user, token_id=5) self.store.get_user_by_access_token = simple_async_mock(user_info) request = Mock(args={}) request.requestHeaders.getRawHeaders = mock_getRawHeaders() f = self.get_failure(self.auth.get_user_by_req(request), MissingClientTokenError).value self.assertEqual(f.code, 401) self.assertEqual(f.errcode, "M_MISSING_TOKEN")
def prepare(self, reactor, clock, hs): self.store = Mock() hs.get_datastore = Mock(return_value=self.store) hs.get_auth_handler().store = self.store self.auth = Auth(hs) # AuthBlocking reads from the hs' config on initialization. We need to # modify its config instead of the hs' self.auth_blocking = self.auth._auth_blocking self.test_user = "******" self.test_token = b"_test_token_" # this is overridden for the appservice tests self.store.get_app_service_by_token = Mock(return_value=None) self.store.insert_client_ip = simple_async_mock(None) self.store.is_support_user = simple_async_mock(False)
def test_get_user_by_req_user_bad_token(self): self.store.get_user_by_access_token = simple_async_mock(None) request = Mock(args={}) request.args[b"access_token"] = [self.test_token] request.requestHeaders.getRawHeaders = mock_getRawHeaders() f = self.get_failure(self.auth.get_user_by_req(request), InvalidClientTokenError).value self.assertEqual(f.code, 401) self.assertEqual(f.errcode, "M_UNKNOWN_TOKEN")
def test_get_user_by_req_appservice_missing_token(self): app_service = Mock(token="foobar", url="a_url", sender=self.test_user) self.store.get_app_service_by_token = Mock(return_value=app_service) self.store.get_user_by_access_token = simple_async_mock(None) request = Mock(args={}) request.requestHeaders.getRawHeaders = mock_getRawHeaders() f = self.get_failure(self.auth.get_user_by_req(request), MissingClientTokenError).value self.assertEqual(f.code, 401) self.assertEqual(f.errcode, "M_MISSING_TOKEN")
def test_get_guest_user_from_macaroon(self): self.store.get_user_by_id = simple_async_mock({"is_guest": True}) self.store.get_user_by_access_token = simple_async_mock(None) user_id = "@baldrick:matrix.org" macaroon = pymacaroons.Macaroon( location=self.hs.config.server_name, identifier="key", key=self.hs.config.macaroon_secret_key, ) macaroon.add_first_party_caveat("gen = 1") macaroon.add_first_party_caveat("type = access") macaroon.add_first_party_caveat("user_id = %s" % (user_id,)) macaroon.add_first_party_caveat("guest = true") serialized = macaroon.serialize() user_info = self.get_success(self.auth.get_user_by_access_token(serialized)) self.assertEqual(user_id, user_info.user_id) self.assertTrue(user_info.is_guest) self.store.get_user_by_id.assert_called_with(user_id)
def test_get_user_by_req_appservice_valid_token_valid_user_id(self): masquerading_user_id = b"@doppelganger:matrix.org" app_service = Mock( token="foobar", url="a_url", sender=self.test_user, ip_range_whitelist=None ) app_service.is_interested_in_user = Mock(return_value=True) self.store.get_app_service_by_token = Mock(return_value=app_service) # This just needs to return a truth-y value. self.store.get_user_by_id = simple_async_mock({"is_guest": False}) self.store.get_user_by_access_token = simple_async_mock(None) request = Mock(args={}) request.getClientIP.return_value = "127.0.0.1" request.args[b"access_token"] = [self.test_token] request.args[b"user_id"] = [masquerading_user_id] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = self.get_success(self.auth.get_user_by_req(request)) self.assertEquals( requester.user.to_string(), masquerading_user_id.decode("utf8") )
def test_extra_attributes(self) -> None: """ Login while using a mapping provider that implements get_extra_attributes. """ token = { "type": "bearer", "id_token": "id_token", "access_token": "access_token", } userinfo = { "sub": "foo", "username": "******", "phone": "1234567", } self.provider._exchange_code = simple_async_mock( return_value=token) # type: ignore[assignment] self.provider._parse_id_token = simple_async_mock( return_value=userinfo) # type: ignore[assignment] auth_handler = self.hs.get_auth_handler() auth_handler.complete_sso_login = simple_async_mock() state = "state" client_redirect_url = "http://client/redirect" session = self._generate_oidc_session_token( state=state, nonce="nonce", client_redirect_url=client_redirect_url, ) request = _build_callback_request("code", state, session) self.get_success(self.handler.handle_oidc_callback(request)) auth_handler.complete_sso_login.assert_called_once_with( "@foo:test", "oidc", request, client_redirect_url, {"phone": "1234567"}, new_user=True, auth_provider_session_id=None, )