diff --git a/digid_eherkenning/admin.py b/digid_eherkenning/admin.py index b3c5a99..aa7d3d4 100644 --- a/digid_eherkenning/admin.py +++ b/digid_eherkenning/admin.py @@ -1,6 +1,8 @@ from datetime import datetime from django.contrib import admin +from django.urls import reverse +from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ from privates.admin import PrivateMediaMixin @@ -8,6 +10,7 @@ from solo.admin import SingletonModelAdmin from .models import ConfigCertificate, DigidConfiguration, EherkenningConfiguration +from .models.base import BaseConfiguration class CustomPrivateFileWidget(PrivateFileWidget): @@ -18,14 +21,39 @@ class CustomPrivateMediaMixin(PrivateMediaMixin): private_media_file_widget = CustomPrivateFileWidget -@admin.register(DigidConfiguration) -class DigidConfigurationAdmin(CustomPrivateMediaMixin, SingletonModelAdmin): - readonly_fields = ("idp_service_entity_id",) - fieldsets = ( +class BaseAdmin(CustomPrivateMediaMixin, SingletonModelAdmin): + readonly_fields = ( + "link_to_certificates", + "idp_service_entity_id", + ) + private_media_fields = ("idp_metadata_file",) + + @admin.display(description=_("certificates")) + def link_to_certificates(self, obj: BaseConfiguration) -> str: + path = reverse( + "admin:digid_eherkenning_configcertificate_changelist", + current_app=self.admin_site.name, + ) + config_type = obj._as_config_type() + qs = ConfigCertificate.objects.filter(config_type=config_type) + url = f"{path}?config_type__exact={config_type}" + return format_html( + '{label}', + url=url, + config_type=config_type.value, + label=_("Manage ({count})").format(count=qs.count()), + ) + + +def _fieldset_factory(middle): + """ + Output custom fieldsets (model-specific) between fixed shared field(set)s. + """ + head = [ ( _("X.509 Certificate"), { - "fields": ("certificate",), + "fields": ("link_to_certificates",), }, ), ( @@ -52,18 +80,8 @@ class DigidConfigurationAdmin(CustomPrivateMediaMixin, SingletonModelAdmin): ), }, ), - ( - _("Service details"), - { - "fields": ( - "service_name", - "service_description", - "requested_attributes", - "attribute_consuming_service_index", - "slo", - ), - }, - ), + ] + tail = [ ( _("Organization details"), { @@ -75,99 +93,80 @@ class DigidConfigurationAdmin(CustomPrivateMediaMixin, SingletonModelAdmin): ), }, ), + ] + + return tuple(head + list(middle) + tail) + + +@admin.register(DigidConfiguration) +class DigidConfigurationAdmin(BaseAdmin): + fieldsets = _fieldset_factory( + [ + ( + _("Service details"), + { + "fields": ( + "service_name", + "service_description", + "requested_attributes", + "attribute_consuming_service_index", + "slo", + ), + }, + ), + ] ) change_form_template = "admin/digid_eherkenning/digidconfiguration/change_form.html" - private_media_fields = ("idp_metadata_file",) @admin.register(EherkenningConfiguration) -class EherkenningConfigurationAdmin(CustomPrivateMediaMixin, SingletonModelAdmin): - readonly_fields = ("idp_service_entity_id",) - fieldsets = ( - ( - _("X.509 Certificate"), - { - "fields": ("certificate",), - }, - ), - ( - _("Identity provider"), - { - "fields": ( - "metadata_file_source", - "idp_service_entity_id", - "idp_metadata_file", - ), - }, - ), - ( - _("SAML configuration"), - { - "fields": ( - "entity_id", - "base_url", - "artifact_resolve_content_type", - "want_assertions_signed", - "want_assertions_encrypted", - "signature_algorithm", - "digest_algorithm", - ), - }, - ), - ( - _("Service details"), - { - "fields": ( - "service_name", - "service_description", - "oin", - "makelaar_id", - "privacy_policy", - "service_language", - ), - }, - ), - ( - _("eHerkenning"), - { - "fields": ( - "eh_requested_attributes", - "eh_attribute_consuming_service_index", - "eh_service_uuid", - "eh_service_instance_uuid", - "eh_loa", - ), - }, - ), - ( - _("eIDAS"), - { - "fields": ( - "no_eidas", - "eidas_requested_attributes", - "eidas_attribute_consuming_service_index", - "eidas_service_uuid", - "eidas_service_instance_uuid", - "eidas_loa", - ), - }, - ), - ( - _("Organization details"), - { - "fields": ( - "technical_contact_person_telephone", - "technical_contact_person_email", - "organization_url", - "organization_name", - ), - }, - ), +class EherkenningConfigurationAdmin(BaseAdmin): + fieldsets = _fieldset_factory( + [ + ( + _("Service details"), + { + "fields": ( + "service_name", + "service_description", + "oin", + "makelaar_id", + "privacy_policy", + "service_language", + ), + }, + ), + ( + _("eHerkenning"), + { + "fields": ( + "eh_requested_attributes", + "eh_attribute_consuming_service_index", + "eh_service_uuid", + "eh_service_instance_uuid", + "eh_loa", + ), + }, + ), + ( + _("eIDAS"), + { + "fields": ( + "no_eidas", + "eidas_requested_attributes", + "eidas_attribute_consuming_service_index", + "eidas_service_uuid", + "eidas_service_instance_uuid", + "eidas_loa", + ), + }, + ), + ] ) + change_form_template = ( "admin/digid_eherkenning/eherkenningconfiguration/change_form.html" ) - private_media_fields = ("idp_metadata_file",) @admin.register(ConfigCertificate) diff --git a/digid_eherkenning/models/base.py b/digid_eherkenning/models/base.py index 3c0c20b..9848561 100644 --- a/digid_eherkenning/models/base.py +++ b/digid_eherkenning/models/base.py @@ -9,16 +9,15 @@ from privates.fields import PrivateMediaFileField from solo.models import SingletonModel -from ..choices import DigestAlgorithms, SignatureAlgorithms, XMLContentTypes +from ..choices import ( + ConfigTypes, + DigestAlgorithms, + SignatureAlgorithms, + XMLContentTypes, +) from .certificates import ConfigCertificate -class ConfigurationManager(models.Manager): - def get_queryset(self): - qs = super().get_queryset() - return qs.select_related("certificate") - - class BaseConfiguration(SingletonModel): idp_metadata_file = PrivateMediaFileField( _("identity provider metadata"), @@ -157,8 +156,6 @@ class BaseConfiguration(SingletonModel): max_length=100, ) - objects = ConfigurationManager() - class Meta: abstract = True @@ -234,4 +231,13 @@ def clean(self): # require that a certificate is configured if not ConfigCertificate.objects.for_config(self).exists(): - raise ValidationError(_("You must select a certificate")) + raise ValidationError( + _( + "You must prepare at least one certificate for the {verbose_name}." + ).format(verbose_name=self._meta.verbose_name) + ) + + @classmethod + def _as_config_type(cls) -> ConfigTypes: + opts = cls._meta + return ConfigTypes(f"{opts.app_label}.{opts.object_name}") diff --git a/digid_eherkenning/models/certificates.py b/digid_eherkenning/models/certificates.py index a881892..ab4d5c0 100644 --- a/digid_eherkenning/models/certificates.py +++ b/digid_eherkenning/models/certificates.py @@ -28,8 +28,7 @@ class ConfigCertificateQuerySet(models.QuerySet): def for_config(self, config: _AnyDigiD | _AnyEH): - opts = config._meta - config_type = ConfigTypes(f"{opts.app_label}.{opts.object_name}") + config_type = config._as_config_type() return self.filter(config_type=config_type)