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", )
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 get_user(tok): if token != tok: return defer.succeed(None) return defer.succeed( TokenLookupResult( user_id=USER_ID, is_guest=False, token_id=1234, device_id="DEVICE", ))
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) 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_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 = Mock( return_value=defer.succeed(user_info)) request = Mock(args={}) request.requestHeaders.getRawHeaders = mock_getRawHeaders() d = defer.ensureDeferred(self.auth.get_user_by_req(request)) f = self.failureResultOf(d, MissingClientTokenError).value self.assertEqual(f.code, 401) self.assertEqual(f.errcode, "M_MISSING_TOKEN")
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 = Mock( return_value=defer.succeed(user_info)) request = Mock(args={}) request.args[b"access_token"] = [self.test_token] request.requestHeaders.getRawHeaders = mock_getRawHeaders() requester = yield defer.ensureDeferred( self.auth.get_user_by_req(request)) self.assertEquals(requester.user.to_string(), self.test_user)
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_get_user_from_macaroon(self): self.store.get_user_by_access_token = simple_async_mock( TokenLookupResult(user_id="@baldrick:matrix.org", device_id="device")) user_id = "@baldrick:matrix.org" macaroon = pymacaroons.Macaroon( location=self.hs.config.server.server_name, identifier="key", key=self.hs.config.key.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, )) user_info = self.get_success( self.auth.get_user_by_access_token(macaroon.serialize())) self.assertEqual(user_id, user_info.user_id) # TODO: device_id should come from the macaroon, but currently comes # from the db. self.assertEqual(user_info.device_id, "device")
async def get_user_by_access_token( self, token: str, rights: str = "access", allow_expired: bool = False, ) -> TokenLookupResult: """Validate access token and get user_id from it Args: token: The access token to get the user by rights: The operation being performed; the access token must allow this allow_expired: If False, raises an InvalidClientTokenError if the token is expired Raises: InvalidClientTokenError if a user by that token exists, but the token is expired InvalidClientCredentialsError if no user by that token exists or the token is invalid """ if rights == "access": # first look in the database r = await self.store.get_user_by_access_token(token) if r: valid_until_ms = r.valid_until_ms if (not allow_expired and valid_until_ms is not None and valid_until_ms < self.clock.time_msec()): # there was a valid access token, but it has expired. # soft-logout the user. raise InvalidClientTokenError( msg="Access token has expired", soft_logout=True) return r # otherwise it needs to be a valid macaroon try: user_id, guest = self._parse_and_validate_macaroon(token, rights) if rights == "access": if not guest: # non-guest access tokens must be in the database logger.warning("Unrecognised access token - not in store.") raise InvalidClientTokenError() # Guest access tokens are not stored in the database (there can # only be one access token per guest, anyway). # # In order to prevent guest access tokens being used as regular # user access tokens (and hence getting around the invalidation # process), we look up the user id and check that it is indeed # a guest user. # # It would of course be much easier to store guest access # tokens in the database as well, but that would break existing # guest tokens. stored_user = await self.store.get_user_by_id(user_id) if not stored_user: raise InvalidClientTokenError("Unknown user_id %s" % user_id) if not stored_user["is_guest"]: raise InvalidClientTokenError( "Guest access token used for regular user") ret = TokenLookupResult( user_id=user_id, is_guest=True, # all guests get the same device id device_id=GUEST_DEVICE_ID, ) elif rights == "delete_pusher": # We don't store these tokens in the database ret = TokenLookupResult(user_id=user_id, is_guest=False) else: raise RuntimeError("Unknown rights setting %s", rights) return ret except ( _InvalidMacaroonException, pymacaroons.exceptions.MacaroonException, TypeError, ValueError, ) as e: logger.warning("Invalid macaroon in auth: %s %s", type(e), e) raise InvalidClientTokenError("Invalid macaroon passed.")
async def get_user_by_access_token( self, token: str, allow_expired: bool = False, ) -> TokenLookupResult: """Validate access token and get user_id from it Args: token: The access token to get the user by allow_expired: If False, raises an InvalidClientTokenError if the token is expired Raises: InvalidClientTokenError if a user by that token exists, but the token is expired InvalidClientCredentialsError if no user by that token exists or the token is invalid """ # First look in the database to see if the access token is present # as an opaque token. r = await self.store.get_user_by_access_token(token) if r: valid_until_ms = r.valid_until_ms if (not allow_expired and valid_until_ms is not None and valid_until_ms < self.clock.time_msec()): # there was a valid access token, but it has expired. # soft-logout the user. raise InvalidClientTokenError(msg="Access token has expired", soft_logout=True) return r # If the token isn't found in the database, then it could still be a # macaroon for a guest, so we check that here. try: user_id = self._macaroon_generator.verify_guest_token(token) # Guest access tokens are not stored in the database (there can # only be one access token per guest, anyway). # # In order to prevent guest access tokens being used as regular # user access tokens (and hence getting around the invalidation # process), we look up the user id and check that it is indeed # a guest user. # # It would of course be much easier to store guest access # tokens in the database as well, but that would break existing # guest tokens. stored_user = await self.store.get_user_by_id(user_id) if not stored_user: raise InvalidClientTokenError("Unknown user_id %s" % user_id) if not stored_user["is_guest"]: raise InvalidClientTokenError( "Guest access token used for regular user") return TokenLookupResult( user_id=user_id, is_guest=True, # all guests get the same device id device_id=GUEST_DEVICE_ID, ) except ( pymacaroons.exceptions.MacaroonException, TypeError, ValueError, ) as e: logger.warning( "Invalid access token in auth: %s %s.", type(e), e, ) raise InvalidClientTokenError("Invalid access token passed.")