示例#1
0
文件: __init__.py 项目: ghedin/pyoidc
    def get_userinfo_claims(self,
                            access_token,
                            endpoint,
                            method="POST",
                            schema_class=OpenIDSchema,
                            **kwargs):

        uir = UserInfoRequest(access_token=access_token)

        h_args = dict([(k, v) for k, v in kwargs.items() if k in HTTP_ARGS])

        if "authn_method" in kwargs:
            http_args = self.init_authentication_method(**kwargs)
        else:
            # If nothing defined this is the default
            http_args = self.init_authentication_method(
                uir, "bearer_header", **kwargs)

        h_args.update(http_args)
        path, body, kwargs = self.get_or_post(endpoint, method, uir, **kwargs)

        try:
            resp = self.http_request(path, method, data=body, **h_args)
        except oauth2.MissingRequiredAttribute:
            raise

        if resp.status_code == 200:
            assert "application/json" in resp.headers["content-type"]
        elif resp.status_code == 500:
            raise PyoidcError("ERROR: Something went wrong: %s" % resp.text)
        else:
            raise PyoidcError("ERROR: Something went wrong [%s]" %
                              resp.status_code)

        return schema_class().from_json(txt=resp.text)
示例#2
0
    def handle_response(self, r, csi):
        data = self.conv.events.last_item(EV_REQUEST)
        state = data['state']

        if 300 < r.status_code < 400:
            resp = self.conv.entity.parse_response(self.response,
                                                   info=r.headers['location'],
                                                   sformat="urlencoded",
                                                   state=state)
        elif r.status_code == 200:
            if "response_mode" in csi and csi["response_mode"] == "form_post":
                resp = self.response()
                forms = BeautifulSoup(r.content).findAll('form')
                for inp in forms[0].find_all("input"):
                    resp[inp.attrs["name"]] = inp.attrs["value"]
            else:
                resp = self.conv.entity.parse_response(
                    self.response,
                    info=r.headers['location'],
                    sformat="urlencoded",
                    state=state)

            resp.verify(keyjar=self.conv.entity.keyjar)
        else:
            resp = r

        if not isinstance(resp, Response):  # Not a HTTP response
            try:
                try:
                    _id_token = resp["id_token"]
                except KeyError:
                    pass
                else:
                    if "kid" not in _id_token.jws_header and not \
                                    _id_token.jws_header["alg"] == "HS256":
                        for key, value in \
                                self.conv.entity.keyjar.issuer_keys.items():
                            if not key == "" and (len(value) > 1 or len(
                                    list(value[0].keys())) > 1):
                                raise PyoidcError(
                                    "No 'kid' in id_token header!")

                    if self.req_args['nonce'] != _id_token['nonce']:
                        raise PyoidcError("invalid nonce! {} != {}".format(
                            self.req_args['nonce'], _id_token['nonce']))

                    if not same_issuer(self.conv.info["issuer"],
                                       _id_token["iss"]):
                        raise IssuerMismatch(" {} != {}".format(
                            self.conv.info["issuer"], _id_token["iss"]))
                    self.conv.entity.id_token = _id_token
            except KeyError:
                pass

        return resp
示例#3
0
文件: __init__.py 项目: ghedin/pyoidc
    def do_user_info_request(self,
                             method="POST",
                             state="",
                             scope="openid",
                             request="openid",
                             **kwargs):

        kwargs["request"] = request
        path, body, method, h_args = self.user_info_request(
            method, state, scope, **kwargs)

        logger.debug("[do_user_info_request] PATH:%s BODY:%s H_ARGS: %s" %
                     (path, body, h_args))

        try:
            resp = self.http_request(path, method, data=body, **h_args)
        except oauth2.MissingRequiredAttribute:
            raise

        if resp.status_code == 200:
            try:
                assert "application/json" in resp.headers["content-type"]
                sformat = "json"
            except AssertionError:
                assert "application/jwt" in resp.headers["content-type"]
                sformat = "jwt"
        elif resp.status_code == 500:
            raise PyoidcError("ERROR: Something went wrong: %s" % resp.text)
        else:
            raise PyoidcError("ERROR: Something went wrong [%s]: %s" %
                              (resp.status_code, resp.text))

        try:
            _schema = kwargs["user_info_schema"]
        except KeyError:
            _schema = OpenIDSchema

        logger.debug("Reponse text: '%s'" % resp.text)

        if sformat == "json":
            return _schema().from_json(txt=resp.text)
        else:
            algo = self.client_prefs["userinfo_signed_response_alg"]
            _kty = alg2keytype(algo)
            # Keys of the OP ?
            try:
                keys = self.keyjar.get_signing_key(_kty, self.kid["sig"][_kty])
            except KeyError:
                keys = self.keyjar.get_signing_key(_kty)

            return _schema().from_jwt(resp.text, keys)
示例#4
0
    def _run(self):
        if self.skip:
            return

        self.conv.trace.info(
            "Access Token Request with op_args: {}, req_args: {}".format(
                self.op_args, self.req_args))
        atr = self.conv.entity.do_access_token_request(
            request_args=self.req_args, **self.op_args)

        if "error" in atr:
            self.conv.trace.response("Access Token response: {}".format(atr))
            return False

        try:
            _jws_alg = atr["id_token"].jws_header["alg"]
        except (KeyError, AttributeError):
            pass
        else:
            if _jws_alg == "none":
                pass
            elif "kid" not in atr[
                    "id_token"].jws_header and not _jws_alg == "HS256":
                keys = self.conv.entity.keyjar.keys_by_alg_and_usage(
                    self.conv.info["issuer"], _jws_alg, "ver")
                if len(keys) > 1:
                    raise PyoidcError("No 'kid' in id_token header!")

        if not same_issuer(self.conv.info["issuer"], atr["id_token"]["iss"]):
            raise IssuerMismatch(" {} != {}".format(self.conv.info["issuer"],
                                                    atr["id_token"]["iss"]))

        self.conv.trace.response(atr)
        assert isinstance(atr, AccessTokenResponse)
示例#5
0
    def provider_config(self,
                        issuer,
                        keys=True,
                        endpoints=True,
                        response_cls=ProviderConfigurationResponse,
                        serv_pattern=OIDCONF_PATTERN):
        if issuer.endswith("/"):
            _issuer = issuer[:-1]
        else:
            _issuer = issuer

        url = serv_pattern % _issuer

        pcr = None
        r = self.http_request(url)
        if r.status_code == 200:
            pcr = response_cls().from_json(r.text)
        elif r.status_code == 302:
            while r.status_code == 302:
                r = self.http_request(r.headers["location"])
                if r.status_code == 200:
                    pcr = response_cls().from_json(r.text)
                    break

        if pcr is None:
            raise PyoidcError("Trying '%s', status %s" % (url, r.status_code))

        self.handle_provider_config(pcr, issuer, keys, endpoints)

        return pcr
示例#6
0
文件: __init__.py 项目: ghedin/pyoidc
    def _parse_request(self, request, data, sformat, client_id=None):
        if sformat == "json":
            request = request().from_json(data)
        elif sformat == "jwt":
            request = request().from_jwt(data, keyjar=self.keyjar)
        elif sformat == "urlencoded":
            if '?' in data:
                parts = urlparse.urlparse(data)
                scheme, netloc, path, params, query, fragment = parts[:6]
            else:
                query = data
            request = request().from_urlencoded(query)
        else:
            raise PyoidcError("Unknown package format: '%s'" % sformat,
                              request)

        # get the verification keys
        if client_id:
            keys = self.keyjar.verify_keys(client_id)
        else:
            try:
                keys = self.keyjar.verify_keys(request["client_id"])
            except KeyError:
                keys = None

        request.verify(key=keys, keyjar=self.keyjar)
        return request
示例#7
0
    def complete(self, state):
        """
        Do the access token request, the last step in a code flow.
        If Implicit flow was used then this method is never used.
        """
        args = {"redirect_uri": self.redirect_uris[0]}
        if "password" in self.config and self.config["password"]:
            logger.info("basic auth")
            http_args = {"password": self.config["password"]}
        elif self.client_secret:
            logger.info("request_body auth")
            http_args = {}
            args.update({
                "client_secret": self.client_secret,
                "client_id": self.client_id,
                "secret_type": self.secret_type
            })
        else:
            raise PyoidcError("Nothing to authenticate with")

        resp = self.do_access_token_request(state=state,
                                            request_args=args,
                                            http_args=http_args)

        logger.info("Access Token Response: %s" % resp)

        if resp.type() == "ErrorResponse":
            raise TokenError(resp.error, resp)

        #self._backup(self.sdb["seed:%s" % _cli.seed])
        self._backup(state)

        return resp
示例#8
0
def claims_ser(val, sformat="urlencoded", lev=0):
    # everything in c_extension
    if isinstance(val, six.string_types):
        item = val
    elif isinstance(val, list):
        item = val[0]
    else:
        item = val

    if isinstance(item, Message):
        return item.serialize(method=sformat, lev=lev + 1)

    if sformat == "urlencoded":
        res = urllib.urlencode(item)
    elif sformat == "json":
        if lev:
            res = item
        else:
            res = json.dumps(item)
    elif sformat == "dict":
        if isinstance(item, dict):
            res = item
        else:
            raise MessageException("Wrong type: %s" % type(item))
    else:
        raise PyoidcError("Unknown sformat: %s" % sformat, val)

    return res
示例#9
0
    def handle_registration_info(self, response):
        if response.status_code == 200:
            resp = ClientInfoResponse().deserialize(response.text, "json")
            self.store_registration_info(resp)
        else:
            err = ErrorResponse().deserialize(response.text, "json")
            raise PyoidcError("Registration failed: %s" % err.get_json())

        return resp
示例#10
0
def factory(msgtype):
    try:
        return MSG[msgtype]
    except KeyError:
        if msgtype == "ErrorResponse":
            return message.ErrorResponse
        elif msgtype == "Message":
            return message.Message
        else:
            raise PyoidcError("Unknown message type: %s" % msgtype)
示例#11
0
    def handle_registration_info(self, response):
        if response.status_code in SUCCESSFUL:
            resp = ClientInfoResponse().deserialize(response.text, "json")
            self.store_response(resp, response.text)
            self.store_registration_info(resp)
        else:
            resp = ErrorResponse().deserialize(response.text, "json")
            try:
                resp.verify()
                self.store_response(resp, response.text)
            except Exception:
                raise PyoidcError('Registration failed: {}'.format(
                    response.text))

        return resp
示例#12
0
    def handle_registration_info(self, response):
        if response.status_code in SUCCESSFUL:
            resp = self.message_factory.get_response_type(
                "registration_endpoint"
            )().deserialize(response.text, "json")
            self.store_response(resp, response.text)
            self.store_registration_info(resp)
        else:
            resp = ErrorResponse().deserialize(response.text, "json")
            try:
                resp.verify()
                self.store_response(resp, response.text)
            except Exception:
                raise PyoidcError("Registration failed: {}".format(response.text))

        return resp
示例#13
0
    def handle_provider_config(self, pcr, issuer, keys=True, endpoints=True):
        """
        Deal with Provider Config Response
        :param pcr: The ProviderConfigResponse instance
        :param issuer: The one I thought should be the issuer of the config
        :param keys: Should I deal with keys
        :param endpoints: Should I deal with endpoints, that is store them
        as attributes in self.
        """

        if "issuer" in pcr:
            _pcr_issuer = pcr["issuer"]
            if pcr["issuer"].endswith("/"):
                if issuer.endswith("/"):
                    _issuer = issuer
                else:
                    _issuer = issuer + "/"
            else:
                if issuer.endswith("/"):
                    _issuer = issuer[:-1]
                else:
                    _issuer = issuer

            try:
                _ = self.allow["issuer_mismatch"]
            except KeyError:
                try:
                    assert _issuer == _pcr_issuer
                except AssertionError:
                    raise PyoidcError(
                        "provider info issuer mismatch '%s' != '%s'" %
                        (_issuer, _pcr_issuer))

            self.provider_info[_pcr_issuer] = pcr
        else:
            _pcr_issuer = issuer

        if endpoints:
            for key, val in pcr.items():
                if key.endswith("_endpoint"):
                    setattr(self, key, val)

        if keys:
            if self.keyjar is None:
                self.keyjar = KeyJar()

            self.keyjar.load_keys(pcr, _pcr_issuer)
示例#14
0
def msg_ser(inst, sformat, lev=0):
    if sformat in ["urlencoded", "json"]:
        if isinstance(inst, dict) or isinstance(inst, Message):
            res = inst.serialize(sformat, lev)
        else:
            res = inst
    elif sformat == "dict":
        if isinstance(inst, Message):
            res = inst.serialize(sformat, lev)
        elif isinstance(inst, dict):
            res = inst
        else:
            raise ValueError("%s" % type(inst))
    else:
        raise PyoidcError("Unknown sformat", inst)

    return res
示例#15
0
def msg_ser(inst, sformat, lev=0):
    if sformat in ["urlencoded", "json"]:
        if isinstance(inst, dict) or isinstance(inst, Message):
            res = inst.serialize(sformat, lev)
        else:
            res = inst
    elif sformat == "dict":
        if isinstance(inst, Message):
            res = inst.serialize(sformat, lev)
        elif isinstance(inst, dict):
            res = inst
        elif isinstance(inst, six.string_types):  # Iff ID Token
            res = inst
        else:
            raise MessageException("Wrong type: %s" % type(inst))
    else:
        raise PyoidcError("Unknown sformat", inst)

    return res
示例#16
0
文件: __init__.py 项目: ghedin/pyoidc
    def construct_UserInfoRequest(self,
                                  request=UserInfoRequest,
                                  request_args=None,
                                  extra_args=None,
                                  **kwargs):

        if request_args is None:
            request_args = {}

        if "access_token" in request_args:
            pass
        else:
            if "scope" not in kwargs:
                kwargs["scope"] = "openid"
            token = self.get_token(**kwargs)
            if token is None:
                raise PyoidcError("No valid token available")

            request_args["access_token"] = token.access_token

        return self.construct_request(request, request_args, extra_args)
示例#17
0
    def _run(self):
        if self.skip:
            return

        self.conv.events.store(EV_REQUEST,
                               "op_args: {}, req_args: {}".format(
                                   self.op_args, self.req_args),
                               direction=OUTGOING)
        atr = self.conv.entity.do_access_token_request(
            request_args=self.req_args, **self.op_args)

        if "error" in atr:
            self.conv.events.store(EV_PROTOCOL_RESPONSE,
                                   atr,
                                   direction=INCOMING)
            return False

        try:
            _jws_alg = atr["id_token"].jws_header["alg"]
        except (KeyError, AttributeError):
            pass
        else:
            if _jws_alg == "none":
                pass
            elif "kid" not in atr[
                    "id_token"].jws_header and not _jws_alg == "HS256":
                keys = self.conv.entity.keyjar.keys_by_alg_and_usage(
                    self.conv.info["issuer"], _jws_alg, "ver")
                if len(keys) > 1:
                    raise PyoidcError("No 'kid' in id_token header!")

        if not same_issuer(self.conv.info["issuer"], atr["id_token"]["iss"]):
            raise IssuerMismatch(" {} != {}".format(self.conv.info["issuer"],
                                                    atr["id_token"]["iss"]))

        #assert isinstance(atr, AccessTokenResponse)
        return atr
示例#18
0
文件: __init__.py 项目: ghedin/pyoidc
    def _id_token_based(self,
                        request,
                        request_args=None,
                        extra_args=None,
                        **kwargs):

        if request_args is None:
            request_args = {}

        try:
            _prop = kwargs["prop"]
        except KeyError:
            _prop = "id_token"

        if _prop in request_args:
            pass
        else:
            id_token = self._get_id_token(**kwargs)
            if id_token is None:
                raise PyoidcError("No valid id token available")

            request_args[_prop] = id_token

        return self.construct_request(request, request_args, extra_args)
示例#19
0
    def do_user_info_request(self,
                             method="POST",
                             state="",
                             scope="openid",
                             request="openid",
                             **kwargs):

        kwargs["request"] = request
        path, body, method, h_args = self.user_info_request(
            method, state, scope, **kwargs)

        logger.debug(
            "[do_user_info_request] PATH:{} BODY:{} H_ARGS: {}".format(
                path, body, h_args))

        if self.events:
            self.events.store('Request', {'body': body})
            self.events.store('request_url', path)
            self.events.store('request_http_args', h_args)

        try:
            resp = self.http_request(path, method, data=body, **h_args)
        except MissingRequiredAttribute:
            raise

        if resp.status_code == 200:
            try:
                assert "application/json" in resp.headers["content-type"]
                sformat = "json"
            except AssertionError:
                assert "application/jwt" in resp.headers["content-type"]
                sformat = "jwt"
        elif resp.status_code == 500:
            raise PyoidcError("ERROR: Something went wrong: %s" % resp.text)
        elif 400 <= resp.status_code < 500:
            # the response text might be a OIDC message
            try:
                res = ErrorResponse().from_json(resp.text)
            except Exception:
                raise RequestError(resp.text)
            else:
                self.store_response(res, resp.text)
                return res
        else:
            raise PyoidcError("ERROR: Something went wrong [%s]: %s" %
                              (resp.status_code, resp.text))

        try:
            _schema = kwargs["user_info_schema"]
        except KeyError:
            _schema = OpenIDSchema

        logger.debug("Reponse text: '{}'".format(resp.text))

        _txt = resp.text
        if sformat == "json":
            res = _schema().from_json(txt=_txt)
        else:
            verify = kwargs.get('verify', True)
            res = _schema().from_jwt(_txt,
                                     keyjar=self.keyjar,
                                     sender=self.provider_info["issuer"],
                                     verify=verify)

        if 'error' in res:  # Error response
            res = UserInfoErrorResponse(**res.to_dict())

        # TODO verify issuer:sub against what's returned in the ID Token

        self.store_response(res, _txt)

        return res
示例#20
0
    def begin(self,
              scope="",
              response_type="",
              use_nonce=False,
              path="",
              **kwargs):
        """ Begin the OIDC flow

        :param scope: Defines which user info claims is wanted
        :param response_type: Controls the parameters returned in the
            response from the Authorization Endpoint
        :param use_nonce: If not implicit flow nonce is optional.
            This defines if it should be used anyway.
        :param path: The path part of the redirect URL
        :return: A 2-tuple, session identifier and URL to which the user
            should be redirected
        """
        _log_info = logger.info

        if self.debug:
            _log_info("- begin -")

        _page = self.config["authz_page"]
        if not path.endswith("/"):
            if _page.startswith("/"):
                self.redirect_uris = [path + _page]
            else:
                self.redirect_uris = ["%s/%s" % (path, _page)]
        else:
            if _page.startswith("/"):
                self.redirect_uris = [path + _page[1:]]
            else:
                self.redirect_uris = ["%s/%s" % (path, _page)]

        # Put myself in the dictionary of sessions, keyed on session-id
        if not self.seed:
            self.seed = rndstr()

        if not scope:
            scope = self.config["scope"]
        if not response_type:
            response_type = self.config["response_type"]

        sid = stateID(path, self.seed)
        self.grant[sid] = Grant(seed=self.seed)

        self._backup(sid)
        self.sdb["seed:%s" % self.seed] = sid

        args = {
            "client_id": self.client_id,
            "state": sid,
            "response_type": response_type,
            "scope": scope,
        }

        # nonce is REQUIRED in implicit flow,
        # OPTIONAL on code flow.
        if "token" in response_type or use_nonce:
            self.nonce = rndstr(12)
            args["nonce"] = self.nonce

        if "max_age" in self.config:
            args["max_age"] = self.config["max_age"]

        _claims = None
        if "user_info" in self.config:
            _claims = ClaimsRequest(userinfo=Claims(
                **self.config["user_info"]))
        if "id_token" in self.config:
            if _claims:
                _claims["id_token"] = Claims(**self.config["id_token"])
            else:
                _claims = ClaimsRequest(id_token=Claims(
                    **self.config["id_token"]))

        if _claims:
            args["claims"] = _claims

        if "request_method" in self.config:
            areq = self.construct_AuthorizationRequest(request_args=args,
                                                       extra_args=None,
                                                       request_param="request")

            if self.config["request_method"] == "file":
                id_request = areq["request"]
                del areq["request"]
                _filedir = self.config["temp_dir"]
                _webpath = self.config["temp_path"]
                _name = rndstr(10)
                filename = os.path.join(_filedir, _name)
                while os.path.exists(filename):
                    _name = rndstr(10)
                    filename = os.path.join(_filedir, _name)
                fid = open(filename, mode="w")
                fid.write(id_request)
                fid.close()
                _webname = "%s%s%s" % (path, _webpath, _name)
                areq["request_uri"] = _webname
                self.request_uri = _webname
                self._backup(sid)
        else:
            if "userinfo_claims" in args:  # can only be carried in an IDRequest
                raise PyoidcError("Need a request method")

            areq = self.construct_AuthorizationRequest(AuthorizationRequest,
                                                       request_args=args)

        location = areq.request(self.authorization_endpoint)

        if self.debug:
            _log_info("Redirecting to: %s" % location)

        return sid, location
示例#21
0
    def do_user_info_request(self,
                             method="POST",
                             state="",
                             scope="openid",
                             request="openid",
                             **kwargs):

        kwargs["request"] = request
        path, body, method, h_args = self.user_info_request(
            method, state, scope, **kwargs)

        logger.debug(
            "[do_user_info_request] PATH:{} BODY:{} H_ARGS: {}".format(
                path, body, h_args))

        if self.events:
            self.events.store('Request', {'body': body})
            self.events.store('request_url', path)
            self.events.store('request_http_args', h_args)

        try:
            resp = self.http_request(path, method, data=body, **h_args)
        except MissingRequiredAttribute:
            raise

        if resp.status_code == 200:
            try:
                assert "application/json" in resp.headers["content-type"]
                sformat = "json"
            except AssertionError:
                assert "application/jwt" in resp.headers["content-type"]
                sformat = "jwt"
        elif resp.status_code == 500:
            raise PyoidcError("ERROR: Something went wrong: %s" % resp.text)
        elif resp.status_code == 401:
            try:
                www_auth_header = resp.headers["WWW-Authenticate"]
                res = ErrorResponse(
                    error="invalid_token",
                    error_description=
                    "Server returned 401 Unauthorized. WWW-Authenticate header:{}"
                    .format(www_auth_header))
            except KeyError:
                #no www-authenticate header. https://tools.ietf.org/html/rfc6750#section-3 reads:
                #the resource server MUST include the HTTP "WWW-Authenticate" response header field
                res = ErrorResponse(
                    error="invalid_token",
                    error_description=
                    "Server returned 401 Unauthorized without a WWW-Authenticate header which is required as per https://tools.ietf.org/html/rfc6750#section-3"
                )
            self.store_response(res, resp.text)
            return res
        elif 400 <= resp.status_code < 500:
            # the response text might be a OIDC message
            try:
                res = ErrorResponse().from_json(resp.text)
            except Exception:
                raise RequestError(resp.text)
            else:
                self.store_response(res, resp.text)
                return res
        else:
            raise PyoidcError("ERROR: Something went wrong [%s]: %s" %
                              (resp.status_code, resp.text))

        try:
            _schema = kwargs["user_info_schema"]
        except KeyError:
            _schema = OpenIDSchema

        logger.debug("Reponse text: '{}'".format(resp.text))

        _txt = resp.text
        if sformat == "json":
            res = _schema().from_json(txt=_txt)
        else:
            verify = kwargs.get('verify', True)
            res = _schema().from_jwt(_txt,
                                     keyjar=self.keyjar,
                                     sender=self.provider_info["issuer"],
                                     verify=verify)

        if 'error' in res:  # Error response
            res = UserInfoErrorResponse(**res.to_dict())

        # TODO verify issuer:sub against what's returned in the ID Token

        self.store_response(res, _txt)

        return res