def get_appservice_by_req(self, request): token = self.get_access_token_from_request(request) service = self.store.get_app_service_by_token(token) if not service: logger.warning("Unrecognised appservice access token.") raise InvalidClientTokenError() request.authenticated_entity = service.sender return defer.succeed(service)
def get_appservice_by_req(self, request: SynapseRequest) -> ApplicationService: token = self.get_access_token_from_request(request) service = self.store.get_app_service_by_token(token) if not service: logger.warning("Unrecognised appservice access token.") raise InvalidClientTokenError() request.requester = create_requester(service.sender, app_service=service) return service
def _parse_and_validate_macaroon(self, token: str, rights: str = "access" ) -> Tuple[str, bool]: """Takes a macaroon and tries to parse and validate it. This is cached if and only if rights == access and there isn't an expiry. On invalid macaroon raises _InvalidMacaroonException Returns: (user_id, is_guest) """ if rights == "access": cached = self.token_cache.get(token, None) if cached: return cached try: macaroon = pymacaroons.Macaroon.deserialize(token) except Exception: # deserialize can throw more-or-less anything # doesn't look like a macaroon: treat it as an opaque token which # must be in the database. # TODO: it would be nice to get rid of this, but apparently some # people use access tokens which aren't macaroons raise _InvalidMacaroonException() try: user_id = get_value_from_macaroon(macaroon, "user_id") guest = False for caveat in macaroon.caveats: if caveat.caveat_id == "guest = true": guest = True self.validate_macaroon(macaroon, rights, user_id=user_id) except ( pymacaroons.exceptions.MacaroonException, KeyError, TypeError, ValueError, ): raise InvalidClientTokenError("Invalid macaroon passed.") if rights == "access": self.token_cache[token] = (user_id, guest) return user_id, guest
def get_user_id_from_macaroon(self, macaroon): """Retrieve the user_id given by the caveats on the macaroon. Does *not* validate the macaroon. Args: macaroon (pymacaroons.Macaroon): The macaroon to validate Returns: (str) user id Raises: InvalidClientCredentialsError if there is no user_id caveat in the macaroon """ user_prefix = "user_id = " for caveat in macaroon.caveats: if caveat.caveat_id.startswith(user_prefix): return caveat.caveat_id[len(user_prefix):] raise InvalidClientTokenError("No user caveat in macaroon")
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.")
def get_user_by_access_token(self, token, rights="access"): """ Validate access token and get user_id from it Args: token (str): The access token to get the user by. rights (str): The operation being performed; the access token must allow this. Returns: Deferred[dict]: dict that includes: `user` (UserID) `is_guest` (bool) `token_id` (int|None): access token id. May be None if guest `device_id` (str|None): device corresponding to access token Raises: InvalidClientCredentialsError if no user by that token exists or the token is invalid. """ if rights == "access": # first look in the database r = yield self._look_up_user_by_access_token(token) if r: valid_until_ms = r["valid_until_ms"] if (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) user = UserID.from_string(user_id) 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 = yield 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 = { "user": user, "is_guest": True, "token_id": None, # 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 = { "user": user, "is_guest": False, "token_id": None, "device_id": None, } 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.")