Exemplo n.º 1
0
 def setup(self, signing_key):
     self.instance = OIDCFrontend(lambda ctx, req: None, INTERNAL_ATTRIBUTES,
                                  dict(issuer=self.ISSUER, signing_key_path=signing_key))
     self.instance.register_endpoints(["foo_backend"])
Exemplo n.º 2
0
class TestOIDCFrontend(object):
    ISSUER = "https://op.example.com"

    @pytest.fixture(autouse=True)
    def setup(self, signing_key):
        self.instance = OIDCFrontend(lambda ctx, req: None, INTERNAL_ATTRIBUTES,
                                     dict(issuer=self.ISSUER, signing_key_path=signing_key))
        self.instance.register_endpoints(["foo_backend"])

    def create_state(self, auth_req):
        state = State()
        state.add(type(self.instance).__name__, {"oidc_request": auth_req.to_urlencoded()})
        return state

    def setup_for_authn_response(self, auth_req):
        context = Context()
        context.state = self.create_state(auth_req)

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.add_attributes(
                DataConverter(INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"]))
        internal_response.set_user_id(USERS["testuser1"]["eduPersonTargetedID"][0])

        self.instance.provider.cdb = {
            "client1": {"response_types": ["id_token"],
                        "redirect_uris": [(auth_req["redirect_uri"], None)],
                        "client_salt": "salt"}}

        return context, internal_response

    def test_handle_authn_response(self):
        client_id = "client1"
        state = "my_state"
        nonce = "nonce"
        redirect_uri = "https://client.example.com"
        claims_req = ClaimsRequest(id_token=Claims(email=None))
        req = AuthorizationRequest(client_id=client_id, state=state, scope="openid",
                                   response_type="id_token",
                                   redirect_uri=redirect_uri,
                                   nonce=nonce,
                                   claims=claims_req)
        context, internal_response = self.setup_for_authn_response(req)
        http_resp = self.instance.handle_authn_response(context, internal_response)
        assert http_resp.message.startswith(redirect_uri)

        resp = AuthorizationResponse().deserialize(urlparse(http_resp.message).fragment)
        assert resp["state"] == state
        id_token = IdToken().from_jwt(resp["id_token"], keyjar=self.instance.provider.keyjar)
        assert id_token["iss"] == self.ISSUER
        assert id_token["nonce"] == nonce
        assert id_token["sub"] == USERS["testuser1"]["eduPersonTargetedID"][0]
        assert id_token["email"] == USERS["testuser1"]["email"][0]

    def test_get_authn_response_query_encoded(self):
        client_id = "client1"
        state = "my_state"
        nonce = "nonce"
        redirect_uri = "https://client.example.com"
        claims_req = ClaimsRequest(id_token=Claims(email=None))
        req = AuthorizationRequest(client_id=client_id, state=state, scope="openid",
                                   response_type="id_token",
                                   redirect_uri=redirect_uri,
                                   nonce=nonce,
                                   claims=claims_req,
                                   response_mode="query")
        context, internal_response = self.setup_for_authn_response(req)
        http_resp = self.instance.handle_authn_response(context, internal_response)
        assert http_resp.message.startswith(redirect_uri)

        resp = AuthorizationResponse().deserialize(urlparse(http_resp.message).query)
        assert resp["state"] == state
        id_token = IdToken().from_jwt(resp["id_token"], keyjar=self.instance.provider.keyjar)
        assert id_token["iss"] == self.ISSUER
        assert id_token["nonce"] == nonce
        assert id_token["sub"] == USERS["testuser1"]["eduPersonTargetedID"][0]
        assert id_token["email"] == USERS["testuser1"]["email"][0]

    def test_handle_backend_error(self):
        client_id = "client1"
        redirect_uri = "https://client.example.com"
        areq = AuthorizationRequest(client_id=client_id, scope="openid", response_type="id_token",
                                    redirect_uri=redirect_uri)

        message = "test error"
        error = SATOSAAuthenticationError(self.create_state(areq), message)
        resp = self.instance.handle_backend_error(error)
        assert resp.message.startswith(redirect_uri)
        error_response = AuthorizationErrorResponse().deserialize(urlparse(resp.message).fragment)
        error_response["error"] = "access_denied"
        error_response["error_description"] == message

    def test_register_client(self):
        redirect_uri = "https://client.example.com"
        registration_request = RegistrationRequest(redirect_uris=[redirect_uri],
                                                   response_types=["id_token"])
        context = Context()
        context.request = registration_request.to_dict()
        registration_response = self.instance._register_client(context)
        assert registration_response.status == "201 Created"

        reg_resp = RegistrationResponse().deserialize(registration_response.message, "json")
        assert "client_id" in reg_resp
        assert reg_resp["client_id"] in self.instance.provider.cdb
        # no need to issue client secret since to token endpoint is published
        assert "client_secret" not in reg_resp
        assert reg_resp["redirect_uris"] == [redirect_uri]
        assert reg_resp["response_types"] == ["id_token"]
        assert reg_resp["id_token_signed_response_alg"] == "RS256"

    def test_register_client_with_wrong_response_type(self):
        redirect_uri = "https://client.example.com"
        registration_request = RegistrationRequest(redirect_uris=[redirect_uri],
                                                   response_types=["code"])
        context = Context()
        context.request = registration_request.to_dict()
        registration_response = self.instance._register_client(context)
        assert registration_response.status == "400 Bad Request"
        error_response = ClientRegistrationErrorResponse().deserialize(
                registration_response.message, "json")
        assert error_response["error"] == "invalid_request"
        assert "response_type" in error_response["error_description"]

    def test_provider_configuration_endpoint(self):
        expected_capabilities = {
            "response_types_supported": ["id_token"],
            "id_token_signing_alg_values_supported": ["RS256"],
            "response_modes_supported": ["fragment", "query"],
            "subject_types_supported": ["public", "pairwise"],
            "grant_types_supported": ["implicit"],
            "claim_types_supported": ["normal"],
            "claims_parameter_supported": True,
            "request_parameter_supported": False,
            "request_uri_parameter_supported": False,
        }

        http_response = self.instance._provider_config(Context())
        provider_config = ProviderConfigurationResponse().deserialize(http_response.message, "json")
        assert all(
                item in provider_config.to_dict().items() for item in expected_capabilities.items())
        assert provider_config["authorization_endpoint"] == "{}/foo_backend/authorization".format(self.ISSUER)