diff --git a/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php b/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php index f6b763c747fc..8070775fce68 100644 --- a/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/InputTextElement.php @@ -95,7 +95,7 @@ public function render() if ($config['readOnly'] ?? false) { // Early return for read only fields if (in_array('password', $evalList, true)) { - $itemValue = $itemValue ? '*********' : ''; + $itemValue = $this->getObfuscatedSecretValue($itemValue); } $disabledFieldAttributes = [ @@ -240,6 +240,7 @@ public function render() $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); $inputType = 'text'; + $hiddenElementProps = ' value="' . htmlspecialchars((string)$itemValue) . '"'; if (in_array('email', $evalList, true)) { $inputType = 'email'; @@ -255,6 +256,7 @@ public function render() } if (in_array('password', $evalList, true)) { $attributes['spellcheck'] = 'false'; + $hiddenElementProps = ' value="' . htmlspecialchars($this->getObfuscatedSecretValue($itemValue)) . '" disabled data-enable-on-modification="true"'; } $mainFieldHtml = []; @@ -262,7 +264,7 @@ public function render() $mainFieldHtml[] = '
'; $mainFieldHtml[] = '
'; $mainFieldHtml[] = ''; - $mainFieldHtml[] = ''; + $mainFieldHtml[] = ''; $mainFieldHtml[] = '
'; if (!empty($valuePickerHtml) || !empty($valueSliderHtml) || !empty($fieldControlHtml)) { $mainFieldHtml[] = '
'; @@ -342,6 +344,21 @@ public function render() return $resultArray; } + /** + * Obfuscated a (hashed) password secret with a static string. + * + * @todo + * + server-side password obfuscation value is `*********` (9 chars) + * + client-side password obfuscation value is `********` (8 chars) + */ + protected function getObfuscatedSecretValue(?string $value): string + { + if ($value === null || $value === '') { + return ''; + } + return '*********'; + } + /** * @return LanguageService */ diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineValidation.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineValidation.js index 894d50c145a7..7b381530ced7 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineValidation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngineValidation.js @@ -245,7 +245,9 @@ define([ for (i = 0; i < evalList.length; i++) { formattedValue = FormEngineValidation.formatValue(evalList[i], formattedValue, config); } - + if ($mainField.prop('disabled') && $mainField.data('enableOnModification')) { + $mainField.prop('disabled', false); + } $mainField.val(newValue); // After updating the value of the main field, dispatch a "change" event to inform e.g. the "RequestUpdate" // component, which always listens to the main field instead of the "human readable field", about it. @@ -570,6 +572,9 @@ define([ modified = true; } if (modified) { + if ($field.prop('disabled') && $field.data('enableOnModification')) { + $field.prop('disabled', false); + } $field.val(newValue); } } diff --git a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputSimpleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputSimpleCest.php index 50237fa1740f..041fdfc989d7 100644 --- a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputSimpleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputSimpleCest.php @@ -141,7 +141,8 @@ protected function simpleInputFieldsDataProvider(): array 'inputValue' => 'Kasper', 'expectedValue' => '********', 'expectedInternalValue' => 'Kasper', - 'expectedValueAfterSave' => 'Kasper', + // even if `password_2` is not hashed, it never should expose the value + 'expectedValueAfterSave' => '*********', 'comment' => '', ], [