示例#1
0
def get_deserialization_method(reqresp):
    """

    :param reqresp: Class instance with attributes: ['status', 'text',
        'headers', 'url']
    :return: Verified body content type
    """
    logger.debug("resp.headers: %s" % (sanitize(reqresp.headers), ))
    logger.debug("resp.txt: %s" % (sanitize(reqresp.text), ))

    try:
        _ctype = reqresp.headers["content-type"]
    except KeyError:
        return 'urlencoded'  # reasonable default ??

    if match_to_("application/json", _ctype) or match_to_(
            'application/jrd+json', _ctype):
        deser_method = 'json'
    elif match_to_("application/jwt", _ctype):
        deser_method = "jwt"
    elif match_to_("application/jose", _ctype):
        deser_method = "jose"
    elif match_to_(URL_ENCODED, _ctype):
        deser_method = 'urlencoded'
    elif match_to_("text/plain", _ctype) or match_to_("test/html", _ctype):
        deser_method = ''
    else:
        deser_method = ''  # reasonable default ??

    return deser_method
示例#2
0
def verify_header(reqresp, body_type):
    """
    
    :param reqresp: Class instance with attributes: ['status', 'text', 
        'headers', 'url'] 
    :param body_type: If information returned in the body part 
    :return: Verified body content type
    """
    logger.debug("resp.headers: %s" % (sanitize(reqresp.headers), ))
    logger.debug("resp.txt: %s" % (sanitize(reqresp.text), ))

    try:
        _ctype = reqresp.headers["content-type"]
    except KeyError:
        if body_type:
            return body_type
        else:
            return 'txt'  # reasonable default ??

    logger.debug('Expected body type: "{}"'.format(body_type))

    if body_type == "":
        if match_to_("application/json", _ctype) or match_to_(
                'application/jrd+json', _ctype):
            body_type = 'json'
        elif match_to_("application/jwt", _ctype):
            body_type = "jwt"
        elif match_to_(URL_ENCODED, _ctype):
            body_type = 'urlencoded'
        else:
            body_type = 'txt'  # reasonable default ??
    elif body_type == "json":
        if match_to_("application/json", _ctype) or match_to_(
                'application/jrd+json', _ctype):
            pass
        elif match_to_("application/jwt", _ctype):
            body_type = "jwt"
        else:
            raise WrongContentType(_ctype)
    elif body_type == "jwt":
        if not match_to_("application/jwt", _ctype):
            raise WrongContentType(_ctype)
    elif body_type == "urlencoded":
        if not match_to_(DEFAULT_POST_CONTENT_TYPE, _ctype):
            # I can live with text/plain
            if not match_to_("text/plain", _ctype):
                raise WrongContentType(_ctype)
    elif body_type == 'txt':
        if match_to_("text/plain", _ctype):
            pass
        elif match_to_("text/html", _ctype):
            pass
        else:
            raise WrongContentType(_ctype)
    else:
        raise ValueError("Unknown return format: %s" % body_type)

    logger.debug('Got body type: "{}"'.format(body_type))
    return body_type
示例#3
0
def collect_user_info(endpoint_context, session, userinfo_claims=None):
    """
    Collect information about a user.
    This can happen in two cases, either when constructing an IdToken or
    when returning user info through the UserInfo endpoint

    :param session: Session information
    :param userinfo_claims: user info claims
    :return: User info
    """
    authn_req = session['authn_req']

    if userinfo_claims is None:
        uic = scope2claims(authn_req["scope"])

        # Get only keys allowed by user and update the dict if such info
        # is stored in session
        perm_set = session.get('permission')
        if perm_set:
            uic = {key: uic[key] for key in uic if key in perm_set}

        uic = update_claims(session, "userinfo", uic)

        if uic:
            userinfo_claims = Claims(**uic)
        else:
            userinfo_claims = None

        logger.debug("userinfo_claim: %s" %
                     sanitize(userinfo_claims.to_dict()))

    logger.debug("Session info: %s" % sanitize(session))

    authn_event = session['authn_event']
    if authn_event:
        uid = authn_event["uid"]
    else:
        uid = session['uid']

    info = endpoint_context.userinfo(uid, authn_req['client_id'],
                                     userinfo_claims)

    if "sub" in userinfo_claims:
        if not claims_match(session["sub"], userinfo_claims["sub"]):
            raise FailedAuthentication("Unmatched sub claim")

    info["sub"] = session["sub"]
    try:
        logger.debug("user_info_response: {}".format(info))
    except UnicodeEncodeError:
        try:
            logger.debug("user_info_response: {}".format(info.encode('utf-8')))
        except Exception:
            pass

    return info
    def __call__(self, url, method="GET", **kwargs):
        """
        Send a HTTP request to a URL using a specified method

        :param url: The URL to access
        :param method: The method to use (GET, POST, ..)
        :param kwargs: extra HTTP request parameters
        :return: A Response
        """

        # copy the default set before starting to modify it.
        _kwargs = copy.copy(self.request_args)
        if kwargs:
            _kwargs.update(kwargs)

        # If I have cookies add them all to the request
        if self.cookiejar:
            _kwargs["cookies"] = self._cookies()
            logger.debug("SENT {} COOKIES".format(len(_kwargs["cookies"])))

        # If I want to modify the request arguments based on URL, method
        # and current arguments I can use this call back function.
        if self.req_callback is not None:
            _kwargs = self.req_callback(method, url, **_kwargs)

        try:
            # Do the request
            r = requests.request(method, url, **_kwargs)
        except Exception as err:
            logger.error(
                "http_request failed: %s, url: %s, htargs: %s, method: %s" % (
                    err, url, sanitize(_kwargs), method))
            raise

        if self.events is not None:
            self.events.store('HTTP response', r, ref=url)

        try:
            _cookie = r.headers["set-cookie"]
            logger.debug("RECEIVED COOKIE")
            try:
                # add received cookies to the cookie jar
                set_cookie(self.cookiejar, SimpleCookie(_cookie))
            except CookieError as err:
                logger.error(err)
                raise NonFatalException(r, "{}".format(err))
        except (AttributeError, KeyError) as err:
            pass

        # return the response
        return r
    def _get_signing_key(self, algorithm, context, kid=None):
        ktype = alg2keytype(algorithm)
        try:
            if kid:
                signing_key = [self._get_key_by_kid(kid, algorithm, context)]
            elif ktype in context.kid["sig"]:
                try:
                    signing_key = [
                        self._get_key_by_kid(context.kid["sig"][ktype],
                                             algorithm, context)
                    ]
                except KeyError:
                    signing_key = self.get_signing_key_from_keyjar(
                        algorithm, context)
            else:
                signing_key = self.get_signing_key_from_keyjar(
                    algorithm, context)
        except (MissingKey, ) as err:
            LOGGER.error("%s", sanitize(err))
            raise

        return signing_key
示例#6
0
    def __call__(self, url, method="GET", **kwargs):
        """
        Send a HTTP request to a URL using a specified method

        :param url: The URL to access
        :param method: The method to use (GET, POST, ..)
        :param kwargs: extra HTTP request parameters
        :return: A Response
        """

        # copy the default set before starting to modify it.
        _kwargs = copy.copy(self.request_args)
        if kwargs:
            _kwargs.update(kwargs)

        # If I have cookies add them all to the request
        self.add_cookies(kwargs)

        # If I want to modify the request arguments based on URL, method
        # and current arguments I can use this call back function.
        self.run_req_callback(url, method, kwargs)

        try:
            # Do the request
            r = requests.request(method, url, **_kwargs)
        except Exception as err:
            logger.error(
                "http_request failed: %s, url: %s, htargs: %s, method: %s" %
                (err, url, sanitize(_kwargs), method))
            raise

        if self.events is not None:
            self.events.store('HTTP response', r, ref=url)

        self.set_cookie(r)

        # return the response
        return r
示例#7
0
    def _verify_sector_identifier(self, request):
        """
        Verify `sector_identifier_uri` is reachable and that it contains
        `redirect_uri`s.

        :param request: Provider registration request
        :return: si_redirects, sector_id
        :raises: InvalidSectorIdentifier
        """
        si_url = request["sector_identifier_uri"]
        try:
            res = self.endpoint_context.httpc.get(si_url)
        except Exception as err:
            logger.error(err)
            res = None

        if not res:
            raise InvalidSectorIdentifier("Couldn't read from sector_identifier_uri")

        logger.debug("sector_identifier_uri => %s", sanitize(res.text))

        try:
            si_redirects = json.loads(res.text)
        except ValueError:
            raise InvalidSectorIdentifier(
                "Error deserializing sector_identifier_uri content"
            )

        if "redirect_uris" in request:
            logger.debug("redirect_uris: %s", request["redirect_uris"])
            for uri in request["redirect_uris"]:
                if uri not in si_redirects:
                    raise InvalidSectorIdentifier(
                        "redirect_uri missing from sector_identifiers"
                    )

        return si_redirects, si_url
示例#8
0
def set_cookie(cookiejar, kaka):
    """PLaces a cookie (a cookielib.Cookie based on a set-cookie header
    line) in the cookie jar.
    Always chose the shortest expires time.

    :param cookiejar:
    :param kaka: Cookie
    """

    # default rfc2109=False
    # max-age, httponly

    for cookie_name, morsel in kaka.items():
        std_attr = ATTRS.copy()
        std_attr["name"] = cookie_name
        _tmp = morsel.coded_value
        if _tmp.startswith('"') and _tmp.endswith('"'):
            std_attr["value"] = _tmp[1:-1]
        else:
            std_attr["value"] = _tmp

        std_attr["version"] = 0
        attr = ""
        # copy attributes that have values
        try:
            for attr in morsel.keys():
                if attr in ATTRS:
                    if morsel[attr]:
                        if attr == "expires":
                            std_attr[attr] = http2time(morsel[attr])
                        else:
                            std_attr[attr] = morsel[attr]
                elif attr == "max-age":
                    if morsel[attr]:
                        std_attr["expires"] = http2time(morsel[attr])
        except TimeFormatError:
            # Ignore cookie
            logger.info(
                "Time format error on %s parameter in received cookie" %
                (sanitize(attr), ))
            continue

        for att, spec in PAIRS.items():
            if std_attr[att]:
                std_attr[spec] = True

        if std_attr["domain"] and std_attr["domain"].startswith("."):
            std_attr["domain_initial_dot"] = True

        if morsel["max-age"] is 0:
            try:
                cookiejar.clear(domain=std_attr["domain"],
                                path=std_attr["path"],
                                name=std_attr["name"])
            except ValueError:
                pass
        else:
            # Fix for Microsoft cookie error
            if "version" in std_attr:
                try:
                    std_attr["version"] = std_attr["version"].split(",")[0]
                except (TypeError, AttributeError):
                    pass

            new_cookie = Cookie(**std_attr)

            cookiejar.set_cookie(new_cookie)
示例#9
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except MessageException as err:
            if "type" not in request:
                return ResponseMessage(error="invalid_type",
                                       error_description="%s" % err)
            else:
                return ResponseMessage(error="invalid_configuration_parameter",
                                       error_description="%s" % err)

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err)

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            while client_id in _context.cdb:
                client_id = rndstr(12)
        else:
            try:
                client_id = request['client_id']
            except KeyError:
                raise ValueError('Missing client_id')

        _rat = rndstr(32)

        _cinfo = {
            "client_id":
            client_id,
            "registration_access_token":
            _rat,
            "registration_client_uri":
            "%s?client_id=%s" % (self.endpoint_path, client_id),
            "client_salt":
            rndstr(8)
        }

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        if set_secret:
            client_secret = secret(_context.seed, client_id)
            _cinfo.update({
                "client_secret":
                client_secret,
                "client_secret_expires_at":
                client_secret_expiration_time()
            })
        else:
            client_secret = ''

        _context.cdb[client_id] = _cinfo
        _context.cdb[_rat] = client_id

        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"])
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict([(k, v) for k, v in _cinfo.items()
                     if k in RegistrationResponse.c_param])

        self.comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        _context.cdb[client_id] = _cinfo

        try:
            _context.cdb.sync()
        except AttributeError:  # Not all databases can be sync'ed
            pass

        logger.info("registration_response: %s" % sanitize(response.to_dict()))

        return response
示例#10
0
    def do_client_registration(self, request, client_id, ignore=None):
        if ignore is None:
            ignore = []

        _context = self.endpoint_context
        _cinfo = _context.cdb[client_id].copy()
        logger.debug("_cinfo: %s" % sanitize(_cinfo))

        for key, val in request.items():
            if key not in ignore:
                _cinfo[key] = val

        if "post_logout_redirect_uris" in request:
            plruri = []
            for uri in request["post_logout_redirect_uris"]:
                if urlparse(uri).fragment:
                    err = ClientRegistrationErrorResponse(
                        error="invalid_configuration_parameter",
                        error_description="post_logout_redirect_uris "
                        "contains "
                        "fragment")
                    return err
                base, query = splitquery(uri)
                if query:
                    plruri.append((base, parse_qs(query)))
                else:
                    plruri.append((base, query))
            _cinfo["post_logout_redirect_uris"] = plruri

        if "redirect_uris" in request:
            try:
                ruri = self.verify_redirect_uris(request)
                _cinfo["redirect_uris"] = ruri
            except InvalidRedirectURIError as e:
                return ClientRegistrationErrorResponse(
                    error="invalid_redirect_uri", error_description=str(e))

        if "sector_identifier_uri" in request:
            try:
                _cinfo["si_redirects"], _cinfo[
                    "sector_id"] = self._verify_sector_identifier(request)
            except InvalidSectorIdentifier as err:
                return ResponseMessage(error="invalid_configuration_parameter",
                                       error_description=err)
        elif "redirect_uris" in request:
            if len(request["redirect_uris"]) > 1:
                # check that the hostnames are the same
                host = ""
                for url in request["redirect_uris"]:
                    part = urlparse(url)
                    _host = part.netloc.split(":")[0]
                    if not host:
                        host = _host
                    else:
                        try:
                            assert host == _host
                        except AssertionError:
                            return ResponseMessage(
                                error="invalid_configuration_parameter",
                                error_description="'sector_identifier_uri' "
                                "must be registered")

        for item in ["policy_uri", "logo_uri", "tos_uri"]:
            if item in request:
                if verify_url(request[item], _cinfo["redirect_uris"]):
                    _cinfo[item] = request[item]
                else:
                    return ResponseMessage(
                        error="invalid_configuration_parameter",
                        error_description="%s pointed to illegal URL" % item)

        # Do I have the necessary keys
        for item in [
                "id_token_signed_response_alg", "userinfo_signed_response_alg"
        ]:
            if item in request:
                if request[item] in _context.provider_info[
                        PREFERENCE2PROVIDER[item]]:
                    ktyp = alg2keytype(request[item])
                    # do I have this ktyp and for EC type keys the curve
                    if ktyp not in ["none", "oct"]:
                        _k = []
                        for iss in ['', _context.issuer]:
                            _k.extend(
                                _context.keyjar.get_signing_key(
                                    ktyp, alg=request[item], owner=iss))
                        if not _k:
                            logger.warning('Lacking support for "{}"'.format(
                                request[item]))
                            del _cinfo[item]

        t = {'jwks_uri': '', 'jwks': None}

        for item in ['jwks_uri', 'jwks']:
            if item in request:
                t[item] = request[item]

        try:
            _context.keyjar.load_keys(client_id,
                                      jwks_uri=t['jwks_uri'],
                                      jwks=t['jwks'])
            try:
                n_keys = len(_context.keyjar[client_id])
                msg = "found {} keys for client_id={}"
                logger.debug(msg.format(n_keys, client_id))
            except KeyError:
                pass
        except Exception as err:
            logger.error("Failed to load client keys: %s" %
                         sanitize(request.to_dict()))
            logger.error("%s", err)
            logger.debug('Verify SSL: {}'.format(_context.keyjar.verify_ssl))
            return ClientRegistrationErrorResponse(
                error="invalid_configuration_parameter",
                error_description="%s" % err)

        return _cinfo
示例#11
0
    def client_registration_setup(self, request, new_id=True, set_secret=True):
        try:
            request.verify()
        except (MessageException, ValueError) as err:
            return ResponseMessage(
                error="invalid_configuration_request", error_description="%s" % err
            )

        request.rm_blanks()
        try:
            self.match_client_request(request)
        except CapabilitiesMisMatch as err:
            return ResponseMessage(
                error="invalid_request",
                error_description="Don't support proposed %s" % err,
            )

        _context = self.endpoint_context
        if new_id:
            # create new id och secret
            client_id = rndstr(12)
            while client_id in _context.cdb:
                client_id = rndstr(12)
        else:
            try:
                client_id = request["client_id"]
            except KeyError:
                raise ValueError("Missing client_id")

        _cinfo = {"client_id": client_id, "client_salt": rndstr(8)}

        if "registration_api" in self.endpoint_context.endpoint:
            self.add_registration_api(_cinfo, client_id, _context)

        if new_id:
            _cinfo["client_id_issued_at"] = utc_time_sans_frac()

        if set_secret:
            client_secret = self.add_client_secret(_cinfo, client_id, _context)
        else:
            client_secret = ""

        _context.cdb[client_id] = _cinfo

        _cinfo = self.do_client_registration(
            request,
            client_id,
            ignore=["redirect_uris", "policy_uri", "logo_uri", "tos_uri"],
        )
        if isinstance(_cinfo, ResponseMessage):
            return _cinfo

        args = dict(
            [(k, v) for k, v in _cinfo.items() if k in RegistrationResponse.c_param]
        )

        comb_uri(args)
        response = RegistrationResponse(**args)

        # Add the client_secret as a symmetric key to the key jar
        if client_secret:
            _context.keyjar.add_symmetric(client_id, str(client_secret))

        _context.cdb[client_id] = _cinfo

        try:
            _context.cdb.sync()
        except AttributeError:  # Not all databases can be sync'ed
            pass

        logger.info("registration_response: %s" % sanitize(response.to_dict()))

        return response
示例#12
0
    def do_client_registration(self, request, client_id, ignore=None):
        if ignore is None:
            ignore = []

        _context = self.endpoint_context
        _cinfo = _context.cdb[client_id].copy()
        logger.debug("_cinfo: %s" % sanitize(_cinfo))

        for key, val in request.items():
            if key not in ignore:
                _cinfo[key] = val

        if "post_logout_redirect_uris" in request:
            plruri = []
            for uri in request["post_logout_redirect_uris"]:
                if urlparse(uri).fragment:
                    err = ClientRegistrationErrorResponse(
                        error="invalid_configuration_parameter",
                        error_description="post_logout_redirect_uris "
                        "contains "
                        "fragment",
                    )
                    return err
                base, query = splitquery(uri)
                if query:
                    plruri.append((base, parse_qs(query)))
                else:
                    plruri.append((base, query))
            _cinfo["post_logout_redirect_uris"] = plruri

        if "redirect_uris" in request:
            try:
                ruri = self.verify_redirect_uris(request)
                _cinfo["redirect_uris"] = ruri
            except InvalidRedirectURIError as e:
                return ClientRegistrationErrorResponse(
                    error="invalid_redirect_uri", error_description=str(e)
                )

        if "sector_identifier_uri" in request:
            try:
                _cinfo["si_redirects"], _cinfo[
                    "sector_id"
                ] = self._verify_sector_identifier(request)
            except InvalidSectorIdentifier as err:
                return ResponseMessage(
                    error="invalid_configuration_parameter", error_description=str(err)
                )

        for item in ["policy_uri", "logo_uri", "tos_uri"]:
            if item in request:
                if verify_url(request[item], _cinfo["redirect_uris"]):
                    _cinfo[item] = request[item]
                else:
                    return ResponseMessage(
                        error="invalid_configuration_parameter",
                        error_description="%s pointed to illegal URL" % item,
                    )

        # Do I have the necessary keys
        for item in ["id_token_signed_response_alg", "userinfo_signed_response_alg"]:
            if item in request:
                if request[item] in _context.provider_info[PREFERENCE2PROVIDER[item]]:
                    ktyp = alg2keytype(request[item])
                    # do I have this ktyp and for EC type keys the curve
                    if ktyp not in ["none", "oct"]:
                        _k = []
                        for iss in ["", _context.issuer]:
                            _k.extend(
                                _context.keyjar.get_signing_key(
                                    ktyp, alg=request[item], owner=iss
                                )
                            )
                        if not _k:
                            logger.warning(
                                'Lacking support for "{}"'.format(request[item])
                            )
                            del _cinfo[item]

        t = {"jwks_uri": "", "jwks": None}

        for item in ["jwks_uri", "jwks"]:
            if item in request:
                t[item] = request[item]

        # if it can't load keys because the URL is false it will
        # just silently fail. Waiting for better times.
        _context.keyjar.load_keys(client_id, jwks_uri=t["jwks_uri"], jwks=t["jwks"])
        try:
            n_keys = 0
            for kb in _context.keyjar[client_id]:
                n_keys += len(kb.keys())
            msg = "found {} keys for client_id={}"
            logger.debug(msg.format(n_keys, client_id))
        except KeyError:
            pass

        return _cinfo