feat: enforce minimum password length
This commit is contained in:
1 parent
161029ec7c
commit
69885b939a
4 files changed
+109
-19
No files matched your search
@@ -12,6 +12,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
@@ -82,16 +83,17 @@ class ResetPasswordController extends AbstractController
|
||||
'type' => PasswordType::class,
|
||||
'first_options' => [
|
||||
'label' => 'Neues Passwort',
|
||||
'constraints' => [
|
||||
new Length(
|
||||
min: 8,
|
||||
minMessage: 'Das Passwort muss mindestens {{ limit }} Zeichen lang sein.',
|
||||
),
|
||||
],
|
||||
],
|
||||
'second_options' => [
|
||||
'label' => 'Neues Passwort wiederholen',
|
||||
],
|
||||
'invalid_message' => 'Die beiden Passwortangaben müssen identisch sein.',
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Bitte angeben',
|
||||
]),
|
||||
],
|
||||
])
|
||||
->getForm()
|
||||
;
|
||||
|
||||
@@ -11,6 +11,9 @@ use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\PasswordStrength;
|
||||
use Symfony\Component\Validator\Constraints\Sequentially;
|
||||
|
||||
/** @extends AbstractType<RegistrationDto> */
|
||||
class RegistrationType extends AbstractType
|
||||
@@ -30,6 +33,12 @@ class RegistrationType extends AbstractType
|
||||
'type' => PasswordType::class,
|
||||
'first_options' => [
|
||||
'label' => 'Passwort',
|
||||
'constraints' => [
|
||||
new Length(
|
||||
min: 8,
|
||||
minMessage: 'Das Passwort muss mindestens {{ limit }} Zeichen lang sein.',
|
||||
),
|
||||
],
|
||||
'attr' => [
|
||||
'autocomplete' => 'new-password',
|
||||
],
|
||||
|
||||
@@ -23,6 +23,7 @@ class RegistrationControllerTest extends WebTestCase
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
'HTTPS' => 'on',
|
||||
'HTTP_HX_REQUEST' => 'true',
|
||||
]);
|
||||
$client->disableReboot();
|
||||
|
||||
@@ -54,8 +55,8 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'secret-password',
|
||||
'registration[plainPassword][second]' => 'secret-password',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
@@ -107,8 +108,8 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'secret-password',
|
||||
'registration[plainPassword][second]' => 'secret-password',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
@@ -153,8 +154,8 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'secret-password',
|
||||
'registration[plainPassword][second]' => 'secret-password',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
@@ -171,6 +172,52 @@ class RegistrationControllerTest extends WebTestCase
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
}
|
||||
|
||||
public function testMismatchedPasswordRegistrationSubmissionIsRejectedBeforeApiCalls(): void
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
'HTTPS' => 'on',
|
||||
]);
|
||||
$client->disableReboot();
|
||||
|
||||
$userRepository = $this->createMock(UserRepository::class);
|
||||
$userRepository->expects(self::never())->method('findOneByNormalizedEmail');
|
||||
|
||||
$rateLimiter = $this->createMock(PublicFormRateLimiter::class);
|
||||
$rateLimiter->expects(self::never())->method('isRegistrationAllowed');
|
||||
|
||||
$apiClient = $this->createMock(ApiClient::class);
|
||||
$apiClient->expects(self::never())->method('register');
|
||||
$apiClient->expects(self::never())->method('getCrmAttributesWithPassword');
|
||||
$apiClient->expects(self::never())->method('getPersonalDataWithPassword');
|
||||
|
||||
self::getContainer()->set(UserRepository::class, $userRepository);
|
||||
self::getContainer()->set(PublicFormRateLimiter::class, $rateLimiter);
|
||||
self::getContainer()->set(ApiClient::class, $apiClient);
|
||||
|
||||
$crawler = $client->request('GET', '/registration');
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
'registration[personalData][dateOfBirth][year]' => '1990',
|
||||
'registration[personalData][street]' => 'Main Street 1',
|
||||
'registration[personalData][postCode]' => '12345',
|
||||
'registration[personalData][city]' => 'Berlin',
|
||||
'registration[personalData][country]' => 'DE',
|
||||
'registration[personalData][nationality]' => 'DE',
|
||||
'registration[personalData][email]' => '[email protected]',
|
||||
'registration[personalData][mobile]' => '0123456789',
|
||||
]);
|
||||
$client->submit($form);
|
||||
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
self::assertStringContainsString('Die beiden Passwortangaben müssen identisch sein.', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testSuccessfulRegistrationDoesNotRefetchPersonalData(): void
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
@@ -214,7 +261,7 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$passwordHasher
|
||||
->expects(self::once())
|
||||
->method('hashPassword')
|
||||
->with(self::isInstanceOf(User::class), 'secret-password')
|
||||
->with(self::isInstanceOf(User::class), 'Str0ng-Passw0rd!')
|
||||
->willReturn('hashed-secret');
|
||||
|
||||
$persistedUser = null;
|
||||
@@ -241,8 +288,8 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'secret-password',
|
||||
'registration[plainPassword][second]' => 'secret-password',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
@@ -319,8 +366,8 @@ class RegistrationControllerTest extends WebTestCase
|
||||
$form = $crawler->selectButton('Jetzt registrieren')->form([
|
||||
'registration[firstName]' => 'Customer',
|
||||
'registration[name]' => 'Example',
|
||||
'registration[plainPassword][first]' => 'secret-password',
|
||||
'registration[plainPassword][second]' => 'secret-password',
|
||||
'registration[plainPassword][first]' => 'Str0ng-Passw0rd!',
|
||||
'registration[plainPassword][second]' => 'Str0ng-Passw0rd!',
|
||||
'registration[personalData][gender]' => 'M',
|
||||
'registration[personalData][dateOfBirth][day]' => '1',
|
||||
'registration[personalData][dateOfBirth][month]' => '1',
|
||||
|
||||
@@ -16,6 +16,7 @@ class ResetPasswordControllerTest extends WebTestCase
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
'HTTPS' => 'on',
|
||||
'HTTP_HX_REQUEST' => 'true',
|
||||
]);
|
||||
$client->disableReboot();
|
||||
|
||||
@@ -121,7 +122,7 @@ class ResetPasswordControllerTest extends WebTestCase
|
||||
$manager
|
||||
->expects(self::once())
|
||||
->method('resetPassword')
|
||||
->with($user, 'new-password');
|
||||
->with($user, 'Str0ng-Passw0rd!');
|
||||
self::getContainer()->set(PasswordResetManager::class, $manager);
|
||||
|
||||
$crawler = $client->request('GET', '/reset-password/[email protected]&nonce=token&expires=9999999999&_hash=signature');
|
||||
@@ -129,8 +130,8 @@ class ResetPasswordControllerTest extends WebTestCase
|
||||
self::assertSelectorTextContains('h1', 'Neues Passwort');
|
||||
|
||||
$form = $crawler->selectButton('Passwort speichern')->form([
|
||||
'form[password][first]' => 'new-password',
|
||||
'form[password][second]' => 'new-password',
|
||||
'form[password][first]' => 'Str0ng-Passw0rd!',
|
||||
'form[password][second]' => 'Str0ng-Passw0rd!',
|
||||
]);
|
||||
$client->submit($form);
|
||||
|
||||
@@ -139,4 +140,35 @@ class ResetPasswordControllerTest extends WebTestCase
|
||||
|
||||
self::assertStringContainsString('Dein Passwort wurde aktualisiert', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testPasswordResetConfirmationRejectsTooShortPasswordBeforeSaving(): void
|
||||
{
|
||||
$client = self::createClient([], [
|
||||
'HTTPS' => 'on',
|
||||
]);
|
||||
$client->disableReboot();
|
||||
|
||||
$user = new User('[email protected]');
|
||||
$manager = $this->createMock(PasswordResetManager::class);
|
||||
$manager
|
||||
->expects(self::exactly(2))
|
||||
->method('validateResetRequest')
|
||||
->with(self::isInstanceOf(Request::class))
|
||||
->willReturn($user);
|
||||
$manager->expects(self::never())->method('resetPassword');
|
||||
self::getContainer()->set(PasswordResetManager::class, $manager);
|
||||
|
||||
$crawler = $client->request('GET', '/reset-password/[email protected]&nonce=token&expires=9999999999&_hash=signature');
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertSelectorTextContains('h1', 'Neues Passwort');
|
||||
|
||||
$form = $crawler->selectButton('Passwort speichern')->form([
|
||||
'form[password][first]' => 'short7',
|
||||
'form[password][second]' => 'short7',
|
||||
]);
|
||||
$client->submit($form);
|
||||
|
||||
self::assertResponseStatusCodeSame(422);
|
||||
self::assertStringContainsString('Das Passwort muss mindestens 8 Zeichen lang sein.', $client->getResponse()->getContent());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user