From 4051107b9968fcbc097af96ed9cf5a6db7181704 Mon Sep 17 00:00:00 2001 From: Thijs Kinkhorst Date: Wed, 8 May 2024 15:20:14 +0200 Subject: [PATCH 1/2] Implement scope checking in the existing attribute validator Advantages: - makes use of existing Engineblock facility - vastly reduces code duplication - makes it configurable which attributes are subject to the check - makes it configurable which attributes will block vs which will only log - makes it possible to display scope violation in the debug page already Todo: - Improve exception message to indicate cause of blockage - Integrate into debug page - Remove now unused previous code --- application/configs/attributes.json | 7 ++ library/EngineBlock/Attributes/Validator.php | 7 +- .../Attributes/Validator/Abstract.php | 9 ++ .../Attributes/Validator/Interface.php | 1 + .../Attributes/Validator/Scope.php | 87 +++++++++++++++++++ .../Command/ValidateRequiredAttributes.php | 2 +- library/EngineBlock/Corto/Filter/Input.php | 8 +- 7 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 library/EngineBlock/Attributes/Validator/Scope.php diff --git a/application/configs/attributes.json b/application/configs/attributes.json index a18ac56f83..f99dbfdad5 100644 --- a/application/configs/attributes.json +++ b/application/configs/attributes.json @@ -168,6 +168,9 @@ "DisplayOrder" : 9, "DisplayConsent" : true, "Conditions": { + "error": { + "scope": true + }, "warning": { "min": "1", "max": "1", @@ -188,6 +191,9 @@ "DisplayOrder" : 10, "DisplayConsent" : true, "Conditions": { + "error": { + "scope": true + }, "warning": { "max": "1", "regex": "#^([a-z0-9][a-z0-9=-]{3,}|[A-Z0-9][A-Z0-9=-]{3,})@[a-z0-9][a-z0-9.-]+\\.[a-z]{2,}$#" @@ -362,6 +368,7 @@ "error": { "min": "1", "max": "1", + "scope": true, "type": "HostName" }, "warning": { diff --git a/library/EngineBlock/Attributes/Validator.php b/library/EngineBlock/Attributes/Validator.php index 738dbad525..30670f455d 100644 --- a/library/EngineBlock/Attributes/Validator.php +++ b/library/EngineBlock/Attributes/Validator.php @@ -48,15 +48,18 @@ class EngineBlock_Attributes_Validator */ private $errors; + private $identityProvider; + public function __construct(array $definitions, EngineBlock_Attributes_Validator_Factory $validatorFactory) { $this->definitions = $definitions; $this->validatorFactory = $validatorFactory; } - public function validate(array $attributes, $excluded = array()) + public function validate(array $attributes, $excluded = array(), $identityProvider = null) { $this->attributes = $attributes; + $this->identityProvider = $identityProvider; $this->validAttributes = array(); $this->warnings = array(); $this->errors = array(); @@ -158,6 +161,7 @@ private function validateAttributeWarnings($attributeName, $definition) if (isset($definition['__original__'])) { $validator->setAttributeAlias($definition['__original__']); } + $validator->setIdentityProvider($this->identityProvider); $validationResult = $validator->validate($this->attributes); @@ -193,6 +197,7 @@ private function validateAttributeErrors($attributeName, $definition) if (isset($definition['__original__'])) { $validator->setAttributeAlias($definition['__original__']); } + $validator->setIdentityProvider($this->identityProvider); $validationResult = $validator->validate($this->attributes); diff --git a/library/EngineBlock/Attributes/Validator/Abstract.php b/library/EngineBlock/Attributes/Validator/Abstract.php index ee54e7792e..8fc19f91d1 100644 --- a/library/EngineBlock/Attributes/Validator/Abstract.php +++ b/library/EngineBlock/Attributes/Validator/Abstract.php @@ -40,12 +40,21 @@ abstract class EngineBlock_Attributes_Validator_Abstract implements EngineBlock_ */ protected $_messages = array(); + protected $_identityProvider; + public function __construct($attributeName, $options) { $this->_attributeName = $attributeName; $this->_options = $options; } + public function setIdentityProvider($identityProvider) + { + $this->_identityProvider = $identityProvider; + return $this; + } + + public function setAttributeAlias($aliasName) { $this->_attributeAlias = $aliasName; diff --git a/library/EngineBlock/Attributes/Validator/Interface.php b/library/EngineBlock/Attributes/Validator/Interface.php index a60a0055ee..9e7d5797cd 100644 --- a/library/EngineBlock/Attributes/Validator/Interface.php +++ b/library/EngineBlock/Attributes/Validator/Interface.php @@ -21,5 +21,6 @@ interface EngineBlock_Attributes_Validator_Interface public function __construct($attributeName, $options); public function validate(array $attributes); public function setAttributeAlias($alias); + public function setIdentityProvider($identityProvider); public function getMessages(); } diff --git a/library/EngineBlock/Attributes/Validator/Scope.php b/library/EngineBlock/Attributes/Validator/Scope.php new file mode 100644 index 0000000000..bc57dd7079 --- /dev/null +++ b/library/EngineBlock/Attributes/Validator/Scope.php @@ -0,0 +1,87 @@ +_attributeName])) { + return true; + } + + $logger = EngineBlock_ApplicationSingleton::getLog(); + + $scopes = $this->_identityProvider->shibMdScopes; + if (empty($scopes)) { + $logger->notice('No shibmd:scope found in the IdP metadata, not verifying ' . $this->_attributeName); + return true; + } + $scopeList = $this->buildScopeList($scopes); + + foreach ($attributes[$this->_attributeName] as $attributeValue) { + if($scopeList->inScope($attributeValue)) { + continue; + } + // consider moving this to inScope() + list(,$suffix) = explode('@', $attributeValue, 2); + if(empty($suffix) || $scopeList->inScope($suffix)) { + continue; + } + $logger->warning(sprintf( + '%s attribute value "%s" is not allowed by configured ShibMdScopes for IdP "%s"', + $this->_attributeName, + $attributeValue, + $this->_identityProvider->entityId + )); + $this->_messages[] = [ + self::ERROR_ATTRIBUTE_VALIDATOR_SCOPE, + $this->_attributeName, + $this->_options, + $attributeValue + ]; + return false; + } + return true; + } + + /** + * @param ShibMdScope[] $scopes + * @return ShibbolethMetadataScopeList + */ + private function buildScopeList(array $scopes) + { + $scopes = array_map( + function (ShibMdScope $scope) { + if (!$scope->regexp) { + return ShibbolethMetadataScope::literal($scope->allowed); + } + + return ShibbolethMetadataScope::regexp($scope->allowed); + }, + $scopes + ); + + return new ShibbolethMetadataScopeList($scopes); + } +} diff --git a/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php b/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php index dd9d09990b..3a25e84287 100644 --- a/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php +++ b/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php @@ -46,7 +46,7 @@ public function execute() $validationResult = EngineBlock_ApplicationSingleton::getInstance() ->getDiContainer() ->getAttributeValidator() - ->validate($this->_responseAttributes, $excluded); + ->validate($this->_responseAttributes, $excluded, $this->_identityProvider); if ($validationResult->hasErrors()) { throw new EngineBlock_Corto_Exception_MissingRequiredFields( diff --git a/library/EngineBlock/Corto/Filter/Input.php b/library/EngineBlock/Corto/Filter/Input.php index 5f753ab516..aa4a9afba2 100644 --- a/library/EngineBlock/Corto/Filter/Input.php +++ b/library/EngineBlock/Corto/Filter/Input.php @@ -64,16 +64,16 @@ public function getCommands() EngineBlock_Corto_Filter_Command_RunAttributeManipulations::TYPE_IDP ), - new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsSchacHomeOrganisation($logger, $blockUsersOnViolation), +// new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsSchacHomeOrganisation($logger, $blockUsersOnViolation), - new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsEduPersonPrincipalName($logger, $blockUsersOnViolation), + // new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsEduPersonPrincipalName($logger, $blockUsersOnViolation), - new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsSubjectId($logger, $blockUsersOnViolation), +// new EngineBlock_Corto_Filter_Command_VerifyShibMdScopingAllowsSubjectId($logger, $blockUsersOnViolation), // Check whether this IdP is allowed to send a response to the destination SP new EngineBlock_Corto_Filter_Command_ValidateAllowedConnection(), - // Require valid UID and SchacHomeOrganization + // Validate attributes that are required and check scopes new EngineBlock_Corto_Filter_Command_ValidateRequiredAttributes(), // Add guest status (isMemberOf) From e2d2b6734787f916c00607be993f1d31e0ece7af Mon Sep 17 00:00:00 2001 From: Thijs Kinkhorst Date: Thu, 18 Jul 2024 07:37:04 +0200 Subject: [PATCH 2/2] Pass attribute validation result to the error screen --- languages/messages.en.php | 8 ++++---- languages/messages.nl.php | 16 ++++++++-------- .../Corto/Exception/MissingRequiredFields.php | 18 ++++++++++++++++-- .../Command/ValidateRequiredAttributes.php | 16 ++++++++++++++-- ...RedirectToFeedbackPageExceptionListener.php | 2 +- .../Features/ClearErrorMessages.feature | 4 ++-- .../Feedback/missing-required-fields.html.twig | 8 +------- 7 files changed, 46 insertions(+), 26 deletions(-) diff --git a/languages/messages.en.php b/languages/messages.en.php index 5b335cb160..35061a9a7d 100644 --- a/languages/messages.en.php +++ b/languages/messages.en.php @@ -202,12 +202,12 @@ 'error_generic_desc_no_sp_name' => 'Logging in has failed and we don\'t know exactly why. Please try again first by going back to the service and logging in again. If this doesn\'t work, please contact the service desk of %idpName%.', 'error_generic_desc_no_idp_name' => 'Logging in has failed and we don\'t know exactly why. Please try again first by going back to %spName% and logging in again. If this doesn\'t work, please contact the service desk of your %organisationNoun%.', 'error_generic_desc_no_names' => 'Logging in has failed and we don\'t know exactly why. Please try again first by going back to the service and logging in again. If this doesn\'t work, please contact the service desk of your %organisationNoun%.', - 'error_missing_required_fields' => 'Error - Missing required fields', + 'error_missing_required_fields' => 'Error - Error validating attribute values', 'error_missing_required_fields_desc'=> '%idpName% does not provide the mandatory information or it has an invalid format. Therefore, you can not use %spName%. Please contact the service desk of %idpName% and tell them one or more of the the following required attributes are not being set correctly for %suiteName%:', - 'error_missing_required_fields_desc_no_idp_name'=> 'Your %organisationNoun% does not provide the mandatory information. Therefore, you can not use %spName%. Please contact your %organisationNoun% and tell them one or more of the the following required attribute(s) are missing within %suiteName%:', - 'error_missing_required_fields_desc_no_sp_name'=> '%idpName% does not provide the mandatory information. Therefore, you can not use this service. Please contact the service desk of %idpName% and tell them one or more of the the following required attribute(s) are missing within %suiteName%:', + 'error_missing_required_fields_desc_no_idp_name'=> 'Your %organisationNoun% does not provide the mandatory information. Therefore, you can not use %spName%. Please contact your %organisationNoun% and tell them one or more of the the following attributes have problems within %suiteName%:', + 'error_missing_required_fields_desc_no_sp_name'=> '%idpName% does not provide the mandatory information. Therefore, you can not use this service. Please contact the service desk of %idpName% and tell them one or more of the the following required attribute(s) have problems within %suiteName%:', 'error_missing_required_fields_desc_no_name'=> ' -Your %organisationNoun% does not provide the mandatory information. Therefore, you can not use this service. Please contact your %organisationNoun% and tell them one or more of the the following required attribute(s) are missing within %suiteName%:', +Your %organisationNoun% does not provide the mandatory information. Therefore, you can not use this service. Please contact your %organisationNoun% and tell them one or more of the the following required attribute(s) have problems within %suiteName%:', 'error_invalid_attribute_value' => 'Error - Attribute value not allowed', 'error_invalid_attribute_value_desc' => '%idpName% sends a value for attribute %attributeName% ("%attributeValue%") which is not allowed for this %organisationNoun%. Therefore you cannot log in. Only %idpName% can resolve this. Please contact the service desk of %idpName% to fix this problem.', 'error_invalid_attribute_value_desc_no_idp_name' => 'Your %organisationNoun% sends a value for attribute %attributeName% ("%attributeValue%") which is not allowed for this %organisationNoun%. Therefore you cannot log in. Only your %organisationNoun% can resolve this. Please contact the service desk of your own %organisationNoun% to fix this problem.', diff --git a/languages/messages.nl.php b/languages/messages.nl.php index aa29bbf6d0..018c063f90 100644 --- a/languages/messages.nl.php +++ b/languages/messages.nl.php @@ -200,14 +200,14 @@ 'error_generic_desc_no_sp_name' => 'Inloggen is niet gelukt en we weten niet precies waarom. Probeer het eerst eens opnieuw door terug te gaan naar de dienst en opnieuw in te loggen. Lukt dit niet, neem dan contact op met de helpdesk van %idpName%.', 'error_generic_desc_no_idp_name' => 'Inloggen is niet gelukt en we weten niet precies waarom. Probeer het eerst eens opnieuw door terug te gaan naar %spName% en opnieuw in te loggen. Lukt dit niet, neem dan contact op met de helpdesk van je %organisationNoun%.', 'error_generic_desc_no_names' => 'Inloggen is niet gelukt en we weten niet precies waarom. Probeer het eerst eens opnieuw door terug te gaan naar de dienst en opnieuw in te loggen. Lukt dit niet, neem dan contact op met de helpdesk van je %organisationNoun%.', - 'error_missing_required_fields' => 'Error - Verplichte velden ontbreken', - 'error_missing_required_fields_desc'=> '%idpName% geeft niet de benodigde informatie vrij. Daarom kun je %spName% niet gebruiken. Neem alstublieft contact op met de helpdesk van %idpName%. Geef hierbij de onderstaande informatie door. Omdat %idpName% niet de juiste attributen aan %suiteName% doorgeeft, of in het onjuiste formaat, is het inloggen mislukt. De volgende attributen zijn vereist om succesvol in te loggen op het %suiteName% platform:', - 'error_missing_required_fields_desc_no_idp_name'=> 'Jouw %organisationNoun% geeft niet de benodigde informatie vrij. Daarom kun je %spName% niet gebruiken. Neem alstublieft contact op met de helpdesk van jouw %organisationNoun%. Geef hierbij de onderstaande informatie door. Omdat je %organisationNoun% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende attributen zijn vereist om succesvol in te loggen op het %suiteName% platform:', - 'error_missing_required_fields_desc_no_sp_name'=> '%idpName% geeft niet de benodigde informatie vrij. Daarom kun je deze applicatie niet gebruiken. Neem alstublieft contact op met de helpdesk van %idpName%. Geef hierbij de onderstaande informatie door. Omdat %idpName% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende attributen zijn vereist om succesvol in te loggen op het %suiteName% platform:', - 'error_missing_required_fields_desc_no_name'=> 'Jouw %organisationNoun% geeft niet de benodigde informatie vrij. Daarom kun je deze applicatie niet gebruiken. Neem alstublieft contact op met jouw %organisationNoun%. Geef hierbij de onderstaande informatie door. Omdat je %organisationNoun% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende attributen zijn vereist om succesvol in te loggen op het %suiteName% platform:', - 'error_invalid_attribute_value' => 'Fout - Attribuutwaarde niet toegestaan', - 'error_invalid_attribute_value_desc' => '%idpName% geeft een waarde door in het attribuut %attributeName% ("%attributeValue%") die niet is toegestaan voor deze %organisationNoun%. Inloggen is daarom niet mogelijk. Alleen %idpName% kan dit oplossen. Neem dus contact op met de helpdesk van %idpName%.', - 'error_invalid_attribute_value_desc_no_idp_name' => 'Je %organisationNoun% geeft een waarde door in het attribuut %attributeName% ("%attributeValue%") die niet is toegestaan voor deze %organisationNoun%. Inloggen is daarom niet mogelijk. Alleen jouw %organisationNoun% kan dit oplossen. Neem dus contact op met de helpdesk van je eigen %organisationNoun%.', + 'error_missing_required_fields' => 'Error - Probleem met attribuutwaarden', + 'error_missing_required_fields_desc'=> '%idpName% geeft niet de juiste informatie vrij. Daarom kun je %spName% niet gebruiken. Neem alstublieft contact op met de helpdesk van %idpName%. Geef hierbij de onderstaande informatie door. Omdat %idpName% niet de juiste attributen aan %suiteName% doorgeeft, of in het onjuiste formaat, is het inloggen mislukt. De volgende problemen zijn gevonden door het %suiteName% platform:', + 'error_missing_required_fields_desc_no_idp_name'=> 'Jouw %organisationNoun% geeft niet de juiste informatie vrij. Daarom kun je %spName% niet gebruiken. Neem alstublieft contact op met de helpdesk van jouw %organisationNoun%. Geef hierbij de onderstaande informatie door. Omdat je %organisationNoun% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende problemen zijn gevonden door het %suiteName% platform:', + 'error_missing_required_fields_desc_no_sp_name'=> '%idpName% geeft niet de juiste informatie vrij. Daarom kun je deze applicatie niet gebruiken. Neem alstublieft contact op met de helpdesk van %idpName%. Geef hierbij de onderstaande informatie door. Omdat %idpName% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende problemen zijn gevonden door het %suiteName% platform:', + 'error_missing_required_fields_desc_no_name'=> 'Jouw %organisationNoun% geeft niet de juiste informatie vrij. Daarom kun je deze applicatie niet gebruiken. Neem alstublieft contact op met jouw %organisationNoun%. Geef hierbij de onderstaande informatie door. Omdat je %organisationNoun% niet de juiste attributen aan %suiteName% doorgeeft is het inloggen mislukt. De volgende problemen zijn gevonden door het %suiteName% platform:', +# 'error_invalid_attribute_value' => 'Fout - Attribuutwaarde niet toegestaan', +# 'error_invalid_attribute_value_desc' => '%idpName% geeft een waarde door in het attribuut %attributeName% ("%attributeValue%") die niet is toegestaan voor deze %organisationNoun%. Inloggen is daarom niet mogelijk. Alleen %idpName% kan dit oplossen. Neem dus contact op met de helpdesk van %idpName%.', +# 'error_invalid_attribute_value_desc_no_idp_name' => 'Je %organisationNoun% geeft een waarde door in het attribuut %attributeName% ("%attributeValue%") die niet is toegestaan voor deze %organisationNoun%. Inloggen is daarom niet mogelijk. Alleen jouw %organisationNoun% kan dit oplossen. Neem dus contact op met de helpdesk van je eigen %organisationNoun%.', 'error_received_error_status_code' => 'Error - Fout bij Identity Provider', 'error_received_error_status_code_desc'=> 'Je %organisationNoun% heeft je de toegang geweigerd tot deze dienst. Je zult dus contact moeten opnemen met de (IT-)helpdesk van je eigen %organisationNoun% om te kijken of dit verholpen kan worden.', 'error_received_invalid_response' => 'Fout - Ongeldig SAML-bericht van %idpName%', diff --git a/library/EngineBlock/Corto/Exception/MissingRequiredFields.php b/library/EngineBlock/Corto/Exception/MissingRequiredFields.php index cf8a1eeef1..8cf4c84bec 100644 --- a/library/EngineBlock/Corto/Exception/MissingRequiredFields.php +++ b/library/EngineBlock/Corto/Exception/MissingRequiredFields.php @@ -16,10 +16,24 @@ * limitations under the License. */ -class EngineBlock_Corto_Exception_MissingRequiredFields extends EngineBlock_Exception +class EngineBlock_Corto_Exception_MissingRequiredFields extends EngineBlock_Exception implements EngineBlock_Corto_Exception_HasFeedbackInfoInterface { - public function __construct($message, $severity = self::CODE_NOTICE, Exception $previous = null) + /** + * @var array + */ + private $feedback; + + public function __construct($message, $errors, $severity = self::CODE_NOTICE, Exception $previous = null) { + $this->feedback = $errors; parent::__construct($message, $severity, $previous); } + + /** + * @return array + */ + public function getFeedbackInfo() + { + return $this->feedback; + } } diff --git a/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php b/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php index 3a25e84287..2104094781 100644 --- a/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php +++ b/library/EngineBlock/Corto/Filter/Command/ValidateRequiredAttributes.php @@ -49,12 +49,24 @@ public function execute() ->validate($this->_responseAttributes, $excluded, $this->_identityProvider); if ($validationResult->hasErrors()) { + $errors = $validationResult->getErrors(); + + $messages = ""; + foreach($errors as $attribute => $violations) { + $messages .= "$attribute: "; + foreach($violations as $violation) { + $messages .= $violation[0].' "' .$violation[3].'"'; + } + $messages .= "\n"; + } + throw new EngineBlock_Corto_Exception_MissingRequiredFields( sprintf( 'Errors validating attributes errors: "%s" attributes: "%s"', - print_r($validationResult->getErrors(), true), + print_r($errors, true), print_r($this->_responseAttributes, true) - ) + ), + ['validationMessages' => $messages] ); } } diff --git a/src/OpenConext/EngineBlockBundle/EventListener/RedirectToFeedbackPageExceptionListener.php b/src/OpenConext/EngineBlockBundle/EventListener/RedirectToFeedbackPageExceptionListener.php index aa17af6f18..8776131046 100644 --- a/src/OpenConext/EngineBlockBundle/EventListener/RedirectToFeedbackPageExceptionListener.php +++ b/src/OpenConext/EngineBlockBundle/EventListener/RedirectToFeedbackPageExceptionListener.php @@ -121,7 +121,7 @@ public function onKernelException(GetResponseForExceptionEvent $event) $message = 'Invalid ACS location'; $redirectToRoute = 'authentication_feedback_invalid_acs_location'; } elseif ($exception instanceof EngineBlock_Corto_Exception_MissingRequiredFields) { - $message = 'Missing Required Fields'; + $message = 'Attribute Validation Error'; $redirectToRoute = 'authentication_feedback_missing_required_fields'; } elseif ($exception instanceof EngineBlock_Corto_Exception_AuthnContextClassRefBlacklisted) { $message = $exception->getMessage(); diff --git a/src/OpenConext/EngineBlockFunctionalTestingBundle/Features/ClearErrorMessages.feature b/src/OpenConext/EngineBlockFunctionalTestingBundle/Features/ClearErrorMessages.feature index 6783f5e926..388fae7314 100644 --- a/src/OpenConext/EngineBlockFunctionalTestingBundle/Features/ClearErrorMessages.feature +++ b/src/OpenConext/EngineBlockFunctionalTestingBundle/Features/ClearErrorMessages.feature @@ -172,7 +172,7 @@ Feature: When I log in at "Dummy SP" And I pass through EngineBlock And I pass through the IdP - Then I should see "Missing required fields" + Then I should see "Error validating attribute values" And I should see "UID" And I should see "UR ID:" And I should see "IP:" @@ -186,7 +186,7 @@ Feature: When I log in at "Dummy SP" And I pass through EngineBlock And I pass through the IdP - Then I should see "Missing required fields" + Then I should see "Error validating attribute values" And I should see "schacHomeOrganization" And I should see "UR ID:" And I should see "IP:" diff --git a/theme/base/templates/modules/Authentication/View/Feedback/missing-required-fields.html.twig b/theme/base/templates/modules/Authentication/View/Feedback/missing-required-fields.html.twig index f070a9c8b5..2cf199a656 100644 --- a/theme/base/templates/modules/Authentication/View/Feedback/missing-required-fields.html.twig +++ b/theme/base/templates/modules/Authentication/View/Feedback/missing-required-fields.html.twig @@ -18,11 +18,5 @@ {% endif %}

-

-

-

- +

{{ feedbackInfoMap['validationMessages'] }}

{% endblock %}