Skip to content

Commit

Permalink
resturctu
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-maertens committed May 22, 2024
1 parent bf1061c commit 270fad9
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 184 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from contextlib import contextmanager
from functools import partial
from pathlib import Path
from unittest.mock import patch

from django.apps import apps
from django.test import override_settings

from django_webtest import WebTest

from openforms.utils.tests.vcr import OFVCRMixin

TEST_FILES = (Path(__file__).parent / "data").resolve()

KEYCLOAK_BASE_URL = "http://localhost:8080/realms/test/protocol/openid-connect"


@contextmanager
def mock_config(model: str, **overrides):
"""
Bundle all the required mocks.
This context manager deliberately prevents the mocked things from being injected in
the test method signature.
"""
defaults = {
"enabled": True,
"oidc_rp_client_id": "testid",
"oidc_rp_client_secret": "7DB3KUAAizYCcmZufpHRVOcD0TOkNO3I",
"oidc_rp_sign_algo": "RS256",
"oidc_rp_scopes_list": ["openid"],
"oidc_op_jwks_endpoint": f"{KEYCLOAK_BASE_URL}/certs",
"oidc_op_authorization_endpoint": f"{KEYCLOAK_BASE_URL}/auth",
"oidc_op_token_endpoint": f"{KEYCLOAK_BASE_URL}/token",
"oidc_op_user_endpoint": f"{KEYCLOAK_BASE_URL}/userinfo",
}
field_values = {**defaults, **overrides}
model_cls = apps.get_model("digid_eherkenning_oidc_generics", model)
with (
# bypass django-solo queries + cache hits
patch(
f"digid_eherkenning_oidc_generics.models.{model}.get_solo",
return_value=model_cls(**field_values),
),
# mock the state & nonce random value generation so we get predictable URLs to
# match with VCR
patch(
"mozilla_django_oidc.views.get_random_string",
return_value="not-a-random-string",
),
):
yield


mock_digid_config = partial(
mock_config,
model="OpenIDConnectPublicConfig",
oidc_rp_scopes_list=["openid", "bsn"],
)
mock_eherkenning_config = partial(
mock_config,
model="OpenIDConnectEHerkenningConfig",
oidc_rp_scopes_list=["openid", "kvk"],
)


@override_settings(CORS_ALLOW_ALL_ORIGINS=True, IS_HTTPS=True)
class IntegrationTestsBase(OFVCRMixin, WebTest):
VCR_TEST_FILES = TEST_FILES
Original file line number Diff line number Diff line change
Expand Up @@ -11,142 +11,19 @@
to bring up a Keycloak instance.
"""

from contextlib import contextmanager
from functools import partial
from pathlib import Path
from unittest.mock import patch

from django.apps import apps
from django.test import override_settings, tag
from django.urls import reverse_lazy
from django.test import tag

import requests
from django_webtest import WebTest
from furl import furl

from openforms.accounts.tests.factories import StaffUserFactory
from openforms.authentication.tests.utils import URLsHelper
from openforms.authentication.views import BACKEND_OUTAGE_RESPONSE_PARAMETER
from openforms.forms.tests.factories import FormFactory
from openforms.utils.tests.vcr import OFVCRMixin

from .base import IntegrationTestsBase, mock_digid_config, mock_eherkenning_config
from .keycloak_utils import keycloak_login

TEST_FILES = (Path(__file__).parent / "data").resolve()

KEYCLOAK_BASE_URL = "http://localhost:8080/realms/test/protocol/openid-connect"


@contextmanager
def mock_config(model: str, **overrides):
"""
Bundle all the required mocks.
This context manager deliberately prevents the mocked things from being injected in
the test method signature.
"""
defaults = {
"enabled": True,
"oidc_rp_client_id": "testid",
"oidc_rp_client_secret": "7DB3KUAAizYCcmZufpHRVOcD0TOkNO3I",
"oidc_rp_sign_algo": "RS256",
"oidc_rp_scopes_list": ["openid"],
"oidc_op_jwks_endpoint": f"{KEYCLOAK_BASE_URL}/certs",
"oidc_op_authorization_endpoint": f"{KEYCLOAK_BASE_URL}/auth",
"oidc_op_token_endpoint": f"{KEYCLOAK_BASE_URL}/token",
"oidc_op_user_endpoint": f"{KEYCLOAK_BASE_URL}/userinfo",
}
field_values = {**defaults, **overrides}
model_cls = apps.get_model("digid_eherkenning_oidc_generics", model)
with (
# bypass django-solo queries + cache hits
patch(
f"digid_eherkenning_oidc_generics.models.{model}.get_solo",
return_value=model_cls(**field_values),
),
# mock the state & nonce random value generation so we get predictable URLs to
# match with VCR
patch(
"mozilla_django_oidc.views.get_random_string",
return_value="not-a-random-string",
),
):
yield


mock_digid_config = partial(
mock_config,
model="OpenIDConnectPublicConfig",
oidc_rp_scopes_list=["openid", "bsn"],
)
mock_eherkenning_config = partial(
mock_config,
model="OpenIDConnectEHerkenningConfig",
oidc_rp_scopes_list=["openid", "kvk"],
)


@override_settings(CORS_ALLOW_ALL_ORIGINS=True, IS_HTTPS=True)
class IntegrationTestsBase(OFVCRMixin, WebTest):
VCR_TEST_FILES = TEST_FILES


class DigiDInitTests(IntegrationTestsBase):
"""
Test the outbound part of OIDC-based DigiD authentication.
"""

CALLBACK_URL = f"http://testserver{reverse_lazy('digid_oidc:callback')}"

@mock_digid_config()
def test_start_flow_redirects_to_oidc_provider(self):
form = FormFactory.create(authentication_backends=["digid_oidc"])
start_url = URLsHelper(form=form).get_auth_start(plugin_id="digid_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_target = furl(response["Location"])
query_params = redirect_target.query.params
self.assertEqual(redirect_target.host, "localhost")
self.assertEqual(redirect_target.port, 8080)
self.assertEqual(
redirect_target.path,
"/realms/test/protocol/openid-connect/auth",
)
self.assertEqual(query_params["scope"], "openid bsn")
self.assertEqual(query_params["client_id"], "testid")
self.assertEqual(query_params["redirect_uri"], self.CALLBACK_URL)

@mock_digid_config(
oidc_op_authorization_endpoint="http://localhost:8080/i-dont-exist"
)
def test_idp_availability_check(self):
form = FormFactory.create(authentication_backends=["digid_oidc"])
url_helper = URLsHelper(form=form)
start_url = url_helper.get_auth_start(plugin_id="digid_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_url = furl(response["Location"])
self.assertEqual(redirect_url.host, "testserver")
self.assertEqual(redirect_url.path, url_helper.form_path)
query_params = redirect_url.query.params
self.assertEqual(query_params[BACKEND_OUTAGE_RESPONSE_PARAMETER], "digid_oidc")

@mock_digid_config(oidc_keycloak_idp_hint="oidc-digid")
def test_keycloak_idp_hint_is_respected(self):
form = FormFactory.create(authentication_backends=["digid_oidc"])
url_helper = URLsHelper(form=form)
start_url = url_helper.get_auth_start(plugin_id="digid_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_url = furl(response["Location"])
self.assertEqual(redirect_url.args["kc_idp_hint"], "oidc-digid")


class DigiDCallbackTests(IntegrationTestsBase):
"""
Expand Down Expand Up @@ -250,65 +127,6 @@ def test_digid_error_reported_for_cancelled_login_with_staff_django_user(self):
self.assertEqual(callback_response.request.url, str(expected_url))


class EHerkenningInitTests(IntegrationTestsBase):
"""
Test the outbound part of OIDC-based eHerkenning authentication.
"""

CALLBACK_URL = f"http://testserver{reverse_lazy('eherkenning_oidc:callback')}"

@mock_eherkenning_config()
def test_start_flow_redirects_to_oidc_provider(self):
form = FormFactory.create(authentication_backends=["eherkenning_oidc"])
start_url = URLsHelper(form=form).get_auth_start(plugin_id="eherkenning_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_target = furl(response["Location"])
query_params = redirect_target.query.params
self.assertEqual(redirect_target.host, "localhost")
self.assertEqual(redirect_target.port, 8080)
self.assertEqual(
redirect_target.path,
"/realms/test/protocol/openid-connect/auth",
)
self.assertEqual(query_params["scope"], "openid kvk")
self.assertEqual(query_params["client_id"], "testid")
self.assertEqual(query_params["redirect_uri"], self.CALLBACK_URL)

@mock_eherkenning_config(
oidc_op_authorization_endpoint="http://localhost:8080/i-dont-exist"
)
def test_idp_availability_check(self):
form = FormFactory.create(authentication_backends=["eherkenning_oidc"])
url_helper = URLsHelper(form=form)
start_url = url_helper.get_auth_start(plugin_id="eherkenning_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_url = furl(response["Location"])
self.assertEqual(redirect_url.host, "testserver")
self.assertEqual(redirect_url.path, url_helper.form_path)
query_params = redirect_url.query.params
self.assertEqual(
query_params[BACKEND_OUTAGE_RESPONSE_PARAMETER], "eherkenning_oidc"
)

@mock_eherkenning_config(oidc_keycloak_idp_hint="oidc-eherkenning")
def test_keycloak_idp_hint_is_respected(self):
form = FormFactory.create(authentication_backends=["eherkenning_oidc"])
url_helper = URLsHelper(form=form)
start_url = url_helper.get_auth_start(plugin_id="eherkenning_oidc")

response = self.app.get(start_url)

self.assertEqual(response.status_code, 302)
redirect_url = furl(response["Location"])
self.assertEqual(redirect_url.args["kc_idp_hint"], "oidc-eherkenning")


class EHerkenningCallbackTests(IntegrationTestsBase):
"""
Test the return/callback side after authenticating with the identity provider.
Expand Down
Loading

0 comments on commit 270fad9

Please sign in to comment.