Esempio n. 1
0
    def create_authn_request(self, session, acr_value=None, **kwargs):
        session["state"] = rndstr(32)
        request_args = {
            "response_type": self.behaviour["response_type"],
            "scope": self.behaviour["scope"],
            "state": session["state"],
            "redirect_uri": self.registration_response["redirect_uris"][0],
        }

        if self.oidc:
            session["nonce"] = rndstr(32)
            request_args["nonce"] = session["nonce"]

        if acr_value is not None:
            request_args["acr_values"] = acr_value

        request_args.update(kwargs)
        cis = self.construct_AuthorizationRequest(request_args=request_args)
        logger.debug("request: %s" % sanitize(cis))

        url, body, ht_args, cis = self.uri_and_body(
            AuthorizationRequest, cis, method="GET", request_args=request_args
        )

        self.authz_req[request_args["state"]] = cis
        logger.debug("body: %s" % sanitize(body))
        logger.info("URL: %s" % sanitize(url))
        logger.debug("ht_args: %s" % sanitize(ht_args))

        resp = Redirect(str(url))
        if ht_args:
            resp.headers.extend([(a, b) for a, b in ht_args.items()])
        logger.debug("resp_headers: %s" % sanitize(resp.headers))
        return resp
Esempio n. 2
0
    def create_authn_request(self, session, acr_value=None, **kwargs):
        session["state"] = rndstr(32)
        request_args = {
            "response_type": self.behaviour["response_type"],
            "scope": self.behaviour["scope"],
            "state": session["state"],
            "redirect_uri": self.registration_response["redirect_uris"][0]
        }

        if self.oidc:
            session["nonce"] = rndstr(32)
            request_args['nonce'] = session['nonce']

        if acr_value is not None:
            request_args["acr_values"] = acr_value

        request_args.update(kwargs)
        cis = self.construct_AuthorizationRequest(request_args=request_args)
        logger.debug("request: %s" % sanitize(cis))

        url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis,
                                                    method="GET",
                                                    request_args=request_args)

        self.authz_req[request_args['state']] = cis
        logger.debug("body: %s" % sanitize(body))
        logger.info("URL: %s" % sanitize(url))
        logger.debug("ht_args: %s" % sanitize(ht_args))

        resp = Redirect(str(url))
        if ht_args:
            resp.headers.extend([(a, b) for a, b in ht_args.items()])
        logger.debug("resp_headers: %s" % sanitize(resp.headers))
        return resp
Esempio n. 3
0
def verify_header(reqresp, body_type):
    logger.debug("resp.headers: %s" % (sanitize(reqresp.headers), ))
    logger.debug("resp.txt: %s" % (sanitize(reqresp.text), ))

    if body_type == "":
        _ctype = reqresp.headers["content-type"]
        if match_to_("application/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 not match_to_("application/json", reqresp.headers["content-type"]):
            if match_to_("application/jwt", reqresp.headers["content-type"]):
                body_type = "jwt"
            else:
                raise ValueError("content-type: %s" %
                                 (reqresp.headers["content-type"], ))
    elif body_type == "jwt":
        if not match_to_("application/jwt", reqresp.headers["content-type"]):
            raise ValueError("Wrong content-type in header, got: {} expected "
                             "'application/jwt'".format(
                                 reqresp.headers["content-type"]))
    elif body_type == "urlencoded":
        if not match_to_(DEFAULT_POST_CONTENT_TYPE,
                         reqresp.headers["content-type"]):
            if not match_to_("text/plain", reqresp.headers["content-type"]):
                raise ValueError('Wrong content-type')
    else:
        raise ValueError("Unknown return format: %s" % body_type)

    return body_type
Esempio n. 4
0
def verify_header(reqresp, body_type):
    logger.debug("resp.headers: %s" % (sanitize(reqresp.headers),))
    logger.debug("resp.txt: %s" % (sanitize(reqresp.text),))

    if body_type == "":
        _ctype = reqresp.headers["content-type"]
        if match_to_("application/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 not match_to_("application/json", reqresp.headers["content-type"]):
            if match_to_("application/jwt", reqresp.headers["content-type"]):
                body_type = "jwt"
            else:
                raise ValueError("content-type: %s" % (reqresp.headers["content-type"],))
    elif body_type == "jwt":
        if not match_to_("application/jwt", reqresp.headers["content-type"]):
            raise ValueError("Wrong content-type in header, got: {} expected "
                             "'application/jwt'".format(reqresp.headers["content-type"]))
    elif body_type == "urlencoded":
        if not match_to_(DEFAULT_POST_CONTENT_TYPE, reqresp.headers["content-type"]):
            if not match_to_("text/plain", reqresp.headers["content-type"]):
                raise ValueError('Wrong content-type')
    else:
        raise ValueError("Unknown return format: %s" % body_type)

    return body_type
Esempio n. 5
0
    def registration_endpoint(self, request, authn=None, **kwargs):
        logger.debug("@registration_endpoint: <<{}>>".format(
            sanitize(request)))

        if isinstance(request, dict):
            request = ClientMetadataStatement(**request)
        else:
            try:
                request = ClientMetadataStatement().deserialize(
                    request, "json")
            except ValueError:
                request = ClientMetadataStatement().deserialize(request)

        logger.info("registration_request:{}".format(
            sanitize(request.to_dict())))

        request_args = self.get_metadata_statement(request)
        request = RegistrationRequest(**request_args)

        result = self.client_registration_setup(request)
        if isinstance(result, Response):
            return result

        return Created(result.to_json(),
                       content="application/json",
                       headers=[("Cache-Control", "no-store")])
Esempio n. 6
0
    def _do_code(self, response, authresp):
        """Perform code flow."""
        # get the access token
        try:
            args = {
                "code": authresp["code"],
                "redirect_uri": self.registration_response["redirect_uris"][0],
                "client_id": self.client_id,
                "client_secret": self.client_secret,
            }

            try:
                args["scope"] = response["scope"]
            except KeyError:
                pass

            atresp = self.do_access_token_request(
                state=authresp["state"],
                request_args=args,
                authn_method=self.registration_response["token_endpoint_auth_method"],
            )
            msg = "Access token response: {}"
            logger.info(msg.format(sanitize(atresp)))
        except Exception as err:
            logger.error("%s" % err)
            raise

        if isinstance(atresp, ErrorResponse):
            msg = "Error response: {}"
            self._err(msg.format(sanitize(atresp.to_dict())))

        _token = atresp["access_token"]

        _id_token = atresp.get("id_token")
        return _token, _id_token
Esempio n. 7
0
    def registration_endpoint(self, request, authn=None, **kwargs):
        logger.debug("@registration_endpoint: <<{}>>".format(
            sanitize(request)))

        if isinstance(request, dict):
            request = ClientMetadataStatement(**request)
        else:
            try:
                request = ClientMetadataStatement().deserialize(
                    request, "json")
            except ValueError:
                request = ClientMetadataStatement().deserialize(request)

        logger.info("registration_request:{}".format(
            sanitize(request.to_dict())))

        res = self.federation_entity.get_metadata_statement(request)

        if res:
            request = self.federation_entity.pick_by_priority(res)
        else:  # Nothing I can use
            return error(error='invalid_request',
                         descr='No signed metadata statement I could use')

        result = self.client_registration_setup(request)
        if isinstance(result, Response):
            return result

        return Created(result.to_json(),
                       content="application/json",
                       headers=[("Cache-Control", "no-store")])
Esempio n. 8
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % sanitize(body))

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("AccessTokenRequest: %s" % sanitize(areq))

        try:
            assert areq["grant_type"] == "authorization_code"
        except AssertionError:
            err = TokenErrorResponse(error="invalid_request",
                                     error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        # assert that the code is valid
        _info = _sdb[areq["code"]]

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info:
            assert areq["redirect_uri"] == _info["redirect_uri"]

        try:
            _tinfo = _sdb.upgrade_to_token(areq["code"], issue_refresh=True)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % sanitize(_tinfo))

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % sanitize(atr))

        return Response(atr.to_json(), content="application/json")
Esempio n. 9
0
    def dynamic_client(self, issuer="", userid=""):
        client = self.client_cls(
            client_authn_method=CLIENT_AUTHN_METHOD,
            verify_ssl=self.verify_ssl,
            **self.jwks_info
        )
        if userid:
            try:
                issuer = client.wf.discovery_query(userid)
            except AttributeError:
                wf = WebFinger(httpd=client)
                issuer = wf.discovery_query(userid)

        if not issuer:
            raise OAuth2Error("Missing issuer")

        logger.info("issuer: {}".format(issuer))

        if issuer in self.client:
            return self.client[issuer]
        else:
            # Gather OP information
            _pcr = client.provider_config(issuer)
            logger.info("Provider info: {}".format(sanitize(_pcr.to_dict())))
            issuer = _pcr["issuer"]  # So no hickup later about trailing '/'
            # register the client
            _cinfo = self.config.CLIENTS[""]["client_info"]
            reg_args = copy.copy(_cinfo)
            h = hashlib.sha256(self.seed)
            h.update(issuer.encode("utf8"))  # issuer has to be bytes
            base_urls = _cinfo["redirect_uris"]

            reg_args["redirect_uris"] = [
                u.format(base=self.base_url, iss=h.hexdigest()) for u in base_urls
            ]
            try:
                reg_args["post_logout_redirect_uris"] = [
                    u.format(base=self.base_url, iss=h.hexdigest())
                    for u in reg_args["post_logout_redirect_uris"]
                ]
            except KeyError:
                pass

            self.get_path(reg_args["redirect_uris"], issuer)
            if client.jwks_uri:
                reg_args["jwks_uri"] = client.jwks_uri

            rr = client.register(_pcr["registration_endpoint"], **reg_args)
            msg = "Registration response: {}"
            logger.info(msg.format(sanitize(rr.to_dict())))

            try:
                client.behaviour.update(**self.config.CLIENTS[""]["behaviour"])
            except KeyError:
                pass

            self.client[issuer] = client
            return client
Esempio n. 10
0
    def verify(self, request, **kwargs):
        """
        Verifies that the given username and password was correct
        :param request: Either the query part of a URL a urlencoded
        body of a HTTP message or a parse such.
        :param kwargs: Catch whatever else is sent.
        :return: redirect back to where ever the base applications
        wants the user after authentication.
        """

        logger.debug("verify(%s)" % sanitize(request))
        if isinstance(request, six.string_types):
            _dict = compact(parse_qs(request))
        elif isinstance(request, dict):
            _dict = request
        else:
            raise ValueError("Wrong type of input")

        logger.debug("dict: %s" % sanitize(_dict))
        # verify username and password
        try:
            self._verify(_dict["password"], _dict["login"])  # dict origin
        except TypeError:
            try:
                self._verify(_dict["password"][0], _dict["login"][0])
            except (AssertionError, KeyError) as err:
                logger.debug("Password verification failed: {}".format(err))
                resp = Unauthorized("Unknown user or wrong password")
                return resp, False
            else:
                try:
                    _qp = _dict["query"]
                except KeyError:
                    _qp = self.get_multi_auth_cookie(kwargs['cookie'])
        except (AssertionError, KeyError) as err:
            logger.debug("Password verification failed: {}".format(err))
            resp = Unauthorized("Unknown user or wrong password")
            return resp, False
        else:
            try:
                _qp = _dict["query"]
            except KeyError:
                _qp = self.get_multi_auth_cookie(kwargs['cookie'])

        logger.debug("Password verification succeeded.")
        # if "cookie" not in kwargs or self.srv.cookie_name not in kwargs["cookie"]:
        headers = [self.create_cookie(_dict["login"], "upm")]
        try:
            return_to = self.generate_return_url(kwargs["return_to"], _qp)
        except KeyError:
            try:
                return_to = self.generate_return_url(self.return_to, _qp,
                                                     kwargs["path"])
            except KeyError:
                return_to = self.generate_return_url(self.return_to, _qp)

        return SeeOther(return_to, headers=headers), True
Esempio n. 11
0
    def verify(self, request, **kwargs):
        """
        Verifies that the given username and password was correct
        :param request: Either the query part of a URL a urlencoded
        body of a HTTP message or a parse such.
        :param kwargs: Catch whatever else is sent.
        :return: redirect back to where ever the base applications
        wants the user after authentication.
        """

        logger.debug("verify(%s)" % sanitize(request))
        if isinstance(request, six.string_types):
            _dict = compact(parse_qs(request))
        elif isinstance(request, dict):
            _dict = request
        else:
            raise ValueError("Wrong type of input")

        logger.debug("dict: %s" % sanitize(_dict))
        # verify username and password
        try:
            self._verify(_dict["password"], _dict["login"])  # dict origin
        except TypeError:
            try:
                self._verify(_dict["password"][0], _dict["login"][0])
            except (AssertionError, KeyError) as err:
                logger.debug("Password verification failed: {}".format(err))
                resp = Unauthorized("Unknown user or wrong password")
                return resp, False
            else:
                try:
                    _qp = _dict["query"]
                except KeyError:
                    _qp = self.get_multi_auth_cookie(kwargs['cookie'])
        except (AssertionError, KeyError) as err:
            logger.debug("Password verification failed: {}".format(err))
            resp = Unauthorized("Unknown user or wrong password")
            return resp, False
        else:
            try:
                _qp = _dict["query"]
            except KeyError:
                _qp = self.get_multi_auth_cookie(kwargs['cookie'])

        logger.debug("Password verification succeeded.")
        # if "cookie" not in kwargs or self.srv.cookie_name not in kwargs["cookie"]:
        headers = [self.create_cookie(_dict["login"], "upm")]
        try:
            return_to = self.generate_return_url(kwargs["return_to"], _qp)
        except KeyError:
            try:
                return_to = self.generate_return_url(self.return_to, _qp,
                                                     kwargs["path"])
            except KeyError:
                return_to = self.generate_return_url(self.return_to, _qp)

        return SeeOther(return_to, headers=headers), True
Esempio n. 12
0
    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        logger.debug("- token -")
        body = kwargs["request"]
        logger.debug("body: %s" % sanitize(body))

        areq = AccessTokenRequest().deserialize(body, "urlencoded")

        try:
            self.client_authn(self, areq, authn)
        except FailedAuthentication as err:
            logger.error(err)
            err = TokenErrorResponse(error="unauthorized_client",
                                     error_description="%s" % err)
            return Response(err.to_json(), content="application/json", status_code=401)

        logger.debug("AccessTokenRequest: %s" % sanitize(areq))

        if areq["grant_type"] != "authorization_code":
            err = TokenErrorResponse(error="invalid_request", error_description="Wrong grant type")
            return Response(err.to_json(), content="application/json", status="401 Unauthorized")

        # assert that the code is valid
        _info = _sdb[areq["code"]]

        resp = self.token_scope_check(areq, _info)
        if resp:
            return resp

        # If redirect_uri was in the initial authorization request
        # verify that the one given here is the correct one.
        if "redirect_uri" in _info and areq["redirect_uri"] != _info["redirect_uri"]:
            logger.error('Redirect_uri mismatch')
            err = TokenErrorResponse(error="unauthorized_client")
            return Unauthorized(err.to_json(), content="application/json")

        try:
            _tinfo = _sdb.upgrade_to_token(areq["code"], issue_refresh=True)
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        logger.debug("_tinfo: %s" % sanitize(_tinfo))

        atr = AccessTokenResponse(**by_schema(AccessTokenResponse, **_tinfo))

        logger.debug("AccessTokenResponse: %s" % sanitize(atr))

        return Response(atr.to_json(), content="application/json", headers=OAUTH2_NOCACHE_HEADERS)
Esempio n. 13
0
    def dynamic_client(self, issuer='', userid=''):
        client = self.client_cls(client_authn_method=CLIENT_AUTHN_METHOD,
                                 verify_ssl=self.verify_ssl,
                                 **self.jwks_info)
        if userid:
            try:
                issuer = client.wf.discovery_query(userid)
            except AttributeError:
                wf = WebFinger(httpd=client)
                issuer = wf.discovery_query(userid)

        if not issuer:
            raise OAuth2Error('Missing issuer')

        logger.info('issuer: {}'.format(issuer))

        if issuer in self.client:
            return self.client[issuer]
        else:
            # Gather OP information
            _pcr = client.provider_config(issuer)
            logger.info('Provider info: {}'.format(sanitize(_pcr.to_dict())))
            issuer = _pcr['issuer']  # So no hickup later about trailing '/'
            # register the client
            _cinfo = self.config.CLIENTS[""]["client_info"]
            reg_args = copy.copy(_cinfo)
            h = hashlib.sha256(self.seed)
            h.update(issuer.encode('utf8'))  # issuer has to be bytes
            base_urls = _cinfo["redirect_uris"]

            reg_args['redirect_uris'] = [
                u.format(base=self.base_url, iss=h.hexdigest())
                for u in base_urls]
            try:
                reg_args['post_logout_redirect_uris'] = [
                    u.format(base=self.base_url, iss=h.hexdigest())
                    for u in reg_args['post_logout_redirect_uris']
                    ]
            except KeyError:
                pass

            self.get_path(reg_args['redirect_uris'], issuer)
            if client.jwks_uri:
                reg_args['jwks_uri'] = client.jwks_uri

            rr = client.register(_pcr["registration_endpoint"], **reg_args)
            msg = 'Registration response: {}'
            logger.info(msg.format(sanitize(rr.to_dict())))

            try:
                client.behaviour.update(**self.config.CLIENTS[""]["behaviour"])
            except KeyError:
                pass

            self.client[issuer] = client
            return client
Esempio n. 14
0
    def claims_info_endpoint(self, request, authn):
        _log_info = logger.info

        _log_info("Claims_info_endpoint query: '%s'" % sanitize(request))

        ucreq = self.srvmethod.parse_userinfo_claims_request(request)
        # Access_token is mandatory in UserInfoClaimsRequest
        uiresp = OpenIDSchema(**self.info_store[ucreq["access_token"]])

        _log_info("returning: %s" % sanitize(uiresp.to_dict()))
        return Response(uiresp.to_json(), content="application/json")
Esempio n. 15
0
    def parse_authz_response(self, query):
        aresp = self.parse_response(AuthorizationResponse,
                                    info=query,
                                    sformat="urlencoded",
                                    keyjar=self.keyjar)
        if aresp.type() == "ErrorResponse":
            logger.info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(aresp.error)

        logger.info("Aresp: %s" % sanitize(aresp))

        return aresp
Esempio n. 16
0
    def parse_authz_response(self, query):
        aresp = self.parse_response(AuthorizationResponse,
                                    info=query,
                                    sformat="urlencoded",
                                    keyjar=self.keyjar)
        if aresp.type() == "ErrorResponse":
            logger.info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(aresp.error)

        logger.info("Aresp: %s" % sanitize(aresp))

        return aresp
Esempio n. 17
0
    def dynamic_client(self, userid='', issuer=''):
        client = self.client_cls(client_authn_method=CLIENT_AUTHN_METHOD,
                                 verify_ssl=self.verify_ssl,
                                 **self.jwks_info)

        if userid:
            issuer = client.wf.discovery_query(userid)

        if not issuer:
            raise OIDCError('Missing issuer')

        logger.info('issuer: {}'.format(issuer))
        if issuer in self.client:
            return self.client[issuer]
        else:
            # Gather OP information
            _pcr = client.provider_config(issuer)
            logger.info('Provider info: {}'.format(sanitize(_pcr.to_dict())))
            # register the client
            _cinfo = self.config.CLIENTS[""]["client_info"]
            reg_args = copy.copy(_cinfo)
            h = hashlib.sha256(self.seed)
            h.update(issuer.encode('utf8'))  # issuer has to be bytes
            base_urls = _cinfo["redirect_uris"]

            reg_args['redirect_uris'] = [
                u.format(base=self.base_url, iss=h.hexdigest())
                for u in base_urls
            ]
            try:
                reg_args['post_logout_redirect_uris'] = [
                    u.format(base=self.base_url, iss=h.hexdigest())
                    for u in reg_args['post_logout_redirect_uris']
                ]
            except KeyError:
                pass

            self.get_path(reg_args['redirect_uris'], issuer)
            if client.jwks_uri:
                reg_args['jwks_uri'] = client.jwks_uri

            rr = client.register(_pcr["registration_endpoint"], **reg_args)
            msg = 'Registration response: {}'
            logger.info(msg.format(sanitize(rr.to_dict())))

            try:
                client.behaviour.update(**self.config.CLIENTS[""]["behaviour"])
            except KeyError:
                pass

            self.client[issuer] = client
            return client
Esempio n. 18
0
    def claims_info_endpoint(self, request, authn):
        _log_info = logger.info

        _log_info("Claims_info_endpoint query: '%s'" % sanitize(request))

        ucreq = self.srvmethod.parse_userinfo_claims_request(request)

        # Bearer header or body
        access_token = bearer_auth(ucreq, authn)
        uiresp = OpenIDSchema(**self.info_store[access_token])

        _log_info("returning: %s" % sanitize(uiresp.to_dict()))
        return Response(uiresp.to_json(), content="application/json")
Esempio n. 19
0
    def claims_info_endpoint(self, request, authn):
        _log_info = logger.info

        _log_info("Claims_info_endpoint query: '%s'" % sanitize(request))

        ucreq = self.srvmethod.parse_userinfo_claims_request(request)

        # Bearer header or body
        access_token = bearer_auth(ucreq, authn)
        uiresp = OpenIDSchema(**self.info_store[access_token])

        _log_info("returning: %s" % sanitize(uiresp.to_dict()))
        return Response(uiresp.to_json(), content="application/json")
Esempio n. 20
0
    def registration_endpoint(self, request, authn=None, **kwargs):
        """

        :param request:
        :param authn:
        :param kwargs:
        :return:
        """
        logger.debug("@registration_endpoint: <<{}>>".format(sanitize(request)))

        if isinstance(request, dict):
            request = ClientMetadataStatement(**request)
        else:
            try:
                request = ClientMetadataStatement().deserialize(request, "json")
            except ValueError:
                request = ClientMetadataStatement().deserialize(request)

        try:
            request.verify()
        except Exception as err:
            return error('Invalid request')

        logger.info(
            "registration_request:{}".format(sanitize(request.to_dict())))

        ms_list = self.federation_entity.get_metadata_statement(request,
                                                                'registration')

        if ms_list:
            ms = self.federation_entity.pick_by_priority(ms_list)
            self.federation = ms.fo
        else:  # Nothing I can use
            return error(error='invalid_request',
                         descr='No signed metadata statement I could use')

        request = RegistrationRequest(**ms.le)
        result = self.client_registration_setup(request)

        if isinstance(result, Response):
            return result

        # TODO This is where the OP should sign the response
        if ms.fo:
            _fo = ms.fo
            sms = self.signer.create_signed_metadata_statement(
                result, 'response', [_fo], single=True)
            self.federation_entity.extend_with_ms(result, {_fo: sms})

        return Created(result.to_json(), content="application/json",
                       headers=[("Cache-Control", "no-store")])
Esempio n. 21
0
    def parse_authz_response(self, query):
        aresp = self.parse_response(
            self.message_factory.get_response_type("authorization_endpoint"),
            info=query,
            sformat="urlencoded",
            keyjar=self.keyjar,
        )
        if aresp.type() == "ErrorResponse":
            logger.info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(aresp.error)

        logger.info("Aresp: %s" % sanitize(aresp))

        return aresp
Esempio n. 22
0
    def verify(self, areq, **kwargs):
        try:
            try:
                argv = {'sender': areq['client_id']}
            except KeyError:
                argv = {}
            bjwt = AuthnToken().from_jwt(areq["client_assertion"],
                                         keyjar=self.cli.keyjar,
                                         **argv)
        except (Invalid, MissingKey) as err:
            logger.info("%s" % sanitize(err))
            raise AuthnFailure("Could not verify client_assertion.")

        logger.debug("authntoken: %s" % sanitize(bjwt.to_dict()))
        areq['parsed_client_assertion'] = bjwt

        # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys()))
        try:
            cid = kwargs["client_id"]
        except KeyError:
            cid = bjwt["iss"]

        try:
            # There might not be a client_id in the request
            assert str(cid) in self.cli.cdb  # It's a client I know
        except KeyError:
            pass

        # aud can be a string or a list
        _aud = bjwt["aud"]
        logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl))

        # figure out authn method
        if alg2keytype(bjwt.jws_header['alg']) == 'oct':  # Symmetric key
            authn_method = 'client_secret_jwt'
        else:
            authn_method = 'private_key_jwt'

        try:
            if isinstance(_aud, six.string_types):
                assert str(_aud).startswith(self.cli.baseurl)
            else:
                for target in _aud:
                    if target.startswith(self.cli.baseurl):
                        return cid, authn_method
                raise NotForMe("Not for me!")
        except AssertionError:
            raise NotForMe("Not for me!")

        return cid, authn_method
Esempio n. 23
0
    def verify(self, areq, **kwargs):
        try:
            try:
                argv = {"sender": areq["client_id"]}
            except KeyError:
                argv = {}
            bjwt = AuthnToken().from_jwt(areq["client_assertion"], keyjar=self.cli.keyjar, **argv)
        except (Invalid, MissingKey) as err:
            logger.info("%s" % sanitize(err))
            raise AuthnFailure("Could not verify client_assertion.")

        logger.debug("authntoken: %s" % sanitize(bjwt.to_dict()))
        areq["parsed_client_assertion"] = bjwt

        # logger.debug("known clients: %s" % sanitize(self.cli.cdb.keys()))
        try:
            cid = kwargs["client_id"]
        except KeyError:
            cid = bjwt["iss"]

        try:
            # There might not be a client_id in the request
            assert str(cid) in self.cli.cdb  # It's a client I know
        except KeyError:
            pass

        # aud can be a string or a list
        _aud = bjwt["aud"]
        logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl))

        # figure out authn method
        if alg2keytype(bjwt.jws_header["alg"]) == "oct":  # Symmetric key
            authn_method = "client_secret_jwt"
        else:
            authn_method = "private_key_jwt"

        try:
            if isinstance(_aud, six.string_types):
                assert str(_aud).startswith(self.cli.baseurl)
            else:
                for target in _aud:
                    if target.startswith(self.cli.baseurl):
                        return cid, authn_method
                raise NotForMe("Not for me!")
        except AssertionError:
            raise NotForMe("Not for me!")

        return cid, authn_method
Esempio n. 24
0
    def load_keys(self, request, client_id, client_secret):
        try:
            self.keyjar.load_keys(request, client_id)
            try:
                n_keys = len(self.keyjar[client_id])
                msg = "Found {} keys for client_id={}"
                logger.debug(msg.format(n_keys, client_id))
            except KeyError:
                pass
        except Exception as err:
            msg = "Failed to load client keys: {}"
            logger.error(msg.format(sanitize(request.to_dict())))
            logger.error("%s", err)
            err = ClientRegistrationError(
                error="invalid_configuration_parameter",
                error_description="%s" % err)
            return Response(err.to_json(), content="application/json",
                            status="400 Bad Request")

        # Add the client_secret as a symmetric key to the keyjar
        _kc = KeyBundle([{"kty": "oct", "key": client_secret,
                          "use": "ver"},
                         {"kty": "oct", "key": client_secret,
                          "use": "sig"}])
        try:
            self.keyjar[client_id].append(_kc)
        except KeyError:
            self.keyjar[client_id] = [_kc]
Esempio n. 25
0
    def authenticated_as(self, cookie=None, **kwargs):
        if cookie is None:
            return None, 0
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            try:
                val = self.getCookieValue(cookie, self.srv.cookie_name)
            except (InvalidCookieSign, AssertionError):
                val = None

            if val is None:
                return None, 0
            else:
                uid, _ts, typ = val

            if typ == "uam":  # short lived
                _now = int(time.time())
                if _now > (int(_ts) + int(self.cookie_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" % (_now, int(_ts),
                                                    int(self.cookie_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" % (
                            _now, int(_ts), int(kwargs["max_age"])))

            return {"uid": uid}, _ts
Esempio n. 26
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

            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
            return None, None, idt
Esempio n. 27
0
    def permissions(self, cookie=None, **kwargs):
        if cookie is None:
            return None
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            val = self.getCookieValue(cookie, self.srv.cookie_name)
            if val is None:
                return None
            else:
                uid, _ts, typ = val

            if typ == "uam":  # short lived
                _now = int(time.time())
                if _now > (int(_ts) + int(self.cookie_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" % (_now, int(_ts),
                                                    int(self.cookie_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" % (
                            _now, int(_ts), int(kwargs["max_age"])))

            return self.permdb[uid]
Esempio n. 28
0
    def providerinfo_endpoint(self, handle="", **kwargs):
        _log_info = logger.info

        _log_info("@providerinfo_endpoint")
        try:
            _response = self.create_providerinfo()
            msg = "provider_info_response: {}"
            _log_info(msg.format(sanitize(_response.to_dict())))
            if self.events:
                self.events.store("Protocol response", _response)

            headers = [("Cache-Control", "no-store")]
            if handle:
                (key, timestamp) = handle
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = error_response("service_error", message)

        return resp
Esempio n. 29
0
    def load_keys(self, request, client_id, client_secret):
        try:
            self.keyjar.load_keys(request, client_id)
            try:
                n_keys = len(self.keyjar[client_id])
                msg = "Found {} keys for client_id={}"
                logger.debug(msg.format(n_keys, client_id))
            except KeyError:
                pass
        except Exception as err:
            msg = "Failed to load client keys: {}"
            logger.error(msg.format(sanitize(request.to_dict())))
            logger.error("%s", err)
            err = ClientRegistrationError(
                error="invalid_configuration_parameter",
                error_description="%s" % err)
            return Response(err.to_json(),
                            content="application/json",
                            status_code="400 Bad Request")

        # Add the client_secret as a symmetric key to the keyjar
        _kc = KeyBundle([{
            "kty": "oct",
            "key": client_secret,
            "use": "ver"
        }, {
            "kty": "oct",
            "key": client_secret,
            "use": "sig"
        }])
        try:
            self.keyjar[client_id].append(_kc)
        except KeyError:
            self.keyjar[client_id] = [_kc]
Esempio n. 30
0
    def providerinfo_endpoint(self, handle="", **kwargs):
        """
        The Provider info endpoint. A request for provider info should be
        handled by this method. It will work as well for requests from
        federation aware RPs as for non-federation aware RPs.

        :param handle: (key, timestamp) tuple used at cookie construction
        :param kwargs: Extra key word arguments.
        :return: Provider Info response
        """
        logger.info("@providerinfo_endpoint")
        try:
            _response = self.create_fed_providerinfo()
            msg = "provider_info_response: {}"
            logger.info(msg.format(sanitize(_response.to_dict())))
            if self.events:
                self.events.store('Protocol response', _response)

            headers = [("Cache-Control", "no-store"), ("x-ffo", "bar")]
            if handle:
                (key, timestamp) = handle
                if key.startswith(STR) and key.endswith(STR):
                    cookie = self.cookie_func(key, self.cookie_name, "pinfo",
                                              self.sso_ttl)
                    headers.append(cookie)

            resp = Response(_response.to_json(),
                            content="application/json",
                            headers=headers)
        except Exception:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            resp = error('service_error', message)

        return resp
Esempio n. 31
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.consumer_config and self.consumer_config["password"]:
            logger.info("basic auth")
            http_args = {"password": self.consumer_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" % sanitize(resp))

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

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

        return resp
Esempio n. 32
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.consumer_config and self.consumer_config[
                "password"]:
            logger.info("basic auth")
            http_args = {"password": self.consumer_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" % sanitize(resp))

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

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

        return resp
Esempio n. 33
0
    def authenticated_as(self, cookie=None, **kwargs):
        if cookie is None:
            return None, 0
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            try:
                val = self.getCookieValue(cookie, self.srv.cookie_name)
            except (InvalidCookieSign, AssertionError):
                val = None

            if val is None:
                return None, 0
            else:
                uid, _ts, typ = val

            if typ == "uam":  # shortlived
                _now = int(time.time())
                if _now > (int(_ts) + int(self.cookie_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" %
                                (_now, int(_ts), int(self.cookie_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" %
                                    (_now, int(_ts), int(kwargs["max_age"])))

            return {"uid": uid}, _ts
Esempio n. 34
0
    def permissions(self, cookie=None, **kwargs):
        if cookie is None:
            return None
        else:
            logger.debug("kwargs: %s" % sanitize(kwargs))

            val = self.getCookieValue(cookie, self.srv.cookie_name)
            if val is None:
                return None
            else:
                uid, _ts, typ = val

            if typ == "uam":  # shortlived
                _now = int(time.time())
                if _now > (int(_ts) + int(self.cookie_ttl * 60)):
                    logger.debug("Authentication timed out")
                    raise ToOld("%d > (%d + %d)" %
                                (_now, int(_ts), int(self.cookie_ttl * 60)))
            else:
                if "max_age" in kwargs and kwargs["max_age"]:
                    _now = int(time.time())
                    if _now > (int(_ts) + int(kwargs["max_age"])):
                        logger.debug("Authentication too old")
                        raise ToOld("%d > (%d + %d)" %
                                    (_now, int(_ts), int(kwargs["max_age"])))

            return self.permdb[uid]
Esempio n. 35
0
    def parse_authz_response(self, query):
        aresp = self.parse_response(
            self.message_factory.get_response_type("authorization_endpoint"),
            info=query,
            sformat="urlencoded",
            keyjar=self.keyjar,
        )
        if isinstance(aresp, ErrorResponse):
            logger.info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(
                aresp.error  # type: ignore # Messages have no classical attrs
            )

        logger.info("Aresp: %s" % sanitize(aresp))

        return aresp
Esempio n. 36
0
def get_client_id(cdb, req, authn):
    """
    Verify the client and return the client id

    :param req: The request
    :param authn: Authentication information from the HTTP header
    :return:
    """

    logger.debug("REQ: %s" % sanitize(req.to_dict()))
    if authn:
        if authn.startswith("Basic "):
            logger.debug("Basic auth")
            (_id, _secret) = base64.b64decode(authn[6:].encode("utf-8")).decode("utf-8").split(":")

            _bid = as_bytes(_id)
            _cinfo = None
            try:
                _cinfo = cdb[_id]
            except KeyError:
                try:
                    _cinfo[_bid]
                except AttributeError:
                    pass

            if not _cinfo:
                logger.debug("Unknown client_id")
                raise FailedAuthentication("Unknown client_id")
            else:
                if not valid_client_info(_cinfo):
                    logger.debug("Invalid Client info")
                    raise FailedAuthentication("Invalid Client")

                if _secret != _cinfo["client_secret"]:
                    logger.debug("Incorrect secret")
                    raise FailedAuthentication("Incorrect secret")
        else:
            if authn[:6].lower() == "bearer":
                logger.debug("Bearer auth")
                _token = authn[7:]
            else:
                raise FailedAuthentication("AuthZ type I don't know")

            try:
                _id = cdb[_token]
            except KeyError:
                logger.debug("Unknown access token")
                raise FailedAuthentication("Unknown access token")
    else:
        try:
            _id = str(req["client_id"])
            if _id not in cdb:
                logger.debug("Unknown client_id")
                raise FailedAuthentication("Unknown client_id")
            if not valid_client_info(cdb[_id]):
                raise FailedAuthentication("Invalid client_id")
        except KeyError:
            raise FailedAuthentication("Missing client_id")

    return _id
Esempio n. 37
0
    def parse_request_response(self,
                               reqresp,
                               response,
                               body_type,
                               state="",
                               **kwargs):

        if reqresp.status_code in SUCCESSFUL:
            body_type = verify_header(reqresp, body_type)
        elif reqresp.status_code in [302, 303]:  # redirect
            pass
        elif reqresp.status_code == 500:
            logger.error("(%d) %s" %
                         (reqresp.status_code, sanitize(reqresp.text)))
            raise ParseError("ERROR: Something went wrong: %s" % reqresp.text)
        elif reqresp.status_code in [400, 401]:
            # expecting an error response
            if issubclass(response, ErrorResponse):
                pass
        else:
            logger.error("(%d) %s" %
                         (reqresp.status_code, sanitize(reqresp.text)))
            raise HttpError("HTTP ERROR: %s [%s] on %s" %
                            (reqresp.text, reqresp.status_code, reqresp.url))

        if body_type:
            if response:
                return self.parse_response(response, reqresp.text, body_type,
                                           state, **kwargs)

        # could be an error response
        if reqresp.status_code in [200, 400]:
            if body_type == 'txt':
                body_type = 'urlencoded'
            try:
                err = ErrorResponse().deserialize(reqresp.message,
                                                  method=body_type)
                try:
                    err.verify()
                except PyoidcError:
                    pass
                else:
                    return err
            except Exception:
                pass

        return reqresp
Esempio n. 38
0
    def verify(self, areq, **kwargs):
        try:
            try:
                argv = {"sender": areq["client_id"]}
            except KeyError:
                argv = {}
            bjwt = AuthnToken().from_jwt(areq["client_assertion"],
                                         keyjar=self.cli.keyjar,
                                         **argv)
        except (Invalid, MissingKey) as err:
            logger.info("%s" % sanitize(err))
            raise AuthnFailure("Could not verify client_assertion.")

        logger.debug("authntoken: %s" % sanitize(bjwt.to_dict()))
        areq["parsed_client_assertion"] = bjwt

        try:
            cid = kwargs["client_id"]
        except KeyError:
            cid = bjwt["iss"]

            # There might not be a client_id in the request
        if cid not in self.cli.cdb:
            raise AuthnFailure("Unknown client id")

        # aud can be a string or a list
        _aud = bjwt["aud"]
        logger.debug("audience: %s, baseurl: %s" % (_aud, self.cli.baseurl))

        # figure out authn method
        if alg2keytype(bjwt.jws_header["alg"]) == "oct":  # Symmetric key
            authn_method = "client_secret_jwt"
        else:
            authn_method = "private_key_jwt"

        if isinstance(_aud, str):
            if not str(_aud).startswith(self.cli.baseurl):
                raise NotForMe("Not for me!")
        else:
            for target in _aud:
                if target.startswith(self.cli.baseurl):
                    return cid, authn_method
            raise NotForMe("Not for me!")

        return cid, authn_method
Esempio n. 39
0
    def _aggregation(self, info):

        jwt_key = self.keyjar.get_signing_key()
        cresp = UserClaimsResponse(jwt=info.to_jwt(key=jwt_key,
                                                   algorithm="RS256"),
                                   claims_names=list(info.keys()))

        logger.info("RESPONSE: %s" % (sanitize(cresp.to_dict()),))
        return cresp
Esempio n. 40
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """Put up the login form."""
        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % sanitize(argv))
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv).decode("utf-8")
        return resp
Esempio n. 41
0
    def _aggregation(self, info):

        jwt_key = self.keyjar.get_signing_key()
        cresp = UserClaimsResponse(jwt=info.to_jwt(key=jwt_key,
                                                   algorithm="RS256"),
                                   claims_names=list(info.keys()))

        logger.info("RESPONSE: %s" % (sanitize(cresp.to_dict()), ))
        return cresp
Esempio n. 42
0
    def __call__(self,
                 userid,
                 client_id,
                 user_info_claims=None,
                 first_only=True,
                 **kwargs):
        _filter = self.filter_pattern % userid
        logger.debug("CLAIMS: %s" % sanitize(user_info_claims))
        _attr = self.attr
        if user_info_claims:
            try:
                _claims = user_info_claims["claims"]
            except KeyError:
                pass
            else:
                avaspec = {}
                for key, val in _claims.items():
                    try:
                        attr = self.openid2ldap[key]
                    except KeyError:
                        logger.warn("OIDC attribute '%s' not defined in map" %
                                    key)
                    else:
                        try:
                            avaspec[attr].append(val)
                        except KeyError:
                            avaspec[attr] = [val]

                _attr.extend(list(avaspec.keys()))

        arg = [self.base, self.scope, _filter, _attr, self.attrsonly]
        try:
            res = self.ld.search_s(*arg)
        except Exception:
            # FIXME: This should catch specific exception from `self.ld.search_s()`
            try:
                self.ld.close()
            except Exception:
                # FIXME: This should catch specific exception from `self.ld.close()`
                pass
            self.bind()
            res = self.ld.search_s(*arg)
        if len(res) == 1:
            # should only be one entry and the information per entry is
            # the tuple (dn, ava)
            newres = {}
            for key, val in res[0][1].items():
                if first_only:
                    val = val[0]  # if more than one just return the first
                try:
                    newres[self.ldap2openid[key]] = val
                except KeyError:
                    newres[key] = val
            return newres
        else:
            return {}
Esempio n. 43
0
    def __call__(self, cookie=None, end_point_index=0, **kwargs):
        """
        Put up the login form
        """
        resp = Response()

        argv = self.templ_arg_func(end_point_index, **kwargs)
        logger.info("do_authentication argv: %s" % sanitize(argv))
        mte = self.template_lookup.get_template(self.mako_template)
        resp.message = mte.render(**argv).decode("utf-8")
        return resp
Esempio n. 44
0
    def claims_endpoint(self, request, http_authz, *args):
        _log_info = logger.info

        ucreq = self.srvmethod.parse_user_claims_request(request)

        _log_info("request: %s" % sanitize(ucreq))

        try:
            resp = self.client_authn(self, ucreq, http_authz)
        except Exception as err:
            _log_info("Failed to verify client due to: %s" % err)
            resp = False

        if "claims_names" in ucreq:
            args = dict([(n, {
                "optional": True
            }) for n in ucreq["claims_names"]])
            uic = Claims(**args)
        else:
            uic = None

        _log_info("User info claims: %s" % sanitize(uic))

        #oicsrv, userdb, subject, client_id="", user_info_claims=None
        info = self.userinfo(ucreq["sub"],
                             user_info_claims=uic,
                             client_id=ucreq["client_id"])

        _log_info("User info: %s" % sanitize(info))

        # Convert to message format
        info = OpenIDSchema(**info)

        if self.do_aggregation(info, ucreq["sub"]):
            cresp = self._aggregation(info)
        else:
            cresp = self._distributed(info)

        _log_info("response: %s" % sanitize(cresp.to_dict()))

        return Response(cresp.to_json(), content="application/json")
Esempio n. 45
0
    def _parse_authz(self, query="", **kwargs):
        _log_info = logger.info
        # Might be an error response
        _log_info("Expect Authorization Response")
        aresp = self.parse_response(
            AuthorizationResponse, info=query, sformat="urlencoded", keyjar=self.keyjar
        )
        if isinstance(aresp, ErrorResponse):
            _log_info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(aresp.get("error"), aresp)

        _log_info("Aresp: %s" % sanitize(aresp))

        _state = aresp["state"]
        try:
            self.update(_state)
        except KeyError:
            raise UnknownState(_state, aresp)

        self.redirect_uris = [self.sdb[_state]["redirect_uris"]]
        return aresp, _state
Esempio n. 46
0
    def claims_endpoint(self, request, http_authz, *args):
        _log_info = logger.info

        ucreq = self.srvmethod.parse_user_claims_request(request)

        _log_info("request: %s" % sanitize(ucreq))

        try:
            resp = self.client_authn(self, ucreq, http_authz)
        except Exception as err:
            _log_info("Failed to verify client due to: %s" % err)
            resp = False

        if "claims_names" in ucreq:
            args = dict([(n, {"optional": True}) for n in
                         ucreq["claims_names"]])
            uic = Claims(**args)
        else:
            uic = None

        _log_info("User info claims: %s" % sanitize(uic))

        #oicsrv, userdb, subject, client_id="", user_info_claims=None
        info = self.userinfo(ucreq["sub"], user_info_claims=uic,
                             client_id=ucreq["client_id"])

        _log_info("User info: %s" % sanitize(info))

        # Convert to message format
        info = OpenIDSchema(**info)

        if self.do_aggregation(info, ucreq["sub"]):
            cresp = self._aggregation(info)
        else:
            cresp = self._distributed(info)

        _log_info("response: %s" % sanitize(cresp.to_dict()))

        return Response(cresp.to_json(), content="application/json")
Esempio n. 47
0
    def _parse_authz(self, query="", **kwargs):
        _log_info = logger.info
        # Might be an error response
        _log_info("Expect Authorization Response")
        aresp = self.parse_response(AuthorizationResponse,
                                    info=query,
                                    sformat="urlencoded",
                                    keyjar=self.keyjar)
        if isinstance(aresp, ErrorResponse):
            _log_info("ErrorResponse: %s" % sanitize(aresp))
            raise AuthzError(aresp.get('error'), aresp)

        _log_info("Aresp: %s" % sanitize(aresp))

        _state = aresp["state"]
        try:
            self.update(_state)
        except KeyError:
            raise UnknownState(_state, aresp)

        self.redirect_uris = [self.sdb[_state]["redirect_uris"]]
        return aresp, _state
Esempio n. 48
0
    def __call__(self, userid, client_id, user_info_claims=None,
                 first_only=True, **kwargs):
        _filter = self.filter_pattern % userid
        logger.debug("CLAIMS: %s" % sanitize(user_info_claims))
        _attr = self.attr
        if user_info_claims:
            try:
                _claims = user_info_claims["claims"]
            except KeyError:
                pass
            else:
                avaspec = {}
                for key, val in _claims.items():
                    try:
                        attr = self.openid2ldap[key]
                    except KeyError:
                        logger.warn("OIDC attribute '%s' not defined in map" % key)
                    else:
                        try:
                            avaspec[attr].append(val)
                        except KeyError:
                            avaspec[attr] = [val]

                _attr.extend(list(avaspec.keys()))

        arg = [self.base, self.scope, _filter, _attr, self.attrsonly]
        try:
            res = self.ld.search_s(*arg)
        except Exception:
            # FIXME: This should catch specific exception from `self.ld.search_s()`
            try:
                self.ld.close()
            except Exception:
                # FIXME: This should catch specific exception from `self.ld.close()`
                pass
            self.bind()
            res = self.ld.search_s(*arg)
        if len(res) == 1:
            # should only be one entry and the information per entry is
            # the tuple (dn, ava)
            newres = {}
            for key, val in res[0][1].items():
                if first_only:
                    val = val[0]  # if more than one just return the first
                try:
                    newres[self.ldap2openid[key]] = val
                except KeyError:
                    newres[key] = val
            return newres
        else:
            return {}
Esempio n. 49
0
    def handle_authorization_response(self, query="", **kwargs):
        """
        This is where we get redirect back to after authorization at the
        authorization server has happened.

        :param query: The query part of the request
        :return: A AccessTokenResponse instance
        """

        logger.debug("- authorization - %s flow -" % self.flow_type)
        logger.debug("QUERY: %s" % sanitize(query))

        if "code" in self.response_type:
            # Might be an error response
            try:
                aresp = self.parse_response(AuthorizationResponse,
                                            info=query, sformat="urlencoded")
            except Exception as err:
                logger.error("%s" % err)
                raise

            if isinstance(aresp, Message):
                if aresp.type().endswith("ErrorResponse"):
                    raise AuthzError(aresp["error"])

            try:
                self.update(aresp["state"])
            except KeyError:
                raise UnknownState(aresp["state"])

            self._backup(aresp["state"])

            return aresp
        else:  # implicit flow
            atr = self.parse_response(AccessTokenResponse,
                                      info=query, sformat="urlencoded",
                                      extended=True)

            if isinstance(atr, Message):
                if atr.type().endswith("ErrorResponse"):
                    raise TokenError(atr["error"])

            try:
                self.update(atr["state"])
            except KeyError:
                raise UnknownState(atr["state"])

            self.seed = self.grant[atr["state"]].seed

            return atr
Esempio n. 50
0
    def http_request(self, url, method="GET", **kwargs):
        """
        Run a HTTP request to fetch the given url

        This wraps the requests library, so you can pass
        most requests kwargs to this method to override
        defaults.

        :param url: The URL to fetch
        :param method: The HTTP method to use.
        :param kwargs: Additional keyword arguments to pass through.

        """
        _kwargs = copy.copy(self.request_args)
        if kwargs:
            _kwargs.update(kwargs)

        if self.cookiejar:
            _kwargs["cookies"] = self._cookies()
            logger.debug("SENT {} COOKIES".format(len(_kwargs["cookies"])))

        if self.req_callback is not None:
            _kwargs = self.req_callback(method, url, **_kwargs)

        try:
            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"]
            # Telekom fix
            # set_cookie = set_cookie.replace(
            # "=;Path=/;Expires=Thu, 01-Jan-1970 00:00:01 GMT;HttpOnly,", "")
            logger.debug("RECEIVED COOKIE")
            try:
                set_cookie(self.cookiejar, SimpleCookie(_cookie))
            except CookieError as err:
                logger.error(err)
                raise NonFatalException(r, "{}".format(err))
        except (AttributeError, KeyError):
            pass

        return r
Esempio n. 51
0
def get_client_id(cdb, req, authn):
    """
    Verify the client and return the client id

    :param req: The request
    :param authn: Authentication information from the HTTP header
    :return:
    """
    logger.debug("REQ: %s" % sanitize(req.to_dict()))
    _secret = None
    if not authn:
        try:
            _id = str(req["client_id"])
        except KeyError:
            raise FailedAuthentication("Missing client_id")
    elif authn.startswith("Basic "):
        logger.debug("Basic auth")
        (_id, _secret) = base64.b64decode(authn[6:].encode("utf-8")).decode("utf-8").split(":")
        # Either as string or encoded
        if _id not in cdb:
            _bid = as_bytes(_id)
            _id = _bid
    elif authn[:6].lower() == "bearer":
        logger.debug("Bearer auth")
        _token = authn[7:]
        try:
            _id = cdb[_token]
        except KeyError:
            logger.debug("Unknown access token")
            raise FailedAuthentication("Unknown access token")
    else:
        raise FailedAuthentication("AuthZ type I don't know")
    # We have the client_id by now, so let's verify it
    _cinfo = cdb.get(_id)
    if _cinfo is None:
        raise FailedAuthentication("Unknown client")
    if not valid_client_info(_cinfo):
        logger.debug("Invalid Client info")
        raise FailedAuthentication("Invalid Client")
    if _secret is not None:
        if _secret != _cinfo["client_secret"]:
            logger.debug("Incorrect secret")
            raise FailedAuthentication("Incorrect secret")
    # All should be good, so return it
    return _id
Esempio n. 52
0
def create_return_url(base, query, **kwargs):
    """
    Add a query string plus extra parameters to a base URL which may contain
    a query part already.

    :param base: redirect_uri may contain a query part, no fragment allowed.
    :param query: Old query part as a string
    :param kwargs: extra query parameters
    :return:
    """
    part = urlsplit(base)
    if part.fragment:
        raise ValueError("Base URL contained parts it shouldn't")

    for key, values in parse_qs(query).items():
        if key in kwargs:
            if isinstance(kwargs[key], str):
                kwargs[key] = [kwargs[key]]
            kwargs[key].extend(values)
        else:
            kwargs[key] = values

    if part.query:
        for key, values in parse_qs(part.query).items():
            if key in kwargs:
                if isinstance(kwargs[key], str):
                    kwargs[key] = [kwargs[key]]
                kwargs[key].extend(values)
            else:
                kwargs[key] = values

        _pre = base.split("?")[0]
    else:
        _pre = base

    logger.debug("kwargs: %s" % sanitize(kwargs))
    if kwargs:
        return "%s?%s" % (_pre, url_encode_params(kwargs))
    else:
        return _pre
Esempio n. 53
0
    def begin(self, baseurl, request, response_type="", **kwargs):
        """ Begin the OAuth2 flow

        :param baseurl: The RPs base
        :param request: The Authorization query
        :param response_type: The response type the AS should use.
            Default 'code'.
        :return: A URL to which the user should be redirected
        """

        logger.debug("- begin -")

        # Store the request and the redirect uri used
        self.redirect_uris = ["%s%s" % (baseurl, self.authz_page)]
        self._request = request

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

        sid = stateID(request, self.seed)
        self.grant[sid] = Grant(seed=self.seed)
        self._backup(sid)
        self.sdb["seed:%s" % self.seed] = sid

        if not response_type:
            if self.response_type:
                response_type = self.response_type
            else:
                self.response_type = response_type = "code"

        location = self.request_info(
            AuthorizationRequest, method="GET", scope=self.scope,
            request_args={"state": sid, "response_type": response_type})[0]

        logger.debug("Redirecting to: %s" % (sanitize(location),))

        return sid, location
Esempio n. 54
0
    def http_request(self, url, method="GET", **kwargs):
        _kwargs = copy.copy(self.request_args)
        if kwargs:
            _kwargs.update(kwargs)

        if self.cookiejar:
            _kwargs["cookies"] = self._cookies()
            logger.debug("SENT {} COOKIES".format(len(_kwargs["cookies"])))

        if self.req_callback is not None:
            _kwargs = self.req_callback(method, url, **_kwargs)

        try:
            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"]
            # Telekom fix
            # set_cookie = set_cookie.replace(
            # "=;Path=/;Expires=Thu, 01-Jan-1970 00:00:01 GMT;HttpOnly,", "")
            logger.debug("RECEIVED COOKIE")
            try:
                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 r
Esempio n. 55
0
    def parse_authz(self, query="", **kwargs):
        """
        This is where we get redirect back to after authorization at the
        authorization server has happened.
        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

            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
            return None, None, idt
Esempio n. 56
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 = cookielib.Cookie(**std_attr)

            cookiejar.set_cookie(new_cookie)
Esempio n. 57
0
def test_sanitize(raw, expected):
    assert sanitize(raw) == expected
Esempio n. 58
0
    def callback(self, response, session, format='dict'):
        """
        This is the method that should be called when an AuthN response has been
        received from the OP.

        :param response: The URL returned by the OP
        :return:
        """
        try:
            authresp = self.parse_response(AuthorizationResponse, response,
                                           sformat=format, keyjar=self.keyjar)
        except ResponseError:
            msg = "Could not parse response: '{}'"
            logger.error(msg.format(sanitize(response)))
            raise OIDCError("Problem parsing response")

        logger.info("AuthorizationReponse: {}".format(sanitize(authresp)))
        if isinstance(authresp, ErrorResponse):
            if authresp["error"] == "login_required":
                return self.create_authn_request(session)
            else:
                raise OIDCError("Access denied")

        _state = authresp["state"]
        # if session["state"] != authresp["state"]:
        #     self._err("Received state not the same as expected.")

        try:
            _id_token = authresp['id_token']
        except KeyError:
            _id_token = None
        else:
            if _id_token['nonce'] != self.authz_req[_state]['nonce']:
                self._err("Received nonce not the same as expected.")

        if self.behaviour["response_type"] == "code":
            # get the access token
            try:
                args = {
                    "code": authresp["code"],
                    "redirect_uri": self.registration_response[
                        "redirect_uris"][0],
                    "client_id": self.client_id,
                    "client_secret": self.client_secret,
                }

                try:
                    args['scope'] = response['scope']
                except KeyError:
                    pass

                atresp = self.do_access_token_request(
                    state=authresp["state"],
                    request_args=args,
                    authn_method=self.registration_response[
                        "token_endpoint_auth_method"])
                msg = 'Access token response: {}'
                logger.info(msg.format(sanitize(atresp)))
            except Exception as err:
                logger.error("%s" % err)
                raise

            if isinstance(atresp, ErrorResponse):
                msg = 'Error response: {}'
                self._err(msg.format(sanitize(atresp.to_dict())))

            _token = atresp['access_token']

            try:
                _id_token = atresp['id_token']
            except KeyError:
                pass
        else:
            _token = authresp['access_token']

        if not self.oidc:
            return {'access_token': _token}

        if _id_token is None:
            self._err("Invalid response: no IdToken")

        if _id_token['iss'] != self.provider_info['issuer']:
            self._err("Issuer mismatch")

        if _id_token['nonce'] != self.authz_req[_state]['nonce']:
            self._err("Nonce mismatch")

        if not self.allow_sign_alg_none:
            if _id_token.jws_header['alg'] == 'none':
                self._err('Do not allow "none" signature algorithm')

        user_id = '{}:{}'.format(_id_token['iss'], _id_token['sub'])

        if self.get_userinfo:
            if self.userinfo_request_method:
                kwargs = {"method": self.userinfo_request_method}
            else:
                kwargs = {}

            if self.has_access_token(state=authresp["state"]):
                inforesp = self.do_user_info_request(state=authresp["state"],
                                                     **kwargs)

                if isinstance(inforesp, ErrorResponse):
                    self._err("Invalid response %s." % inforesp["error"])

                userinfo = inforesp.to_dict()

                if _id_token['sub'] != userinfo['sub']:
                    self._err("Invalid response: userid mismatch")

                logger.debug("UserInfo: %s" % sanitize(inforesp))

                try:
                    self.id_token[user_id] = _id_token
                except TypeError:
                    self.id_token = {user_id: _id_token}
            else:
                userinfo = {}
                for attr in OpenIDSchema.c_param:
                    try:
                        userinfo[attr] = _id_token[attr]
                    except KeyError:
                        pass

            return {'user_id': user_id, 'userinfo': userinfo,
                    'id_token': _id_token, 'access_token': _token}
        else:
            return {'user_id': user_id, 'id_token': _id_token,
                    'access_token': _token}
Esempio n. 59
0
    def from_jwt(self, txt, key=None, verify=True, keyjar=None, **kwargs):
        """
        Given a signed and/or encrypted JWT, verify its correctness and then
        create a class instance from the content.

        :param txt: The JWT
        :param key: keys that might be used to decrypt and/or verify the
            signature of the JWT
        :param verify: Whether the signature should be verified or not
        :param keyjar: A KeyJar that might contain the necessary key.
        :param kwargs: Extra key word arguments
        :return: A class instance
        """
        # if key is None and keyjar is not None:
        #     key = keyjar.get_verify_key(owner="")
        # elif key is None:
        #     key = []
        #
        # if keyjar is not None and "sender" in kwargs:
        #     key.extend(keyjar.get_verify_key(owner=kwargs["sender"]))

        _jw = jwe.factory(txt)
        if _jw:
            logger.debug("JWE headers: {}".format(_jw.jwt.headers))

            if "algs" in kwargs and "encalg" in kwargs["algs"]:
                if kwargs["algs"]["encalg"] != _jw["alg"]:
                    raise WrongEncryptionAlgorithm("%s != %s" % (_jw["alg"], kwargs["algs"]["encalg"]))
                if kwargs["algs"]["encenc"] != _jw["enc"]:
                    raise WrongEncryptionAlgorithm("%s != %s" % (_jw["enc"], kwargs["algs"]["encenc"]))
            if keyjar:
                dkeys = keyjar.get_decrypt_key(owner="")
                if "sender" in kwargs:
                    dkeys.extend(keyjar.get_verify_key(owner=kwargs["sender"]))
            elif key:
                dkeys = key
            else:
                dkeys = []

            logger.debug('Decrypt class: {}'.format(_jw.__class__))
            _res = _jw.decrypt(txt, dkeys)
            logger.debug('decrypted message:{}'.format(_res))
            if isinstance(_res, tuple):
                txt = as_unicode(_res[0])
            elif isinstance(_res, list) and len(_res) == 2:
                txt = as_unicode(_res[0])
            else:
                txt = as_unicode(_res)
            self.jwe_header = _jw.jwt.headers

        _jw = jws.factory(txt)
        if _jw:
            if "algs" in kwargs and "sign" in kwargs["algs"]:
                _alg = _jw.jwt.headers["alg"]
                if kwargs["algs"]["sign"] != _alg:
                    raise WrongSigningAlgorithm("%s != %s" % (_alg, kwargs["algs"]["sign"]))
            try:
                _jwt = JWT().unpack(txt)
                jso = _jwt.payload()
                _header = _jwt.headers

                if key is None and keyjar is not None:
                    key = keyjar.get_verify_key(owner="")
                elif key is None:
                    key = []

                if keyjar is not None and "sender" in kwargs:
                    key.extend(keyjar.get_verify_key(owner=kwargs["sender"]))

                logger.debug("Raw JSON: {}".format(sanitize(jso)))
                logger.debug("JWS header: {}".format(sanitize(_header)))
                if _header["alg"] == "none":
                    pass
                elif verify:
                    if keyjar:
                        key = self.get_verify_keys(keyjar, key, jso, _header,
                                                   _jw, **kwargs)

                    if "alg" in _header and _header["alg"] != "none":
                        if not key:
                            raise MissingSigningKey(
                                "alg=%s" % _header["alg"])

                    logger.debug("Found signing key.")
                    try:
                        _jw.verify_compact(txt, key)
                    except NoSuitableSigningKeys:
                        if keyjar:
                            update_keyjar(keyjar)
                            key = self.get_verify_keys(keyjar, key, jso,
                                                       _header, _jw, **kwargs)
                            _jw.verify_compact(txt, key)
            except Exception:
                raise
            else:
                self.jws_header = _jwt.headers
        else:
            jso = json.loads(txt)

        self.jwt = txt
        return self.from_dict(jso)
Esempio n. 60
0
def test_sanitize_preserves_original():
    old = {'passwd': 'secret'}
    new = sanitize(old)
    assert old['passwd'] == 'secret'
    assert new['passwd'] == '<REDACTED>'