예제 #1
0
def test_server_parse_parse_authorization_request():
    srv = Server()
    ar = AuthorizationRequest(
        response_type=["code"],
        client_id="foobar",
        redirect_uri="http://foobar.example.com/oaclient",
        state="cold")

    uencq = ar.to_urlencoded()

    areq = srv.parse_authorization_request(query=uencq)

    assert areq.type() == "AuthorizationRequest"
    assert areq["response_type"] == ["code"]
    assert areq["client_id"] == "foobar"
    assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert areq["state"] == "cold"

    urluenc = "%s?%s" % ("https://example.com/authz", uencq)

    areq = srv.parse_authorization_request(url=urluenc)

    assert areq.type() == "AuthorizationRequest"
    assert areq["response_type"] == ["code"]
    assert areq["client_id"] == "foobar"
    assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert areq["state"] == "cold"
예제 #2
0
 def __init__(self, jwt_keys=None, name=""):
     Server.__init__(self, jwt_keys=jwt_keys)
     self.sdb = SessionDB()
     self.name = name
     self.client = {}
     self.registration_expires_in = 3600
     self.host = ""
예제 #3
0
 def __init__(self, jwt_keys=None, name=""):
     Server.__init__(self, jwt_keys=jwt_keys)
     self.sdb = SessionDB()
     self.name = name
     self.client = {}
     self.registration_expires_in = 3600
     self.host = ""
예제 #4
0
def test_server_parse_parse_authorization_request():
    srv = Server()
    ar = AuthorizationRequest(response_type=["code"],
                              client_id="foobar",
                              redirect_uri="http://foobar.example.com/oaclient",
                              state="cold")

    uencq = ar.to_urlencoded()

    areq = srv.parse_authorization_request(query=uencq)

    assert areq.type() == "AuthorizationRequest"
    assert areq["response_type"] == ["code"]
    assert areq["client_id"] == "foobar"
    assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert areq["state"] == "cold"

    urluenc = "%s?%s" % ("https://example.com/authz", uencq)

    areq = srv.parse_authorization_request(url=urluenc)

    assert areq.type() == "AuthorizationRequest"
    assert areq["response_type"] == ["code"]
    assert areq["client_id"] == "foobar"
    assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert areq["state"] == "cold"
예제 #5
0
def test_server_parse_token_request():
    atr = AccessTokenRequest(
        grant_type="authorization_code",
        code="SplxlOBeZQQYbYS6WxSbIA",
        redirect_uri="https://client.example.com/cb",
        extra="foo",
    )

    uenc = atr.to_urlencoded()

    srv = Server()
    tr = srv.parse_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "AccessTokenRequest"
    assert _eq(tr.keys(), ["code", "redirect_uri", "grant_type", "extra"])

    assert tr["grant_type"] == "authorization_code"
    assert tr["code"] == "SplxlOBeZQQYbYS6WxSbIA"

    tr = srv.parse_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "AccessTokenRequest"
    assert _eq(tr.keys(), ["code", "grant_type", "redirect_uri", "extra"])

    assert tr["extra"] == "foo"
예제 #6
0
def test_server_parse_token_request():
    atr = AccessTokenRequest(grant_type="authorization_code",
                             code="SplxlOBeZQQYbYS6WxSbIA",
                             redirect_uri="https://client.example.com/cb",
                             extra="foo")

    uenc = atr.to_urlencoded()

    srv = Server()
    tr = srv.parse_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "AccessTokenRequest"
    assert _eq(tr.keys(), ['code', 'redirect_uri', 'grant_type', 'extra'])

    assert tr["grant_type"] == "authorization_code"
    assert tr["code"] == "SplxlOBeZQQYbYS6WxSbIA"

    tr = srv.parse_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "AccessTokenRequest"
    assert _eq(tr.keys(), ['code', 'grant_type', 'redirect_uri', 'extra'])

    assert tr["extra"] == "foo"
예제 #7
0
    def __init__(self,
                 name,
                 sdb,
                 cdb,
                 authn_broker,
                 authz,
                 client_authn,
                 symkey="",
                 urlmap=None,
                 iv=0,
                 default_scope="",
                 ca_bundle=None,
                 verify_ssl=True,
                 default_acr="",
                 baseurl=''):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr().encode("utf-8")
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"
        self.baseurl = baseurl
        self.keyjar = None
        self.trace = None
        self.events = None
예제 #8
0
def test_server_parse_refresh_token_request():
    ratr = RefreshAccessTokenRequest(refresh_token="ababababab", client_id="Client_id")

    uenc = ratr.to_urlencoded()

    srv = Server()
    tr = srv.parse_refresh_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "RefreshAccessTokenRequest"
    assert tr["refresh_token"] == "ababababab"
    assert tr["client_id"] == "Client_id"
예제 #9
0
def test_server_parse_refresh_token_request():
    ratr = RefreshAccessTokenRequest(refresh_token="ababababab",
                                     client_id="Client_id")

    uenc = ratr.to_urlencoded()

    srv = Server()
    tr = srv.parse_refresh_token_request(body=uenc)
    print tr.keys()

    assert tr.type() == "RefreshAccessTokenRequest"
    assert tr["refresh_token"] == "ababababab"
    assert tr["client_id"] == "Client_id"
예제 #10
0
파일: provider.py 프로젝트: biancini/pyoidc
    def __init__(self, name, sdb, cdb, authn, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope=""):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.srvmethod = SrvMethod()
        self.authn = authn
        if authn:
            self.authn.srv = self
        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr()
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }
예제 #11
0
def test_server_parse_jwt_request():
    srv = Server()
    ar = AuthorizationRequest(
        response_type=["code"], client_id="foobar", redirect_uri="http://foobar.example.com/oaclient", state="cold"
    )

    srv.keyjar["foobar"] = KeyBundle({"hmac": "A1B2C3D4"}, usage=["ver", "sig"])
    srv.keyjar[""] = KeyBundle({"hmac": "A1B2C3D4"}, usage=["ver", "sig"])

    keys = srv.keyjar.get_signing_key(owner="foobar")
    _jwt = ar.to_jwt(key=keys, algorithm="HS256")

    req = srv.parse_jwt_request(txt=_jwt)

    assert req.type() == "AuthorizationRequest"
    assert req["response_type"] == ["code"]
    assert req["client_id"] == "foobar"
    assert req["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert req["state"] == "cold"
예제 #12
0
def test_server_parse_jwt_request():
    srv = Server()
    ar = AuthorizationRequest(response_type=["code"],
                 client_id="foobar",
                 redirect_uri="http://foobar.example.com/oaclient",
                 state="cold")

    srv.keystore.set_verify_key("A1B2C3D4", owner="foobar")
    srv.keystore.set_sign_key("A1B2C3D4", owner="foobar")

    keys = srv.keystore.get_sign_key(owner="foobar")
    _jwt = ar.to_jwt(key=keys, algorithm="HS256")

    req = srv.parse_jwt_request(txt=_jwt)

    assert req.type() == "AuthorizationRequest"
    assert req["response_type"] == ["code"]
    assert req["client_id"] == "foobar"
    assert req["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert req["state"] == "cold"
예제 #13
0
def test_server_parse_jwt_request():
    srv = Server()
    ar = AuthorizationRequest(
        response_type=["code"],
        client_id="foobar",
        redirect_uri="http://foobar.example.com/oaclient",
        state="cold")

    srv.keyjar["foobar"] = KeyBundle([{
        "kty": "oct",
        "key": "A1B2C3D4",
        "use": "ver"
    }, {
        "kty": "oct",
        "key": "A1B2C3D4",
        "use": "sig"
    }])
    srv.keyjar[""] = KeyBundle([{
        "kty": "oct",
        "key": "A1B2C3D4",
        "use": "ver"
    }, {
        "kty": "oct",
        "key": "A1B2C3D4",
        "use": "sig"
    }])

    keys = srv.keyjar.get_signing_key(owner="foobar")
    _jwt = ar.to_jwt(key=keys, algorithm="HS256")

    req = srv.parse_jwt_request(txt=_jwt)

    assert req.type() == "AuthorizationRequest"
    assert req["response_type"] == ["code"]
    assert req["client_id"] == "foobar"
    assert req["redirect_uri"] == "http://foobar.example.com/oaclient"
    assert req["state"] == "cold"
예제 #14
0
파일: provider.py 프로젝트: nynymike/pyoidc
    def __init__(self, name, sdb, cdb, authn_broker, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope="",
                 ca_bundle=None, verify_ssl=True, default_acr="",
                 baseurl=''):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr().encode("utf-8")
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"
        self.baseurl = baseurl
        self.keyjar = None
        self.trace = None
        self.events = None
예제 #15
0
def test_device_flow():
    _client = Client()
    cli = DeviceFlowClient(_client)

    _server = Server()
    srv = DeviceFlowServer(_server)

    # init
    req = AuthorizationRequest(client_id=cli.host.client_id,
                               response_type='device_code')

    resp = srv.device_endpoint(req)

    # Polling

    req = TokenRequest(
        grant_type="urn:ietf:params:oauth:grant-type:device_code",
        device_code=resp['device_dode'], client_id=cli.host.client_id)

    resp = srv.token_endpoint(req)
예제 #16
0
class TestServer(object):
    @pytest.fixture(autouse=True)
    def create_server(self):
        self.srv = Server()  # pylint: disable=attribute-defined-outside-init

    def test_parse_authz_req(self):
        ar = AuthorizationRequest(
            response_type=["code"],
            client_id="foobar",
            redirect_uri="http://foobar.example.com/oaclient",
            state="cold",
        )

        uencq = ar.to_urlencoded()

        areq = self.srv.parse_authorization_request(query=uencq)

        assert isinstance(areq, AuthorizationRequest)
        assert areq["response_type"] == ["code"]
        assert areq["client_id"] == "foobar"
        assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert areq["state"] == "cold"

        urluenc = "%s?%s" % ("https://example.com/authz", uencq)
        areq = self.srv.parse_authorization_request(url=urluenc)

        assert isinstance(areq, AuthorizationRequest)
        assert areq["response_type"] == ["code"]
        assert areq["client_id"] == "foobar"
        assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert areq["state"] == "cold"

    def test_parse_jwt_request(self):
        ar = AuthorizationRequest(
            response_type=["code"],
            client_id="foobar",
            redirect_uri="http://foobar.example.com/oaclient",
            state="cold",
        )

        self.srv.keyjar["foobar"] = KeyBundle([
            {
                "kty": "oct",
                "key": "A1B2C3D4".encode("utf-8"),
                "use": "ver"
            },
            {
                "kty": "oct",
                "key": "A1B2C3D4".encode("utf-8"),
                "use": "sig"
            },
        ])
        self.srv.keyjar[""] = KeyBundle([
            {
                "kty": "oct",
                "key": "A1B2C3D4".encode("utf-8"),
                "use": "ver"
            },
            {
                "kty": "oct",
                "key": "A1B2C3D4".encode("utf-8"),
                "use": "sig"
            },
        ])

        keys = self.srv.keyjar.get_signing_key(owner="foobar")
        _jwt = ar.to_jwt(key=keys, algorithm="HS256")

        req = self.srv.parse_jwt_request(txt=_jwt)

        assert isinstance(req, AuthorizationRequest)
        assert req["response_type"] == ["code"]
        assert req["client_id"] == "foobar"
        assert req["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert req["state"] == "cold"

    def test_server_parse_token_request(self):
        atr = AccessTokenRequest(
            grant_type="authorization_code",
            code="SplxlOBeZQQYbYS6WxSbIA",
            redirect_uri="https://client.example.com/cb",
            extra="foo",
        )
        uenc = atr.to_urlencoded()

        tr = self.srv.parse_token_request(body=uenc)

        assert isinstance(tr, AccessTokenRequest)
        assert _eq(tr.keys(), ["code", "redirect_uri", "grant_type", "extra"])

        assert tr["grant_type"] == "authorization_code"
        assert tr["code"] == "SplxlOBeZQQYbYS6WxSbIA"

        tr = self.srv.parse_token_request(body=uenc)

        assert isinstance(tr, AccessTokenRequest)
        assert _eq(tr.keys(), ["code", "grant_type", "redirect_uri", "extra"])

        assert tr["extra"] == "foo"

    def test_server_parse_refresh_token_request(self):
        ratr = RefreshAccessTokenRequest(refresh_token="ababababab",
                                         client_id="Client_id")
        uenc = ratr.to_urlencoded()

        tr = self.srv.parse_refresh_token_request(body=uenc)

        assert isinstance(tr, RefreshAccessTokenRequest)
        assert tr["refresh_token"] == "ababababab"
        assert tr["client_id"] == "Client_id"
예제 #17
0
 def create_server(self):
     self.srv = Server()  # pylint: disable=attribute-defined-outside-init
예제 #18
0
class TestServer(object):
    @pytest.fixture(autouse=True)
    def create_server(self):
        self.srv = Server()  # pylint: disable=attribute-defined-outside-init

    def test_parse_authz_req(self):
        ar = AuthorizationRequest(
            response_type=["code"], client_id="foobar", redirect_uri="http://foobar.example.com/oaclient", state="cold"
        )

        uencq = ar.to_urlencoded()

        areq = self.srv.parse_authorization_request(query=uencq)

        assert isinstance(areq, AuthorizationRequest)
        assert areq["response_type"] == ["code"]
        assert areq["client_id"] == "foobar"
        assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert areq["state"] == "cold"

        urluenc = "%s?%s" % ("https://example.com/authz", uencq)
        areq = self.srv.parse_authorization_request(url=urluenc)

        assert isinstance(areq, AuthorizationRequest)
        assert areq["response_type"] == ["code"]
        assert areq["client_id"] == "foobar"
        assert areq["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert areq["state"] == "cold"

    def test_parse_jwt_request(self):
        ar = AuthorizationRequest(
            response_type=["code"], client_id="foobar", redirect_uri="http://foobar.example.com/oaclient", state="cold"
        )

        self.srv.keyjar["foobar"] = KeyBundle(
            [
                {"kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "ver"},
                {"kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "sig"},
            ]
        )
        self.srv.keyjar[""] = KeyBundle(
            [
                {"kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "ver"},
                {"kty": "oct", "key": "A1B2C3D4".encode("utf-8"), "use": "sig"},
            ]
        )

        keys = self.srv.keyjar.get_signing_key(owner="foobar")
        _jwt = ar.to_jwt(key=keys, algorithm="HS256")

        req = self.srv.parse_jwt_request(txt=_jwt)

        assert isinstance(req, AuthorizationRequest)
        assert req["response_type"] == ["code"]
        assert req["client_id"] == "foobar"
        assert req["redirect_uri"] == "http://foobar.example.com/oaclient"
        assert req["state"] == "cold"

    def test_server_parse_token_request(self):
        atr = AccessTokenRequest(
            grant_type="authorization_code",
            code="SplxlOBeZQQYbYS6WxSbIA",
            redirect_uri="https://client.example.com/cb",
            extra="foo",
        )
        uenc = atr.to_urlencoded()

        tr = self.srv.parse_token_request(body=uenc)

        assert isinstance(tr, AccessTokenRequest)
        assert _eq(tr.keys(), ["code", "redirect_uri", "grant_type", "extra"])

        assert tr["grant_type"] == "authorization_code"
        assert tr["code"] == "SplxlOBeZQQYbYS6WxSbIA"

        tr = self.srv.parse_token_request(body=uenc)

        assert isinstance(tr, AccessTokenRequest)
        assert _eq(tr.keys(), ["code", "grant_type", "redirect_uri", "extra"])

        assert tr["extra"] == "foo"

    def test_server_parse_refresh_token_request(self):
        ratr = RefreshAccessTokenRequest(refresh_token="ababababab", client_id="Client_id")
        uenc = ratr.to_urlencoded()

        tr = self.srv.parse_refresh_token_request(body=uenc)

        assert isinstance(tr, RefreshAccessTokenRequest)
        assert tr["refresh_token"] == "ababababab"
        assert tr["client_id"] == "Client_id"
예제 #19
0
 def create_server(self):
     self.srv = Server()  # pylint: disable=attribute-defined-outside-init
예제 #20
0
class Provider(object):
    endp = [AuthorizationEndpoint, TokenEndpoint]

    def __init__(self,
                 name,
                 sdb,
                 cdb,
                 authn_broker,
                 authz,
                 client_authn,
                 symkey="",
                 urlmap=None,
                 iv=0,
                 default_scope="",
                 ca_bundle=None,
                 verify_ssl=True,
                 default_acr=""):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr()
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"

    def endpoints(self):
        for endp in self.endp:
            yield endp(None).name

    # def authn_reply(self, areq, aresp, bsid, **kwargs):
    #     """
    #
    #     :param areq: Authorization Request
    #     :param aresp: Authorization Response
    #     :param bsid: Session id
    #     :param kwargs: Additional keyword args
    #     :return:
    #     """
    #     if "redirect_uri" in areq:
    #         # TODO verify that the uri is reasonable
    #         redirect_uri = areq["redirect_uri"]
    #     else:
    #         redirect_uri = self.urlmap[areq["client_id"]]
    #
    #     location = location_url(areq["response_type"], redirect_uri,
    #                             aresp.to_urlencoded())
    #
    #     LOG_DEBUG("Redirected to: '%s' (%s)" % (location, type(location)))
    #
    #     # set cookie containing session ID
    #
    #     cookie = make_cookie(self.cookie_name, bsid, self.seed)
    #
    #     return Redirect(str(location), headers=[cookie])
    #
    # def authn_response(self, areq, **kwargs):
    #     """
    #
    #     :param areq: Authorization request
    #     :param kwargs: Extra keyword arguments
    #     :return:
    #     """
    #     scode = kwargs["code"]
    #     areq["response_type"].sort()
    #     _rtype = " ".join(areq["response_type"])
    #     return self.response_type_map[_rtype](areq=areq, scode=scode,
    #                                           sdb=self.sdb)

    @staticmethod
    def input(query="", post=None):
        # Support GET and POST
        if query:
            return query
        elif post:
            return post
        else:
            raise MissingParameter("No input")

    @staticmethod
    def _error_response(error, descr=None):
        logger.error("%s" % error)
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(),
                        content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _error(error, descr=None):
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(),
                        content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _authz_error(error, descr=None):

        response = AuthorizationErrorResponse(error=error)
        if descr:
            response["error_description"] = descr

        return Response(response.to_json(),
                        content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _redirect_authz_error(error,
                              redirect_uri,
                              descr=None,
                              state="",
                              return_type=None):
        err = AuthorizationErrorResponse(error=error)
        if descr:
            err["error_description"] = descr
        if state:
            err["state"] = state
        if return_type is None or return_type == ["code"]:
            location = err.request(redirect_uri)
        else:
            location = err.request(redirect_uri, True)
        return Redirect(location)

    def _verify_redirect_uri(self, areq):
        """
        MUST NOT contain a fragment
        MAY contain query component

        :return: An error response if the redirect URI is faulty otherwise
            None
        """
        try:
            _redirect_uri = urlparse.unquote(areq["redirect_uri"])

            part = urlparse.urlparse(_redirect_uri)
            if part.fragment:
                raise URIError("Contains fragment")

            (_base, _query) = urllib.splitquery(_redirect_uri)
            if _query:
                _query = urlparse.parse_qs(_query)

            match = False
            for regbase, rquery in self.cdb[
                    areq["client_id"]]["redirect_uris"]:
                if _base == regbase or _redirect_uri.startswith(regbase):
                    # every registered query component must exist in the
                    # redirect_uri
                    if rquery:
                        for key, vals in rquery.items():
                            assert key in _query
                            for val in vals:
                                assert val in _query[key]
                    # and vice versa, every query component in the redirect_uri
                    # must be registered
                    if _query:
                        if rquery is None:
                            raise ValueError
                        for key, vals in _query.items():
                            assert key in rquery
                            for val in vals:
                                assert val in rquery[key]
                    match = True
                    break
            if not match:
                raise RedirectURIError("Doesn't match any registered uris")
            # ignore query components that are not registered
            return None
        except Exception:
            logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"])
            try:
                _cinfo = self.cdb[areq["client_id"]]
            except KeyError:
                logger.info("Unknown client: %s" % areq["client_id"])
                raise UnknownClient(areq["client_id"])
            else:
                logger.info("Registered redirect_uris: %s" % _cinfo)
                raise RedirectURIError("Faulty redirect_uri: %s" %
                                       areq["redirect_uri"])

    def get_redirect_uri(self, areq):
        """ verify that the redirect URI is reasonable

        :param areq: The Authorization request
        :return: Tuple of (redirect_uri, Response instance)
            Response instance is not None of matching redirect_uri failed
        """
        if 'redirect_uri' in areq:
            self._verify_redirect_uri(areq)
            uri = areq["redirect_uri"]
        else:
            raise ParameterError(
                "Missing redirect_uri and more than one or none registered")

        return uri

    def pick_auth(self, areq, comparision_type=""):
        """

        :param areq: AuthorizationRequest instance
        :param comparision_type: How to pick the authentication method
        :return: An authentication method and its authn class ref
        """
        if comparision_type == "any":
            return self.authn_broker[0]

        try:
            if len(self.authn_broker) == 1:
                return self.authn_broker[0]
            elif "acr_values" in areq:
                if not comparision_type:
                    comparision_type = "exact"

                if not isinstance(areq["acr_values"], list):
                    areq["acr_values"] = [areq["acr_values"]]

                for acr in areq["acr_values"]:
                    res = self.authn_broker.pick(acr, comparision_type)
                    logger.debug("Picked AuthN broker for ACR %s: %s" %
                                 (str(acr), str(res)))
                    if res:
                        #Return the best guess by pick.
                        return res[0]
            else:  # same as any
                try:
                    acrs = areq["claims"]["id_token"]["acr"]["values"]
                except KeyError:
                    return self.authn_broker[0]
                else:
                    for acr in acrs:
                        res = self.authn_broker.pick(acr, comparision_type)
                        logger.debug("Picked AuthN broker for ACR %s: %s" %
                                     (str(acr), str(res)))
                        if res:
                            #Return the best guess by pick.
                            return res[0]

        except KeyError as exc:
            logger.debug(
                "An error occured while picking the authN broker: %s" %
                str(exc))

        # return the best I have
        return None, None

    def auth_init(self, request):
        """

        :param request: The AuthorizationRequest
        :return:
        """
        logger.debug("Request: '%s'" % request)
        # Same serialization used for GET and POST
        try:
            areq = self.server.parse_authorization_request(query=request)
        except (MissingRequiredValue, MissingRequiredAttribute) as err:
            logger.debug("%s" % err)
            areq = AuthorizationRequest().deserialize(request, "urlencoded")
            try:
                redirect_uri = self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError), err:
                return self._error("invalid_request", "%s" % err)
            try:
                _rtype = areq["response_type"]
            except:
                _rtype = ["code"]
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err, areq["state"],
                                              _rtype)
        except KeyError:
            areq = AuthorizationRequest().deserialize(request, "urlencoded")
            # verify the redirect_uri
            try:
                self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError), err:
                return self._error("invalid_request", "%s" % err)
예제 #21
0
class Provider(object):
    endp = [AuthorizationEndpoint, TokenEndpoint]

    def __init__(self, name, sdb, cdb, authn_broker, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope="",
                 ca_bundle=None, verify_ssl=True, default_acr="",
                 baseurl=''):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr().encode("utf-8")
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"
        self.baseurl = baseurl
        self.keyjar = None
        self.trace = None

    @staticmethod
    def input(query="", post=None):
        # Support GET and POST
        if query:
            return query
        elif post:
            return post
        else:
            raise MissingParameter("No input")

    @staticmethod
    def _error_response(error, descr=None):
        logger.error("%s" % error)
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _error(error, descr=None):
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _authz_error(error, descr=None):

        response = AuthorizationErrorResponse(error=error)
        if descr:
            response["error_description"] = descr

        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _redirect_authz_error(error, redirect_uri, descr=None, state="",
                              return_type=None):
        err = AuthorizationErrorResponse(error=error)
        if descr:
            err["error_description"] = descr
        if state:
            err["state"] = state
        if return_type is None or return_type == ["code"]:
            location = err.request(redirect_uri)
        else:
            location = err.request(redirect_uri, True)
        return SeeOther(location)

    def _verify_redirect_uri(self, areq):
        """
        MUST NOT contain a fragment
        MAY contain query component

        :return: An error response if the redirect URI is faulty otherwise
            None
        """
        try:
            _redirect_uri = unquote(areq["redirect_uri"])

            part = urlparse(_redirect_uri)
            if part.fragment:
                raise URIError("Contains fragment")

            (_base, _query) = splitquery(_redirect_uri)
            if _query:
                _query = parse_qs(_query)

            match = False
            for regbase, rquery in self.cdb[str(areq["client_id"])][
                "redirect_uris"]:
                if _base == regbase or _redirect_uri.startswith(regbase):
                    # every registered query component must exist in the
                    # redirect_uri
                    if rquery:
                        for key, vals in rquery.items():
                            assert key in _query
                            for val in vals:
                                assert val in _query[key]
                    # and vice versa, every query component in the redirect_uri
                    # must be registered
                    if _query:
                        if rquery is None:
                            raise ValueError
                        for key, vals in _query.items():
                            assert key in rquery
                            for val in vals:
                                assert val in rquery[key]
                    match = True
                    break
            if not match:
                raise RedirectURIError("Doesn't match any registered uris")
            # ignore query components that are not registered
            return None
        except Exception as err:
            logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"])
            try:
                _cinfo = self.cdb[str(areq["client_id"])]
            except KeyError:
                logger.info("Unknown client: %s" % areq["client_id"])
                raise UnknownClient(areq["client_id"])
            else:
                logger.info("Registered redirect_uris: %s" % _cinfo)
                raise RedirectURIError(
                    "Faulty redirect_uri: %s" % areq["redirect_uri"])

    def get_redirect_uri(self, areq):
        """ verify that the redirect URI is reasonable

        :param areq: The Authorization request
        :return: Tuple of (redirect_uri, Response instance)
            Response instance is not None of matching redirect_uri failed
        """
        if 'redirect_uri' in areq:
            self._verify_redirect_uri(areq)
            uri = areq["redirect_uri"]
        else:
            raise ParameterError(
                "Missing redirect_uri and more than one or none registered")

        return uri

    def pick_auth(self, areq, comparision_type=""):
        """

        :param areq: AuthorizationRequest instance
        :param comparision_type: How to pick the authentication method
        :return: An authentication method and its authn class ref
        """
        if comparision_type == "any":
            return self.authn_broker[0]

        try:
            if len(self.authn_broker) == 1:
                return self.authn_broker[0]
            elif "acr_values" in areq:
                if not comparision_type:
                    comparision_type = "exact"

                if not isinstance(areq["acr_values"], list):
                    areq["acr_values"] = [areq["acr_values"]]

                for acr in areq["acr_values"]:
                    res = self.authn_broker.pick(acr, comparision_type)
                    logger.debug("Picked AuthN broker for ACR %s: %s" % (
                        str(acr), str(res)))
                    if res:
                        # Return the best guess by pick.
                        return res[0]
            else:  # same as any
                try:
                    acrs = areq["claims"]["id_token"]["acr"]["values"]
                except KeyError:
                    return self.authn_broker[0]
                else:
                    for acr in acrs:
                        res = self.authn_broker.pick(acr, comparision_type)
                        logger.debug("Picked AuthN broker for ACR %s: %s" % (
                            str(acr), str(res)))
                        if res:
                            # Return the best guess by pick.
                            return res[0]

        except KeyError as exc:
            logger.debug(
                "An error occured while picking the authN broker: %s" % str(
                    exc))

        # return the best I have
        return None, None

    def auth_init(self, request, request_class=AuthorizationRequest):
        """

        :param request: The AuthorizationRequest
        :return:
        """
        logger.debug("Request: '%s'" % request)
        # Same serialization used for GET and POST
        try:
            areq = self.server.parse_authorization_request(
                request=request_class, query=request)
        except (MissingRequiredValue, MissingRequiredAttribute) as err:
            logger.debug("%s" % err)
            areq = request_class().deserialize(request, "urlencoded")
            try:
                redirect_uri = self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError) as err:
                return self._error("invalid_request", "%s" % err)
            try:
                _rtype = areq["response_type"]
            except:
                _rtype = ["code"]
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err, areq["state"],
                                              _rtype)
        except KeyError:
            areq = request_class().deserialize(request, "urlencoded")
            # verify the redirect_uri
            try:
                self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError) as err:
                return self._error("invalid_request", "%s" % err)
        except Exception as err:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            logger.debug("Bad request: %s (%s)" % (err, err.__class__.__name__))
            return BadRequest("%s" % err)

        if not areq:
            logger.debug("No AuthzRequest")
            return self._error("invalid_request", "Can not parse AuthzRequest")

        if self.trace:
            self.trace.info('{}'.format(areq.to_dict()))
            if 'request' in areq:
                if areq['request'].jwe_header:
                    self.trace.info('request.jwe_header: {}'.format(
                        areq['request'].jwe_header))
                if areq['request'].jws_header:
                    self.trace.info('request.jws_header: {}'.format(
                        areq['request'].jws_header))

        try:
            _cinfo = self.cdb[areq['client_id']]
        except KeyError:
            return self._error('unauthorized_client', 'unknown client')
        else:
            if ' '.join(areq["response_type"]) not in _cinfo['response_types']:
                return self._error("invalid_request",
                                   "Trying to use unregistered response_typ")

        logger.debug("AuthzRequest: %s" % (areq.to_dict(),))
        try:
            redirect_uri = self.get_redirect_uri(areq)
        except (RedirectURIError, ParameterError, UnknownClient) as err:
            return self._error("invalid_request", "%s" % err)

        try:
            keyjar = self.keyjar
        except AttributeError:
            keyjar = ""

        try:
            # verify that the request message is correct
            areq.verify(keyjar=keyjar, opponent_id=areq["client_id"])
        except (MissingRequiredAttribute, ValueError) as err:
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err)

        return {"areq": areq, "redirect_uri": redirect_uri}

    @staticmethod
    def _acr_claims(areq):
        try:
            acrdef = areq["claims"]["id_token"]["acr"]
        except KeyError:
            return None
        else:
            if isinstance(acrdef, dict):
                try:
                    return [acrdef["value"]]
                except KeyError:
                    try:
                        return acrdef["values"]
                    except KeyError:
                        pass

        return None

    def do_auth(self, areq, redirect_uri, cinfo, request, cookie, **kwargs):
        """

        :param areq:
        :param redirect_uri:
        :param cinfo:
        :param request:
        :param cookie:
        :param authn:
        :param kwargs:
        :return:
        """
        acrs = self._acr_claims(areq)
        if acrs:
            # If acr claims are present the picked acr value MUST match
            # one of the given
            tup = (None, None)
            for acr in acrs:
                res = self.authn_broker.pick(acr, "exact")
                logger.debug("Picked AuthN broker for ACR %s: %s" % (
                    str(acr), str(res)))
                if res:  # Return the best guess by pick.
                    tup = res[0]
                    break
            authn, authn_class_ref = tup
        else:
            authn, authn_class_ref = self.pick_auth(areq)
            if not authn:
                authn, authn_class_ref = self.pick_auth(areq, "better")
                if not authn:
                    authn, authn_class_ref = self.pick_auth(areq, "any")

        if authn is None:
            return self._redirect_authz_error("access_denied", redirect_uri,
                                              return_type=areq["response_type"])

        try:
            try:
                _auth_info = kwargs["authn"]
            except KeyError:
                _auth_info = ""

            if "upm_answer" in areq and areq["upm_answer"] == "true":
                _max_age = 0
            else:
                _max_age = max_age(areq)

            identity, _ts = authn.authenticated_as(
                cookie, authorization=_auth_info, max_age=_max_age)
        except (NoSuchAuthentication, TamperAllert):
            identity = None
            _ts = 0
        except ToOld:
            logger.info("Too old authentication")
            identity = None
            _ts = 0
        else:
            logger.info("No active authentication")

        # gather information to be used by the authentication method
        authn_args = {"query": request,
                      "authn_class_ref": authn_class_ref}

        if "req_user" in kwargs:
            authn_args["as_user"] = kwargs["req_user"],

        for attr in ["policy_uri", "logo_uri", "tos_uri"]:
            try:
                authn_args[attr] = cinfo[attr]
            except KeyError:
                pass

        for attr in ["ui_locales", "acr_values"]:
            try:
                authn_args[attr] = areq[attr]
            except KeyError:
                pass

        # To authenticate or Not
        if identity is None:  # No!
            if "prompt" in areq and "none" in areq["prompt"]:
                # Need to authenticate but not allowed
                return self._redirect_authz_error(
                    "login_required", redirect_uri,
                    return_type=areq["response_type"])
            else:
                return authn(**authn_args)
        else:
            if re_authenticate(areq, authn):
                # demand re-authentication
                return authn(**authn_args)
            else:
                # I get back a dictionary
                user = identity["uid"]
                if "req_user" in kwargs:
                    sids_for_sub = self.sdb.get_sids_by_sub(kwargs["req_user"])
                    if sids_for_sub and user != \
                            self.sdb.get_authentication_event(
                                sids_for_sub[-1]).uid:
                        logger.debug("Wanted to be someone else!")
                        if "prompt" in areq and "none" in areq["prompt"]:
                            # Need to authenticate but not allowed
                            return self._redirect_authz_error("login_required",
                                                              redirect_uri)
                        else:
                            return authn(**authn_args)

        authn_event = AuthnEvent(identity["uid"], identity.get('salt', ''),
                                 authn_info=authn_class_ref,
                                 time_stamp=_ts)

        return {"authn_event": authn_event, "identity": identity, "user": user}

    def setup_session(self, areq, authn_event, cinfo):
        sid = self.sdb.create_authz_session(authn_event, areq)
        self.sdb.do_sub(sid, '')
        return sid

    def authorization_endpoint(self, request="", cookie="", **kwargs):
        """ The AuthorizationRequest endpoint

        :param request: The client request
        """

        info = self.auth_init(request)
        if isinstance(info, Response):
            return info

        _cid = info["areq"]["client_id"]
        cinfo = self.cdb[_cid]

        authnres = self.do_auth(info["areq"], info["redirect_uri"],
                                cinfo, request, cookie, **kwargs)

        if isinstance(authnres, Response):
            return authnres

        logger.debug("- authenticated -")
        logger.debug("AREQ keys: %s" % info["areq"].keys())

        sid = self.setup_session(info["areq"], authnres["authn_event"],
                                 cinfo)

        return self.authz_part2(authnres["user"], info["areq"], sid,
                                cookie=cookie)

    def aresp_check(self, aresp, areq):
        return ""

    def create_authn_response(self, areq, sid):
        rtype = areq["response_type"][0]
        _func = self.response_type_map[rtype]
        aresp = _func(areq=areq, scode=self.sdb[sid]["code"], sdb=self.sdb,
                      myself=self.baseurl)

        if rtype == "code":
            fragment_enc = False
        else:
            fragment_enc = True

        return aresp, fragment_enc

    def response_mode(self, areq, fragment_enc, **kwargs):
        resp_mode = areq["response_mode"]

        if resp_mode == 'fragment' and not fragment_enc:
            # Can't be done
            raise InvalidRequest("wrong response_mode")
        elif resp_mode == 'query' and fragment_enc:
            # Can't be done
            return InvalidRequest("wrong response_mode")
        return None

    def authz_part2(self, user, areq, sid, **kwargs):
        """
        After the authentication this is where you should end up

        :param user:
        :param areq: The Authorization Request
        :param sid: Session key
        :param kwargs: possible other parameters
        :return: A redirect to the redirect_uri of the client
        """
        result = self._complete_authz(user, areq, sid, **kwargs)
        if isinstance(result, Response):
            return result
        else:
            aresp, headers, redirect_uri, fragment_enc = result

        # Mix-Up mitigation
        aresp['iss'] = self.baseurl
        aresp['client_id'] = areq['client_id']

        # Just do whatever is the default
        location = aresp.request(redirect_uri, fragment_enc)
        logger.debug("Redirected to: '%s' (%s)" % (location, type(location)))
        return SeeOther(str(location), headers=headers)

    def _complete_authz(self, user, areq, sid, **kwargs):
        _log_debug = logger.debug
        _log_debug("- in authenticated() -")

        # Do the authorization
        try:
            permission = self.authz(user, client_id=areq['client_id'])
            self.sdb.update(sid, "permission", permission)
        except Exception:
            raise

        _log_debug("response type: %s" % areq["response_type"])

        if self.sdb.is_revoked(sid):
            return self._error(error="access_denied",
                               descr="Token is revoked")

        info = self.create_authn_response(areq, sid)
        if isinstance(info, Response):
            return info
        else:
            aresp, fragment_enc = info

        try:
            redirect_uri = self.get_redirect_uri(areq)
        except (RedirectURIError, ParameterError) as err:
            return BadRequest("%s" % err)

        # Must not use HTTP unless implicit grant type and native application

        info = self.aresp_check(aresp, areq)
        if isinstance(info, Response):
            return info

        headers = []
        try:
            _kaka = kwargs["cookie"]
        except KeyError:
            pass
        else:
            if _kaka and self.cookie_name not in _kaka:  # Don't overwrite
                # cookie
                headers.append(
                    self.cookie_func(user, typ="sso", ttl=self.sso_ttl))

        # Now about the response_mode. Should not be set if it's obvious
        # from the response_type. Knows about 'query', 'fragment' and
        # 'form_post'.

        if "response_mode" in areq:
            try:
                resp = self.response_mode(areq, fragment_enc, aresp=aresp,
                                          redirect_uri=redirect_uri,
                                          headers=headers)
            except InvalidRequest as err:
                return self._error("invalid_request", err)
            else:
                if resp is not None:
                    return resp

        return aresp, headers, redirect_uri, fragment_enc

    def token_scope_check(self, areq, info):
        """ Not implemented here """
        # if not self.subset(areq["scope"], _info["scope"]):
        # LOG_INFO("Asked for scope which is not subset of previous defined")
        # err = TokenErrorResponse(error="invalid_scope")
        #     return Response(err.to_json(), content="application/json")
        return None

    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        LOG_DEBUG("- token -")
        body = kwargs["request"]
        LOG_DEBUG("body: %s" % body)

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

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

        LOG_DEBUG("AccessTokenRequest: %s" % 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")

        LOG_DEBUG("_tinfo: %s" % _tinfo)

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

        LOG_DEBUG("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")

    def verify_endpoint(self, request="", cookie=None, **kwargs):
        _req = parse_qs(request)
        try:
            areq = parse_qs(_req["query"][0])
        except KeyError:
            return BadRequest()

        authn, acr = self.pick_auth(areq=areq)
        kwargs["cookie"] = cookie
        return authn.verify(_req, **kwargs)

    def write_session_cookie(self, value):
        return make_cookie(self.session_cookie_name, value, self.seed, path="/")

    def delete_session_cookie(self):
        return make_cookie(self.session_cookie_name, "", b"", path="/",
                           expire=-1)

    def _compute_session_state(self, state, salt, client_id, redirect_uri):
        parsed_uri = urlparse(redirect_uri)
        rp_origin_url = "{uri.scheme}://{uri.netloc}".format(uri=parsed_uri)
        session_str = client_id + " " + rp_origin_url + " " + state + " " + salt
        return hashlib.sha256(
            session_str.encode("utf-8")).hexdigest() + "." + salt
예제 #22
0
class Provider(object):
    endp = [AuthorizationEndpoint, TokenEndpoint]

    def __init__(self, name, sdb, cdb, authn_broker, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope="",
                 ca_bundle=None, verify_ssl=True, default_acr=""):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr()
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"

    def endpoints(self):
        for endp in self.endp:
            yield endp(None).name

    # def authn_reply(self, areq, aresp, bsid, **kwargs):
    #     """
    #
    #     :param areq: Authorization Request
    #     :param aresp: Authorization Response
    #     :param bsid: Session id
    #     :param kwargs: Additional keyword args
    #     :return:
    #     """
    #     if "redirect_uri" in areq:
    #         # TODO verify that the uri is reasonable
    #         redirect_uri = areq["redirect_uri"]
    #     else:
    #         redirect_uri = self.urlmap[areq["client_id"]]
    #
    #     location = location_url(areq["response_type"], redirect_uri,
    #                             aresp.to_urlencoded())
    #
    #     LOG_DEBUG("Redirected to: '%s' (%s)" % (location, type(location)))
    #
    #     # set cookie containing session ID
    #
    #     cookie = make_cookie(self.cookie_name, bsid, self.seed)
    #
    #     return Redirect(str(location), headers=[cookie])
    #
    # def authn_response(self, areq, **kwargs):
    #     """
    #
    #     :param areq: Authorization request
    #     :param kwargs: Extra keyword arguments
    #     :return:
    #     """
    #     scode = kwargs["code"]
    #     areq["response_type"].sort()
    #     _rtype = " ".join(areq["response_type"])
    #     return self.response_type_map[_rtype](areq=areq, scode=scode,
    #                                           sdb=self.sdb)

    @staticmethod
    def input(query="", post=None):
        # Support GET and POST
        if query:
            return query
        elif post:
            return post
        else:
            raise MissingParameter("No input")

    @staticmethod
    def _error_response(error, descr=None):
        logger.error("%s" % error)
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _error(error, descr=None):
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _authz_error(error, descr=None):

        response = AuthorizationErrorResponse(error=error)
        if descr:
            response["error_description"] = descr

        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _redirect_authz_error(error, redirect_uri, descr=None, state="",
                              return_type=None):
        err = AuthorizationErrorResponse(error=error)
        if descr:
            err["error_description"] = descr
        if state:
            err["state"] = state
        if return_type is None or return_type == ["code"]:
            location = err.request(redirect_uri)
        else:
            location = err.request(redirect_uri, True)
        return Redirect(location)

    def _verify_redirect_uri(self, areq):
        """
        MUST NOT contain a fragment
        MAY contain query component

        :return: An error response if the redirect URI is faulty otherwise
            None
        """
        try:
            _redirect_uri = urlparse.unquote(areq["redirect_uri"])

            part = urlparse.urlparse(_redirect_uri)
            if part.fragment:
                raise URIError("Contains fragment")

            (_base, _query) = urllib.splitquery(_redirect_uri)
            if _query:
                _query = urlparse.parse_qs(_query)

            match = False
            for regbase, rquery in self.cdb[areq["client_id"]]["redirect_uris"]:
                if _base == regbase or _redirect_uri.startswith(regbase):
                    # every registered query component must exist in the
                    # redirect_uri
                    if rquery:
                        for key, vals in rquery.items():
                            assert key in _query
                            for val in vals:
                                assert val in _query[key]
                    # and vice versa, every query component in the redirect_uri
                    # must be registered
                    if _query:
                        if rquery is None:
                            raise ValueError
                        for key, vals in _query.items():
                            assert key in rquery
                            for val in vals:
                                assert val in rquery[key]
                    match = True
                    break
            if not match:
                raise RedirectURIError("Doesn't match any registered uris")
            # ignore query components that are not registered
            return None
        except Exception:
            logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"])
            try:
                _cinfo = self.cdb[areq["client_id"]]
            except KeyError:
                logger.info("Unknown client: %s" % areq["client_id"])
                raise UnknownClient(areq["client_id"])
            else:
                logger.info("Registered redirect_uris: %s" % _cinfo)
                raise RedirectURIError(
                    "Faulty redirect_uri: %s" % areq["redirect_uri"])

    def get_redirect_uri(self, areq):
        """ verify that the redirect URI is reasonable

        :param areq: The Authorization request
        :return: Tuple of (redirect_uri, Response instance)
            Response instance is not None of matching redirect_uri failed
        """
        if 'redirect_uri' in areq:
            self._verify_redirect_uri(areq)
            uri = areq["redirect_uri"]
        else:
            raise ParameterError(
                "Missing redirect_uri and more than one or none registered")

        return uri

    def pick_auth(self, areq, comparision_type=""):
        """

        :param areq: AuthorizationRequest instance
        :param comparision_type: How to pick the authentication method
        :return: An authentication method and its authn class ref
        """
        if comparision_type == "any":
            return self.authn_broker[0]

        try:
            if len(self.authn_broker) == 1:
                return self.authn_broker[0]
            elif "acr_values" in areq:
                if not comparision_type:
                    comparision_type = "exact"

                if not isinstance(areq["acr_values"], list):
                    areq["acr_values"] = [areq["acr_values"]]

                for acr in areq["acr_values"]:
                    res = self.authn_broker.pick(acr, comparision_type)
                    logger.debug("Picked AuthN broker for ACR %s: %s" % (
                        str(acr), str(res)))
                    if res:
                        #Return the best guess by pick.
                        return res[0]
            else:  # same as any
                try:
                    acrs = areq["claims"]["id_token"]["acr"]["values"]
                except KeyError:
                    return self.authn_broker[0]
                else:
                    for acr in acrs:
                        res = self.authn_broker.pick(acr, comparision_type)
                        logger.debug("Picked AuthN broker for ACR %s: %s" % (
                            str(acr), str(res)))
                        if res:
                            #Return the best guess by pick.
                            return res[0]

        except KeyError as exc:
            logger.debug(
                "An error occured while picking the authN broker: %s" % str(
                    exc))

        # return the best I have
        return None, None

    def auth_init(self, request):
        """

        :param request: The AuthorizationRequest
        :return:
        """
        logger.debug("Request: '%s'" % request)
        # Same serialization used for GET and POST
        try:
            areq = self.server.parse_authorization_request(query=request)
        except (MissingRequiredValue, MissingRequiredAttribute) as err:
            logger.debug("%s" % err)
            areq = AuthorizationRequest().deserialize(request, "urlencoded")
            try:
                redirect_uri = self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError), err:
                return self._error("invalid_request", "%s" % err)
            try:
                _rtype = areq["response_type"]
            except:
                _rtype = ["code"]
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err, areq["state"],
                                              _rtype)
        except KeyError:
            areq = AuthorizationRequest().deserialize(request, "urlencoded")
            # verify the redirect_uri
            try:
                self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError), err:
                return self._error("invalid_request", "%s" % err)
예제 #23
0
파일: provider.py 프로젝트: biancini/pyoidc
class Provider(object):
    def __init__(self, name, sdb, cdb, authn, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope=""):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.srvmethod = SrvMethod()
        self.authn = authn
        if authn:
            self.authn.srv = self
        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr()
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

    def subset(self, li1, li2):
        """
        Verify that all items in li1 appears in li2

        :param li1: List 1
        :param li2: List 2
        :return: True if all items in li1 appears in li2
        """
        for item in li1:
            try:
                assert item in li2
            except AssertionError:
                return False
        return True

    def get_client_id(self, 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" % req.to_dict())
        if authn:
            if authn.startswith("Basic "):
                logger.debug("Basic auth")
                (_id, _secret) = base64.b64decode(authn[6:]).split(":")
                if _id not in self.cdb:
                    logger.debug("Unknown client_id")
                    raise FailedAuthentication("Unknown client_id")
                else:
                    try:
                        assert _secret == self.cdb[_id]["client_secret"]
                    except AssertionError:
                        logger.debug("Incorrect secret")
                        raise FailedAuthentication("Incorrect secret")
            else:
                try:
                    assert authn[:6].lower() == "bearer"
                    logger.debug("Bearer auth")
                    _token = authn[7:]
                except AssertionError:
                    raise FailedAuthentication("AuthZ type I don't know")

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

        return _id

    def authn_reply(self, areq, aresp, bsid, **kwargs):

        if "redirect_uri" in areq:
            # TODO verify that the uri is reasonable
            redirect_uri = areq["redirect_uri"]
        else:
            redirect_uri = self.urlmap[areq["client_id"]]

        location = location_url(areq["response_type"], redirect_uri,
                                aresp.to_urlencoded())

        LOG_DEBUG("Redirected to: '%s' (%s)" % (location, type(location)))

        # set cookie containing session ID

        cookie = make_cookie(self.cookie_name, bsid, self.seed)

        return Redirect(str(location), headers=[cookie])

    def authn_response(self, areq, **kwargs):
        """

        :param areq: Authorization request
        :param kwargs: Extra keyword arguments
        :return:
        """
        scode = kwargs["code"]
        areq["response_type"].sort()
        _rtype = " ".join(areq["response_type"])
        return self.response_type_map[_rtype](areq=areq, scode=scode,
                                              sdb=self.sdb)

    def input(self, query="", post=None):
        # Support GET and POST
        if query:
            return query
        elif post:
            return post
        else:
            raise MissingParameter("No input")

    def authorization_endpoint(self, request="", **kwargs):
        """ The AuthorizationRequest endpoint

        :param query: The query part of the request URL
        """
        _sdb = self.sdb

        LOG_DEBUG("- authorization -")
        LOG_DEBUG("Query: '%s'" % request)

        try:
            kaka = kwargs["cookie"]
        except KeyError:
            kaka = None
        identity = self.authn.authenticated_as(kaka)
        if identity is None:  # No!
            return self.authn(query=request)
        else:
            # I get back a dictionary
            user = identity["uid"]

        LOG_DEBUG("- authenticated -")

        try:
            areq = self.srvmethod.parse_authorization_request(query=request)
        except MissingRequiredAttribute, err:
            return BadRequest("%s" % err)
        except Exception, err:
            return BadRequest("%s" % err)
예제 #24
0
class Provider(object):
    endp = [AuthorizationEndpoint, TokenEndpoint]

    def __init__(self, name, sdb, cdb, authn_broker, authz, client_authn,
                 symkey="", urlmap=None, iv=0, default_scope="",
                 ca_bundle=None, verify_ssl=True, default_acr=""):
        self.name = name
        self.sdb = sdb
        self.cdb = cdb
        self.server = Server(ca_certs=ca_bundle, verify_ssl=verify_ssl)

        self.authn_broker = authn_broker
        if authn_broker is None:
            # default cookie function
            self.cookie_func = CookieDealer(srv=self).create_cookie
        else:
            self.cookie_func = self.authn_broker[0][0].create_cookie
            for item in self.authn_broker:
                item.srv = self

        self.authz = authz
        self.client_authn = client_authn
        self.symkey = symkey
        self.seed = rndstr().encode("utf-8")
        self.iv = iv or os.urandom(16)
        self.cookie_name = "pyoidc"
        self.default_scope = default_scope
        self.sso_ttl = 0
        self.default_acr = default_acr

        if urlmap is None:
            self.urlmap = {}
        else:
            self.urlmap = urlmap

        self.response_type_map = {
            "code": code_response,
            "token": token_response,
            "none": none_response,
        }

        self.session_cookie_name = "pyoic_session"

    # def authn_reply(self, areq, aresp, bsid, **kwargs):
    #     """
    #
    #     :param areq: Authorization Request
    #     :param aresp: Authorization Response
    #     :param bsid: Session id
    #     :param kwargs: Additional keyword args
    #     :return:
    #     """
    #     if "redirect_uri" in areq:
    #         # TODO verify that the uri is reasonable
    #         redirect_uri = areq["redirect_uri"]
    #     else:
    #         redirect_uri = self.urlmap[areq["client_id"]]
    #
    #     location = location_url(areq["response_type"], redirect_uri,
    #                             aresp.to_urlencoded())
    #
    #     LOG_DEBUG("Redirected to: '%s' (%s)" % (location, type(location)))
    #
    #     # set cookie containing session ID
    #
    #     cookie = make_cookie(self.cookie_name, bsid, self.seed)
    #
    #     return Redirect(str(location), headers=[cookie])
    #
    # def authn_response(self, areq, **kwargs):
    #     """
    #
    #     :param areq: Authorization request
    #     :param kwargs: Extra keyword arguments
    #     :return:
    #     """
    #     scode = kwargs["code"]
    #     areq["response_type"].sort()
    #     _rtype = " ".join(areq["response_type"])
    #     return self.response_type_map[_rtype](areq=areq, scode=scode,
    #                                           sdb=self.sdb)

    @staticmethod
    def input(query="", post=None):
        # Support GET and POST
        if query:
            return query
        elif post:
            return post
        else:
            raise MissingParameter("No input")

    @staticmethod
    def _error_response(error, descr=None):
        logger.error("%s" % error)
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _error(error, descr=None):
        response = ErrorResponse(error=error, error_description=descr)
        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _authz_error(error, descr=None):

        response = AuthorizationErrorResponse(error=error)
        if descr:
            response["error_description"] = descr

        return Response(response.to_json(), content="application/json",
                        status="400 Bad Request")

    @staticmethod
    def _redirect_authz_error(error, redirect_uri, descr=None, state="",
                              return_type=None):
        err = AuthorizationErrorResponse(error=error)
        if descr:
            err["error_description"] = descr
        if state:
            err["state"] = state
        if return_type is None or return_type == ["code"]:
            location = err.request(redirect_uri)
        else:
            location = err.request(redirect_uri, True)
        return Redirect(location)

    def _verify_redirect_uri(self, areq):
        """
        MUST NOT contain a fragment
        MAY contain query component

        :return: An error response if the redirect URI is faulty otherwise
            None
        """
        try:
            _redirect_uri = urlparse.unquote(areq["redirect_uri"])

            part = urlparse.urlparse(_redirect_uri)
            if part.fragment:
                raise URIError("Contains fragment")

            (_base, _query) = splitquery(_redirect_uri)
            if _query:
                _query = urlparse.parse_qs(_query)

            match = False
            for regbase, rquery in self.cdb[str(areq["client_id"])]["redirect_uris"]:
                if _base == regbase or _redirect_uri.startswith(regbase):
                    # every registered query component must exist in the
                    # redirect_uri
                    if rquery:
                        for key, vals in rquery.items():
                            assert key in _query
                            for val in vals:
                                assert val in _query[key]
                    # and vice versa, every query component in the redirect_uri
                    # must be registered
                    if _query:
                        if rquery is None:
                            raise ValueError
                        for key, vals in _query.items():
                            assert key in rquery
                            for val in vals:
                                assert val in rquery[key]
                    match = True
                    break
            if not match:
                raise RedirectURIError("Doesn't match any registered uris")
            # ignore query components that are not registered
            return None
        except Exception as err:
            logger.error("Faulty redirect_uri: %s" % areq["redirect_uri"])
            try:
                _cinfo = self.cdb[str(areq["client_id"])]
            except KeyError:
                logger.info("Unknown client: %s" % areq["client_id"])
                raise UnknownClient(areq["client_id"])
            else:
                logger.info("Registered redirect_uris: %s" % _cinfo)
                raise RedirectURIError(
                    "Faulty redirect_uri: %s" % areq["redirect_uri"])

    def get_redirect_uri(self, areq):
        """ verify that the redirect URI is reasonable

        :param areq: The Authorization request
        :return: Tuple of (redirect_uri, Response instance)
            Response instance is not None of matching redirect_uri failed
        """
        if 'redirect_uri' in areq:
            self._verify_redirect_uri(areq)
            uri = areq["redirect_uri"]
        else:
            raise ParameterError(
                "Missing redirect_uri and more than one or none registered")

        return uri

    def pick_auth(self, areq, comparision_type=""):
        """

        :param areq: AuthorizationRequest instance
        :param comparision_type: How to pick the authentication method
        :return: An authentication method and its authn class ref
        """
        if comparision_type == "any":
            return self.authn_broker[0]

        try:
            if len(self.authn_broker) == 1:
                return self.authn_broker[0]
            elif "acr_values" in areq:
                if not comparision_type:
                    comparision_type = "exact"

                if not isinstance(areq["acr_values"], list):
                    areq["acr_values"] = [areq["acr_values"]]

                for acr in areq["acr_values"]:
                    res = self.authn_broker.pick(acr, comparision_type)
                    logger.debug("Picked AuthN broker for ACR %s: %s" % (
                        str(acr), str(res)))
                    if res:
                        # Return the best guess by pick.
                        return res[0]
            else:  # same as any
                try:
                    acrs = areq["claims"]["id_token"]["acr"]["values"]
                except KeyError:
                    return self.authn_broker[0]
                else:
                    for acr in acrs:
                        res = self.authn_broker.pick(acr, comparision_type)
                        logger.debug("Picked AuthN broker for ACR %s: %s" % (
                            str(acr), str(res)))
                        if res:
                            # Return the best guess by pick.
                            return res[0]

        except KeyError as exc:
            logger.debug(
                "An error occured while picking the authN broker: %s" % str(
                    exc))

        # return the best I have
        return None, None

    def auth_init(self, request, request_class=AuthorizationRequest):
        """

        :param request: The AuthorizationRequest
        :return:
        """
        logger.debug("Request: '%s'" % request)
        # Same serialization used for GET and POST
        try:
            areq = self.server.parse_authorization_request(
                request=request_class, query=request)
        except (MissingRequiredValue, MissingRequiredAttribute) as err:
            logger.debug("%s" % err)
            areq = request_class().deserialize(request, "urlencoded")
            try:
                redirect_uri = self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError) as err:
                return self._error("invalid_request", "%s" % err)
            try:
                _rtype = areq["response_type"]
            except:
                _rtype = ["code"]
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err, areq["state"],
                                              _rtype)
        except KeyError:
            areq = request_class().deserialize(request, "urlencoded")
            # verify the redirect_uri
            try:
                self.get_redirect_uri(areq)
            except (RedirectURIError, ParameterError) as err:
                return self._error("invalid_request", "%s" % err)
        except Exception as err:
            message = traceback.format_exception(*sys.exc_info())
            logger.error(message)
            logger.debug("Bad request: %s (%s)" % (err, err.__class__.__name__))
            return BadRequest("%s" % err)

        if not areq:
            logger.debug("No AuthzRequest")
            return self._error("invalid_request", "Can not parse AuthzRequest")

        logger.debug("AuthzRequest: %s" % (areq.to_dict(),))
        try:
            redirect_uri = self.get_redirect_uri(areq)
        except (RedirectURIError, ParameterError, UnknownClient) as err:
            return self._error("invalid_request", "%s" % err)

        try:
            keyjar = self.keyjar
        except AttributeError:
            keyjar = ""

        try:
            # verify that the request message is correct
            areq.verify(keyjar=keyjar, opponent_id=areq["client_id"])
        except (MissingRequiredAttribute, ValueError) as err:
            return self._redirect_authz_error("invalid_request", redirect_uri,
                                              "%s" % err)

        return {"areq": areq, "redirect_uri": redirect_uri}

    @staticmethod
    def _acr_claims(areq):
        try:
            acrdef = areq["claims"]["id_token"]["acr"]
        except KeyError:
            return None
        else:
            if isinstance(acrdef, dict):
                try:
                    return [acrdef["value"]]
                except KeyError:
                    try:
                        return acrdef["values"]
                    except KeyError:
                        pass

        return None

    def do_auth(self, areq, redirect_uri, cinfo, request, cookie, **kwargs):
        """

        :param areq:
        :param redirect_uri:
        :param cinfo:
        :param request:
        :param cookie:
        :param authn:
        :param kwargs:
        :return:
        """
        acrs = self._acr_claims(areq)
        if acrs:
            # If acr claims are present the picked acr value MUST match
            # one of the given
            tup = (None, None)
            for acr in acrs:
                res = self.authn_broker.pick(acr, "exact")
                logger.debug("Picked AuthN broker for ACR %s: %s" % (
                    str(acr), str(res)))
                if res:  # Return the best guess by pick.
                    tup = res[0]
                    break
            authn, authn_class_ref = tup
        else:
            authn, authn_class_ref = self.pick_auth(areq)
            if not authn:
                authn, authn_class_ref = self.pick_auth(areq, "better")
                if not authn:
                    authn, authn_class_ref = self.pick_auth(areq, "any")

        if authn is None:
            return self._redirect_authz_error("access_denied", redirect_uri,
                                              return_type=areq["response_type"])

        try:
            try:
                _auth_info = kwargs["authn"]
            except KeyError:
                _auth_info = ""

            if "upm_answer" in areq and areq["upm_answer"] == "true":
                _max_age = 0
            else:
                _max_age = max_age(areq)

            identity, _ts = authn.authenticated_as(
                cookie, authorization=_auth_info, max_age=_max_age)
        except (NoSuchAuthentication, TamperAllert):
            identity = None
            _ts = 0
        except ToOld:
            logger.info("Too old authentication")
            identity = None
            _ts = 0
        else:
            logger.info("No active authentication")

        # gather information to be used by the authentication method
        authn_args = {"query": request,
                      "authn_class_ref": authn_class_ref}

        if "req_user" in kwargs:
            authn_args["as_user"] = kwargs["req_user"],

        for attr in ["policy_uri", "logo_uri", "tos_uri"]:
            try:
                authn_args[attr] = cinfo[attr]
            except KeyError:
                pass

        for attr in ["ui_locales", "acr_values"]:
            try:
                authn_args[attr] = areq[attr]
            except KeyError:
                pass

        # To authenticate or Not
        if identity is None:  # No!
            if "prompt" in areq and "none" in areq["prompt"]:
                # Need to authenticate but not allowed
                return self._redirect_authz_error(
                    "login_required", redirect_uri,
                    return_type=areq["response_type"])
            else:
                return authn(**authn_args)
        else:
            if re_authenticate(areq, authn):
                # demand re-authentication
                return authn(**authn_args)
            else:
                # I get back a dictionary
                user = identity["uid"]
                if "req_user" in kwargs:
                    sids_for_sub = self.sdb.get_sids_by_sub(kwargs["req_user"])
                    if sids_for_sub and user != self.sdb.get_authentication_event(
                            sids_for_sub[-1]).uid:
                        logger.debug("Wanted to be someone else!")
                        if "prompt" in areq and "none" in areq["prompt"]:
                            # Need to authenticate but not allowed
                            return self._redirect_authz_error("login_required",
                                                              redirect_uri)
                        else:
                            return authn(**authn_args)

        authn_event = AuthnEvent(identity["uid"], identity.get('salt', ''),
                                 authn_info=authn_class_ref,
                                 time_stamp=_ts)

        return {"authn_event": authn_event, "identity": identity, "user": user}

    def setup_session(self, areq, authn_event, cinfo):
        sid = self.sdb.create_authz_session(authn_event, areq)
        self.sdb.do_sub(sid, '')
        return sid

    def authorization_endpoint(self, request="", cookie="", **kwargs):
        """ The AuthorizationRequest endpoint

        :param request: The client request
        """

        info = self.auth_init(request)
        if isinstance(info, Response):
            return info

        _cid = info["areq"]["client_id"]
        cinfo = self.cdb[_cid]

        authnres = self.do_auth(info["areq"], info["redirect_uri"],
                                cinfo, request, cookie, **kwargs)

        if isinstance(authnres, Response):
            return authnres

        logger.debug("- authenticated -")
        logger.debug("AREQ keys: %s" % info["areq"].keys())

        sid = self.setup_session(info["areq"], authnres["authn_event"],
                                 cinfo)

        return self.authz_part2(authnres["user"], info["areq"], sid,
                                cookie=cookie)

    def aresp_check(self, aresp, areq):
        return ""

    def create_authn_response(self, areq, sid):
        rtype = areq["response_type"][0]
        _func = self.response_type_map[rtype]
        aresp = _func(areq=areq, scode=self.sdb[sid]["code"], sdb=self.sdb)

        if rtype == "code":
            fragment_enc = False
        else:
            fragment_enc = True

        return aresp, fragment_enc

    def response_mode(self, areq, fragment_enc, **kwargs):
        resp_mode = areq["response_mode"]

        if resp_mode == 'fragment' and not fragment_enc:
            # Can't be done
            raise InvalidRequest("wrong response_mode")
        elif resp_mode == 'query' and fragment_enc:
            # Can't be done
            return InvalidRequest("wrong response_mode")
        return None

    def authz_part2(self, user, areq, sid, **kwargs):
        """
        After the authentication this is where you should end up

        :param user:
        :param areq: The Authorization Request
        :param sid: Session key
        :param kwargs: possible other parameters
        :return: A redirect to the redirect_uri of the client
        """
        result = self._complete_authz(user, areq, sid, **kwargs)
        if isinstance(result, Response):
            return result
        else:
            aresp, headers, redirect_uri, fragment_enc = result

        # Just do whatever is the default
        location = aresp.request(redirect_uri, fragment_enc)
        logger.debug("Redirected to: '%s' (%s)" % (location, type(location)))
        return Redirect(str(location), headers=headers)

    def _complete_authz(self, user, areq, sid, **kwargs):
        _log_debug = logger.debug
        _log_debug("- in authenticated() -")

        # Do the authorization
        try:
            permission = self.authz(user, client_id=areq['client_id'])
            self.sdb.update(sid, "permission", permission)
        except Exception:
            raise

        _log_debug("response type: %s" % areq["response_type"])

        if self.sdb.is_revoked(sid):
            return self._error(error="access_denied",
                               descr="Token is revoked")

        info = self.create_authn_response(areq, sid)
        if isinstance(info, Response):
            return info
        else:
            aresp, fragment_enc = info

        try:
            redirect_uri = self.get_redirect_uri(areq)
        except (RedirectURIError, ParameterError) as err:
            return BadRequest("%s" % err)

        # Must not use HTTP unless implicit grant type and native application

        info = self.aresp_check(aresp, areq)
        if isinstance(info, Response):
            return info

        headers = []
        try:
            _kaka = kwargs["cookie"]
        except KeyError:
            pass
        else:
            if _kaka and self.cookie_name not in _kaka:  # Don't overwrite cookie
                headers.append(
                    self.cookie_func(user, typ="sso", ttl=self.sso_ttl))

        # Now about the response_mode. Should not be set if it's obvious
        # from the response_type. Knows about 'query', 'fragment' and
        # 'form_post'.

        if "response_mode" in areq:
            try:
                resp = self.response_mode(areq, fragment_enc, aresp=aresp,
                                          redirect_uri=redirect_uri,
                                          headers=headers)
            except InvalidRequest as err:
                return self._error("invalid_request", err)
            else:
                if resp is not None:
                    return resp

        return aresp, headers, redirect_uri, fragment_enc

    def token_scope_check(self, areq, info):
        """ Not implemented here """
        # if not self.subset(areq["scope"], _info["scope"]):
        # LOG_INFO("Asked for scope which is not subset of previous defined")
        # err = TokenErrorResponse(error="invalid_scope")
        #     return Response(err.to_json(), content="application/json")
        return None

    def token_endpoint(self, authn="", **kwargs):
        """
        This is where clients come to get their access tokens
        """

        _sdb = self.sdb

        LOG_DEBUG("- token -")
        body = kwargs["request"]
        LOG_DEBUG("body: %s" % body)

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

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

        LOG_DEBUG("AccessTokenRequest: %s" % 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"])
        except AccessCodeUsed:
            err = TokenErrorResponse(error="invalid_grant",
                                     error_description="Access grant used")
            return Response(err.to_json(), content="application/json",
                            status="401 Unauthorized")

        LOG_DEBUG("_tinfo: %s" % _tinfo)

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

        LOG_DEBUG("AccessTokenResponse: %s" % atr)

        return Response(atr.to_json(), content="application/json")

    def verify_endpoint(self, request="", cookie=None, **kwargs):
        _req = urlparse.parse_qs(request)
        try:
            areq = urlparse.parse_qs(_req["query"][0])
        except KeyError:
            return BadRequest()

        authn, acr = self.pick_auth(areq=areq)
        kwargs["cookie"] = cookie
        return authn.verify(_req, **kwargs)

    def write_session_cookie(self, value):
        return make_cookie(self.session_cookie_name, value, self.seed, path="/")

    def delete_session_cookie(self):
        return make_cookie(self.session_cookie_name, "", b"", path="/",
                           expire=-1)

    def _compute_session_state(self, state, salt, client_id, redirect_uri):
        parsed_uri = urlparse.urlparse(redirect_uri)
        rp_origin_url = "{uri.scheme}://{uri.netloc}".format(uri=parsed_uri)
        session_str = client_id + " " + rp_origin_url + " " + state + " " + salt
        return hashlib.sha256(session_str.encode("utf-8")).hexdigest() + "." + salt