Beispiel #1
0
 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)
Beispiel #2
0
 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
Beispiel #3
0
    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
Beispiel #4
0
    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")
Beispiel #5
0
    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.")
Beispiel #6
0
    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.")
Beispiel #7
0
    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.")