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
+119
View File
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace App\Tests\Form;
use App\BusProNet\DataProvider\CountryDataProvider;
use App\BusProNet\Form\CountryType;
use App\BusProNet\Model\PersonalData;
use App\Form\PersonalDataType;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
use Symfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Forms;
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
class PersonalDataTypeTest extends TestCase
{
public function testEmailIsEditableByDefault(): void
{
$form = $this->createForm(new PersonalData());
self::assertFalse($form->get('email')->getConfig()->getOption('disabled'));
}
public function testEmailIsRenderedLockedWithATooltipWhenNotEditable(): void
{
$form = $this->createForm(new PersonalData(), ['email_editable' => false]);
$config = $form->get('email')->getConfig();
self::assertTrue($config->getOption('disabled'));
self::assertSame(
PersonalDataType::LOCKED_EMAIL_HINT,
$config->getOption('attr')['title'] ?? null,
);
}
/**
* The BPN address level e-mail is a login identity shared across every person on the address.
* A submitted value must not be able to reach the model - otherwise saving the form would
* overwrite somebody else's login.
*/
public function testSubmittedEmailIsIgnoredWhenNotEditable(): void
{
$personalData = new PersonalData();
$personalData->communication->email = '[email protected]';
$form = $this->createForm($personalData, ['email_editable' => false]);
$form->submit($this->submittedValues(['email' => '[email protected]']));
self::assertTrue($form->isSynchronized());
self::assertSame('[email protected]', $personalData->communication->email);
}
public function testSubmittedEmailIsAppliedByDefault(): void
{
$personalData = new PersonalData();
$personalData->communication->email = '[email protected]';
$form = $this->createForm($personalData);
$form->submit($this->submittedValues(['email' => '[email protected]']));
self::assertTrue($form->isSynchronized());
self::assertSame('[email protected]', $personalData->communication->email);
}
/**
* @param array<string, mixed> $overrides
*
* @return array<string, mixed>
*/
private function submittedValues(array $overrides = []): array
{
return array_merge([
'gender' => 'W',
'dateOfBirth' => '01.01.1990',
'street' => 'Musterweg 1',
'postCode' => '12345',
'city' => 'Musterstadt',
'country' => 'D',
'nationality' => 'D',
'phone' => '',
'mobile' => '0170 1234567',
], $overrides);
}
/**
* @param array<string, mixed> $options
*/
private function createForm(PersonalData $personalData, array $options = []): FormInterface
{
$countries = $this->createMock(CountryDataProvider::class);
$countries->method('getAll')->willReturn([]);
return Forms::createFormFactoryBuilder()
->addType(new CountryType($countries))
->addTypeExtension(new TextTypeHtmlSanitizerExtension($this->sanitizers()))
->getFormFactory()
->create(PersonalDataType::class, $personalData, $options)
;
}
private function sanitizers(): ContainerInterface
{
return new class implements ContainerInterface {
public function get(string $id): HtmlSanitizer
{
return new HtmlSanitizer(new HtmlSanitizerConfig());
}
public function has(string $id): bool
{
return true;
}
};
}
}