From d9a31ec7b47547f09947c87b8d97f46e45a05e2b Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Fri, 24 May 2024 18:19:31 +0200 Subject: [PATCH] :recycle: Replace digid_eherkenning_oidc_generics with digid_eherkenning.oidc --- .../__init__.py | 65 -- src/digid_eherkenning_oidc_generics/admin.py | 103 --- src/digid_eherkenning_oidc_generics/apps.py | 8 - src/digid_eherkenning_oidc_generics/forms.py | 38 - ...nitial_squashed_0007_auto_20221213_1347.py | 801 ------------------ .../migrations/0002_auto_20240207_1546.py | 52 -- .../migrations/__init__.py | 0 src/digid_eherkenning_oidc_generics/models.py | 261 ------ .../tests/__init__.py | 0 .../tests/test_callback_endpoints.py | 45 - src/digid_eherkenning_oidc_generics/utils.py | 29 - src/digid_eherkenning_oidc_generics/views.py | 62 -- .../contrib/digid_eherkenning_oidc/admin.py | 48 +- .../digid_eherkenning_oidc/backends.py | 22 +- .../migrations/0001_initial.py | 27 +- .../contrib/digid_eherkenning_oidc/models.py | 63 +- .../contrib/digid_eherkenning_oidc/plugin.py | 20 +- .../digid_eherkenning_oidc/tests/base.py | 8 +- .../tests/test_admin.py | 44 +- .../contrib/digid_eherkenning_oidc/views.py | 24 +- src/openforms/conf/base.py | 2 +- .../fixtures/default_admin_index.json | 577 +++++-------- 22 files changed, 344 insertions(+), 1955 deletions(-) delete mode 100644 src/digid_eherkenning_oidc_generics/__init__.py delete mode 100644 src/digid_eherkenning_oidc_generics/admin.py delete mode 100644 src/digid_eherkenning_oidc_generics/apps.py delete mode 100644 src/digid_eherkenning_oidc_generics/forms.py delete mode 100644 src/digid_eherkenning_oidc_generics/migrations/0001_initial_squashed_0007_auto_20221213_1347.py delete mode 100644 src/digid_eherkenning_oidc_generics/migrations/0002_auto_20240207_1546.py delete mode 100644 src/digid_eherkenning_oidc_generics/migrations/__init__.py delete mode 100644 src/digid_eherkenning_oidc_generics/models.py delete mode 100644 src/digid_eherkenning_oidc_generics/tests/__init__.py delete mode 100644 src/digid_eherkenning_oidc_generics/tests/test_callback_endpoints.py delete mode 100644 src/digid_eherkenning_oidc_generics/utils.py delete mode 100644 src/digid_eherkenning_oidc_generics/views.py diff --git a/src/digid_eherkenning_oidc_generics/__init__.py b/src/digid_eherkenning_oidc_generics/__init__.py deleted file mode 100644 index c22f7e7a64..0000000000 --- a/src/digid_eherkenning_oidc_generics/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -DigiD-eHerkenning-OIDC-generics abstracts authenticating over OIDC. - -DigiD/eHerkenning are typically exposed via SAML bindings, but there exist identity -providers that abstract this and instead offer an OpenID Connect flow to log in with -DigiD and/or eHerkenning. This package facilitates integrating with such providers. - -The architecture and authentication flows are tricky in some places. Here's an attempt -to explain it. - -**Configuration** - -Each authentication means (DigiD, eHerkenning, mandate (machtigen) variants...) is -mapped to an OpenID client configuration, which roughly holds: - -- the OIDC endpoints to use/redirect users to -- the OAUTH2 client ID and secret to use, which indicate to the IdP which authentication - means they should send the user to -- which claims to look up/extract from the UserInfo endpoint/JWT - -These are stored in (subclasses of) the -:class:`~digid_herkenning_oidc_generics.models.OpenIDConnectBaseConfig` model. - -**Authentication flow** - -When a user starts a login flow, they: - -1. Click the appriopriate button/link -2. A Django view processes this and looks up the relevant configuration -3. The view redirects the user to the identity provider (typically a different domain) -4. Authenticate with the IdP -5. The IdP redirects back to our application -6. Our callback view performs the OIDC exchange and extracts + stores the relevant user - information -7. Finally, the callback view looks up where the user needs to be redirected to and - sends them that way. - -Steps 2-3 are called the "init" phase in this package, while steps 6-7 are the -"callback" phase. - -**Init phase** - -The mozilla-django-oidc-db package provides the -:class:`~mozilla_django_oidc_db.views.OIDCInit` view class, for the init phase. It -ensures that the specified config class is persisted in the authentication state. - -This package provides concrete views bound to configuration classes: - -* :attr:`~digid_herkenning_oidc_generics.views.digid_init` -* :attr:`~digid_herkenning_oidc_generics.views.digid_machtigen_init` -* :attr:`~digid_herkenning_oidc_generics.views.eherkenning_init` -* :attr:`~digid_herkenning_oidc_generics.views.eherkenning_bewindvoering_init` - -**Callback phase** - -The callback phase validates the code and state, and loads which configuration class -needs to be used from the state. With this information, the authentication backends -from ``settings.AUTHENTICATION_BACKENDS`` are tried in order. Typically this will -use the backend shipped in mozilla-django-oidc-db, or a subclass of it. - -The OpenID connect flow exchanges the code for the an access token (and ID token), and -the user details are retrieved. You should provide a customized backend to determine -what needs to be done with this user information, e.g. create a django user or store -the information in the django session. -""" diff --git a/src/digid_eherkenning_oidc_generics/admin.py b/src/digid_eherkenning_oidc_generics/admin.py deleted file mode 100644 index 7188839e18..0000000000 --- a/src/digid_eherkenning_oidc_generics/admin.py +++ /dev/null @@ -1,103 +0,0 @@ -from collections.abc import Sequence -from copy import deepcopy - -from django.contrib import admin -from django.utils.translation import gettext_lazy as _ - -from solo.admin import SingletonModelAdmin - -from .forms import admin_modelform_factory -from .models import ( - OpenIDConnectDigiDMachtigenConfig, - OpenIDConnectEHerkenningBewindvoeringConfig, - OpenIDConnectEHerkenningConfig, - OpenIDConnectPublicConfig, -) - -# Using a dict because these retain ordering, and it makes things a bit more readable. -ATTRIBUTES_MAPPING_TITLE = _("Attributes to extract from claim") -COMMON_FIELDSETS = { - _("Activation"): { - "fields": ("enabled",), - }, - _("Common settings"): { - "fields": ( - "oidc_rp_client_id", - "oidc_rp_client_secret", - "oidc_rp_scopes_list", - "oidc_rp_sign_algo", - "oidc_rp_idp_sign_key", - ), - }, - ATTRIBUTES_MAPPING_TITLE: { - "fields": (), # populated from the factory function below - }, - _("Endpoints"): { - "fields": ( - "oidc_op_discovery_endpoint", - "oidc_op_jwks_endpoint", - "oidc_op_authorization_endpoint", - "oidc_op_token_endpoint", - "oidc_token_use_basic_auth", - "oidc_op_user_endpoint", - "oidc_op_logout_endpoint", - ), - }, - _("Keycloak specific settings"): { - "fields": ("oidc_keycloak_idp_hint",), - "classes": ["collapse in"], - }, - _("Advanced settings"): { - "fields": ( - "oidc_use_nonce", - "oidc_nonce_size", - "oidc_state_size", - "oidc_exempt_urls", - "userinfo_claims_source", - ), - "classes": ["collapse in"], - }, -} - - -def fieldsets_factory(claim_mapping_fields: Sequence[str]): - """ - Apply the shared fieldsets configuration with the model-specific overrides. - """ - _fieldsets = deepcopy(COMMON_FIELDSETS) - _fieldsets[ATTRIBUTES_MAPPING_TITLE]["fields"] += tuple(claim_mapping_fields) - return tuple((label, config) for label, config in _fieldsets.items()) - - -@admin.register(OpenIDConnectPublicConfig) -class OpenIDConnectConfigDigiDAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OpenIDConnectPublicConfig) - fieldsets = fieldsets_factory(claim_mapping_fields=["identifier_claim_name"]) - - -@admin.register(OpenIDConnectEHerkenningConfig) -class OpenIDConnectConfigEHerkenningAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OpenIDConnectEHerkenningConfig) - fieldsets = fieldsets_factory(claim_mapping_fields=["identifier_claim_name"]) - - -@admin.register(OpenIDConnectDigiDMachtigenConfig) -class OpenIDConnectConfigDigiDMachtigenAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OpenIDConnectDigiDMachtigenConfig) - fieldsets = fieldsets_factory( - claim_mapping_fields=[ - "vertegenwoordigde_claim_name", - "gemachtigde_claim_name", - ] - ) - - -@admin.register(OpenIDConnectEHerkenningBewindvoeringConfig) -class OpenIDConnectConfigEHerkenningBewindvoeringAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OpenIDConnectEHerkenningBewindvoeringConfig) - fieldsets = fieldsets_factory( - claim_mapping_fields=[ - "vertegenwoordigde_company_claim_name", - "gemachtigde_person_claim_name", - ] - ) diff --git a/src/digid_eherkenning_oidc_generics/apps.py b/src/digid_eherkenning_oidc_generics/apps.py deleted file mode 100644 index 9389622472..0000000000 --- a/src/digid_eherkenning_oidc_generics/apps.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ - - -class DigiDeHerkenningOIDCAppConfig(AppConfig): - name = "digid_eherkenning_oidc_generics" - verbose_name = _("DigiD & eHerkenning via OpenID Connect") - label = "digid_eherkenning_oidc_generics" diff --git a/src/digid_eherkenning_oidc_generics/forms.py b/src/digid_eherkenning_oidc_generics/forms.py deleted file mode 100644 index aeed63dc44..0000000000 --- a/src/digid_eherkenning_oidc_generics/forms.py +++ /dev/null @@ -1,38 +0,0 @@ -from copy import deepcopy - -from django.forms import modelform_factory - -from mozilla_django_oidc_db.constants import OIDC_MAPPING -from mozilla_django_oidc_db.forms import OpenIDConnectConfigForm - -from .models import OpenIDConnectBaseConfig - - -def admin_modelform_factory(model: type[OpenIDConnectBaseConfig], *args, **kwargs): - """ - Factory function to generate a model form class for a given configuration model. - - The configuration model is expected to be a subclass of - :class:`~digid_eherkenning_oidc_generics.models.OpenIDConnectBaseConfig`. - - Additional args and kwargs are forwarded to django's - :func:`django.forms.modelform_factory`. - """ - kwargs.setdefault("form", OpenIDConnectConfigForm) - Form = modelform_factory(model, *args, **kwargs) - - assert issubclass( - Form, OpenIDConnectConfigForm - ), "The base form class must be a subclass of OpenIDConnectConfigForm." - - # update the mapping of discovery endpoint keys to model fields, since our base - # model adds the ``oidc_op_logout_endpoint`` field. - Form.oidc_mapping = { - **deepcopy(OIDC_MAPPING), - "oidc_op_logout_endpoint": "end_session_endpoint", - } - Form.required_endpoints = [ - *Form.required_endpoints, - "oidc_op_logout_endpoint", - ] - return Form diff --git a/src/digid_eherkenning_oidc_generics/migrations/0001_initial_squashed_0007_auto_20221213_1347.py b/src/digid_eherkenning_oidc_generics/migrations/0001_initial_squashed_0007_auto_20221213_1347.py deleted file mode 100644 index a2b2cd44f3..0000000000 --- a/src/digid_eherkenning_oidc_generics/migrations/0001_initial_squashed_0007_auto_20221213_1347.py +++ /dev/null @@ -1,801 +0,0 @@ -# Generated by Django 3.2.20 on 2023-09-08 10:43 - -from django.db import migrations, models - -import django_jsonform.models.fields -import mozilla_django_oidc_db.models - -import digid_eherkenning_oidc_generics.models - - -class Migration(migrations.Migration): - dependencies = [] - - operations = [ - migrations.CreateModel( - name="OpenIDConnectDigiDMachtigenConfig", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "enabled", - models.BooleanField( - default=False, - help_text="Indicates whether OpenID Connect for authentication/authorization is enabled", - verbose_name="enable", - ), - ), - ( - "oidc_rp_client_id", - models.CharField( - help_text="OpenID Connect client ID provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect client ID", - ), - ), - ( - "oidc_rp_client_secret", - models.CharField( - help_text="OpenID Connect secret provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect secret", - ), - ), - ( - "oidc_rp_sign_algo", - models.CharField( - default="HS256", - help_text="Algorithm the Identity Provider uses to sign ID tokens", - max_length=50, - verbose_name="OpenID sign algorithm", - ), - ), - ( - "oidc_op_discovery_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). If this is provided, the remaining endpoints can be omitted, as they will be derived from this endpoint.", - max_length=1000, - verbose_name="Discovery endpoint", - ), - ), - ( - "oidc_op_jwks_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider JSON Web Key Set endpoint. Required if `RS256` is used as signing algorithm.", - max_length=1000, - verbose_name="JSON Web Key Set endpoint", - ), - ), - ( - "oidc_op_authorization_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider authorization endpoint", - max_length=1000, - verbose_name="Authorization endpoint", - ), - ), - ( - "oidc_op_token_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider token endpoint", - max_length=1000, - verbose_name="Token endpoint", - ), - ), - ( - "oidc_op_user_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider userinfo endpoint", - max_length=1000, - verbose_name="User endpoint", - ), - ), - ( - "oidc_rp_idp_sign_key", - models.CharField( - blank=True, - help_text="Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format.", - max_length=1000, - verbose_name="Sign key", - ), - ), - ( - "oidc_op_logout_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider logout endpoint", - max_length=1000, - verbose_name="Logout endpoint", - ), - ), - ( - "oidc_keycloak_idp_hint", - models.CharField( - blank=True, - help_text="Specific for Keycloak: parameter that indicates which identity provider should be used (therefore skipping the Keycloak login screen).", - max_length=1000, - verbose_name="Keycloak Identity Provider hint", - ), - ), - ( - "vertegenwoordigde_claim_name", - models.CharField( - default="aanvrager.bsn", - help_text="Name of the claim in which the BSN of the person being represented is stored", - max_length=50, - verbose_name="vertegenwoordigde claim name", - ), - ), - ( - "gemachtigde_claim_name", - models.CharField( - default="gemachtigde.bsn", - help_text="Name of the claim in which the BSN of the person representing someone else is stored", - max_length=50, - verbose_name="gemachtigde claim name", - ), - ), - ( - "oidc_rp_scopes_list", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=50, verbose_name="OpenID Connect scope" - ), - blank=True, - default=digid_eherkenning_oidc_generics.models.get_default_scopes_bsn, - help_text="OpenID Connect scopes that are requested during login. These scopes are hardcoded and must be supported by the identity provider", - size=None, - verbose_name="OpenID Connect scopes", - ), - ), - ( - "oidc_exempt_urls", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=1000, verbose_name="Exempt URL" - ), - blank=True, - default=list, - help_text="This is a list of absolute url paths, regular expressions for url paths, or Django view names. This plus the mozilla-django-oidc urls are exempted from the session renewal by the SessionRefresh middleware.", - size=None, - verbose_name="URLs exempt from session renewal", - ), - ), - ( - "oidc_nonce_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect nonce verification", - verbose_name="Nonce size", - ), - ), - ( - "oidc_state_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect state verification", - verbose_name="State size", - ), - ), - ( - "oidc_use_nonce", - models.BooleanField( - default=True, - help_text="Controls whether the OpenID Connect client uses nonce verification", - verbose_name="Use nonce", - ), - ), - ( - "userinfo_claims_source", - models.CharField( - choices=[ - ("userinfo_endpoint", "Userinfo endpoint"), - ("id_token", "ID token"), - ], - default="userinfo_endpoint", - help_text="Indicates the source from which the user information claims should be extracted.", - max_length=100, - verbose_name="user information claims extracted from", - ), - ), - ], - options={ - "verbose_name": "OpenID Connect configuration for DigiD Machtigen", - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name="OpenIDConnectEHerkenningBewindvoeringConfig", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "enabled", - models.BooleanField( - default=False, - help_text="Indicates whether OpenID Connect for authentication/authorization is enabled", - verbose_name="enable", - ), - ), - ( - "oidc_rp_client_id", - models.CharField( - help_text="OpenID Connect client ID provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect client ID", - ), - ), - ( - "oidc_rp_client_secret", - models.CharField( - help_text="OpenID Connect secret provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect secret", - ), - ), - ( - "oidc_rp_sign_algo", - models.CharField( - default="HS256", - help_text="Algorithm the Identity Provider uses to sign ID tokens", - max_length=50, - verbose_name="OpenID sign algorithm", - ), - ), - ( - "oidc_op_discovery_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). If this is provided, the remaining endpoints can be omitted, as they will be derived from this endpoint.", - max_length=1000, - verbose_name="Discovery endpoint", - ), - ), - ( - "oidc_op_jwks_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider JSON Web Key Set endpoint. Required if `RS256` is used as signing algorithm.", - max_length=1000, - verbose_name="JSON Web Key Set endpoint", - ), - ), - ( - "oidc_op_authorization_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider authorization endpoint", - max_length=1000, - verbose_name="Authorization endpoint", - ), - ), - ( - "oidc_op_token_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider token endpoint", - max_length=1000, - verbose_name="Token endpoint", - ), - ), - ( - "oidc_op_user_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider userinfo endpoint", - max_length=1000, - verbose_name="User endpoint", - ), - ), - ( - "oidc_rp_idp_sign_key", - models.CharField( - blank=True, - help_text="Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format.", - max_length=1000, - verbose_name="Sign key", - ), - ), - ( - "oidc_use_nonce", - models.BooleanField( - default=True, - help_text="Controls whether the OpenID Connect client uses nonce verification", - verbose_name="Use nonce", - ), - ), - ( - "oidc_nonce_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect nonce verification", - verbose_name="Nonce size", - ), - ), - ( - "oidc_state_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect state verification", - verbose_name="State size", - ), - ), - ( - "oidc_exempt_urls", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=1000, verbose_name="Exempt URL" - ), - blank=True, - default=list, - help_text="This is a list of absolute url paths, regular expressions for url paths, or Django view names. This plus the mozilla-django-oidc urls are exempted from the session renewal by the SessionRefresh middleware.", - size=None, - verbose_name="URLs exempt from session renewal", - ), - ), - ( - "oidc_op_logout_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider logout endpoint", - max_length=1000, - verbose_name="Logout endpoint", - ), - ), - ( - "oidc_keycloak_idp_hint", - models.CharField( - blank=True, - help_text="Specific for Keycloak: parameter that indicates which identity provider should be used (therefore skipping the Keycloak login screen).", - max_length=1000, - verbose_name="Keycloak Identity Provider hint", - ), - ), - ( - "vertegenwoordigde_company_claim_name", - models.CharField( - default="aanvrager.kvk", - help_text="Name of the claim in which the KVK of the company being represented is stored", - max_length=50, - verbose_name="vertegenwoordigde company claim name", - ), - ), - ( - "gemachtigde_person_claim_name", - models.CharField( - default="gemachtigde.pseudoID", - help_text="Name of the claim in which the ID of the person representing a company is stored", - max_length=50, - verbose_name="gemachtigde person claim name", - ), - ), - ( - "oidc_rp_scopes_list", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=50, verbose_name="OpenID Connect scope" - ), - blank=True, - default=digid_eherkenning_oidc_generics.models.get_default_scopes_bsn, - help_text="OpenID Connect scopes that are requested during login. These scopes are hardcoded and must be supported by the identity provider", - size=None, - verbose_name="OpenID Connect scopes", - ), - ), - ( - "userinfo_claims_source", - models.CharField( - choices=[ - ("userinfo_endpoint", "Userinfo endpoint"), - ("id_token", "ID token"), - ], - default="userinfo_endpoint", - help_text="Indicates the source from which the user information claims should be extracted.", - max_length=100, - verbose_name="user information claims extracted from", - ), - ), - ], - options={ - "verbose_name": "OpenID Connect configuration for eHerkenning Bewindvoering", - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name="OpenIDConnectEHerkenningConfig", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "enabled", - models.BooleanField( - default=False, - help_text="Indicates whether OpenID Connect for authentication/authorization is enabled", - verbose_name="enable", - ), - ), - ( - "oidc_rp_client_id", - models.CharField( - help_text="OpenID Connect client ID provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect client ID", - ), - ), - ( - "oidc_rp_client_secret", - models.CharField( - help_text="OpenID Connect secret provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect secret", - ), - ), - ( - "oidc_rp_sign_algo", - models.CharField( - default="HS256", - help_text="Algorithm the Identity Provider uses to sign ID tokens", - max_length=50, - verbose_name="OpenID sign algorithm", - ), - ), - ( - "oidc_op_discovery_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). If this is provided, the remaining endpoints can be omitted, as they will be derived from this endpoint.", - max_length=1000, - verbose_name="Discovery endpoint", - ), - ), - ( - "oidc_op_jwks_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider JSON Web Key Set endpoint. Required if `RS256` is used as signing algorithm.", - max_length=1000, - verbose_name="JSON Web Key Set endpoint", - ), - ), - ( - "oidc_op_authorization_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider authorization endpoint", - max_length=1000, - verbose_name="Authorization endpoint", - ), - ), - ( - "oidc_op_token_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider token endpoint", - max_length=1000, - verbose_name="Token endpoint", - ), - ), - ( - "oidc_op_user_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider userinfo endpoint", - max_length=1000, - verbose_name="User endpoint", - ), - ), - ( - "oidc_rp_idp_sign_key", - models.CharField( - blank=True, - help_text="Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format.", - max_length=1000, - verbose_name="Sign key", - ), - ), - ( - "oidc_op_logout_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider logout endpoint", - max_length=1000, - verbose_name="Logout endpoint", - ), - ), - ( - "oidc_keycloak_idp_hint", - models.CharField( - blank=True, - help_text="Specific for Keycloak: parameter that indicates which identity provider should be used (therefore skipping the Keycloak login screen).", - max_length=1000, - verbose_name="Keycloak Identity Provider hint", - ), - ), - ( - "identifier_claim_name", - models.CharField( - default="kvk", - help_text="The name of the claim in which the KVK of the user is stored", - max_length=100, - verbose_name="KVK claim name", - ), - ), - ( - "oidc_rp_scopes_list", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=50, verbose_name="OpenID Connect scope" - ), - blank=True, - default=digid_eherkenning_oidc_generics.models.get_default_scopes_kvk, - help_text="OpenID Connect scopes that are requested during login. These scopes are hardcoded and must be supported by the identity provider", - size=None, - verbose_name="OpenID Connect scopes", - ), - ), - ( - "oidc_exempt_urls", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=1000, verbose_name="Exempt URL" - ), - blank=True, - default=list, - help_text="This is a list of absolute url paths, regular expressions for url paths, or Django view names. This plus the mozilla-django-oidc urls are exempted from the session renewal by the SessionRefresh middleware.", - size=None, - verbose_name="URLs exempt from session renewal", - ), - ), - ( - "oidc_nonce_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect nonce verification", - verbose_name="Nonce size", - ), - ), - ( - "oidc_state_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect state verification", - verbose_name="State size", - ), - ), - ( - "oidc_use_nonce", - models.BooleanField( - default=True, - help_text="Controls whether the OpenID Connect client uses nonce verification", - verbose_name="Use nonce", - ), - ), - ( - "userinfo_claims_source", - models.CharField( - choices=[ - ("userinfo_endpoint", "Userinfo endpoint"), - ("id_token", "ID token"), - ], - default="userinfo_endpoint", - help_text="Indicates the source from which the user information claims should be extracted.", - max_length=100, - verbose_name="user information claims extracted from", - ), - ), - ], - options={ - "verbose_name": "OpenID Connect configuration for eHerkenning", - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name="OpenIDConnectPublicConfig", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "enabled", - models.BooleanField( - default=False, - help_text="Indicates whether OpenID Connect for authentication/authorization is enabled", - verbose_name="enable", - ), - ), - ( - "oidc_rp_client_id", - models.CharField( - help_text="OpenID Connect client ID provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect client ID", - ), - ), - ( - "oidc_rp_client_secret", - models.CharField( - help_text="OpenID Connect secret provided by the OIDC Provider", - max_length=1000, - verbose_name="OpenID Connect secret", - ), - ), - ( - "oidc_rp_sign_algo", - models.CharField( - default="HS256", - help_text="Algorithm the Identity Provider uses to sign ID tokens", - max_length=50, - verbose_name="OpenID sign algorithm", - ), - ), - ( - "oidc_op_discovery_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider discovery endpoint ending with a slash (`.well-known/...` will be added automatically). If this is provided, the remaining endpoints can be omitted, as they will be derived from this endpoint.", - max_length=1000, - verbose_name="Discovery endpoint", - ), - ), - ( - "oidc_op_jwks_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider JSON Web Key Set endpoint. Required if `RS256` is used as signing algorithm.", - max_length=1000, - verbose_name="JSON Web Key Set endpoint", - ), - ), - ( - "oidc_op_authorization_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider authorization endpoint", - max_length=1000, - verbose_name="Authorization endpoint", - ), - ), - ( - "oidc_op_token_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider token endpoint", - max_length=1000, - verbose_name="Token endpoint", - ), - ), - ( - "oidc_op_user_endpoint", - models.URLField( - help_text="URL of your OpenID Connect provider userinfo endpoint", - max_length=1000, - verbose_name="User endpoint", - ), - ), - ( - "oidc_rp_idp_sign_key", - models.CharField( - blank=True, - help_text="Key the Identity Provider uses to sign ID tokens in the case of an RSA sign algorithm. Should be the signing key in PEM or DER format.", - max_length=1000, - verbose_name="Sign key", - ), - ), - ( - "oidc_op_logout_endpoint", - models.URLField( - blank=True, - help_text="URL of your OpenID Connect provider logout endpoint", - max_length=1000, - verbose_name="Logout endpoint", - ), - ), - ( - "oidc_keycloak_idp_hint", - models.CharField( - blank=True, - help_text="Specific for Keycloak: parameter that indicates which identity provider should be used (therefore skipping the Keycloak login screen).", - max_length=1000, - verbose_name="Keycloak Identity Provider hint", - ), - ), - ( - "identifier_claim_name", - models.CharField( - default="bsn", - help_text="The name of the claim in which the BSN of the user is stored", - max_length=100, - verbose_name="BSN claim name", - ), - ), - ( - "oidc_rp_scopes_list", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=50, verbose_name="OpenID Connect scope" - ), - blank=True, - default=digid_eherkenning_oidc_generics.models.get_default_scopes_bsn, - help_text="OpenID Connect scopes that are requested during login. These scopes are hardcoded and must be supported by the identity provider", - size=None, - verbose_name="OpenID Connect scopes", - ), - ), - ( - "oidc_exempt_urls", - django_jsonform.models.fields.ArrayField( - base_field=models.CharField( - max_length=1000, verbose_name="Exempt URL" - ), - blank=True, - default=list, - help_text="This is a list of absolute url paths, regular expressions for url paths, or Django view names. This plus the mozilla-django-oidc urls are exempted from the session renewal by the SessionRefresh middleware.", - size=None, - verbose_name="URLs exempt from session renewal", - ), - ), - ( - "oidc_nonce_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect nonce verification", - verbose_name="Nonce size", - ), - ), - ( - "oidc_state_size", - models.PositiveIntegerField( - default=32, - help_text="Sets the length of the random string used for OpenID Connect state verification", - verbose_name="State size", - ), - ), - ( - "oidc_use_nonce", - models.BooleanField( - default=True, - help_text="Controls whether the OpenID Connect client uses nonce verification", - verbose_name="Use nonce", - ), - ), - ( - "userinfo_claims_source", - models.CharField( - choices=[ - ("userinfo_endpoint", "Userinfo endpoint"), - ("id_token", "ID token"), - ], - default="userinfo_endpoint", - help_text="Indicates the source from which the user information claims should be extracted.", - max_length=100, - verbose_name="user information claims extracted from", - ), - ), - ], - options={ - "verbose_name": "OpenID Connect configuration for DigiD", - }, - bases=(models.Model,), - ), - ] diff --git a/src/digid_eherkenning_oidc_generics/migrations/0002_auto_20240207_1546.py b/src/digid_eherkenning_oidc_generics/migrations/0002_auto_20240207_1546.py deleted file mode 100644 index fa17bca23b..0000000000 --- a/src/digid_eherkenning_oidc_generics/migrations/0002_auto_20240207_1546.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 3.2.24 on 2024-02-07 14:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ( - "digid_eherkenning_oidc_generics", - "0001_initial_squashed_0007_auto_20221213_1347", - ), - ] - - operations = [ - migrations.AddField( - model_name="openidconnectdigidmachtigenconfig", - name="oidc_token_use_basic_auth", - field=models.BooleanField( - default=False, - help_text="If enabled, the client ID and secret are sent in the HTTP Basic auth header when obtaining the access token. Otherwise, they are sent in the request body.", - verbose_name="Use Basic auth for token endpoint", - ), - ), - migrations.AddField( - model_name="openidconnecteherkenningbewindvoeringconfig", - name="oidc_token_use_basic_auth", - field=models.BooleanField( - default=False, - help_text="If enabled, the client ID and secret are sent in the HTTP Basic auth header when obtaining the access token. Otherwise, they are sent in the request body.", - verbose_name="Use Basic auth for token endpoint", - ), - ), - migrations.AddField( - model_name="openidconnecteherkenningconfig", - name="oidc_token_use_basic_auth", - field=models.BooleanField( - default=False, - help_text="If enabled, the client ID and secret are sent in the HTTP Basic auth header when obtaining the access token. Otherwise, they are sent in the request body.", - verbose_name="Use Basic auth for token endpoint", - ), - ), - migrations.AddField( - model_name="openidconnectpublicconfig", - name="oidc_token_use_basic_auth", - field=models.BooleanField( - default=False, - help_text="If enabled, the client ID and secret are sent in the HTTP Basic auth header when obtaining the access token. Otherwise, they are sent in the request body.", - verbose_name="Use Basic auth for token endpoint", - ), - ), - ] diff --git a/src/digid_eherkenning_oidc_generics/migrations/__init__.py b/src/digid_eherkenning_oidc_generics/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/digid_eherkenning_oidc_generics/models.py b/src/digid_eherkenning_oidc_generics/models.py deleted file mode 100644 index 67cd90c52a..0000000000 --- a/src/digid_eherkenning_oidc_generics/models.py +++ /dev/null @@ -1,261 +0,0 @@ -import warnings -from collections.abc import Collection -from typing import ClassVar - -from django.conf import settings -from django.db import models -from django.utils.functional import classproperty -from django.utils.translation import gettext_lazy as _ - -from django_jsonform.models.fields import ArrayField -from mozilla_django_oidc_db.models import OpenIDConnectConfigBase -from mozilla_django_oidc_db.typing import ClaimPath - - -def get_default_scopes_bsn(): - """ - Returns the default scopes to request for OpenID Connect logins - """ - return ["openid", "bsn"] - - -def get_default_scopes_kvk(): - """ - Returns the default scopes to request for OpenID Connect logins - """ - return ["openid", "kvk"] - - -class OpenIDConnectBaseConfig(OpenIDConnectConfigBase): - """ - Configuration for DigiD authentication via OpenID connect - """ - - oidc_op_logout_endpoint = models.URLField( - _("Logout endpoint"), - max_length=1000, - help_text=_("URL of your OpenID Connect provider logout endpoint"), - blank=True, - ) - - # Keycloak specific config - oidc_keycloak_idp_hint = models.CharField( - _("Keycloak Identity Provider hint"), - max_length=1000, - help_text=_( - "Specific for Keycloak: parameter that indicates which identity provider " - "should be used (therefore skipping the Keycloak login screen)." - ), - blank=True, - ) - - # FIXME: this url/namespace is not defined anywhere inside this package, it's - # currently specific to Open Forms. - # Probably best to tackle this when different configs are no longer applied through - # django-solo model subclassing. - oidc_authentication_callback_url: ClassVar[str] = "authentication:oidc-callback" - - class Meta: - verbose_name = _("OpenID Connect configuration") - abstract = True - - @classproperty - def oidcdb_check_idp_availability(cls) -> bool: - return True - - -class OpenIDConnectPublicConfig(OpenIDConnectBaseConfig): - """ - Configuration for DigiD authentication via OpenID connect - """ - - identifier_claim_name = models.CharField( - _("BSN claim name"), - max_length=100, - help_text=_("The name of the claim in which the BSN of the user is stored"), - default="bsn", - ) - oidc_rp_scopes_list = ArrayField( - verbose_name=_("OpenID Connect scopes"), - base_field=models.CharField(_("OpenID Connect scope"), max_length=50), - default=get_default_scopes_bsn, - blank=True, - help_text=_( - "OpenID Connect scopes that are requested during login. " - "These scopes are hardcoded and must be supported by the identity provider" - ), - ) - - custom_oidc_db_prefix: ClassVar[str] = "digid_oidc" - - class Meta: - verbose_name = _("OpenID Connect configuration for DigiD") - - @classproperty - def oidc_authentication_callback_url(cls) -> str: # type: ignore - if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: - warnings.warn( - "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", - DeprecationWarning, - ) - return "digid_oidc:callback" - return super().oidc_authentication_callback_url - - @property - def oidcdb_username_claim(self) -> list[str]: - return [self.identifier_claim_name] - - -class OpenIDConnectDigiDMachtigenConfig(OpenIDConnectBaseConfig): - # TODO: support periods in claim keys - vertegenwoordigde_claim_name = models.CharField( - verbose_name=_("vertegenwoordigde claim name"), - default="aanvrager.bsn", - max_length=50, - help_text=_( - "Name of the claim in which the BSN of the person being represented is stored" - ), - ) - gemachtigde_claim_name = models.CharField( - verbose_name=_("gemachtigde claim name"), - default="gemachtigde.bsn", - max_length=50, - help_text=_( - "Name of the claim in which the BSN of the person representing someone else is stored" - ), - ) - oidc_rp_scopes_list = ArrayField( - verbose_name=_("OpenID Connect scopes"), - base_field=models.CharField(_("OpenID Connect scope"), max_length=50), - default=get_default_scopes_bsn, - blank=True, - help_text=_( - "OpenID Connect scopes that are requested during login. " - "These scopes are hardcoded and must be supported by the identity provider" - ), - ) - - custom_oidc_db_prefix: ClassVar[str] = "digid_machtigen_oidc" - - class Meta: - verbose_name = _("OpenID Connect configuration for DigiD Machtigen") - - @classproperty - def oidc_authentication_callback_url(cls) -> str: # type: ignore - if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: - warnings.warn( - "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", - DeprecationWarning, - ) - return "digid_machtigen_oidc:callback" - return super().oidc_authentication_callback_url - - @property - def digid_eherkenning_machtigen_claims(self) -> dict[str, ClaimPath]: - return { - "vertegenwoordigde": [self.vertegenwoordigde_claim_name], - "gemachtigde": [self.gemachtigde_claim_name], - } - - @property - def oidcdb_sensitive_claims(self) -> Collection[ClaimPath]: - return list(self.digid_eherkenning_machtigen_claims.values()) - - -class OpenIDConnectEHerkenningConfig(OpenIDConnectBaseConfig): - """ - Configuration for eHerkenning authentication via OpenID connect - """ - - identifier_claim_name = models.CharField( - _("KVK claim name"), - max_length=100, - help_text=_("The name of the claim in which the KVK of the user is stored"), - default="kvk", - ) - oidc_rp_scopes_list = ArrayField( - verbose_name=_("OpenID Connect scopes"), - base_field=models.CharField(_("OpenID Connect scope"), max_length=50), - default=get_default_scopes_kvk, - blank=True, - help_text=_( - "OpenID Connect scopes that are requested during login. " - "These scopes are hardcoded and must be supported by the identity provider" - ), - ) - - custom_oidc_db_prefix: ClassVar[str] = "eherkenning_oidc" - - class Meta: - verbose_name = _("OpenID Connect configuration for eHerkenning") - - @classproperty - def oidc_authentication_callback_url(cls) -> str: # type: ignore - if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: - warnings.warn( - "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", - DeprecationWarning, - ) - return "eherkenning_oidc:callback" - return super().oidc_authentication_callback_url - - @property - def oidcdb_username_claim(self) -> list[str]: - return [self.identifier_claim_name] - - -class OpenIDConnectEHerkenningBewindvoeringConfig(OpenIDConnectBaseConfig): - # TODO: support periods in claim keys - vertegenwoordigde_company_claim_name = models.CharField( - verbose_name=_("vertegenwoordigde company claim name"), - default="aanvrager.kvk", - max_length=50, - help_text=_( - "Name of the claim in which the KVK of the company being represented is stored" - ), - ) - gemachtigde_person_claim_name = models.CharField( - verbose_name=_("gemachtigde person claim name"), - default="gemachtigde.pseudoID", - max_length=50, - help_text=_( - "Name of the claim in which the ID of the person representing a company is stored" - ), - ) - oidc_rp_scopes_list = ArrayField( - verbose_name=_("OpenID Connect scopes"), - base_field=models.CharField(_("OpenID Connect scope"), max_length=50), - default=get_default_scopes_bsn, - blank=True, - help_text=_( - "OpenID Connect scopes that are requested during login. " - "These scopes are hardcoded and must be supported by the identity provider" - ), - ) - - custom_oidc_db_prefix: ClassVar[str] = "eherkenning_bewindvoering_oidc" - - class Meta: - verbose_name = _("OpenID Connect configuration for eHerkenning Bewindvoering") - - @classproperty - def oidc_authentication_callback_url(cls) -> str: # type: ignore - if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: - warnings.warn( - "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", - DeprecationWarning, - ) - return "eherkenning_bewindvoering_oidc:callback" - return super().oidc_authentication_callback_url - - @property - def digid_eherkenning_machtigen_claims(self) -> dict[str, ClaimPath]: - # TODO: this nomenclature isn't entirely correct - return { - "vertegenwoordigde": [self.vertegenwoordigde_company_claim_name], - "gemachtigde": [self.gemachtigde_person_claim_name], - } - - @property - def oidcdb_sensitive_claims(self) -> Collection[ClaimPath]: - return list(self.digid_eherkenning_machtigen_claims.values()) diff --git a/src/digid_eherkenning_oidc_generics/tests/__init__.py b/src/digid_eherkenning_oidc_generics/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/digid_eherkenning_oidc_generics/tests/test_callback_endpoints.py b/src/digid_eherkenning_oidc_generics/tests/test_callback_endpoints.py deleted file mode 100644 index da73bf32ff..0000000000 --- a/src/digid_eherkenning_oidc_generics/tests/test_callback_endpoints.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.test import SimpleTestCase, override_settings -from django.urls import reverse - -from ..models import ( - OpenIDConnectDigiDMachtigenConfig, - OpenIDConnectEHerkenningBewindvoeringConfig, - OpenIDConnectEHerkenningConfig, - OpenIDConnectPublicConfig, -) - - -class CallbackEndpointTests(SimpleTestCase): - - @override_settings(USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS=True) - def test_legacy_behaviour(self): - expected = ( - (OpenIDConnectPublicConfig, "/digid-oidc/callback/"), - (OpenIDConnectEHerkenningConfig, "/eherkenning-oidc/callback/"), - (OpenIDConnectDigiDMachtigenConfig, "/digid-machtigen-oidc/callback/"), - ( - OpenIDConnectEHerkenningBewindvoeringConfig, - "/eherkenning-bewindvoering-oidc/callback/", - ), - ) - - for config, expected_path in expected: - with self.subTest(config=config): - callback_path = reverse(config.oidc_authentication_callback_url) - - self.assertEqual(callback_path, expected_path) - - @override_settings(USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS=False) - def test_new_behaviour(self): - expected = ( - OpenIDConnectPublicConfig, - OpenIDConnectEHerkenningConfig, - OpenIDConnectDigiDMachtigenConfig, - OpenIDConnectEHerkenningBewindvoeringConfig, - ) - - for config in expected: - with self.subTest(config=config): - callback_path = reverse(config.oidc_authentication_callback_url) - - self.assertEqual(callback_path, "/auth/oidc/callback/") diff --git a/src/digid_eherkenning_oidc_generics/utils.py b/src/digid_eherkenning_oidc_generics/utils.py deleted file mode 100644 index 84b29d4363..0000000000 --- a/src/digid_eherkenning_oidc_generics/utils.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging - -import requests - -from .models import OpenIDConnectBaseConfig - -logger = logging.getLogger(__name__) - - -def do_op_logout(config: OpenIDConnectBaseConfig, id_token: str) -> None: - """ - Perform the logout with the OpenID Provider. - - Standard: https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout - """ - logout_endpoint = config.oidc_op_logout_endpoint - if not logout_endpoint: - return - - response = requests.post(logout_endpoint, data={"id_token_hint": id_token}) - if not response.ok: - logger.warning( - "Failed to log out the user at the OpenID Provider. Status code: %s", - response.status_code, - extra={ - "response": response, - "status_code": response.status_code, - }, - ) diff --git a/src/digid_eherkenning_oidc_generics/views.py b/src/digid_eherkenning_oidc_generics/views.py deleted file mode 100644 index ec9e4c33c8..0000000000 --- a/src/digid_eherkenning_oidc_generics/views.py +++ /dev/null @@ -1,62 +0,0 @@ -import logging - -from django.contrib.auth.models import AbstractBaseUser, AnonymousUser -from django.http import HttpResponseRedirect - -from mozilla_django_oidc_db.views import ( - OIDCAuthenticationCallbackView as BaseCallbackView, -) - -logger = logging.getLogger(__name__) - - -class OIDCAuthenticationCallbackView(BaseCallbackView): - """ - Check if the 'created user' from the authentication backend needs to be logged in. - - If we only want to perform the claim processing, then no real user is expected to - be returend from the authentication backend, and hence we also don't want to try - to log in in this dummy user (as in, set ``request.user`` to a django user - instance). - - Note that we deliberately don't perform these changes in :meth:`get` (anymore), - since we miss the upstream library changes/fixes when we make invasive changes. - Instead, the authentication backend receives all the necessary information and *is* - the right place to implement this logic. - """ - - expect_django_user: bool = True - """ - Set to ``True`` if a Django user is expected to be created by the backend. - - The OIDC backend is used just to obtain the claims via OIDC, but doesn't always need - to result in a real Django user record being created. - """ - - user: AbstractBaseUser | AnonymousUser # set on succesful auth/code exchange - - def login_success(self): - """ - Overridden to not actually log the user in, since setting the BSN/KVK/... in - the session variables is all that matters. - """ - assert self.user - - match (self.expect_django_user, self.user.pk): - case (False, pk) if pk is not None: - raise TypeError( - "A real Django user instance was returned from the authentication " - "backend. This is a configuration/programming mistake!" - ) - case (True, None): - raise TypeError( - "A fake Django user instance was returned from the authentication " - "backend. This is a configuration/programming mistake!" - ) - - # default behaviour which logs the user in *iff* we expect a real Django user - # to be returned, otherwise ignore all that. - if self.expect_django_user: - return super().login_success() - - return HttpResponseRedirect(self.success_url) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/admin.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/admin.py index 847614f42a..0600709a56 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/admin.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/admin.py @@ -2,32 +2,32 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ +from digid_eherkenning.oidc.admin import fieldsets_factory +from digid_eherkenning.oidc.forms import admin_modelform_factory +from digid_eherkenning.oidc.models import ( + DigiDConfig, + DigiDMachtigenConfig, + EHerkenningBewindvoeringConfig, + EHerkenningConfig, +) from mozilla_django_oidc_db.forms import OpenIDConnectConfigForm from solo.admin import SingletonModelAdmin -from digid_eherkenning_oidc_generics.admin import fieldsets_factory -from digid_eherkenning_oidc_generics.forms import admin_modelform_factory -from digid_eherkenning_oidc_generics.models import ( - OpenIDConnectDigiDMachtigenConfig, - OpenIDConnectEHerkenningBewindvoeringConfig, - OpenIDConnectEHerkenningConfig, - OpenIDConnectPublicConfig, -) from openforms.forms.models import Form from .models import ( - OIDCDigiDConfig, - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, - OIDCEHerkenningConfig, + OFDigiDConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, + OFEHerkenningConfig, ) from .plugin import get_config_to_plugin # unregister the default app admins so we can add in our own behaviour -admin.site.unregister(OpenIDConnectPublicConfig) -admin.site.unregister(OpenIDConnectDigiDMachtigenConfig) -admin.site.unregister(OpenIDConnectEHerkenningConfig) -admin.site.unregister(OpenIDConnectEHerkenningBewindvoeringConfig) +admin.site.unregister(DigiDMachtigenConfig) +admin.site.unregister(EHerkenningBewindvoeringConfig) +admin.site.unregister(EHerkenningConfig) +admin.site.unregister(DigiDConfig) class OIDCConfigForm(OpenIDConnectConfigForm): @@ -63,15 +63,15 @@ def clean_enabled(self): return enabled -@admin.register(OIDCDigiDConfig) +@admin.register(OFDigiDConfig) class DigiDConfigAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OIDCDigiDConfig, form=OIDCConfigForm) + form = admin_modelform_factory(OFDigiDConfig, form=OIDCConfigForm) fieldsets = fieldsets_factory(claim_mapping_fields=["identifier_claim_name"]) -@admin.register(OIDCDigiDMachtigenConfig) +@admin.register(OFDigiDMachtigenConfig) class DigiDMachtigenConfigAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OIDCDigiDMachtigenConfig, form=OIDCConfigForm) + form = admin_modelform_factory(OFDigiDMachtigenConfig, form=OIDCConfigForm) fieldsets = fieldsets_factory( claim_mapping_fields=[ "vertegenwoordigde_claim_name", @@ -80,16 +80,16 @@ class DigiDMachtigenConfigAdmin(SingletonModelAdmin): ) -@admin.register(OIDCEHerkenningConfig) +@admin.register(OFEHerkenningConfig) class EHerkenningConfigAdmin(SingletonModelAdmin): - form = admin_modelform_factory(OIDCEHerkenningConfig, form=OIDCConfigForm) + form = admin_modelform_factory(OFEHerkenningConfig, form=OIDCConfigForm) fieldsets = fieldsets_factory(claim_mapping_fields=["identifier_claim_name"]) -@admin.register(OIDCEHerkenningBewindvoeringConfig) +@admin.register(OFEHerkenningBewindvoeringConfig) class EHerkenningBewindvoeringConfigAdmin(SingletonModelAdmin): form = admin_modelform_factory( - OIDCEHerkenningBewindvoeringConfig, form=OIDCConfigForm + OFEHerkenningBewindvoeringConfig, form=OIDCConfigForm ) fieldsets = fieldsets_factory( claim_mapping_fields=[ diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py index 3aa9f5f7b2..fcbb51c714 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py @@ -4,6 +4,7 @@ from django.core.exceptions import PermissionDenied from django.http import HttpRequest +from digid_eherkenning.oidc.models import OpenIDConnectBaseConfig from glom import Path, PathAccessError, glom from mozilla_django_oidc_db.backends import OIDCAuthenticationBackend from mozilla_django_oidc_db.config import dynamic_setting @@ -11,14 +12,13 @@ from mozilla_django_oidc_db.utils import obfuscate_claims from typing_extensions import override -from digid_eherkenning_oidc_generics.models import OpenIDConnectBaseConfig from openforms.typing import JSONObject from .models import ( - OIDCDigiDConfig, - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, - OIDCEHerkenningConfig, + OFDigiDConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, + OFEHerkenningConfig, ) from .plugin import get_config_to_plugin @@ -76,12 +76,12 @@ def get_or_create_user( @override def verify_claims(self, claims) -> bool: """Verify the provided claims to decide if authentication should be allowed.""" - if self.config_class in (OIDCDigiDConfig, OIDCEHerkenningConfig): + if self.config_class in (OFDigiDConfig, OFEHerkenningConfig): return super().verify_claims(claims) assert self.config_class in ( - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, ) assert claims, "Empty claims should have been blocked earlier" @@ -114,13 +114,13 @@ def _extract_and_store_claims(self, claims: JSONObject) -> None: session_value: JSONObject | str # DigiD/eHerkenning without machtigen -> stores the BSN/KVK directly - if self.config_class in (OIDCDigiDConfig, OIDCEHerkenningConfig): + if self.config_class in (OFDigiDConfig, OFEHerkenningConfig): claim_bits = self.OIDCDB_USERNAME_CLAIM session_value = glom(claims, Path(*claim_bits), default="") # DigiD/eHerkenning with machtigen -> store a dict of relevant claims. elif self.config_class in ( - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, ): session_value = { key: glom(claims, Path(*claim_bits)) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/migrations/0001_initial.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/migrations/0001_initial.py index 9490390ef1..d7f103c0ef 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/migrations/0001_initial.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.11 on 2024-05-24 12:50 +# Generated by Django 4.2.11 on 2024-05-24 16:18 from django.db import migrations @@ -8,52 +8,51 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("digid_eherkenning_oidc_generics", "0002_auto_20240207_1546"), + ( + "digid_eherkenning_oidc_generics", + "0003_rename_openidconnectpublicconfig_digidconfig_and_more", + ), ] operations = [ migrations.CreateModel( - name="OIDCDigiDConfig", + name="OFDigiDConfig", fields=[], options={ "proxy": True, "indexes": [], "constraints": [], }, - bases=("digid_eherkenning_oidc_generics.openidconnectpublicconfig",), + bases=("digid_eherkenning_oidc_generics.digidconfig",), ), migrations.CreateModel( - name="OIDCDigiDMachtigenConfig", + name="OFDigiDMachtigenConfig", fields=[], options={ "proxy": True, "indexes": [], "constraints": [], }, - bases=( - "digid_eherkenning_oidc_generics.openidconnectdigidmachtigenconfig", - ), + bases=("digid_eherkenning_oidc_generics.digidmachtigenconfig",), ), migrations.CreateModel( - name="OIDCEHerkenningBewindvoeringConfig", + name="OFEHerkenningBewindvoeringConfig", fields=[], options={ "proxy": True, "indexes": [], "constraints": [], }, - bases=( - "digid_eherkenning_oidc_generics.openidconnecteherkenningbewindvoeringconfig", - ), + bases=("digid_eherkenning_oidc_generics.eherkenningbewindvoeringconfig",), ), migrations.CreateModel( - name="OIDCEHerkenningConfig", + name="OFEHerkenningConfig", fields=[], options={ "proxy": True, "indexes": [], "constraints": [], }, - bases=("digid_eherkenning_oidc_generics.openidconnecteherkenningconfig",), + bases=("digid_eherkenning_oidc_generics.eherkenningconfig",), ), ] diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/models.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/models.py index df5e7425ca..60ab35071e 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/models.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/models.py @@ -1,8 +1,13 @@ -from digid_eherkenning_oidc_generics.models import ( - OpenIDConnectDigiDMachtigenConfig, - OpenIDConnectEHerkenningBewindvoeringConfig, - OpenIDConnectEHerkenningConfig, - OpenIDConnectPublicConfig, +import warnings + +from django.conf import settings +from django.utils.functional import classproperty + +from digid_eherkenning.oidc.models import ( + DigiDConfig, + DigiDMachtigenConfig, + EHerkenningBewindvoeringConfig, + EHerkenningConfig, ) @@ -12,29 +17,69 @@ def get_callback_view(self): return callback_view -class OIDCDigiDConfig(OpenIDConnectPublicConfig): +class OFDigiDConfig(DigiDConfig): class Meta: proxy = True get_callback_view = get_callback_view + @classproperty + def oidc_authentication_callback_url(cls) -> str: # type: ignore + if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: + warnings.warn( + "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", + DeprecationWarning, + ) + return "digid_oidc:callback" + return "oidc_authentication_callback" + -class OIDCDigiDMachtigenConfig(OpenIDConnectDigiDMachtigenConfig): +class OFDigiDMachtigenConfig(DigiDMachtigenConfig): class Meta: proxy = True get_callback_view = get_callback_view + @classproperty + def oidc_authentication_callback_url(cls) -> str: # type: ignore + if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: + warnings.warn( + "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", + DeprecationWarning, + ) + return "digid_machtigen_oidc:callback" + return "oidc_authentication_callback" -class OIDCEHerkenningConfig(OpenIDConnectEHerkenningConfig): + +class OFEHerkenningConfig(EHerkenningConfig): class Meta: proxy = True get_callback_view = get_callback_view + @classproperty + def oidc_authentication_callback_url(cls) -> str: # type: ignore + if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: + warnings.warn( + "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", + DeprecationWarning, + ) + return "eherkenning_oidc:callback" + return "oidc_authentication_callback" + -class OIDCEHerkenningBewindvoeringConfig(OpenIDConnectEHerkenningBewindvoeringConfig): +class OFEHerkenningBewindvoeringConfig(EHerkenningBewindvoeringConfig): class Meta: proxy = True get_callback_view = get_callback_view + + @classproperty + def oidc_authentication_callback_url(cls) -> str: # type: ignore + if settings.USE_LEGACY_DIGID_EH_OIDC_ENDPOINTS: + warnings.warn( + "Legacy DigiD-eHerkenning callback endpoints will be removed in 3.0", + DeprecationWarning, + ) + return "eherkenning_bewindvoering_oidc:callback" + return "oidc_authentication_callback" diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py index ed90375deb..afbd6400d5 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py @@ -10,10 +10,10 @@ ) from django.utils.translation import gettext_lazy as _ +from digid_eherkenning.oidc.models import OpenIDConnectBaseConfig +from digid_eherkenning.oidc.utils import do_op_logout from mozilla_django_oidc_db.views import _RETURN_URL_SESSION_KEY -from digid_eherkenning_oidc_generics.models import OpenIDConnectBaseConfig -from digid_eherkenning_oidc_generics.utils import do_op_logout from openforms.contrib.digid_eherkenning.utils import ( get_digid_logo, get_eherkenning_logo, @@ -27,10 +27,10 @@ from ...exceptions import InvalidCoSignData from ...registry import register from .models import ( - OIDCDigiDConfig, - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, - OIDCEHerkenningConfig, + OFDigiDConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, + OFEHerkenningConfig, ) from .views import ( digid_init, @@ -150,7 +150,7 @@ class DigiDOIDCAuthentication(OIDCAuthentication[str]): verbose_name = _("DigiD via OpenID Connect") provides_auth = AuthAttribute.bsn session_key = "digid_oidc:bsn" - config_class = OIDCDigiDConfig + config_class = OFDigiDConfig init_view = staticmethod(digid_init) def get_label(self) -> str: @@ -165,7 +165,7 @@ class eHerkenningOIDCAuthentication(OIDCAuthentication[str]): verbose_name = _("eHerkenning via OpenID Connect") provides_auth = AuthAttribute.kvk session_key = "eherkenning_oidc:kvk" - config_class = OIDCEHerkenningConfig + config_class = OFEHerkenningConfig init_view = staticmethod(eherkenning_init) def get_label(self) -> str: @@ -180,7 +180,7 @@ class DigiDMachtigenOIDCAuthentication(OIDCAuthentication[JSONObject]): verbose_name = _("DigiD Machtigen via OpenID Connect") provides_auth = AuthAttribute.bsn session_key = "digid_machtigen_oidc:machtigen" - config_class = OIDCDigiDMachtigenConfig + config_class = OFDigiDMachtigenConfig init_view = staticmethod(digid_machtigen_init) is_for_gemachtigde = True @@ -205,7 +205,7 @@ class EHerkenningBewindvoeringOIDCAuthentication(OIDCAuthentication[JSONObject]) verbose_name = _("eHerkenning bewindvoering via OpenID Connect") provides_auth = AuthAttribute.kvk session_key = "eherkenning_bewindvoering_oidc:machtigen" - config_class = OIDCEHerkenningBewindvoeringConfig + config_class = OFEHerkenningBewindvoeringConfig init_view = staticmethod(eherkenning_bewindvoering_init) is_for_gemachtigde = True diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/base.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/base.py index 6fc6424b80..bfd1010cde 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/base.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/base.py @@ -20,24 +20,24 @@ def mock_config(model: str, **overrides): mock_digid_config = partial( mock_config, - model="OIDCDigiDConfig", + model="OFDigiDConfig", oidc_rp_scopes_list=["openid", "bsn"], ) mock_eherkenning_config = partial( mock_config, - model="OIDCEHerkenningConfig", + model="OFEHerkenningConfig", oidc_rp_scopes_list=["openid", "kvk"], ) mock_digid_machtigen_config = partial( mock_config, - model="OIDCDigiDMachtigenConfig", + model="OFDigiDMachtigenConfig", oidc_rp_scopes_list=["openid", "bsn"], ) mock_eherkenning_bewindvoering_config = partial( mock_config, - model="OIDCEHerkenningBewindvoeringConfig", + model="OFEHerkenningBewindvoeringConfig", oidc_rp_scopes_list=["openid", "bsn"], ) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/test_admin.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/test_admin.py index c30863b4d9..fe90c35b3f 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/test_admin.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/tests/test_admin.py @@ -10,10 +10,10 @@ from openforms.forms.tests.factories import FormFactory from ..models import ( - OIDCDigiDConfig, - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, - OIDCEHerkenningConfig, + OFDigiDConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, + OFEHerkenningConfig, ) @@ -25,16 +25,14 @@ class AdminTestsBase(WebTest): class DigiDConfigAdminTests(AdminTestsBase): - CHANGE_PAGE_URL = reverse_lazy( - "admin:digid_eherkenning_oidc_oidcdigidconfig_change" - ) + CHANGE_PAGE_URL = reverse_lazy("admin:digid_eherkenning_oidc_ofdigidconfig_change") @classmethod def setUpTestData(cls): super().setUpTestData() # minimal configuration to pass form validation & not do network IO - cls.config = config = OIDCDigiDConfig( + cls.config = config = OFDigiDConfig( enabled=True, oidc_rp_client_id="testclient", oidc_rp_client_secret="secret", @@ -50,7 +48,7 @@ def test_can_disable_backend_iff_unused_in_forms(self): FormFactory.create(authentication_backends=["other-backend"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidcdigidconfig_form"] + form = change_page.forms["ofdigidconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -67,7 +65,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): FormFactory.create(authentication_backends=["digid_oidc"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidcdigidconfig_form"] + form = change_page.forms["ofdigidconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -84,7 +82,7 @@ def test_leave_enabled(self): FormFactory.create(authentication_backends=["other-backend"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidcdigidconfig_form"] + form = change_page.forms["ofdigidconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -100,7 +98,7 @@ def test_leave_enabled(self): class DigiDMachtigenConfigAdminTests(AdminTestsBase): CHANGE_PAGE_URL = reverse_lazy( - "admin:digid_eherkenning_oidc_oidcdigidmachtigenconfig_change" + "admin:digid_eherkenning_oidc_ofdigidmachtigenconfig_change" ) @classmethod @@ -108,7 +106,7 @@ def setUpTestData(cls): super().setUpTestData() # minimal configuration to pass form validation & not do network IO - cls.config = config = OIDCDigiDMachtigenConfig( + cls.config = config = OFDigiDMachtigenConfig( enabled=True, oidc_rp_client_id="testclient", oidc_rp_client_secret="secret", @@ -124,7 +122,7 @@ def test_can_disable_backend_iff_unused_in_forms(self): FormFactory.create(authentication_backends=["other-backend"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidcdigidmachtigenconfig_form"] + form = change_page.forms["ofdigidmachtigenconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -141,7 +139,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): FormFactory.create(authentication_backends=["digid_machtigen_oidc"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidcdigidmachtigenconfig_form"] + form = change_page.forms["ofdigidmachtigenconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -157,7 +155,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): class EHerkenningConfigAdminTests(AdminTestsBase): CHANGE_PAGE_URL = reverse_lazy( - "admin:digid_eherkenning_oidc_oidceherkenningconfig_change" + "admin:digid_eherkenning_oidc_ofeherkenningconfig_change" ) @classmethod @@ -165,7 +163,7 @@ def setUpTestData(cls): super().setUpTestData() # minimal configuration to pass form validation & not do network IO - cls.config = config = OIDCEHerkenningConfig( + cls.config = config = OFEHerkenningConfig( enabled=True, oidc_rp_client_id="testclient", oidc_rp_client_secret="secret", @@ -181,7 +179,7 @@ def test_can_disable_backend_iff_unused_in_forms(self): FormFactory.create(authentication_backends=["other-backend"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidceherkenningconfig_form"] + form = change_page.forms["ofeherkenningconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -198,7 +196,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): FormFactory.create(authentication_backends=["eherkenning_oidc"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidceherkenningconfig_form"] + form = change_page.forms["ofeherkenningconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -214,7 +212,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): class EHerkenningBewindvoeringConfigAdminTests(AdminTestsBase): CHANGE_PAGE_URL = reverse_lazy( - "admin:digid_eherkenning_oidc_oidceherkenningbewindvoeringconfig_change" + "admin:digid_eherkenning_oidc_ofeherkenningbewindvoeringconfig_change" ) @classmethod @@ -222,7 +220,7 @@ def setUpTestData(cls): super().setUpTestData() # minimal configuration to pass form validation & not do network IO - cls.config = config = OIDCEHerkenningBewindvoeringConfig( + cls.config = config = OFEHerkenningBewindvoeringConfig( enabled=True, oidc_rp_client_id="testclient", oidc_rp_client_secret="secret", @@ -238,7 +236,7 @@ def test_can_disable_backend_iff_unused_in_forms(self): FormFactory.create(authentication_backends=["other-backend"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidceherkenningbewindvoeringconfig_form"] + form = change_page.forms["ofeherkenningbewindvoeringconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) @@ -255,7 +253,7 @@ def test_cannot_disable_backend_if_used_in_any_form(self): FormFactory.create(authentication_backends=["eherkenning_bewindvoering_oidc"]) change_page = self.app.get(self.CHANGE_PAGE_URL, user=self.user) - form = change_page.forms["oidceherkenningbewindvoeringconfig_form"] + form = change_page.forms["ofeherkenningbewindvoeringconfig_form"] # set the value manually, normally this is done through JS form["oidc_rp_scopes_list"] = json.dumps(self.config.oidc_rp_scopes_list) form["oidc_exempt_urls"] = json.dumps(self.config.oidc_exempt_urls) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py index 5d40c81d95..b4cd33dc19 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py @@ -2,14 +2,14 @@ from django.http import HttpRequest +from digid_eherkenning.oidc.models import OpenIDConnectBaseConfig +from digid_eherkenning.oidc.views import ( + OIDCAuthenticationCallbackView as _OIDCAuthenticationCallbackView, +) from furl import furl from mozilla_django_oidc_db.config import lookup_config from mozilla_django_oidc_db.views import _RETURN_URL_SESSION_KEY, OIDCInit -from digid_eherkenning_oidc_generics.models import OpenIDConnectBaseConfig -from digid_eherkenning_oidc_generics.views import ( - OIDCAuthenticationCallbackView as _OIDCAuthenticationCallbackView, -) from openforms.authentication.contrib.digid.views import ( DIGID_MESSAGE_PARAMETER, LOGIN_CANCELLED, @@ -20,10 +20,10 @@ from ...views import BACKEND_OUTAGE_RESPONSE_PARAMETER from .models import ( - OIDCDigiDConfig, - OIDCDigiDMachtigenConfig, - OIDCEHerkenningBewindvoeringConfig, - OIDCEHerkenningConfig, + OFDigiDConfig, + OFDigiDMachtigenConfig, + OFEHerkenningBewindvoeringConfig, + OFEHerkenningConfig, ) logger = logging.getLogger(__name__) @@ -115,19 +115,19 @@ def failure_url(self) -> str: digid_init = OIDCInit.as_view( - config_class=OIDCDigiDConfig, + config_class=OFDigiDConfig, allow_next_from_query=False, ) digid_machtigen_init = OIDCInit.as_view( - config_class=OIDCDigiDMachtigenConfig, + config_class=OFDigiDMachtigenConfig, allow_next_from_query=False, ) eherkenning_init = OIDCInit.as_view( - config_class=OIDCEHerkenningConfig, + config_class=OFEHerkenningConfig, allow_next_from_query=False, ) eherkenning_bewindvoering_init = OIDCInit.as_view( - config_class=OIDCEHerkenningBewindvoeringConfig, + config_class=OFEHerkenningBewindvoeringConfig, allow_next_from_query=False, ) diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py index 3007e797fb..c4d41e2a14 100644 --- a/src/openforms/conf/base.py +++ b/src/openforms/conf/base.py @@ -187,7 +187,7 @@ "stuf.stuf_zds", "mozilla_django_oidc", "mozilla_django_oidc_db", - "digid_eherkenning_oidc_generics", + "digid_eherkenning.oidc", "django_filters", "csp", "cspreports", diff --git a/src/openforms/fixtures/default_admin_index.json b/src/openforms/fixtures/default_admin_index.json index bf369afe3b..18ff7dcf15 100644 --- a/src/openforms/fixtures/default_admin_index.json +++ b/src/openforms/fixtures/default_admin_index.json @@ -1,424 +1,235 @@ [ -{ + { "model": "admin_index.appgroup", "fields": { - "order": 0, - "translations": { - "en": "Accounts", - "nl": "Accounts" - }, - "name": "Accounts", - "slug": "accounts", - "models": [ - [ - "accounts", - "user" - ], - [ - "accounts", - "userpreferences" - ], - [ - "auth", - "group" - ], - [ - "authtoken", - "token" - ], - [ - "authtoken", - "tokenproxy" - ], - [ - "mozilla_django_oidc_db", - "openidconnectconfig" - ], - [ - "otp_static", - "staticdevice" - ], - [ - "otp_totp", - "totpdevice" - ], - [ - "two_factor_webauthn", - "webauthndevice" - ] - ] + "order": 0, + "translations": { + "en": "Accounts", + "nl": "Accounts" + }, + "name": "Accounts", + "slug": "accounts", + "models": [ + ["accounts", "user"], + ["accounts", "userpreferences"], + ["auth", "group"], + ["authtoken", "token"], + ["authtoken", "tokenproxy"], + ["mozilla_django_oidc_db", "openidconnectconfig"], + ["otp_static", "staticdevice"], + ["otp_totp", "totpdevice"], + ["two_factor_webauthn", "webauthndevice"] + ] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 1, - "translations": { - "en": "Forms", - "nl": "Formulieren" - }, - "name": "Formulieren", - "slug": "formulieren", - "models": [ - [ - "forms", - "category" - ], - [ - "forms", - "form" - ], - [ - "forms", - "formstatistics" - ] - ] + "order": 1, + "translations": { + "en": "Forms", + "nl": "Formulieren" + }, + "name": "Formulieren", + "slug": "formulieren", + "models": [ + ["forms", "category"], + ["forms", "form"], + ["forms", "formstatistics"] + ] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 2, - "translations": { - "en": "Submissions", - "nl": "Inzendingen" - }, - "name": "Inzendingen", - "slug": "inzendingen", - "models": [ - [ - "appointments", - "appointmentinfo" - ], - [ - "payments", - "submissionpayment" - ], - [ - "submissions", - "submission" - ], - [ - "submissions", - "submissionfileattachment" - ], - [ - "submissions", - "submissionreport" - ] - ] + "order": 2, + "translations": { + "en": "Submissions", + "nl": "Inzendingen" + }, + "name": "Inzendingen", + "slug": "inzendingen", + "models": [ + ["appointments", "appointmentinfo"], + ["payments", "submissionpayment"], + ["submissions", "submission"], + ["submissions", "submissionfileattachment"], + ["submissions", "submissionreport"] + ] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 4, - "translations": { - "en": "Products", - "nl": "Producten" - }, - "name": "Producten", - "slug": "producten", - "models": [ - [ - "products", - "product" - ] - ] + "order": 4, + "translations": { + "en": "Products", + "nl": "Producten" + }, + "name": "Producten", + "slug": "producten", + "models": [["products", "product"]] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 5, - "translations": { - "en": "Configuration", - "nl": "Configuratie" - }, - "name": "Configuratie", - "slug": "configuratie", - "models": [ - [ - "admin_index", - "appgroup" - ], - [ - "analytics_tools", - "analyticstoolsconfiguration" - ], - [ - "appointments", - "appointmentsconfig" - ], - [ - "config", - "cspsetting" - ], - [ - "config", - "globalconfiguration" - ], - [ - "config", - "theme" - ], - [ - "cookie_consent", - "cookie" - ], - [ - "cookie_consent", - "cookiegroup" - ], - [ - "digid_eherkenning", - "digidconfiguration" - ], - [ - "digid_eherkenning", - "eherkenningconfiguration" - ], - [ - "digid_eherkenning_oidc_generics", - "openidconnectdigidmachtigenconfig" - ], - [ - "digid_eherkenning_oidc_generics", - "openidconnecteherkenningbewindvoeringconfig" - ], - [ - "digid_eherkenning_oidc_generics", - "openidconnecteherkenningconfig" - ], - [ - "digid_eherkenning_oidc_generics", - "openidconnectpublicconfig" - ], - [ - "django_camunda", - "camundaconfig" - ], - [ - "django_yubin", - "blacklist" - ], - [ - "log_outgoing_requests", - "outgoingrequestslogconfig" - ], - [ - "multidomain", - "domain" - ], - [ - "np_family_members", - "familymemberstypeconfig" - ], - [ - "prefill", - "prefillconfig" - ], - [ - "simple_certmanager", - "certificate" - ], - [ - "soap", - "soapservice" - ], - [ - "variables", - "servicefetchconfiguration" - ], - [ - "zgw_consumers", - "nlxconfig" - ], - [ - "zgw_consumers", - "service" - ] - ] + "order": 5, + "translations": { + "en": "Configuration", + "nl": "Configuratie" + }, + "name": "Configuratie", + "slug": "configuratie", + "models": [ + ["admin_index", "appgroup"], + ["analytics_tools", "analyticstoolsconfiguration"], + ["appointments", "appointmentsconfig"], + ["config", "cspsetting"], + ["config", "globalconfiguration"], + ["config", "theme"], + ["cookie_consent", "cookie"], + ["cookie_consent", "cookiegroup"], + ["digid_eherkenning", "digidconfiguration"], + ["digid_eherkenning", "eherkenningconfiguration"], + ["digid_eherkenning_oidc_generics", "digidmachtigenconfig"], + ["digid_eherkenning_oidc_generics", "eherkenningbewindvoeringconfig"], + ["digid_eherkenning_oidc_generics", "eherkenningconfig"], + ["digid_eherkenning_oidc_generics", "digidconfig"], + ["django_camunda", "camundaconfig"], + ["django_yubin", "blacklist"], + ["log_outgoing_requests", "outgoingrequestslogconfig"], + ["multidomain", "domain"], + ["np_family_members", "familymemberstypeconfig"], + ["prefill", "prefillconfig"], + ["simple_certmanager", "certificate"], + ["soap", "soapservice"], + ["variables", "servicefetchconfiguration"], + ["zgw_consumers", "nlxconfig"], + ["zgw_consumers", "service"] + ] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 6, - "translations": { - "en": "Logs", - "nl": "Logs" - }, - "name": "Logs", - "slug": "logs", - "models": [ - [ - "axes", - "accessattempt" - ], - [ - "axes", - "accessfailurelog" - ], - [ - "axes", - "accesslog" - ], - [ - "cookie_consent", - "logitem" - ], - [ - "cspreports", - "cspreport" - ], - [ - "django_yubin", - "log" - ], - [ - "django_yubin", - "message" - ], - [ - "logging", - "avgtimelinelogproxy" - ], - [ - "logging", - "timelinelogproxy" - ], - [ - "log_outgoing_requests", - "outgoingrequestslog" - ], - [ - "submissions", - "temporaryfileupload" - ], - [ - "timeline_logger", - "timelinelog" - ] - ] + "order": 6, + "translations": { + "en": "Logs", + "nl": "Logs" + }, + "name": "Logs", + "slug": "logs", + "models": [ + ["axes", "accessattempt"], + ["axes", "accessfailurelog"], + ["axes", "accesslog"], + ["cookie_consent", "logitem"], + ["cspreports", "cspreport"], + ["django_yubin", "log"], + ["django_yubin", "message"], + ["logging", "avgtimelinelogproxy"], + ["logging", "timelinelogproxy"], + ["log_outgoing_requests", "outgoingrequestslog"], + ["submissions", "temporaryfileupload"], + ["timeline_logger", "timelinelog"] + ] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 7, - "translations": { - "en": "Useful links", - "nl": "Handige links" - }, - "name": "Handige links", - "slug": "handige-links", - "models": [] + "order": 7, + "translations": { + "en": "Useful links", + "nl": "Handige links" + }, + "name": "Handige links", + "slug": "handige-links", + "models": [] } -}, -{ + }, + { "model": "admin_index.appgroup", "fields": { - "order": 3, - "translations": { - "en": "Appointments", - "nl": "Afspraken" - }, - "name": "Afspraken", - "slug": "afspraken", - "models": [ - [ - "appointments", - "appointment" - ], - [ - "appointments", - "appointmentinfo" - ], - [ - "appointments", - "appointmentproduct" - ], - [ - "appointments", - "appointmentsconfig" - ] - ] + "order": 3, + "translations": { + "en": "Appointments", + "nl": "Afspraken" + }, + "name": "Afspraken", + "slug": "afspraken", + "models": [ + ["appointments", "appointment"], + ["appointments", "appointmentinfo"], + ["appointments", "appointmentproduct"], + ["appointments", "appointmentsconfig"] + ] } -}, -{ + }, + { "model": "admin_index.applink", "fields": { - "order": 0, - "translations": { - "en": "Test email backend", - "nl": "E-mail-backend testen" - }, - "app_group": [ - "configuratie" - ], - "name": "Test email backend", - "link": "/admin/email/test/" + "order": 0, + "translations": { + "en": "Test email backend", + "nl": "E-mail-backend testen" + }, + "app_group": ["configuratie"], + "name": "Test email backend", + "link": "/admin/email/test/" } -}, -{ + }, + { "model": "admin_index.applink", "fields": { - "order": 1, - "translations": { - "en": "API Documentation", - "nl": "API Documentatie" - }, - "app_group": [ - "handige-links" - ], - "name": "API Documentatie", - "link": "/api/docs/" + "order": 1, + "translations": { + "en": "API Documentation", + "nl": "API Documentatie" + }, + "app_group": ["handige-links"], + "name": "API Documentatie", + "link": "/api/docs/" } -}, -{ + }, + { "model": "admin_index.applink", "fields": { - "order": 2, - "translations": { - "en": "Documentation", - "nl": "Documentatie" - }, - "app_group": [ - "handige-links" - ], - "name": "Documentatie", - "link": "http://open-forms.readthedocs.io/" + "order": 2, + "translations": { + "en": "Documentation", + "nl": "Documentatie" + }, + "app_group": ["handige-links"], + "name": "Documentatie", + "link": "http://open-forms.readthedocs.io/" } -}, -{ + }, + { "model": "admin_index.applink", "fields": { - "order": 3, - "translations": { - "en": "Github", - "nl": "Github" - }, - "app_group": [ - "handige-links" - ], - "name": "Github", - "link": "https://github.com/open-formulieren/open-forms" + "order": 3, + "translations": { + "en": "Github", + "nl": "Github" + }, + "app_group": ["handige-links"], + "name": "Github", + "link": "https://github.com/open-formulieren/open-forms" } -}, -{ + }, + { "model": "admin_index.applink", "fields": { - "order": 4, - "translations": { - "en": "Configuration overview", - "nl": "Configuratie overzicht" - }, - "app_group": [ - "configuratie" - ], - "name": "Configuratie overzicht", - "link": "/admin/config/overview/" + "order": 4, + "translations": { + "en": "Configuration overview", + "nl": "Configuratie overzicht" + }, + "app_group": ["configuratie"], + "name": "Configuratie overzicht", + "link": "/admin/config/overview/" } -} + } ]