diff --git a/src/openapi.yaml b/src/openapi.yaml index 135bee6329..495ec09871 100644 --- a/src/openapi.yaml +++ b/src/openapi.yaml @@ -9541,11 +9541,13 @@ components: type: string description: The human-readable name for a plugin. requiresAuth: - type: string - nullable: true - title: Required authentication attribute - description: The authentication attribute required for this plugin to lookup - remote data. + type: array + items: + type: string + nullable: true + title: Required authentication attribute + description: The authentication attribute required for this plugin to + lookup remote data. configurationContext: nullable: true title: Extra configuration context @@ -9555,7 +9557,6 @@ components: - configurationContext - id - label - - requiresAuth PrefillTargetPaths: type: object properties: diff --git a/src/openforms/js/compiled-lang/en.json b/src/openforms/js/compiled-lang/en.json index 5fd1d83f4e..80798c51bb 100644 --- a/src/openforms/js/compiled-lang/en.json +++ b/src/openforms/js/compiled-lang/en.json @@ -1819,6 +1819,28 @@ "value": "Save as new" } ], + "EmcgDt": [ + { + "type": 0, + "value": "Component \"" + }, + { + "type": 1, + "value": "label" + }, + { + "type": 0, + "value": "\" uses a prefill that requires the \"" + }, + { + "type": 1, + "value": "requiredAuthAttribute" + }, + { + "type": 0, + "value": "\" attributes. Please select an authentication plugin that provides these attributes." + } + ], "ErwGIQ": [ { "type": 0, @@ -3529,28 +3551,6 @@ "value": "Maximum selected checkboxes (e.g. 1)" } ], - "VQYmOD": [ - { - "type": 0, - "value": "Component \"" - }, - { - "type": 1, - "value": "label" - }, - { - "type": 0, - "value": "\" uses a prefill that requires the \"" - }, - { - "type": 1, - "value": "requiredAuthAttribute" - }, - { - "type": 0, - "value": "\" attribute. Please select an authentication plugin that provides this attribute." - } - ], "VUOOSy": [ { "type": 0, diff --git a/src/openforms/js/compiled-lang/nl.json b/src/openforms/js/compiled-lang/nl.json index 2d9af4180a..aa8aa373a5 100644 --- a/src/openforms/js/compiled-lang/nl.json +++ b/src/openforms/js/compiled-lang/nl.json @@ -1840,6 +1840,28 @@ "value": "Opslaan als nieuw" } ], + "EmcgDt": [ + { + "type": 0, + "value": "Component \"" + }, + { + "type": 1, + "value": "label" + }, + { + "type": 0, + "value": "\" uses a prefill that requires the \"" + }, + { + "type": 1, + "value": "requiredAuthAttribute" + }, + { + "type": 0, + "value": "\" attributes. Please select an authentication plugin that provides these attributes." + } + ], "ErwGIQ": [ { "type": 0, @@ -3542,28 +3564,6 @@ "value": "Maximaal aantal aangevinkte opties (bijv. 1)" } ], - "VQYmOD": [ - { - "type": 0, - "value": "De component \"" - }, - { - "type": 1, - "value": "label" - }, - { - "type": 0, - "value": "\" gebruikt een prefill die het \"" - }, - { - "type": 1, - "value": "requiredAuthAttribute" - }, - { - "type": 0, - "value": "\"-attribuut nodig heeft. Gebruik een authenticatiemethode die dit attribuut aanbiedt." - } - ], "VUOOSy": [ { "type": 0, diff --git a/src/openforms/js/components/admin/form_design/PluginWarning.js b/src/openforms/js/components/admin/form_design/PluginWarning.js index 3d750162af..0f2b69318b 100644 --- a/src/openforms/js/components/admin/form_design/PluginWarning.js +++ b/src/openforms/js/components/admin/form_design/PluginWarning.js @@ -35,7 +35,7 @@ const PluginWarning = ({loginRequired, configuration}) => { const authPlugin = availableAuthPlugins.find(plugin => plugin.id === pluginName); if (!authPlugin) break; - if (authPlugin.providesAuth.includes(requiredAuthAttribute)) { + if (requiredAuthAttribute.includes(authPlugin.providesAuth)) { pluginProvidesAttribute = true; break; } @@ -46,12 +46,12 @@ const PluginWarning = ({loginRequired, configuration}) => { ); diff --git a/src/openforms/js/lang/en.json b/src/openforms/js/lang/en.json index 5320769b35..bdfd08f1ec 100644 --- a/src/openforms/js/lang/en.json +++ b/src/openforms/js/lang/en.json @@ -774,6 +774,11 @@ "description": "Save as new service fetch configuration button label", "originalDefault": "Save as new" }, + "EmcgDt": { + "defaultMessage": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attributes. Please select an authentication plugin that provides these attributes.", + "description": "Prefill plugin requires unavailable auth attribute warning", + "originalDefault": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attributes. Please select an authentication plugin that provides these attributes." + }, "EtAvFF": { "defaultMessage": "Duplicate this form", "description": "Copy form button title", @@ -1694,11 +1699,6 @@ "description": "Confirmation page content label", "originalDefault": "Page content" }, - "VQYmOD": { - "defaultMessage": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attribute. Please select an authentication plugin that provides this attribute.", - "description": "Prefill plugin requires unavailable auth attribute warning", - "originalDefault": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attribute. Please select an authentication plugin that provides this attribute." - }, "VUOOSy": { "defaultMessage": "Name", "description": "Camunda complex process var 'name' label", diff --git a/src/openforms/js/lang/nl.json b/src/openforms/js/lang/nl.json index 0eb58fb0ff..14bcdb82ac 100644 --- a/src/openforms/js/lang/nl.json +++ b/src/openforms/js/lang/nl.json @@ -781,6 +781,11 @@ "description": "Save as new service fetch configuration button label", "originalDefault": "Save as new" }, + "EmcgDt": { + "defaultMessage": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attributes. Please select an authentication plugin that provides these attributes.", + "description": "Prefill plugin requires unavailable auth attribute warning", + "originalDefault": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attributes. Please select an authentication plugin that provides these attributes." + }, "EtAvFF": { "defaultMessage": "Maak een kopie van dit formulier", "description": "Copy form button title", @@ -1710,11 +1715,6 @@ "description": "Confirmation page content label", "originalDefault": "Page content" }, - "VQYmOD": { - "defaultMessage": "De component \"{label}\" gebruikt een prefill die het \"{requiredAuthAttribute}\"-attribuut nodig heeft. Gebruik een authenticatiemethode die dit attribuut aanbiedt.", - "description": "Prefill plugin requires unavailable auth attribute warning", - "originalDefault": "Component \"{label}\" uses a prefill that requires the \"{requiredAuthAttribute}\" attribute. Please select an authentication plugin that provides this attribute." - }, "VUOOSy": { "defaultMessage": "Naam", "description": "Camunda complex process var 'name' label", diff --git a/src/openforms/prefill/api/serializers.py b/src/openforms/prefill/api/serializers.py index 00b3d10edc..c6cb11a67d 100644 --- a/src/openforms/prefill/api/serializers.py +++ b/src/openforms/prefill/api/serializers.py @@ -12,12 +12,15 @@ class PrefillPluginSerializer(PluginBaseSerializer): - requires_auth = serializers.CharField( - label=_("Required authentication attribute"), - help_text=_( - "The authentication attribute required for this plugin to lookup remote data." + requires_auth = serializers.ListField( + child=serializers.CharField( + label=_("Required authentication attribute"), + help_text=_( + "The authentication attribute required for this plugin to lookup remote data." + ), + allow_null=True, ), - allow_null=True, + default=list, ) configuration_context = serializers.JSONField( label=_("Extra configuration context"), diff --git a/src/openforms/prefill/api/tests/test_endpoints.py b/src/openforms/prefill/api/tests/test_endpoints.py index 68eab0062d..3d6160d56f 100644 --- a/src/openforms/prefill/api/tests/test_endpoints.py +++ b/src/openforms/prefill/api/tests/test_endpoints.py @@ -12,7 +12,7 @@ class TestPrefill(BasePlugin): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) verbose_name = "Test" def get_available_attributes(self): @@ -26,7 +26,7 @@ def get_available_attributes(self): @register("onlyvars") class OnlyVarsPrefill(BasePlugin): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) verbose_name = "Only Vars" for_components = () @@ -36,7 +36,7 @@ def get_available_attributes(self): @register("vanityplates") class VanityPlatePrefill(BasePlugin): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) verbose_name = "Vanity Plates" for_components = {"licenseplate"} @@ -111,19 +111,19 @@ def test_prefill_list(self): { "id": "test", "label": "Test", - "requiresAuth": AuthAttribute.bsn, + "requiresAuth": (AuthAttribute.bsn,), "configurationContext": None, }, { "id": "onlyvars", "label": "Only Vars", - "requiresAuth": AuthAttribute.bsn, + "requiresAuth": (AuthAttribute.bsn,), "configurationContext": None, }, { "id": "vanityplates", "label": "Vanity Plates", - "requiresAuth": AuthAttribute.bsn, + "requiresAuth": (AuthAttribute.bsn,), "configurationContext": None, }, ] @@ -141,14 +141,14 @@ def test_prefill_list_for_component_type(self): { "id": "test", "label": "Test", - "requiresAuth": AuthAttribute.bsn, + "requiresAuth": (AuthAttribute.bsn,), "configurationContext": None, }, # spec'd for licenseplate { "id": "vanityplates", "label": "Vanity Plates", - "requiresAuth": AuthAttribute.bsn, + "requiresAuth": (AuthAttribute.bsn,), "configurationContext": None, }, ] diff --git a/src/openforms/prefill/base.py b/src/openforms/prefill/base.py index 81b6b61292..7f87b47128 100644 --- a/src/openforms/prefill/base.py +++ b/src/openforms/prefill/base.py @@ -1,3 +1,4 @@ +from collections.abc import Collection from typing import Any, Container, Iterable, TypedDict from rest_framework import serializers @@ -29,7 +30,7 @@ class Options(TypedDict): class BasePlugin[OptionsT: Options](AbstractBasePlugin): - requires_auth: AuthAttribute | None = None + requires_auth: Collection[AuthAttribute] | None = None for_components: Container[str] = AllComponentTypes() options: SerializerCls = EmptyOptions @@ -139,7 +140,8 @@ def get_identifier_value( if ( identifier_role == IdentifierRoles.main - and submission.auth_info.attribute == cls.requires_auth + and cls.requires_auth + and submission.auth_info.attribute in cls.requires_auth ): return submission.auth_info.value diff --git a/src/openforms/prefill/co_sign.py b/src/openforms/prefill/co_sign.py index 057436fe8d..24457ea681 100644 --- a/src/openforms/prefill/co_sign.py +++ b/src/openforms/prefill/co_sign.py @@ -8,6 +8,7 @@ """ import logging +from collections.abc import Collection from openforms.authentication.service import AuthAttribute from openforms.submissions.cosigning import CosignV1Data @@ -26,11 +27,19 @@ def get_default_plugin_for_auth_attribute( - auth_attribute: AuthAttribute | None, + auth_attribute: Collection[AuthAttribute] | None, ) -> str | None: - if not auth_attribute or not ( - config_field := AUTH_ATTRIBUTE_TO_CONFIG_FIELD.get(auth_attribute) - ): + if not auth_attribute: + logger.info("No auth_attribute provided") + return + + config_field = "" + for attribute in auth_attribute: + if AUTH_ATTRIBUTE_TO_CONFIG_FIELD.get(attribute): + config_field = AUTH_ATTRIBUTE_TO_CONFIG_FIELD[attribute] + break + + if not config_field: logger.info("Unsupported auth_attribute '%s'", auth_attribute) return @@ -44,7 +53,7 @@ def get_default_plugin_for_auth_attribute( def add_co_sign_representation( - submission: Submission, auth_attribute: AuthAttribute | None + submission: Submission, auth_attribute: Collection[AuthAttribute] | None ): default_plugin = get_default_plugin_for_auth_attribute(auth_attribute) # configuration may be incomplete, do nothing in that case! diff --git a/src/openforms/prefill/contrib/haalcentraal_brp/plugin.py b/src/openforms/prefill/contrib/haalcentraal_brp/plugin.py index 5492cba531..ff59a12fd2 100644 --- a/src/openforms/prefill/contrib/haalcentraal_brp/plugin.py +++ b/src/openforms/prefill/contrib/haalcentraal_brp/plugin.py @@ -45,7 +45,7 @@ def get_attributes_cls(): @register(PLUGIN_IDENTIFIER) class HaalCentraalPrefill(BasePlugin): verbose_name = _("Haal Centraal: BRP Personen Bevragen") - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) @staticmethod def get_available_attributes() -> list[tuple[str, str]]: @@ -84,7 +84,8 @@ def get_identifier_value( if ( identifier_role == IdentifierRoles.main - and submission.auth_info.attribute == cls.requires_auth + and cls.requires_auth + and submission.auth_info.attribute in cls.requires_auth ): return submission.auth_info.value diff --git a/src/openforms/prefill/contrib/kvk/plugin.py b/src/openforms/prefill/contrib/kvk/plugin.py index a2bdf1b994..3853527664 100644 --- a/src/openforms/prefill/contrib/kvk/plugin.py +++ b/src/openforms/prefill/contrib/kvk/plugin.py @@ -35,7 +35,7 @@ def _select_address(items, type_): class KVK_KVKNumberPrefill(BasePlugin): verbose_name = _("KvK Company by KvK number") - requires_auth = AuthAttribute.kvk + requires_auth = (AuthAttribute.kvk,) @staticmethod def get_available_attributes() -> list[tuple[str, str]]: @@ -50,7 +50,8 @@ def get_identifier_value( if ( identifier_role == IdentifierRoles.main - and submission.auth_info.attribute == cls.requires_auth + and cls.requires_auth + and submission.auth_info.attribute in cls.requires_auth ): return submission.auth_info.value diff --git a/src/openforms/prefill/contrib/stufbg/plugin.py b/src/openforms/prefill/contrib/stufbg/plugin.py index 68c7cfb5dd..c7bef58acf 100644 --- a/src/openforms/prefill/contrib/stufbg/plugin.py +++ b/src/openforms/prefill/contrib/stufbg/plugin.py @@ -95,7 +95,7 @@ @register("stufbg") class StufBgPrefill(BasePlugin): verbose_name = _("StUF-BG") - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) @staticmethod def get_available_attributes() -> list[tuple[str, str]]: @@ -138,7 +138,8 @@ def get_identifier_value( if ( identifier_role == IdentifierRoles.main - and submission.auth_info.attribute == cls.requires_auth + and cls.requires_auth + and submission.auth_info.attribute in cls.requires_auth ): return submission.auth_info.value diff --git a/src/openforms/prefill/contrib/suwinet/plugin.py b/src/openforms/prefill/contrib/suwinet/plugin.py index c3d1782b05..f71897f8ab 100644 --- a/src/openforms/prefill/contrib/suwinet/plugin.py +++ b/src/openforms/prefill/contrib/suwinet/plugin.py @@ -28,7 +28,7 @@ def _get_client() -> SuwinetClient | None: @register("suwinet") class SuwinetPrefill(BasePlugin): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) verbose_name = _("Suwinet") for_components = () diff --git a/src/openforms/prefill/fields.py b/src/openforms/prefill/fields.py index c3a2a01e90..7b408f41ab 100644 --- a/src/openforms/prefill/fields.py +++ b/src/openforms/prefill/fields.py @@ -35,7 +35,7 @@ def formfield(self, **kwargs): def _get_plugin_choices(self): choices = [] for plugin in register.iter_enabled_plugins(): - if plugin.requires_auth != self.auth_attribute: + if plugin.requires_auth and self.auth_attribute not in plugin.requires_auth: continue choices.append((plugin.identifier, plugin.get_label())) return choices diff --git a/src/openforms/prefill/sources.py b/src/openforms/prefill/sources.py index ca1dc68ec9..b49c2aa9f8 100644 --- a/src/openforms/prefill/sources.py +++ b/src/openforms/prefill/sources.py @@ -14,6 +14,7 @@ ) from openforms.typing import JSONEncodable +from .base import BasePlugin from .constants import IdentifierRoles from .registry import Registry @@ -51,10 +52,9 @@ def fetch_prefill_values_from_attribute( @elasticapm.capture_span(span_type="app.prefill") def invoke_plugin( - item: tuple[str, IdentifierRoles, list[dict[str, str]]] + item: tuple[BasePlugin, IdentifierRoles, list[dict[str, str]]] ) -> tuple[list[dict[str, str]], dict[str, JSONEncodable]]: - plugin_id, identifier_role, fields = item - plugin = register[plugin_id] + plugin, identifier_role, fields = item if not plugin.is_enabled: raise PluginNotEnabled() @@ -70,17 +70,22 @@ def invoke_plugin( if values: logevent.prefill_retrieve_success(submission, plugin, fields) else: - if (required_attribute := plugin.requires_auth) is None or ( - (auth_info := getattr(submission, "auth_info", None)) - and auth_info.attribute == required_attribute - ): - logevent.prefill_retrieve_empty(submission, plugin, fields) + logevent.prefill_retrieve_empty(submission, plugin, fields) return fields, values invoke_plugin_args = [] for plugin_id, field_groups in grouped_fields.items(): + plugin = register[plugin_id] + + # check if we need to run the plugin when the user is authenticated + if (required_attribute := plugin.requires_auth) is not None and ( + (auth_info := getattr(submission, "auth_info", None)) + and auth_info not in required_attribute + ): + continue + for identifier_role, fields in field_groups.items(): - invoke_plugin_args.append((plugin_id, identifier_role, fields)) + invoke_plugin_args.append((plugin, identifier_role, fields)) with parallel() as executor: results = executor.map(invoke_plugin, invoke_plugin_args) diff --git a/src/openforms/prefill/tests/test_prefill_hook.py b/src/openforms/prefill/tests/test_prefill_hook.py index 76ff099849..3030944cf3 100644 --- a/src/openforms/prefill/tests/test_prefill_hook.py +++ b/src/openforms/prefill/tests/test_prefill_hook.py @@ -614,7 +614,7 @@ def test_demo_prefill_get_identifier_not_authenticated(self): def test_demo_prefill_get_identifier_authenticated(self): class TestPlugin(BasePlugin): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) plugin = TestPlugin(identifier="test") @@ -646,7 +646,7 @@ def test_prefill_logging_with_mismatching_login_method(self): @register("demo") class MismatchPlugin(DemoPrefill): - requires_auth = AuthAttribute.bsn + requires_auth = (AuthAttribute.bsn,) @staticmethod def get_prefill_values(submission, attributes, identifier_role):