fix: make email readonly in personal data form to prevent user lockout

This commit is contained in:
Björn Fromme
2026-08-11 18:08:48 +02:00
parent a1506ef58d
commit 70a10c4dba
8 changed files with 296 additions and 18 deletions
@@ -84,11 +84,20 @@ class PersonalDataController extends AbstractController
$newsletterSubscribed = $this->newsletterManager->hasConfirmedOptIn($email);
$newsletterPendingConfirmation = $this->newsletterManager->hasPendingConfirmation($email);
// The e-mail is deliberately locked here: BPN stores it on the address, where it doubles as
// the login identity of any person on that address. Accepting a submitted value would let
// one household member overwrite another's login. It is only ever echoed back to BPN as read.
$personalDataForm = $this->createForm(PersonalDataType::class, $personalData, [
'attr' => ['novalidate' => 'novalidate'],
'validation_groups' => ['personal_data'],
'email_editable' => false,
]);
$personalDataForm->handleRequest($request);
// A failed load yields an empty PersonalData. Updating from it would send blank values for
// fields we never read - including the e-mail - so the form must not accept a submission.
if (null !== $personalData->addressId) {
$personalDataForm->handleRequest($request);
}
if ($personalDataForm->isSubmitted() && $personalDataForm->isValid()) {
try {
+20
View File
@@ -11,10 +11,13 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/** @extends AbstractType<mixed> */
class PersonalDataType extends AbstractType
{
public const string LOCKED_EMAIL_HINT = 'Deine E-Mail-Adresse ist auch dein Benutzername. Bitte wende dich an den Kundenservice, wenn du sie ändern möchtest.';
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
@@ -61,9 +64,18 @@ class PersonalDataType extends AbstractType
'placeholder' => 'Bitte auswählen...',
'preferred_choices' => ['D', 'A', 'CH'],
])
// The address level e-mail doubles as the BPN login identity, and a single BPN address
// can carry several of them for different persons. Editing it here would let one
// household member overwrite another's login, so forms that only change an existing
// address lock the field: Symfony ignores submitted data for disabled fields, which
// keeps the value BPN handed us on its way back into the update payload.
->add('email', EmailType::class, [
'label' => 'E-Mail',
'property_path' => 'communication.email',
'disabled' => false === $options['email_editable'],
'attr' => false === $options['email_editable'] ? [
'title' => self::LOCKED_EMAIL_HINT,
] : [],
])
->add('phone', TextType::class, [
'label' => 'Telefon',
@@ -78,4 +90,12 @@ class PersonalDataType extends AbstractType
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver
->setDefault('email_editable', true)
->setAllowedTypes('email_editable', 'bool')
;
}
}