Beispiel #1
0
    def parse_response(self,
                       response: Type[Message],
                       info: str = "",
                       sformat: ENCODINGS = "json",
                       state: str = "",
                       **kwargs) -> Message:
        """
        Parse a response.

        :param response: Response type
        :param info: The response, can be either in a JSON or an urlencoded
            format
        :param sformat: Which serialization that was used
        :param state: The state
        :param kwargs: Extra key word arguments
        :return: The parsed and to some extend verified response
        """
        _r2e = self.response2error

        if sformat == "urlencoded":
            info = self.get_urlinfo(info)

        resp = response().deserialize(info, sformat, **kwargs)
        msg = 'Initial response parsing => "{}"'
        logger.debug(msg.format(sanitize(resp.to_dict())))
        if self.events:
            self.events.store("Response", resp.to_dict())

        if "error" in resp and not isinstance(resp, ErrorResponse):
            resp = None
            errmsgs = []  # type: List[Any]
            try:
                errmsgs = _r2e[response.__name__]
            except KeyError:
                errmsgs = [ErrorResponse]

            try:
                for errmsg in errmsgs:
                    try:
                        resp = errmsg().deserialize(info, sformat)
                        resp.verify()
                        break
                    except Exception:
                        resp = None
            except KeyError:
                pass
        elif resp.only_extras():
            resp = None
        else:
            kwargs["client_id"] = self.client_id
            try:
                kwargs["iss"] = self.provider_info["issuer"]
            except (KeyError, AttributeError):
                if self.issuer:
                    kwargs["iss"] = self.issuer

            if "key" not in kwargs and "keyjar" not in kwargs:
                kwargs["keyjar"] = self.keyjar

            logger.debug("Verify response with {}".format(sanitize(kwargs)))
            verf = resp.verify(**kwargs)

            if not verf:
                logger.error("Verification of the response failed")
                raise PyoidcError("Verification of the response failed")
            if resp.type() == "AuthorizationResponse" and "scope" not in resp:
                try:
                    resp["scope"] = kwargs["scope"]
                except KeyError:
                    pass

        if not resp:
            logger.error("Missing or faulty response")
            raise ResponseError("Missing or faulty response")

        self.store_response(resp, info)

        if isinstance(resp, (AuthorizationResponse, AccessTokenResponse)):
            try:
                _state = resp["state"]
            except (AttributeError, KeyError):
                _state = ""

            if not _state:
                _state = state

            try:
                self.grant[_state].update(resp)
            except KeyError:
                self.grant[_state] = self.grant_class(resp=resp)

            if "id_token" in resp and self.sso_db is not None:
                session_update(self.sso_db, _state, "sub",
                               resp["id_token"]["sub"])
                session_update(self.sso_db, _state, "issuer",
                               resp["id_token"]["iss"])
                if "sid" in resp["id_token"]:
                    session_update(self.sso_db, _state, "smid",
                                   resp["id_token"]["sid"])
        return resp
Beispiel #2
0
    def parse_authz(
        self,
        query="",
        **kwargs
    ) -> Union[http_util.BadRequest,
               Tuple[Optional[AuthorizationResponse],
                     Optional[AccessTokenResponse], Optional[IdToken], ], ]:
        """
        Parse authorization response from server.

        Couple of cases
        ["code"]
        ["code", "token"]
        ["code", "id_token", "token"]
        ["id_token"]
        ["id_token", "token"]
        ["token"]
        """
        _log_info = logger.info
        logger.debug("- authorization -")

        # FIXME: This shouldn't be here... We should rather raise a sepcific Client error
        # That would simplify the return value of this function
        # and drop bunch of assertions from tests added in this commit.
        if not query:
            return http_util.BadRequest("Missing query")

        _log_info("response: %s" % sanitize(query))

        if "algs" not in kwargs:
            kwargs["algs"] = self.sign_enc_algs("id_token")
        if "code" in self.consumer_config["response_type"]:
            aresp, _state = self._parse_authz(query, **kwargs)

            # May have token and id_token information too
            if "access_token" in aresp:
                atr = clean_response(aresp)
                self.access_token = atr
                # update the grant object
                self.get_grant(state=_state).add_token(atr)
            else:
                atr = None

            self._backup(_state)

            try:
                idt = aresp["id_token"]
            except KeyError:
                idt = None
            else:
                try:
                    session_update(self.sdb, idt["sid"], "smid", _state)
                except KeyError:
                    pass

        elif "token" in self.consumer_config["response_type"]:  # implicit flow
            _log_info("Expect Access Token Response")
            aresp = None
            _state = None
            atr = self.parse_response(AccessTokenResponse,
                                      info=query,
                                      sformat="urlencoded",
                                      keyjar=self.keyjar,
                                      **kwargs)
            if isinstance(atr, ErrorResponse):
                raise TokenError(atr.get("error"), atr)

            idt = atr.get("id_token")

        else:  # only id_token
            aresp, _state = self._parse_authz(query, **kwargs)

            try:
                idt = aresp["id_token"]
            except KeyError:
                idt = None
            else:
                try:
                    session_update(self.sso_db, _state, "smid", idt["sid"])
                except KeyError:
                    pass
            # Null the aresp as only id_token should be returned
            aresp = atr = None

        # Verify the IdToken if it was present
        if idt is not None:
            self.verify_id_token(idt, self.authz_req.get(_state
                                                         or atr["state"]))
        return aresp, atr, idt
Beispiel #3
0
    def parse_authz(self, query="", **kwargs):
        """
        Parse authorization response from server.

        Couple of cases
        ["code"]
        ["code", "token"]
        ["code", "id_token", "token"]
        ["id_token"]
        ["id_token", "token"]
        ["token"]

        :return: A AccessTokenResponse instance
        """
        _log_info = logger.info
        logger.debug("- authorization -")

        if not query:
            return http_util.BadRequest("Missing query")

        _log_info("response: %s" % sanitize(query))

        if "code" in self.consumer_config["response_type"]:
            aresp, _state = self._parse_authz(query, **kwargs)

            # May have token and id_token information too
            if "access_token" in aresp:
                atr = clean_response(aresp)
                self.access_token = atr
                # update the grant object
                self.get_grant(state=_state).add_token(atr)
            else:
                atr = None

            self._backup(_state)

            try:
                idt = aresp["id_token"]
            except KeyError:
                idt = None
            else:
                try:
                    session_update(self.sdb, idt["sid"], "smid", _state)
                except KeyError:
                    pass

            return aresp, atr, idt
        elif "token" in self.consumer_config["response_type"]:  # implicit flow
            _log_info("Expect Access Token Response")
            atr = self.parse_response(
                AccessTokenResponse,
                info=query,
                sformat="urlencoded",
                keyjar=self.keyjar,
                **kwargs,
            )
            if isinstance(atr, ErrorResponse):
                raise TokenError(atr.get("error"), atr)

            idt = None
            return None, atr, idt
        else:  # only id_token
            aresp, _state = self._parse_authz(query, **kwargs)

            try:
                idt = aresp["id_token"]
            except KeyError:
                idt = None
            else:
                try:
                    session_update(self.sso_db, _state, "smid", idt["sid"])
                except KeyError:
                    pass

            return None, None, idt
def test_session_update():
    with pytest.raises(KeyError):
        session_update({}, "session_id", "attr", "val")