From 6ccb7ba01a8623d134491b1522f6d3c3e3fc9553 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Fri, 19 Jul 2024 11:10:24 +0200 Subject: [PATCH] :fire: Delete metadata generation management commands As these will need to be updated for the multi-certificate feature to be supported, it was checked if they are used in the first place. DevOps says it's not used, so removing them is far easier. The metadata itself is still accessible through URLs/views and the admin interface from each configuration page, it's just the CLI integration that is removed. --- CHANGELOG.rst | 13 + .../management/commands/_base.py | 226 ------------------ .../commands/generate_digid_metadata.py | 41 ---- .../generate_eherkenning_dienstcatalogus.py | 63 ----- .../commands/generate_eherkenning_metadata.py | 51 ---- 5 files changed, 13 insertions(+), 381 deletions(-) delete mode 100644 digid_eherkenning/management/commands/_base.py delete mode 100644 digid_eherkenning/management/commands/generate_digid_metadata.py delete mode 100644 digid_eherkenning/management/commands/generate_eherkenning_dienstcatalogus.py delete mode 100644 digid_eherkenning/management/commands/generate_eherkenning_metadata.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b1e4ed5..bee4c9b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,19 @@ Changelog ========= +0.17.0 (DEVELOPMENT) +==================== + +**💥⚠️ Breaking changes** + +* Removed the ``generate_digid_metadata``, ``generate_eherkenning_metadata`` and + ``generate_eherkenning_dienstcatalogus`` management commands. This metadata is + available through the admin interface and existing URLs/views. + +**Features** + +... + 0.16.0 (2024-07-02) =================== diff --git a/digid_eherkenning/management/commands/_base.py b/digid_eherkenning/management/commands/_base.py deleted file mode 100644 index 9aa7c14..0000000 --- a/digid_eherkenning/management/commands/_base.py +++ /dev/null @@ -1,226 +0,0 @@ -from pathlib import Path -from typing import Sequence, Type - -from django.core.files import File -from django.core.management.base import BaseCommand -from django.db import transaction - -from simple_certmanager.constants import CertificateTypes -from simple_certmanager.models import Certificate - -from ...models.base import BaseConfiguration - -try: - from argparse import BooleanOptionalAction -except ImportError: - from ..utils import BooleanOptionalAction - - -class SamlMetadataBaseCommand(BaseCommand): - config_model: Type[BaseConfiguration] - default_certificate_label: str - - def add_arguments(self, parser): - """ - Add arguments that map to configuration model fields. - - You can use a different flag, but then ensure the ``dest`` kwarg is specified. - Options are applied to the specified configuration model instance if a model - field with the same name as the option exists. - """ - # check current config to determine if an option is required or not - config = self._get_config() - has_private_key = config.certificate and config.certificate.private_key - has_certificate = config.certificate and config.certificate.public_certificate - - parser.add_argument( - "--want-assertions-encrypted", - action="store_true", - help="If True the XML assertions need to be encrypted. Defaults to False", - ) - parser.add_argument( - "--no-only-assertions-signed", - dest="want_assertions_signed", - action="store_false", - help=( - "If True, the XML assertions need to be signed, otherwise the whole " - "response needs to be signed. Defaults to only assertions signed." - ), - ) - parser.add_argument( - "--key-file", - required=not has_private_key, - help=( - "The filepath to the TLS key. This will be used both by the SOAP " - "client and for signing the requests." - ), - ) - parser.add_argument( - "--cert-file", - required=not has_certificate, - help=( - "The filepath to the TLS certificate. This will be used both by the " - "SOAP client and for signing the requests." - ), - ) - parser.add_argument( - "--key-passphrase", - help="Passphrase for SOAP client", - default=None, - ) - parser.add_argument( - "--signature-algorithm", - help="Signature algorithm, defaults to RSA_SHA1", - default="http://www.w3.org/2000/09/xmldsig#rsa-sha1", - ) - parser.add_argument( - "--digest-algorithm", - help="Digest algorithm, defaults to SHA1", - default="http://www.w3.org/2000/09/xmldsig#sha1", - ) - parser.add_argument( - "--entity-id", - required=not config.entity_id, - help="Service provider entity ID", - ) - parser.add_argument( - "--base-url", - required=not config.base_url, - help="Base URL of the application", - ) - parser.add_argument( - "--service-name", - required=not config.service_name, - help="The name of the service for which DigiD login is required", - ) - parser.add_argument( - "--service-description", - required=not config.service_description, - help="A description of the service for which DigiD login is required", - ) - parser.add_argument( - "--technical-contact-person-telephone", - help=( - "Telephone number of the technical person responsible for this DigiD " - "setup. For it to be used, --technical-contact-person-email should " - "also be set." - ), - ) - parser.add_argument( - "--technical-contact-person-email", - help=( - "Email address of the technical person responsible for this DigiD " - "setup. For it to be used, --technical-contact-person-telephone " - "should also be set." - ), - ) - parser.add_argument( - "--organization-name", - help=( - "Name of the organisation providing the service for which DigiD login " - "is setup. For it to be used, also --organization-url should be filled." - ), - ) - parser.add_argument( - "--organization-url", - help=( - "URL of the organisation providing the service for which DigiD login " - "is setup. For it to be used, also --organization-name should be " - "filled." - ), - ) - parser.add_argument( - "--output-file", - help=( - "Name of the file to which to write the metadata. Otherwise will be " - "printed on stdout" - ), - ) - parser.add_argument( - "--test", - "--debug", - action="store_true", - help="If True the metadata is printed to stdout. Defaults to False", - ) - parser.add_argument( - "--save-config", - action=BooleanOptionalAction, - required=True, - help="Save the configuration overrides specified via the command line.", - ) - - def get_filename(self) -> str: # pragma:nocover - raise NotImplementedError - - def generate_metadata(self, options: dict) -> bytes: # pragma:nocover - raise NotImplementedError - - def _get_config(self): - if not hasattr(self, "_config"): - self._config = self.config_model.get_solo() - return self._config - - def _set_certificate(self, config: BaseConfiguration, options: dict): - certificate = config.certificate - - # no certificate exists yet -> create one - if certificate is None: - certificate = Certificate.objects.create( - label=self.default_certificate_label, - type=CertificateTypes.key_pair, - ) - config.certificate = certificate - - # enforce that the specified key/certificate are used - for option, filefield in ( - ("key_file", "private_key"), - ("cert_file", "public_certificate"), - ): - filepath = options[option] - if not filepath: - continue - - path = Path(filepath) - with path.open("rb") as infile: - field_file = getattr(certificate, filefield) - field_file.save(path.name, File(infile), save=False) - - certificate.save() - - @transaction.atomic - def _generate_metadata(self, options: dict) -> bytes: - valid_field_names = [f.name for f in self.config_model._meta.get_fields()] - config = self._get_config() - - self._set_certificate(config, options) - - for key, value in options.items(): - if key not in valid_field_names: - continue - # optional, unspecified -> go with the model default or current value - if value is None: - continue - setattr(config, key, value) - - config.save() - - metadata = self.generate_metadata(options) - - transaction.set_rollback(not options["save_config"]) - - return metadata - - def handle(self, *args, **options): - metadata_content = self._generate_metadata(options) - - if options["test"]: - self.stdout.write(metadata_content.decode("utf-8")) - return - - filename = options["output_file"] or self.get_filename() - with open(filename, "xb") as metadata_file: - metadata_file.write(metadata_content) - - self.stdout.write( - self.style.SUCCESS("Metadata file successfully generated: %s" % filename) - ) diff --git a/digid_eherkenning/management/commands/generate_digid_metadata.py b/digid_eherkenning/management/commands/generate_digid_metadata.py deleted file mode 100644 index 3353031..0000000 --- a/digid_eherkenning/management/commands/generate_digid_metadata.py +++ /dev/null @@ -1,41 +0,0 @@ -from django.utils import timezone - -from ...models import DigidConfiguration -from ...saml2.digid import generate_digid_metadata -from ._base import SamlMetadataBaseCommand - -try: - from argparse import BooleanOptionalAction -except ImportError: - from ..utils import BooleanOptionalAction - - -class Command(SamlMetadataBaseCommand): - help = "Create the DigiD metadata file" - config_model = DigidConfiguration - default_certificate_label = "DigiD" - - def add_arguments(self, parser): - super().add_arguments(parser) - - config: DigidConfiguration = self._get_config() - - parser.add_argument( - "--slo", - default=config.slo, - action=BooleanOptionalAction, - help="If '--slo' is present, Single Logout is supported. To turn it off use '--no-slo'", - ) - parser.add_argument( - "--attribute-consuming-service-index", - type=str, - help="Attribute consuming service index, defaults to 1", - default="1", - ) - - def get_filename(self): - date_string = timezone.now().date().isoformat() - return f"digid-metadata-{date_string}.xml" - - def generate_metadata(self, options): - return generate_digid_metadata() diff --git a/digid_eherkenning/management/commands/generate_eherkenning_dienstcatalogus.py b/digid_eherkenning/management/commands/generate_eherkenning_dienstcatalogus.py deleted file mode 100644 index 60a7a29..0000000 --- a/digid_eherkenning/management/commands/generate_eherkenning_dienstcatalogus.py +++ /dev/null @@ -1,63 +0,0 @@ -from django.utils import timezone - -from ...models import EherkenningConfiguration -from ...saml2.eherkenning import generate_dienst_catalogus_metadata -from .generate_eherkenning_metadata import Command as EherkenningCommand - - -def _remove_action_by_dest(parser, dest: str): - for action in parser._actions: - if action.dest != dest: - continue - parser._remove_action(action) - break - - for action in parser._action_groups: - for group_action in action._group_actions: - if group_action.dest != dest: - continue - action._group_actions.remove(group_action) - return - - -class Command(EherkenningCommand): - help = "Create the eHerkenning dienstcatalogus file" - - def add_arguments(self, parser): - super().add_arguments(parser) - - # delete arguments that we don't use - dests_to_delete = [ - "want_assertions_encrypted", - "want_assertions_signed", - "technical_contact_person_telephone", - "technical_contact_person_email", - "organization_url", - ] - # remove actions not relevant for this command, but still re-use the bulk - # from the eherkenning metadata generation command - for dest in dests_to_delete: - _remove_action_by_dest(parser, dest) - - config: EherkenningConfiguration = self._get_config() - - parser.add_argument( - "--privacy-policy", - required=not config.privacy_policy, - help=( - "The URL where the privacy policy from the organisation providing the " - "service can be found." - ), - ) - parser.add_argument( - "--makelaar-id", - required=not config.makelaar_id, - help="OIN of the broker used to set up eHerkenning/eIDAS.", - ) - - def get_filename(self): - date_string = timezone.now().date().isoformat() - return f"eherkenning-dienstcatalogus-{date_string}.xml" - - def generate_metadata(self, options): - return generate_dienst_catalogus_metadata() diff --git a/digid_eherkenning/management/commands/generate_eherkenning_metadata.py b/digid_eherkenning/management/commands/generate_eherkenning_metadata.py deleted file mode 100644 index 9995ed7..0000000 --- a/digid_eherkenning/management/commands/generate_eherkenning_metadata.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.utils import timezone - -from ...models import EherkenningConfiguration -from ...saml2.eherkenning import generate_eherkenning_metadata -from ._base import SamlMetadataBaseCommand - - -class Command(SamlMetadataBaseCommand): - help = "Create the eHerkenning metadata file" - config_model = EherkenningConfiguration - default_certificate_label = "eHerkenning/eIDAS" - - def add_arguments(self, parser): - super().add_arguments(parser) - - config: EherkenningConfiguration = self._get_config() - - parser.add_argument( - "--loa", - help="Level of Assurance (LoA) to use for all the services.", - default="urn:etoegang:core:assurance-class:loa3", - ) - parser.add_argument( - "--eh-attribute-consuming-service-index", - help="Attribute consuming service index for the eHerkenning service, defaults to 9052", - default="9052", - ) - parser.add_argument( - "--eidas-attribute-consuming-service-index", - help="Attribute consuming service index for the eHerkenning service, defaults to 9053", - default="9053", - ) - parser.add_argument( - "--oin", - required=not config.oin, - default=config.oin, - help="The OIN of the company providing the service.", - ) - parser.add_argument( - "--no-eidas", - action="store_true", - help="If True, then the service catalogue will contain only the eHerkenning service. Defaults to False.", - default=False, - ) - - def get_filename(self): - date_string = timezone.now().date().isoformat() - return f"eherkenning-metadata-{date_string}.xml" - - def generate_metadata(self, options): - return generate_eherkenning_metadata()