diff --git a/src/eherkenning/tests/test_mock_views.py b/src/eherkenning/tests/test_mock_views.py index c8964affb3..00778fc6dc 100644 --- a/src/eherkenning/tests/test_mock_views.py +++ b/src/eherkenning/tests/test_mock_views.py @@ -108,8 +108,15 @@ def test_get_returns_valid_response(self): self.assertContains(response, reverse("login")) self.assertNoEHerkenningURLS(response) - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") - def test_post_redirects_and_authenticates(self, mock_kvk): + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="123456789", + autospec=True, + ) + @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches", autospec=True) + def test_post_redirects_and_authenticates( + self, mock_kvk, mock_retrieve_rsin_with_kvk + ): mock_kvk.return_value = [ {"kvkNummer": "29664887", "vestigingsnummer": "1234"}, {"kvkNummer": "29664887", "vestigingsnummer": "5678"}, diff --git a/src/open_inwoner/accounts/tests/test_auth.py b/src/open_inwoner/accounts/tests/test_auth.py index 9ea470b54e..6efe7d8b78 100644 --- a/src/open_inwoner/accounts/tests/test_auth.py +++ b/src/open_inwoner/accounts/tests/test_auth.py @@ -20,6 +20,7 @@ from open_inwoner.haalcentraal.tests.mixins import HaalCentraalMixin from open_inwoner.kvk.branches import get_kvk_branch_number from open_inwoner.kvk.tests.factories import CertificateFactory +from open_inwoner.openzaak.models import OpenZaakConfig from ...cms.collaborate.cms_apps import CollaborateApphook from ...cms.tests import cms_tools @@ -34,6 +35,9 @@ eHerkenningUserFactory, ) +RETURN_URL = "/" +CANCEL_URL = reverse("login") + @override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls") class DigiDRegistrationTest(AssertRedirectsMixin, HaalCentraalMixin, WebTest): @@ -559,6 +563,46 @@ def test_eherkenning_fail_without_invite_redirects_to_login_page(self, m): self.assertRedirectsLogin(response, with_host=True) + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="", + autospec=True, + ) + @patch( + "open_inwoner.accounts.views.auth.OpenZaakConfig.get_solo", + return_value=OpenZaakConfig(fetch_eherkenning_zaken_with_rsin=True), + autospec=True, + ) + def test_login_as_eenmanszaak_blocked( + self, mock_oz_config, mock_retrieve_rsin_with_kvk + ): + url = reverse("eherkenning-mock:password") + params = { + "acs": f"http://testserver{reverse('eherkenning:acs')}", + "next": RETURN_URL, + "cancel": CANCEL_URL, + } + url = f"{url}?{urlencode(params)}" + + data = { + "auth_name": "29664887", + "auth_pass": "company@localhost", + } + + # post our password to the IDP + response = self.client.post(url, data, follow=False) + + # it will redirect to our ACS + self.assertEqual(response.status_code, 302) + self.assertIn(reverse("eherkenning:acs"), response["Location"]) + + # follow the ACS redirect and get/create the user + response = self.client.get(response["Location"]) + + # User is logged out and redirected to login view + self.assertNotIn("_auth_user_id", self.app.session) + self.assertRedirectsLogin(response, with_host=True) + @patch("eherkenning.validators.KVKValidator.__call__") def test_eherkenning_fail_without_invite_and_next_url_redirects_to_login_page( self, m @@ -614,12 +658,24 @@ def test_eherkenning_fail_with_invite_redirects_to_register_page(self, m): f"http://testserver{reverse('django_registration_register')}?invite={invite.key}", ) - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="123456789", + autospec=True, + ) + @patch( + "open_inwoner.kvk.client.KvKClient.get_all_company_branches", + autospec=True, + ) @patch( "open_inwoner.kvk.models.KvKConfig.get_solo", + autospec=True, ) def test_invite_url_not_in_session_after_successful_login( - self, mock_solo, mock_kvk + self, + mock_solo, + mock_kvk, + mock_retrieve_rsin_with_kvk, ): mock_kvk.return_value = [ {"kvkNummer": "12345678", "vestigingsnummer": "1234"}, @@ -687,7 +743,7 @@ def test_redirect_flow_with_no_vestigingsnummer(self, mock_solo, mock_kvk): mock_solo.return_value.server_certificate = CertificateFactory() user = eHerkenningUserFactory.create( - kvk="12345678", email="user-12345678@localhost" + kvk="12345678", rsin="123456789", email="user-12345678@localhost" ) url = reverse("eherkenning-mock:password") @@ -1048,8 +1104,16 @@ def test_digid_user_success(self): self.assertEqual(users.first().email, "test@example.com") self.assertEqual(users.last().email, "test@example.com") - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") - def test_eherkenning_user_success(self, mock_kvk): + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="123456789", + autospec=True, + ) + @patch( + "open_inwoner.kvk.client.KvKClient.get_all_company_branches", + autospec=True, + ) + def test_eherkenning_user_success(self, mock_kvk, mock_retrieve_rsin_with_kvk): """Assert that eHerkenning users can register with duplicate emails""" mock_kvk.return_value = [ @@ -1068,6 +1132,7 @@ def test_eherkenning_user_success(self, mock_kvk): test_user = eHerkenningUserFactory.create( email="test@localhost", kvk="64819772", + rsin="123456789", ) url = reverse("eherkenning-mock:password") diff --git a/src/open_inwoner/accounts/tests/test_oidc_views.py b/src/open_inwoner/accounts/tests/test_oidc_views.py index 1e1ffbe332..d35995ffb6 100644 --- a/src/open_inwoner/accounts/tests/test_oidc_views.py +++ b/src/open_inwoner/accounts/tests/test_oidc_views.py @@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError from django.test import TestCase, modify_settings, override_settings from django.urls import reverse +from django.utils.translation import gettext as _ import requests import requests_mock @@ -25,6 +26,7 @@ from open_inwoner.configurations.choices import OpenIDDisplayChoices from open_inwoner.configurations.models import SiteConfiguration from open_inwoner.kvk.branches import KVK_BRANCH_SESSION_VARIABLE +from open_inwoner.openzaak.models import OpenZaakConfig from ..choices import LoginTypeChoices from .factories import DigidUserFactory, UserFactory, eHerkenningUserFactory @@ -977,6 +979,7 @@ def test_existing_kvk_creates_no_new_user( first_name="John", last_name="Doe", kvk="12345678", + rsin="123456789", email="user-12345678@localhost", is_prepopulated=True, ) @@ -1031,7 +1034,9 @@ def test_new_user_is_created_when_new_kvk( mock_retrieve_rsin_with_kvk.return_value = "123456789" # set up a user with a non existing email address mock_get_userinfo.return_value = {"sub": "00000000"} - eHerkenningUserFactory.create(kvk="12345678", email="existing_user@example.com") + eHerkenningUserFactory.create( + kvk="12345678", rsin="123456789", email="existing_user@example.com" + ) session = self.client.session session["oidc_states"] = {"mock": {"nonce": "nonce"}} session.save() @@ -1109,14 +1114,32 @@ def test_logout(self, mock_get_solo): ] } ) - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token") + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="123456789", + autospec=True, + ) + @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches", autospec=True) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token", + autospec=True, + ) @patch( "digid_eherkenning_oidc_generics.models.OpenIDConnectEHerkenningConfig.get_solo", return_value=OpenIDConnectEHerkenningConfig(id=1, enabled=True), + autospec=True, ) def test_error_first_cleared_after_succesful_login( self, @@ -1126,6 +1149,7 @@ def test_error_first_cleared_after_succesful_login( mock_store_tokens, mock_get_userinfo, mock_kvk, + mock_retrieve_rsin_with_kvk, ): mock_get_userinfo.return_value = { "sub": "some_username", @@ -1318,17 +1342,111 @@ def test_login_validation_error( self.assertEqual(error_msg, str(GENERIC_EHERKENNING_ERROR_MSG)) - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") - @patch("open_inwoner.utils.context_processors.SiteConfiguration") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token") + @patch( + "open_inwoner.accounts.views.auth.OpenZaakConfig.get_solo", + return_value=OpenZaakConfig(fetch_eherkenning_zaken_with_rsin=True), + autospec=True, + ) + @patch("open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", autospec=True) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token", + autospec=True, + ) + @patch( + "digid_eherkenning_oidc_generics.models.OpenIDConnectEHerkenningConfig.get_solo", + return_value=OpenIDConnectEHerkenningConfig( + id=1, enabled=True, identifier_claim_name="sub" + ), + autospec=True, + ) + def test_login_as_eenmanszaak_blocked( + self, + mock_get_solo, + mock_get_token, + mock_verify_token, + mock_store_tokens, + mock_get_userinfo, + mock_retrieve_rsin_with_kvk, + mock_oz_config, + ): + """ + Eenmanszaken do not have an RSIN, which means that if we have a feature flag + to fetch resources using RSIN (from Open Zaak or Open Klant) enabled, we cannot + let eenmanszaken log in using eHerkenning + """ + mock_retrieve_rsin_with_kvk.return_value = "" + # set up a user with a non existing email address + mock_get_userinfo.return_value = {"sub": "00000000"} + eHerkenningUserFactory.create(kvk="12345678", email="existing_user@example.com") + session = self.client.session + session["oidc_states"] = {"mock": {"nonce": "nonce"}} + session.save() + callback_url = reverse("eherkenning_oidc:callback") + + self.assertFalse(User.objects.filter(email="new_user@example.com").exists()) + + # enter the login flow + callback_response = self.client.get( + callback_url, {"code": "mock", "state": "mock"} + ) + + # User is logged out and redirected to login view + self.assertNotIn("_auth_user_id", self.app.session) + self.assertRedirects( + callback_response, reverse("login"), fetch_redirect_response=False + ) + + response = self.client.get(callback_response.url) + + self.assertContains(response, _("Use DigiD to log in as a sole proprietor.")) + + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + return_value="123456789", + autospec=True, + ) + @patch( + "open_inwoner.kvk.client.KvKClient.get_all_company_branches", + autospec=True, + ) + @patch( + "open_inwoner.utils.context_processors.SiteConfiguration", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token", + autospec=True, + ) @patch( "digid_eherkenning_oidc_generics.models.OpenIDConnectEHerkenningConfig.get_solo", return_value=OpenIDConnectEHerkenningConfig( id=1, enabled=True, oidc_op_authorization_endpoint="http://idp.local/auth" ), + autospec=True, ) def test_redirect_after_login_with_registration_and_branch_selection( self, @@ -1339,6 +1457,7 @@ def test_redirect_after_login_with_registration_and_branch_selection( mock_get_userinfo, mock_siteconfig, mock_kvk, + mock_retrieve_rsin_with_kvk, ): """ Full authentication flow with redirect after successful login @@ -1409,17 +1528,40 @@ def test_redirect_after_login_with_registration_and_branch_selection( self.assertEqual(profile_response.status_code, 200) - @patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches") - @patch("open_inwoner.utils.context_processors.SiteConfiguration") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token") - @patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token") + @patch( + "open_inwoner.kvk.signals.KvKClient.retrieve_rsin_with_kvk", + autospec=True, + ) + @patch( + "open_inwoner.kvk.client.KvKClient.get_all_company_branches", + autospec=True, + ) + @patch( + "open_inwoner.utils.context_processors.SiteConfiguration", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token", + autospec=True, + ) + @patch( + "mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token", + autospec=True, + ) @patch( "digid_eherkenning_oidc_generics.models.OpenIDConnectEHerkenningConfig.get_solo", return_value=OpenIDConnectEHerkenningConfig( id=1, enabled=True, oidc_op_authorization_endpoint="http://idp.local/auth" ), + autospec=True, ) def test_redirect_after_login_no_registration_with_branch_selection( self, @@ -1430,11 +1572,12 @@ def test_redirect_after_login_no_registration_with_branch_selection( mock_get_userinfo, mock_siteconfig, mock_kvk, + mock_retrieve_rsin_with_kvk, ): """ Full authentication flow with redirect after successful login """ - user = eHerkenningUserFactory.create(kvk="12345678") + user = eHerkenningUserFactory.create(kvk="12345678", rsin="123456789") mock_get_userinfo.return_value = { "sub": "some_username", "kvk": "12345678", @@ -1512,7 +1655,7 @@ def test_redirect_after_login_no_registration_and_no_branch_selection( """ Full authentication flow with redirect after successful login """ - user = eHerkenningUserFactory.create(kvk="12345678") + user = eHerkenningUserFactory.create(kvk="12345678", rsin="123456789") mock_get_userinfo.return_value = { "sub": "some_username", "kvk": "12345678",