From fa08b63c9f269e484daa3651c6ca1ae9c91ec54b Mon Sep 17 00:00:00 2001 From: SonnyBA Date: Fri, 29 Nov 2024 15:48:20 +0100 Subject: [PATCH] [#66] update zgw consumers (#196) --------- Co-authored-by: Steven Bal --- CHANGELOG.rst | 14 ++++ .../configuration/opennotifs_config.rst | 20 ++--- requirements/base.in | 3 - requirements/base.txt | 23 +++--- requirements/ci.txt | 32 ++++---- requirements/dev.txt | 32 ++++---- requirements/test-tools.in | 2 + requirements/testing.in | 8 -- src/nrc/api/admin.py | 8 +- src/nrc/api/tests/test_abonnement.py | 5 +- src/nrc/api/tests/test_kanaal.py | 5 +- src/nrc/api/tests/test_notificatie.py | 1 - src/nrc/api/tests/test_validation.py | 22 +----- src/nrc/conf/includes/base.py | 2 +- src/nrc/config/authorization.py | 78 +++++++++++++------ .../management/commands/migrate_domains.py | 6 +- src/nrc/fixtures/default_admin_index.json | 4 +- .../commands/test_setup_configuration.py | 18 ++--- .../test_authorization_configuration.py | 31 ++++---- .../tests/test_handle_auth_notifications.py | 27 +++++-- 20 files changed, 184 insertions(+), 157 deletions(-) delete mode 100644 requirements/testing.in diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 11870338..86e73877 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,20 @@ Changes ======= +1.x.x (TBD) +----------- +**Bugfixes and QOL** + +* [#66] updated commonground-api-common to 2.1.0 (TBD) + +.. warning:: + + Configuring external services is now done through the `Service` model. This + replaces the `APICredential` model in the admin interface. A data migration + was added to move to the `Service` model. It is advised to verify the `Service` + instances in the admin to check that the data migration was ran as expected. + + 1.7.1 (2024-10-04) ------------------ diff --git a/docs/installation/configuration/opennotifs_config.rst b/docs/installation/configuration/opennotifs_config.rst index 5ad23a87..c7040964 100644 --- a/docs/installation/configuration/opennotifs_config.rst +++ b/docs/installation/configuration/opennotifs_config.rst @@ -36,17 +36,19 @@ using Open Zaak as the example. 1. Configure the credentials for the Open Zaak Autorisaties API (so Open Notificaties can access the Autorisaties API): - a. Navigate to **API Autorisaties > Externe API credentials** - b. Click **Externe API credential toevoegen**. + a. Navigate to **API Autorisaties > Services** + b. Click **Service toevoegen**. c. Fill out the form: - - **API root**: *The URL of the Open Zaak Autorisaties API endpoint* + - **Api root url**: *The URL of the Open Zaak Autorisaties API endpoint* - **Label**: *For example:* ``Open Zaak Autorisaties`` - - **Client ID**: *For example:* ``open-notificaties`` + - **Client id**: *For example:* ``open-notificaties`` - **Secret**: *Some random string* - - **User ID**: *Same as the Client ID* - - **User representation**: *For example:* ``Open Notificaties`` + - **Authorization type**: *ZGW client_id + secret* + - **Gebruikers ID**: *Same as the Client ID* + - **Gebruikersweergave**: *For example:* ``Open Notificaties`` + - **Service slug**: *For example:* ``authorization-api-service`` Make sure Open Notificaties is authorized in Open Zaak to access the Autorisaties API by using the same Client ID and Secret as given here. @@ -56,8 +58,8 @@ using Open Zaak as the example. 2. Then we need to allow Open Zaak to access Open Notificaties (for authentication purposes, so we can then check its authorizations): - a. Navigate to **API Autorisaties > Client credentials** - b. Click **Client credential toevoegen**. + a. Navigate to **API Autorisaties > Autorisatiegegevens** + b. Click **Autorisatiegegeven toevoegen**. c. Fill out the form: - **Client ID**: *For example:* ``open-zaak`` @@ -121,4 +123,4 @@ All done! .. _`documentation of Open Zaak`: https://open-zaak.readthedocs.io/en/latest/installation/config/openzaak_config.html#configure-notificaties-api .. _`configuration of Open Zaak`: https://open-zaak.readthedocs.io/en/stable/installation/config/openzaak_config_cli.html#open-zaak-configuration .. _`manual`: https://open-notificaties.readthedocs.io/en/stable/manual/subscriptions.html#aanmaken-abonnement -.. _`API specification`: https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-zaak/open-notificaties/1.0.0/src/openapi.yaml#tag/notificaties \ No newline at end of file +.. _`API specification`: https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/open-zaak/open-notificaties/1.0.0/src/openapi.yaml#tag/notificaties diff --git a/requirements/base.in b/requirements/base.in index 624d7683..eb5f3665 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -4,6 +4,3 @@ open-api-framework markdown self-certifi furl - -# API libraries -gemma-zds-client diff --git a/requirements/base.txt b/requirements/base.txt index e7a8d9b4..0fd5c195 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -7,7 +7,10 @@ amqp==5.2.0 # via kombu ape-pie==0.2.0 - # via zgw-consumers + # via + # commonground-api-common + # notifications-api-common + # zgw-consumers asgiref==3.8.1 # via # django @@ -59,7 +62,7 @@ click-plugins==1.1.1 # via celery click-repl==0.3.0 # via celery -commonground-api-common==1.13.2 +commonground-api-common==2.1.0 # via open-api-framework coreapi==2.3.3 # via commonground-api-common @@ -201,11 +204,6 @@ furl==2.1.3 # via # -r requirements/base.in # ape-pie -gemma-zds-client==1.0.1 - # via - # -r requirements/base.in - # commonground-api-common - # notifications-api-common glom==23.5.0 # via mozilla-django-oidc-db humanize==4.10.0 @@ -242,7 +240,7 @@ mozilla-django-oidc==4.0.1 # via mozilla-django-oidc-db mozilla-django-oidc-db==0.19.0 # via open-api-framework -notifications-api-common==0.2.2 +notifications-api-common==0.3.1 # via commonground-api-common open-api-framework==0.8.1 # via -r requirements/base.in @@ -265,7 +263,6 @@ pycparser==2.22 pyjwt==2.9.0 # via # commonground-api-common - # gemma-zds-client # zgw-consumers pyopenssl==24.2.1 # via @@ -289,7 +286,6 @@ pyyaml==6.0.2 # via # drf-spectacular # drf-yasg - # gemma-zds-client # oyaml qrcode==7.4.2 # via django-two-factor-auth @@ -305,10 +301,12 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests - # gemma-zds-client # mozilla-django-oidc # open-api-framework + # requests-mock # zgw-consumers +requests-mock==1.12.1 + # via commonground-api-common rpds-py==0.20.0 # via # jsonschema @@ -362,7 +360,8 @@ webencodings==0.5.1 # via bleach wrapt==1.16.0 # via elastic-apm -zgw-consumers==0.34.0 +zgw-consumers==0.35.1 # via + # commonground-api-common # notifications-api-common # open-api-framework diff --git a/requirements/ci.txt b/requirements/ci.txt index 27cb8488..093b6678 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -13,6 +13,8 @@ amqp==5.2.0 ape-pie==0.2.0 # via # -r requirements/base.txt + # commonground-api-common + # notifications-api-common # zgw-consumers asgiref==3.8.1 # via @@ -100,9 +102,10 @@ click-repl==0.3.0 # celery codecov==2.1.13 # via -r requirements/ci.in -commonground-api-common==1.13.2 +commonground-api-common[testutils]==2.1.0 # via # -r requirements/base.txt + # -r requirements/test-tools.in # open-api-framework commonmark==0.9.1 # via recommonmark @@ -163,6 +166,7 @@ django==4.2.15 # notifications-api-common # open-api-framework # zgw-consumers + # zgw-consumers-oas django-admin-index==3.1.1 # via # -r requirements/base.txt @@ -324,7 +328,9 @@ face==20.1.1 factory-boy==3.2.1 # via -r requirements/test-tools.in faker==13.15.1 - # via factory-boy + # via + # factory-boy + # zgw-consumers-oas flake8==7.0.0 # via -r requirements/test-tools.in flower==2.0.1 @@ -337,11 +343,6 @@ furl==2.1.3 # via # -r requirements/base.txt # ape-pie -gemma-zds-client==1.0.1 - # via - # -r requirements/base.txt - # commonground-api-common - # notifications-api-common glom==23.5.0 # via # -r requirements/base.txt @@ -425,7 +426,7 @@ multidict==6.0.5 # via yarl mypy-extensions==0.4.3 # via black -notifications-api-common==0.2.2 +notifications-api-common==0.3.1 # via # -r requirements/base.txt # commonground-api-common @@ -484,7 +485,6 @@ pyjwt==2.9.0 # via # -r requirements/base.txt # commonground-api-common - # gemma-zds-client # zgw-consumers pyopenssl==24.2.1 # via @@ -524,9 +524,9 @@ pyyaml==6.0.2 # -r requirements/base.txt # drf-spectacular # drf-yasg - # gemma-zds-client # oyaml # vcrpy + # zgw-consumers-oas qrcode==7.4.2 # via # -r requirements/base.txt @@ -550,14 +550,16 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests - # gemma-zds-client # mozilla-django-oidc # open-api-framework # requests-mock # sphinx # zgw-consumers requests-mock==1.12.1 - # via -r requirements/test-tools.in + # via + # -r requirements/base.txt + # -r requirements/test-tools.in + # commonground-api-common rpds-py==0.20.0 # via # -r requirements/base.txt @@ -630,6 +632,7 @@ typing-extensions==4.12.2 # mozilla-django-oidc-db # qrcode # zgw-consumers + # zgw-consumers-oas tzdata==2024.1 # via # -r requirements/base.txt @@ -683,8 +686,11 @@ wrapt==1.16.0 # vcrpy yarl==1.9.4 # via vcrpy -zgw-consumers==0.34.0 +zgw-consumers==0.35.1 # via # -r requirements/base.txt + # commonground-api-common # notifications-api-common # open-api-framework +zgw-consumers-oas==1.0.0 + # via commonground-api-common diff --git a/requirements/dev.txt b/requirements/dev.txt index 91971b6f..5cad684b 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -13,6 +13,8 @@ amqp==5.2.0 ape-pie==0.2.0 # via # -r requirements/base.txt + # commonground-api-common + # notifications-api-common # zgw-consumers asgiref==3.8.1 # via @@ -103,9 +105,10 @@ click-repl==0.3.0 # via # -r requirements/base.txt # celery -commonground-api-common==1.13.2 +commonground-api-common[testutils]==2.1.0 # via # -r requirements/base.txt + # -r requirements/test-tools.in # open-api-framework commonmark==0.9.1 # via recommonmark @@ -166,6 +169,7 @@ django==4.2.15 # notifications-api-common # open-api-framework # zgw-consumers + # zgw-consumers-oas django-admin-index==3.1.1 # via # -r requirements/base.txt @@ -329,7 +333,9 @@ face==20.1.1 factory-boy==3.2.1 # via -r requirements/test-tools.in faker==13.15.1 - # via factory-boy + # via + # factory-boy + # zgw-consumers-oas flake8==7.0.0 # via -r requirements/test-tools.in flower==2.0.1 @@ -342,11 +348,6 @@ furl==2.1.3 # via # -r requirements/base.txt # ape-pie -gemma-zds-client==1.0.1 - # via - # -r requirements/base.txt - # commonground-api-common - # notifications-api-common gitdb==4.0.9 # via gitpython gitpython==3.1.41 @@ -432,7 +433,7 @@ multidict==6.0.5 # via yarl mypy-extensions==0.4.3 # via black -notifications-api-common==0.2.2 +notifications-api-common==0.3.1 # via # -r requirements/base.txt # commonground-api-common @@ -491,7 +492,6 @@ pyjwt==2.9.0 # via # -r requirements/base.txt # commonground-api-common - # gemma-zds-client # zgw-consumers pyopenssl==24.2.1 # via @@ -531,9 +531,9 @@ pyyaml==6.0.2 # -r requirements/base.txt # drf-spectacular # drf-yasg - # gemma-zds-client # oyaml # vcrpy + # zgw-consumers-oas qrcode==7.4.2 # via # -r requirements/base.txt @@ -556,14 +556,16 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests - # gemma-zds-client # mozilla-django-oidc # open-api-framework # requests-mock # sphinx # zgw-consumers requests-mock==1.12.1 - # via -r requirements/test-tools.in + # via + # -r requirements/base.txt + # -r requirements/test-tools.in + # commonground-api-common rpds-py==0.20.0 # via # -r requirements/base.txt @@ -637,6 +639,7 @@ typing-extensions==4.12.2 # mozilla-django-oidc-db # qrcode # zgw-consumers + # zgw-consumers-oas tzdata==2024.1 # via # -r requirements/base.txt @@ -692,11 +695,14 @@ wrapt==1.16.0 # vcrpy yarl==1.9.4 # via vcrpy -zgw-consumers==0.34.0 +zgw-consumers==0.35.1 # via # -r requirements/base.txt + # commonground-api-common # notifications-api-common # open-api-framework +zgw-consumers-oas==1.0.0 + # via commonground-api-common # The following packages are considered to be unsafe in a requirements file: # pip diff --git a/requirements/test-tools.in b/requirements/test-tools.in index abf63be7..6601f1d8 100644 --- a/requirements/test-tools.in +++ b/requirements/test-tools.in @@ -17,3 +17,5 @@ recommonmark Sphinx sphinx-rtd-theme sphinx-tabs + +commonground-api-common[testutils]>=2.0.1 diff --git a/requirements/testing.in b/requirements/testing.in deleted file mode 100644 index 4c8ae87f..00000000 --- a/requirements/testing.in +++ /dev/null @@ -1,8 +0,0 @@ -# Shared between dev and Travis requirements to run our tests -black -coverage -factory_boy -freezegun -isort -requests-mock -tblib diff --git a/src/nrc/api/admin.py b/src/nrc/api/admin.py index c1214fba..6f7455b2 100644 --- a/src/nrc/api/admin.py +++ b/src/nrc/api/admin.py @@ -2,8 +2,8 @@ from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ +from vng_api_common.authorizations.utils import generate_jwt from vng_api_common.models import JWTSecret -from zds_client import ClientAuth admin.site.unregister(JWTSecret) @@ -16,13 +16,11 @@ class JWTSecretAdmin(admin.ModelAdmin): readonly_fields = ("get_jwt",) @admin.display(description="jwt") - def get_jwt(self, obj): + def get_jwt(self, obj: JWTSecret): if obj.identifier and obj.secret: - auth = ClientAuth(obj.identifier, obj.secret) - jwt = auth.credentials()["Authorization"] return format_html( '{val}

{hint}

', - val=jwt, + val=generate_jwt(obj.identifier, obj.secret, "", ""), hint=_("Gebruik het JWT-token nooit direct in een applicatie."), ) return "" diff --git a/src/nrc/api/tests/test_abonnement.py b/src/nrc/api/tests/test_abonnement.py index a9fc2e51..f7db67aa 100644 --- a/src/nrc/api/tests/test_abonnement.py +++ b/src/nrc/api/tests/test_abonnement.py @@ -9,10 +9,7 @@ from nrc.datamodel.tests.factories import AbonnementFactory, KanaalFactory -@override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_200", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", -) +@override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_200") class AbonnementenTests(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True diff --git a/src/nrc/api/tests/test_kanaal.py b/src/nrc/api/tests/test_kanaal.py index e1612447..58250669 100644 --- a/src/nrc/api/tests/test_kanaal.py +++ b/src/nrc/api/tests/test_kanaal.py @@ -8,10 +8,7 @@ from nrc.datamodel.tests.factories import KanaalFactory -@override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_200", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", -) +@override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_200") class KanalenTests(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True diff --git a/src/nrc/api/tests/test_notificatie.py b/src/nrc/api/tests/test_notificatie.py index 998855f4..0476b61b 100644 --- a/src/nrc/api/tests/test_notificatie.py +++ b/src/nrc/api/tests/test_notificatie.py @@ -25,7 +25,6 @@ @patch("nrc.api.serializers.deliver_message.delay") @override_settings( LINK_FETCHER="vng_api_common.mocks.link_fetcher_200", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", LOG_NOTIFICATIONS_IN_DB=True, ) class NotificatieTests(JWTAuthMixin, APITestCase): diff --git a/src/nrc/api/tests/test_validation.py b/src/nrc/api/tests/test_validation.py index b95d0cb6..d39171be 100644 --- a/src/nrc/api/tests/test_validation.py +++ b/src/nrc/api/tests/test_validation.py @@ -15,10 +15,7 @@ class AbonnementenValidationTests(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True - @override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_404", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", - ) + @override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_404") def test_abonnementen_invalid_callback_url(self): KanaalFactory.create( naam="zaken", filters=["bron", "zaaktype", "vertrouwelijkheidaanduiding"] @@ -62,10 +59,7 @@ def test_abonnementen_invalid_callback_url(self): error = get_validation_errors(response, "nonFieldErrors") self.assertEqual(error["code"], "invalid-callback-url") - @override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_404", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", - ) + @override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_404") def test_abonnementen_callback_url_accept_20x_status_codes(self): KanaalFactory.create( naam="zaken", filters=["bron", "zaaktype", "vertrouwelijkheidaanduiding"] @@ -113,7 +107,6 @@ def test_abonnementen_callback_url_accept_20x_status_codes(self): @override_settings( LINK_FETCHER="vng_api_common.mocks.link_fetcher_404", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", TEST_CALLBACK_AUTH=True, ) def test_abonnementen_callback_url_no_auth(self): @@ -159,7 +152,6 @@ def test_abonnementen_callback_url_no_auth(self): @override_settings( LINK_FETCHER="vng_api_common.mocks.link_fetcher_404", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", TEST_CALLBACK_AUTH=True, ) def test_webhooksite_whitelisted(self): @@ -189,10 +181,7 @@ def test_webhooksite_whitelisted(self): class KanalenValidationTests(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True - @override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_404", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", - ) + @override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_404") def test_kanalen_invalid_documentatie_link_url(self): abonnement_create_url = get_operation_url("kanaal_create") @@ -212,10 +201,7 @@ class NotificatiesValidationTests(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True @freeze_time("2019-01-01T12:00:00Z") - @override_settings( - LINK_FETCHER="vng_api_common.mocks.link_fetcher_200", - ZDS_CLIENT_CLASS="vng_api_common.mocks.MockClient", - ) + @override_settings(LINK_FETCHER="vng_api_common.mocks.link_fetcher_200") @patch("jwt.api_jwt.PyJWT._validate_iat", return_value=None) def test_notificaties_aanmaakdatum_in_future_fails(self, mock_validate_iat): KanaalFactory.create(naam="zaken") diff --git a/src/nrc/conf/includes/base.py b/src/nrc/conf/includes/base.py index 01c47ac8..fc9f4fd2 100644 --- a/src/nrc/conf/includes/base.py +++ b/src/nrc/conf/includes/base.py @@ -27,7 +27,7 @@ MIDDLEWARE.insert( MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + 1, - "vng_api_common.middleware.AuthMiddleware", + "vng_api_common.authorizations.middleware.AuthMiddleware", ) MIDDLEWARE = MIDDLEWARE + ["vng_api_common.middleware.APIVersionHeaderMiddleware"] diff --git a/src/nrc/config/authorization.py b/src/nrc/config/authorization.py index e15f6c58..0537f4c9 100644 --- a/src/nrc/config/authorization.py +++ b/src/nrc/config/authorization.py @@ -1,5 +1,7 @@ # SPDX-License-Identifier: EUPL-1.2 # Copyright (C) 2022 Dimpact +from typing import Iterable + from django.conf import settings from django.urls import reverse @@ -7,12 +9,29 @@ from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import SelfTestFailed from vng_api_common.authorizations.models import AuthorizationsConfig, ComponentTypes -from vng_api_common.models import APICredential, JWTSecret -from zds_client import ClientAuth +from vng_api_common.authorizations.utils import generate_jwt +from vng_api_common.models import JWTSecret +from zgw_consumers.models import Service from nrc.utils import build_absolute_url +def _generate_service_slug(existing_slugs: Iterable[str]) -> str: + default_slug = "authorization-api-service" + + if not existing_slugs or default_slug not in existing_slugs: + return default_slug + + slug = default_slug + count = 1 + + while slug in existing_slugs: + count += 1 + slug = f"{default_slug}-{count}" + + return slug + + class AuthorizationStep(BaseConfigurationStep): """ Open Notificaties uses Autorisaties API to check permissions of the clients. @@ -33,45 +52,57 @@ class AuthorizationStep(BaseConfigurationStep): enable_setting = "AUTHORIZATION_CONFIG_ENABLE" def is_configured(self) -> bool: - credential = APICredential.objects.filter( - api_root=settings.AUTORISATIES_API_ROOT - ) auth_config = AuthorizationsConfig.get_solo() + service = auth_config.authorizations_api_service - return credential.exists() and bool( - auth_config.api_root == settings.AUTORISATIES_API_ROOT - ) + if not service: + return False + + return service.api_root == settings.AUTORISATIES_API_ROOT def configure(self) -> None: # Step 1 auth_config = AuthorizationsConfig.get_solo() - if auth_config.api_root != settings.AUTORISATIES_API_ROOT: - auth_config.api_root = settings.AUTORISATIES_API_ROOT + + if auth_config.component != ComponentTypes.nrc: auth_config.component = ComponentTypes.nrc - auth_config.save(update_fields=["api_root", "component"]) # Step 2 organization = ( settings.OPENNOTIFICATIES_ORGANIZATION or settings.NOTIF_OPENZAAK_CLIENT_ID ) - APICredential.objects.update_or_create( + + service, _ = Service.objects.update_or_create( api_root=settings.AUTORISATIES_API_ROOT, - defaults={ - "label": "Open Zaak Autorisaties API", - "client_id": settings.NOTIF_OPENZAAK_CLIENT_ID, - "secret": settings.NOTIF_OPENZAAK_SECRET, - "user_id": settings.NOTIF_OPENZAAK_CLIENT_ID, - "user_representation": f"Open Notificaties {organization}", - }, + defaults=dict( + label="Open Zaak Autorisaties API", + client_id=settings.NOTIF_OPENZAAK_CLIENT_ID, + secret=settings.NOTIF_OPENZAAK_SECRET, + user_id=settings.NOTIF_OPENZAAK_CLIENT_ID, + user_representation=f"Open Notificaties {organization}", + ), ) + if not service.slug: + slugs = Service.objects.values_list("slug", flat=True) + service.slug = _generate_service_slug(slugs) + service.save(update_fields=("slug",)) + + auth_config.authorizations_api_service = service + auth_config.save(update_fields=("component", "authorizations_api_service")) + def test_configuration(self) -> None: """ This check depends on the configuration of permissions in Open Zaak """ client = AuthorizationsConfig.get_client() + + if not client: + raise SelfTestFailed("No service configured for the Autorisaties API") + try: - client.list("applicatie") + response: requests.Response = client.get("applicaties") + response.raise_for_status() except requests.RequestException as exc: raise SelfTestFailed( "Could not retrieve list of applications from Autorisaties API." @@ -115,14 +146,13 @@ def test_configuration(self): """ endpoint = reverse("kanaal-list", kwargs={"version": "1"}) full_url = build_absolute_url(endpoint, request=None) - auth = ClientAuth( - client_id=settings.OPENZAAK_NOTIF_CLIENT_ID, - secret=settings.OPENZAAK_NOTIF_SECRET, + token = generate_jwt( + settings.OPENZAAK_NOTIF_CLIENT_ID, settings.OPENZAAK_NOTIF_SECRET, "", "" ) try: response = requests.get( - full_url, headers={**auth.credentials(), "Accept": "application/json"} + full_url, headers={"Authorization": token, "Accept": "application/json"} ) response.raise_for_status() except requests.RequestException as exc: diff --git a/src/nrc/datamodel/management/commands/migrate_domains.py b/src/nrc/datamodel/management/commands/migrate_domains.py index 41143ef8..2ac7ca1f 100644 --- a/src/nrc/datamodel/management/commands/migrate_domains.py +++ b/src/nrc/datamodel/management/commands/migrate_domains.py @@ -25,11 +25,7 @@ def _base_mapping(variable: tuple) -> tuple: ) -BASE_MAPPING = ( - _base_mapping(("vng_api_common.APICredential", "api_root")) - + _base_mapping(("authorizations.AuthorizationsConfig", "api_root")) - + _base_mapping(("notifications.NotificationsConfig", "api_root")) -) +BASE_MAPPING = _base_mapping(("zgw_consumers.Service", "api_root")) MAPPING = BASE_MAPPING + ( ("datamodel.Kanaal", "documentatie_link", *ZRC), diff --git a/src/nrc/fixtures/default_admin_index.json b/src/nrc/fixtures/default_admin_index.json index 5f44c14c..82e61102 100644 --- a/src/nrc/fixtures/default_admin_index.json +++ b/src/nrc/fixtures/default_admin_index.json @@ -37,8 +37,8 @@ "applicatie" ], [ - "vng_api_common", - "apicredential" + "zgw_consumers", + "service" ], [ "vng_api_common", diff --git a/src/nrc/tests/commands/test_setup_configuration.py b/src/nrc/tests/commands/test_setup_configuration.py index 4c43b9e5..d087bdb9 100644 --- a/src/nrc/tests/commands/test_setup_configuration.py +++ b/src/nrc/tests/commands/test_setup_configuration.py @@ -11,9 +11,7 @@ from jwt import decode from rest_framework import status from vng_api_common.authorizations.models import AuthorizationsConfig -from zds_client.auth import ClientAuth -from zgw_consumers.constants import APITypes -from zgw_consumers.test import mock_service_oas_get +from vng_api_common.authorizations.utils import generate_jwt from nrc.config.authorization import AuthorizationStep, OpenZaakAuthStep from nrc.config.notification_retry import NotificationRetryConfigurationStep @@ -48,9 +46,6 @@ def test_setup_configuration(self, m): _uuid = uuid.uuid4() m.get("http://open-notificaties.example.com/", status_code=200) m.get("http://open-notificaties.example.com/api/v1/kanaal", json=[]) - mock_service_oas_get( - m, "https://oz.example.com/autorisaties/api/v1/", APITypes.ac - ) m.get( "https://oz.example.com/autorisaties/api/v1/applicaties", json={ @@ -96,7 +91,7 @@ def test_setup_configuration(self, m): ac_client = AuthorizationsConfig.get_client() self.assertIsNotNone(ac_client) - ac_client.list("applicatie") + ac_client.get("applicaties") create_call = m.last_request self.assertEqual( @@ -104,16 +99,18 @@ def test_setup_configuration(self, m): "https://oz.example.com/autorisaties/api/v1/applicaties", ) self.assertIn("Authorization", create_call.headers) + header_jwt = create_call.headers["Authorization"].split(" ")[1] decoded_jwt = decode(header_jwt, options={"verify_signature": False}) + self.assertEqual(decoded_jwt["client_id"], "notif-client-id") with self.subTest("Open Zaak can query Notification API"): - auth = ClientAuth("oz-client-id", "oz-secret") + token = generate_jwt("oz-client-id", "oz-secret", "", "") response = self.client.get( reverse("kanaal-list", kwargs={"version": 1}), - HTTP_AUTHORIZATION=auth.credentials()["Authorization"], + HTTP_AUTHORIZATION=token, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -122,9 +119,6 @@ def test_setup_configuration(self, m): def test_setup_configuration_selftest_fails(self, m): m.get("http://open-notificaties.example.com/", exc=requests.ConnectionError) m.get("http://open-notificaties.example.com/api/v1/kanaal", json=[]) - mock_service_oas_get( - m, "https://oz.example.com/autorisaties/api/v1/", APITypes.ac - ) m.get("https://oz.example.com/autorisaties/api/v1/applicaties", json=[]) with self.assertRaisesMessage( diff --git a/src/nrc/tests/config/test_authorization_configuration.py b/src/nrc/tests/config/test_authorization_configuration.py index 31507365..dfa4fd48 100644 --- a/src/nrc/tests/config/test_authorization_configuration.py +++ b/src/nrc/tests/config/test_authorization_configuration.py @@ -5,10 +5,8 @@ import requests import requests_mock from django_setup_configuration.exceptions import SelfTestFailed -from vng_api_common.authorizations.models import AuthorizationsConfig -from vng_api_common.models import APICredential, JWTSecret -from zgw_consumers.constants import APITypes -from zgw_consumers.test import mock_service_oas_get +from vng_api_common.authorizations.models import AuthorizationsConfig, ComponentTypes +from vng_api_common.models import JWTSecret from nrc.config.authorization import AuthorizationStep, OpenZaakAuthStep @@ -25,21 +23,21 @@ def test_configure(self): configuration.configure() config = AuthorizationsConfig.get_solo() - self.assertEqual(config.api_root, "https://oz.example.com/autorisaties/api/v1/") + service = config.authorizations_api_service - api_credentials = APICredential.objects.get( - api_root="https://oz.example.com/autorisaties/api/v1/" + self.assertEqual(config.component, ComponentTypes.nrc) + self.assertEqual( + service.api_root, "https://oz.example.com/autorisaties/api/v1/" ) - self.assertEqual(api_credentials.client_id, "notif-client-id") - self.assertEqual(api_credentials.secret, "notif-secret") + + self.assertEqual(service.client_id, "notif-client-id") + self.assertEqual(service.secret, "notif-secret") @requests_mock.Mocker() def test_selftest_ok(self, m): configuration = AuthorizationStep() configuration.configure() - mock_service_oas_get( - m, "https://oz.example.com/autorisaties/api/v1/", APITypes.ac - ) + m.get("https://oz.example.com/autorisaties/api/v1/applicaties", json=[]) configuration.test_configuration() @@ -52,12 +50,11 @@ def test_selftest_ok(self, m): def test_selftest_fail(self, m): configuration = AuthorizationStep() configuration.configure() - mock_service_oas_get( - m, "https://oz.example.com/autorisaties/api/v1/", APITypes.ac - ) - m.get("https://oz.example.com/autorisaties/api/v1/applicaties", json=[]) - configuration.test_configuration() + m.get("https://oz.example.com/autorisaties/api/v1/applicaties", status_code=403) + + with self.assertRaises(SelfTestFailed): + configuration.test_configuration() self.assertEqual( m.last_request.url, "https://oz.example.com/autorisaties/api/v1/applicaties" diff --git a/src/nrc/tests/test_handle_auth_notifications.py b/src/nrc/tests/test_handle_auth_notifications.py index 66eb4e0b..be923cf7 100644 --- a/src/nrc/tests/test_handle_auth_notifications.py +++ b/src/nrc/tests/test_handle_auth_notifications.py @@ -8,17 +8,21 @@ from vng_api_common.authorizations.models import Applicatie, AuthorizationsConfig from vng_api_common.constants import CommonResourceAction, VertrouwelijkheidsAanduiding from vng_api_common.tests import JWTAuthMixin, reverse -from zgw_consumers.constants import APITypes -from zgw_consumers.test import mock_service_oas_get +from zgw_consumers.test.factories import ServiceFactory class HandleAuthNotifTestCase(JWTAuthMixin, APITestCase): heeft_alle_autorisaties = True def test_handle_create_auth(self): + service = ServiceFactory(api_root="https://autorisaties-api.vng.cloud/api/v1") config = AuthorizationsConfig.get_solo() + + config.authorizations_api_service = service + config.save(update_fields=("authorizations_api_service",)) + uuid = _uuid.uuid4() - applicatie_url = f"{config.api_root}/applicaties/{uuid}" + applicatie_url = f"{service.api_root}/applicaties/{uuid}" webhook_url = reverse("notificaties-webhook") response_data = { @@ -45,7 +49,6 @@ def test_handle_create_auth(self): } with requests_mock.Mocker() as m: - mock_service_oas_get(m, url=config.api_root, service=APITypes.ac) m.get(applicatie_url, json=response_data) response = self.client.post(webhook_url, data) @@ -62,8 +65,14 @@ def test_handle_update_auth(self): client_ids=["id1"], label="before", heeft_alle_autorisaties=True ) uuid = applicatie.uuid + + service = ServiceFactory(api_root="https://autorisaties-api.vng.cloud/api/v1") + config = AuthorizationsConfig.get_solo() - applicatie_url = f"{config.api_root}/applicaties/{uuid}" + config.authorizations_api_service = service + config.save(update_fields=("authorizations_api_service",)) + + applicatie_url = f"{service.api_root}/applicaties/{uuid}" self.assertEqual(applicatie.autorisaties.count(), 0) @@ -115,8 +124,14 @@ def test_handle_delete_auth(self): client_ids=["id1"], label="for delete", heeft_alle_autorisaties=True ) uuid = applicatie.uuid + + service = ServiceFactory(api_root="https://autorisaties-api.vng.cloud/api/v1") + config = AuthorizationsConfig.get_solo() - applicatie_url = f"{config.api_root}/applicaties/{uuid}" + config.authorizations_api_service = service + config.save(update_fields=("authorizations_api_service",)) + + applicatie_url = f"{service.api_root}/applicaties/{uuid}" webhook_url = reverse( "notificaties-webhook", kwargs={"version": settings.REST_FRAMEWORK["DEFAULT_VERSION"]},