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()