Skip to content

Commit

Permalink
♻️ [#200] Refactor ConfigurationSteps for new setup-config version
Browse files Browse the repository at this point in the history
several steps have been moved to their respective libraries
  • Loading branch information
stevenbal committed Dec 6, 2024
1 parent ced8e54 commit f48ba98
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 233 deletions.
43 changes: 5 additions & 38 deletions src/nrc/conf/includes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

#
Expand All @@ -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", "")
150 changes: 17 additions & 133 deletions src/nrc/config/authorization.py
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
20 changes: 20 additions & 0 deletions src/nrc/config/models.py
Original file line number Diff line number Diff line change
@@ -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",)}
37 changes: 0 additions & 37 deletions src/nrc/config/notification_retry.py

This file was deleted.

33 changes: 8 additions & 25 deletions src/nrc/config/site.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
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.
**NOTE:** Site configuration will be depreciated
"""

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

0 comments on commit f48ba98

Please sign in to comment.