diff --git a/src/nrc/conf/includes/base.py b/src/nrc/conf/includes/base.py index fc9f4fd2..1d6ad442 100644 --- a/src/nrc/conf/includes/base.py +++ b/src/nrc/conf/includes/base.py @@ -132,10 +132,11 @@ # Django setup configuration # SETUP_CONFIGURATION_STEPS = [ - "nrc.config.site.SiteConfigurationStep", + "zgw_consumers.contrib.setup_configuration.steps.ServiceConfigurationStep", + "vng_api_common.contrib.setup_configuration.steps.JWTSecretsConfigurationStep", "nrc.config.authorization.AuthorizationStep", - "nrc.config.authorization.OpenZaakAuthStep", - "nrc.config.notification_retry.NotificationRetryConfigurationStep", + "notifications_api_common.contrib.setup_configuration.steps.NotificationConfigurationStep", + "nrc.config.site.SiteConfigurationStep", ] # @@ -158,38 +159,4 @@ # Open Notificaties settings # -# Settings for setup_configuration command -# sites config -SITES_CONFIG_ENABLE = config("SITES_CONFIG_ENABLE", default=False, add_to_docs=False) -OPENNOTIFICATIES_DOMAIN = config("OPENNOTIFICATIES_DOMAIN", "", add_to_docs=False) -OPENNOTIFICATIES_ORGANIZATION = config( - "OPENNOTIFICATIES_ORGANIZATION", "", add_to_docs=False -) -# notif -> OZ auth config -AUTHORIZATION_CONFIG_ENABLE = config( - "AUTHORIZATION_CONFIG_ENABLE", default=False, add_to_docs=False -) -AUTORISATIES_API_ROOT = config("AUTORISATIES_API_ROOT", "", add_to_docs=False) -NOTIF_OPENZAAK_CLIENT_ID = config("NOTIF_OPENZAAK_CLIENT_ID", "", add_to_docs=False) -NOTIF_OPENZAAK_SECRET = config("NOTIF_OPENZAAK_SECRET", "", add_to_docs=False) -# OZ -> notif config -OPENZAAK_NOTIF_CONFIG_ENABLE = config( - "OPENZAAK_NOTIF_CONFIG_ENABLE", default=False, add_to_docs=False -) -OPENZAAK_NOTIF_CLIENT_ID = config("OPENZAAK_NOTIF_CLIENT_ID", "", add_to_docs=False) -OPENZAAK_NOTIF_SECRET = config("OPENZAAK_NOTIF_SECRET", "", add_to_docs=False) - -# setup configuration for Notification retry -# Retry settings for delivering notifications to subscriptions -NOTIFICATION_RETRY_CONFIG_ENABLE = config( - "NOTIFICATION_RETRY_CONFIG_ENABLE", default=False, add_to_docs=False -) -NOTIFICATION_DELIVERY_MAX_RETRIES = config( - "NOTIFICATION_DELIVERY_MAX_RETRIES", None, add_to_docs=False -) -NOTIFICATION_DELIVERY_RETRY_BACKOFF = config( - "NOTIFICATION_DELIVERY_RETRY_BACKOFF", None, add_to_docs=False -) -NOTIFICATION_DELIVERY_RETRY_BACKOFF_MAX = config( - "NOTIFICATION_DELIVERY_RETRY_BACKOFF_MAX", None, add_to_docs=False -) +OPENNOTIFICATIES_DOMAIN = config("OPENNOTIFICATIES_DOMAIN", "") diff --git a/src/nrc/config/authorization.py b/src/nrc/config/authorization.py index 0537f4c9..cea0d93b 100644 --- a/src/nrc/config/authorization.py +++ b/src/nrc/config/authorization.py @@ -1,38 +1,24 @@ # SPDX-License-Identifier: EUPL-1.2 # Copyright (C) 2022 Dimpact -from typing import Iterable - -from django.conf import settings -from django.urls import reverse - -import requests 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.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" +from .models import AuthorizationsConfigModel - 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 +def get_service(slug: str) -> Service: + """ + Try to find a Service and re-raise DoesNotExist with the identifier to make debugging + easier + """ + try: + return Service.objects.get(slug=slug) + except Service.DoesNotExist as e: + raise Service.DoesNotExist(f"{str(e)} (identifier = {slug})") -class AuthorizationStep(BaseConfigurationStep): +class AuthorizationStep(BaseConfigurationStep[AuthorizationsConfigModel]): """ Open Notificaties uses Autorisaties API to check permissions of the clients. @@ -43,119 +29,17 @@ class AuthorizationStep(BaseConfigurationStep): If the client_id or secret is changed, run this command with 'overwrite' flag """ - verbose_name = "Authorization Configuration" - required_settings = [ - "AUTORISATIES_API_ROOT", - "NOTIF_OPENZAAK_CLIENT_ID", - "NOTIF_OPENZAAK_SECRET", - ] - enable_setting = "AUTHORIZATION_CONFIG_ENABLE" + verbose_name = "Configuration for Autorisaties API" + config_model = AuthorizationsConfigModel + namespace = "autorisaties_api" + enable_setting = "autorisaties_api_config_enable" - def is_configured(self) -> bool: - auth_config = AuthorizationsConfig.get_solo() - service = auth_config.authorizations_api_service - - if not service: - return False - - return service.api_root == settings.AUTORISATIES_API_ROOT - - def configure(self) -> None: - # Step 1 + def execute(self, model: AuthorizationsConfigModel) -> None: auth_config = AuthorizationsConfig.get_solo() if auth_config.component != ComponentTypes.nrc: auth_config.component = ComponentTypes.nrc - # Step 2 - organization = ( - settings.OPENNOTIFICATIES_ORGANIZATION or settings.NOTIF_OPENZAAK_CLIENT_ID - ) - - service, _ = Service.objects.update_or_create( - api_root=settings.AUTORISATIES_API_ROOT, - 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",)) - + service = get_service(model.authorizations_api_service_identifier) 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: - 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." - ) from exc - - -class OpenZaakAuthStep(BaseConfigurationStep): - """ - Configure credentials for Open Zaak to request Open Notificaties - This step takes care only of Open Zaak authentication. Permissions should be - set up in the Autorisaties component of the Open Zaak itself. - - Normal mode doesn't change the secret after its initial creation. - If the secret is changed, run this command with 'overwrite' flag - """ - - verbose_name = "Open Zaak Authentication Configuration" - required_settings = [ - "OPENZAAK_NOTIF_CLIENT_ID", - "OPENZAAK_NOTIF_SECRET", - ] - enable_setting = "OPENZAAK_NOTIF_CONFIG_ENABLE" - - def is_configured(self) -> bool: - return JWTSecret.objects.filter( - identifier=settings.OPENZAAK_NOTIF_CLIENT_ID - ).exists() - - def configure(self): - jwt_secret, created = JWTSecret.objects.get_or_create( - identifier=settings.OPENZAAK_NOTIF_CLIENT_ID, - defaults={"secret": settings.OPENZAAK_NOTIF_SECRET}, - ) - if jwt_secret.secret != settings.OPENZAAK_NOTIF_SECRET: - jwt_secret.secret = settings.OPENZAAK_NOTIF_SECRET - jwt_secret.save(update_fields=["secret"]) - - def test_configuration(self): - """ - This check depends on the configuration of permissions in Open Zaak - """ - endpoint = reverse("kanaal-list", kwargs={"version": "1"}) - full_url = build_absolute_url(endpoint, request=None) - token = generate_jwt( - settings.OPENZAAK_NOTIF_CLIENT_ID, settings.OPENZAAK_NOTIF_SECRET, "", "" - ) - - try: - response = requests.get( - full_url, headers={"Authorization": token, "Accept": "application/json"} - ) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed( - f"Could not list kanalen for {settings.NOTIF_OPENZAAK_CLIENT_ID}" - ) from exc diff --git a/src/nrc/config/models.py b/src/nrc/config/models.py new file mode 100644 index 00000000..6aa6e6d5 --- /dev/null +++ b/src/nrc/config/models.py @@ -0,0 +1,20 @@ +from django.contrib.sites.models import Site + +from django_setup_configuration.fields import DjangoModelRef +from django_setup_configuration.models import ConfigurationModel +from pydantic import Field +from vng_api_common.authorizations.models import AuthorizationsConfig + + +class AuthorizationsConfigModel(ConfigurationModel): + authorizations_api_service_identifier: str = DjangoModelRef( + AuthorizationsConfig, "authorizations_api_service" + ) + + +class SiteConfigModel(ConfigurationModel): + organization: str = Field() + """The name of the organization that owns this Open Notificaties instance""" + + class Meta: + django_model_refs = {Site: ("domain",)} diff --git a/src/nrc/config/notification_retry.py b/src/nrc/config/notification_retry.py deleted file mode 100644 index 15b3e3de..00000000 --- a/src/nrc/config/notification_retry.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.conf import settings - -from django_setup_configuration.configuration import BaseConfigurationStep -from notifications_api_common.models import NotificationsConfig - - -class NotificationRetryConfigurationStep(BaseConfigurationStep): - """ - Configure the notifications retry behaviour. - """ - - verbose_name = "Notification retry configuration" - required_settings = [] - optional_settings = [ - "NOTIFICATION_DELIVERY_MAX_RETRIES", - "NOTIFICATION_DELIVERY_RETRY_BACKOFF", - "NOTIFICATION_DELIVERY_RETRY_BACKOFF_MAX", - ] - enable_setting = "NOTIFICATION_RETRY_CONFIG_ENABLE" - - def is_configured(self) -> bool: - config = NotificationsConfig.get_solo() - for setting_name in self.optional_settings: - # It is considered configured if one or more fields have non-default values - model_field = getattr(NotificationsConfig, setting_name.lower()).field - if getattr(config, setting_name.lower()) != model_field.default: - return True - return False - - def configure(self): - config = NotificationsConfig.get_solo() - for setting_name in self.optional_settings: - if (setting_value := getattr(settings, setting_name)) is not None: - setattr(config, setting_name.lower(), setting_value) - config.save() - - def test_configuration(self): ... diff --git a/src/nrc/config/site.py b/src/nrc/config/site.py index b85e3fd5..e472982f 100644 --- a/src/nrc/config/site.py +++ b/src/nrc/config/site.py @@ -1,15 +1,11 @@ -from django.conf import settings from django.contrib.sites.models import Site -from django.urls import reverse -import requests from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed -from nrc.utils import build_absolute_url +from .models import SiteConfigModel -class SiteConfigurationStep(BaseConfigurationStep): +class SiteConfigurationStep(BaseConfigurationStep[SiteConfigModel]): """ Configure the application site/domain. @@ -17,25 +13,12 @@ class SiteConfigurationStep(BaseConfigurationStep): """ verbose_name = "Site Configuration" - required_settings = ["OPENNOTIFICATIES_DOMAIN", "OPENNOTIFICATIES_ORGANIZATION"] - enable_setting = "SITES_CONFIG_ENABLE" + config_model = SiteConfigModel + namespace = "site_config" + enable_setting = "site_config_enable" - def is_configured(self) -> bool: + def execute(self, model: SiteConfigModel) -> None: site = Site.objects.get_current() - return site.domain == settings.OPENNOTIFICATIES_DOMAIN - - def configure(self): - site = Site.objects.get_current() - site.domain = settings.OPENNOTIFICATIES_DOMAIN - site.name = ( - f"Open Notificaties {settings.OPENNOTIFICATIES_ORGANIZATION}".strip() - ) + site.domain = model.domain + site.name = f"Open Notificaties {model.organization}".strip() site.save() - - def test_configuration(self): - full_url = build_absolute_url(reverse("home")) - try: - response = requests.get(full_url) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc