Skip to content

Commit 64fa4b5

Browse files
committed
feature #54881 [Validator] Make PasswordStrengthValidator::estimateStrength() public (yalit)
This PR was merged into the 7.2 branch. Discussion ---------- [Validator] Make `PasswordStrengthValidator::estimateStrength()` public | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | no | New feature? | yes (allows for externally | Deprecations? | no | Issues | na | License | MIT Linked to the PasswordStrength Constraint, the need is to be able to get the "strength" of a password and not just only if it's strong enough. For instance, the need is to show a "score" bar in the frontend. Currently, the strength is being computed by a private function within the PasswordStrengthValidator not accessible directly. The proposed change is to extract that computation in a specific `PasswordStrengthEstimator` service for it to be accessible from the "external" world. The best way to do so would be to inject the service in the Validator constructor but doing that it would break the compatibility as the current constructor is receiving a Closure to allow for self-strength computation. So here the service is called from within the Validator directly and so maintaining the Backward Compatibility I created the same PR but with no Backward Compatibility (service injected in the constructor) here : #54882 Commits ------- e058d92 [Validator] Make `PasswordStrengthValidator::estimateStrength()` public
2 parents 0d01ce0 + e058d92 commit 64fa4b5

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.2
5+
---
6+
7+
* Make `PasswordStrengthValidator::estimateStrength()` public
8+
49
7.1
510
---
611

src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public function validate(#[\SensitiveParameter] mixed $value, Constraint $constr
5757
*
5858
* @return PasswordStrength::STRENGTH_*
5959
*/
60-
private static function estimateStrength(#[\SensitiveParameter] string $password): int
60+
public static function estimateStrength(#[\SensitiveParameter] string $password): int
6161
{
6262
if (!$length = \strlen($password)) {
6363
return PasswordStrength::STRENGTH_VERY_WEAK;

src/Symfony/Component/Validator/Tests/Constraints/PasswordStrengthValidatorTest.php

+16
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,20 @@ public static function provideInvalidConstraints(): iterable
9292
'0',
9393
];
9494
}
95+
96+
/**
97+
* @dataProvider getPasswordValues
98+
*/
99+
public function testStrengthEstimator(string $password, int $expectedStrength)
100+
{
101+
self::assertSame($expectedStrength, PasswordStrengthValidator::estimateStrength((string) $password));
102+
}
103+
104+
public static function getPasswordValues(): iterable
105+
{
106+
yield ['How-is-this', PasswordStrength::STRENGTH_WEAK];
107+
yield ['Reasonable-pwd', PasswordStrength::STRENGTH_MEDIUM];
108+
yield ['This 1s a very g00d Pa55word! ;-)', PasswordStrength::STRENGTH_VERY_STRONG];
109+
yield ['pudding-smack-👌🏼-fox-😎', PasswordStrength::STRENGTH_VERY_STRONG];
110+
}
95111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Tests\Constraints;
13+
14+
use Symfony\Component\Validator\Constraints\PasswordStrength;
15+
use Symfony\Component\Validator\Constraints\PasswordStrengthValidator;
16+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
17+
use Symfony\Component\Validator\Tests\Constraints\Fixtures\StringableValue;
18+
19+
class PasswordStrengthValidatorWithClosureTest extends ConstraintValidatorTestCase
20+
{
21+
protected function createValidator(): PasswordStrengthValidator
22+
{
23+
return new PasswordStrengthValidator(static function (string $value) {
24+
$length = \strlen($value);
25+
26+
return match (true) {
27+
$length < 6 => PasswordStrength::STRENGTH_VERY_WEAK,
28+
$length < 10 => PasswordStrength::STRENGTH_WEAK,
29+
$length < 15 => PasswordStrength::STRENGTH_MEDIUM,
30+
$length < 20 => PasswordStrength::STRENGTH_STRONG,
31+
default => PasswordStrength::STRENGTH_VERY_STRONG,
32+
};
33+
});
34+
}
35+
36+
/**
37+
* @dataProvider getValidValues
38+
*/
39+
public function testValidValues(string|\Stringable $value, int $expectedStrength)
40+
{
41+
$this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength));
42+
43+
$this->assertNoViolation();
44+
45+
if (PasswordStrength::STRENGTH_VERY_STRONG === $expectedStrength) {
46+
return;
47+
}
48+
49+
$this->validator->validate($value, new PasswordStrength(minScore: $expectedStrength + 1));
50+
51+
$this->buildViolation('The password strength is too low. Please use a stronger password.')
52+
->setCode(PasswordStrength::PASSWORD_STRENGTH_ERROR)
53+
->setParameter('{{ strength }}', $expectedStrength)
54+
->assertRaised();
55+
}
56+
57+
public static function getValidValues(): iterable
58+
{
59+
yield ['az34tyu', PasswordStrength::STRENGTH_WEAK];
60+
yield ['A med1um one', PasswordStrength::STRENGTH_MEDIUM];
61+
yield ['a str0ng3r one doh', PasswordStrength::STRENGTH_STRONG];
62+
yield [new StringableValue('HeloW0rld'), PasswordStrength::STRENGTH_WEAK];
63+
}
64+
65+
/**
66+
* @dataProvider provideInvalidConstraints
67+
*/
68+
public function testThePasswordIsWeak(PasswordStrength $constraint, string $password, string $expectedMessage, string $expectedCode, string $strength)
69+
{
70+
$this->validator->validate($password, $constraint);
71+
72+
$this->buildViolation($expectedMessage)
73+
->setCode($expectedCode)
74+
->setParameters([
75+
'{{ strength }}' => $strength,
76+
])
77+
->assertRaised();
78+
}
79+
80+
public static function provideInvalidConstraints(): iterable
81+
{
82+
yield [
83+
new PasswordStrength(),
84+
'password',
85+
'The password strength is too low. Please use a stronger password.',
86+
PasswordStrength::PASSWORD_STRENGTH_ERROR,
87+
(string) PasswordStrength::STRENGTH_WEAK,
88+
];
89+
yield [
90+
new PasswordStrength(minScore: PasswordStrength::STRENGTH_VERY_STRONG),
91+
'Good password?',
92+
'The password strength is too low. Please use a stronger password.',
93+
PasswordStrength::PASSWORD_STRENGTH_ERROR,
94+
(string) PasswordStrength::STRENGTH_MEDIUM,
95+
];
96+
yield [
97+
new PasswordStrength(message: 'This password should be strong.'),
98+
'password',
99+
'This password should be strong.',
100+
PasswordStrength::PASSWORD_STRENGTH_ERROR,
101+
(string) PasswordStrength::STRENGTH_WEAK,
102+
];
103+
}
104+
}

0 commit comments

Comments
 (0)