@@ -28,40 +28,22 @@ class NotCompromisedPasswordValidatorTest extends ConstraintValidatorTestCase
28
28
private const PASSWORD_TRIGGERING_AN_ERROR_RANGE_URL = 'https://api.pwnedpasswords.com/range/3EF27 ' ; // https://api.pwnedpasswords.com/range/3EF27 is the range for the value "apiError"
29
29
private const PASSWORD_LEAKED = 'maman ' ;
30
30
private const PASSWORD_NOT_LEAKED = ']<0585"%sb^5aa$w6!b38",,72?dp3r4\45b28Hy ' ;
31
+ private const PASSWORD_NON_UTF8_LEAKED = 'мама ' ;
32
+ private const PASSWORD_NON_UTF8_NOT_LEAKED = 'м<в0dp3r4\45b28Hy ' ;
31
33
32
34
private const RETURN = [
33
35
'35E033023A46402F94CFB4F654C5BFE44A1:1 ' ,
34
36
'35F079CECCC31812288257CD770AA7968D7:53 ' ,
35
- '36039744C253F9B2A4E90CBEDB02EBFB82D:5 ' , // this is the matching line, password: maman
37
+ '36039744C253F9B2A4E90CBEDB02EBFB82D:5 ' , // UTF-8 leaked password: maman
38
+ '273CA8A2A78C9B2D724144F4FAF4D221C86:6 ' , // ISO-8859-5 leaked password: мама
36
39
'3686792BBC66A72D40D928ED15621124CFE:7 ' ,
37
40
'36EEC709091B810AA240179A44317ED415C:2 ' ,
38
41
];
39
42
40
43
protected function createValidator ()
41
44
{
42
- $ httpClientStub = $ this ->createMock (HttpClientInterface::class);
43
- $ httpClientStub ->method ('request ' )->will (
44
- $ this ->returnCallback (function (string $ method , string $ url ): ResponseInterface {
45
- if (self ::PASSWORD_TRIGGERING_AN_ERROR_RANGE_URL === $ url ) {
46
- throw new class ('Problem contacting the Have I been Pwned API. ' ) extends \Exception implements ServerExceptionInterface {
47
- public function getResponse (): ResponseInterface
48
- {
49
- throw new \RuntimeException ('Not implemented ' );
50
- }
51
- };
52
- }
53
-
54
- $ responseStub = $ this ->createMock (ResponseInterface::class);
55
- $ responseStub
56
- ->method ('getContent ' )
57
- ->willReturn (implode ("\r\n" , self ::RETURN ));
58
-
59
- return $ responseStub ;
60
- })
61
- );
62
-
63
- // Pass HttpClient::create() instead of this mock to run the tests against the real API
64
- return new NotCompromisedPasswordValidator ($ httpClientStub );
45
+ // Pass HttpClient::create() instead of the mock to run the tests against the real API
46
+ return new NotCompromisedPasswordValidator ($ this ->createHttpClientStub ());
65
47
}
66
48
67
49
public function testNullIsValid ()
@@ -112,6 +94,29 @@ public function testValidPassword()
112
94
$ this ->assertNoViolation ();
113
95
}
114
96
97
+ public function testNonUtf8CharsetValid ()
98
+ {
99
+ $ validator = new NotCompromisedPasswordValidator ($ this ->createHttpClientStub (), 'ISO-8859-5 ' );
100
+ $ validator ->validate (mb_convert_encoding (self ::PASSWORD_NON_UTF8_NOT_LEAKED , 'ISO-8859-5 ' , 'UTF-8 ' ), new NotCompromisedPassword ());
101
+
102
+ $ this ->assertNoViolation ();
103
+ }
104
+
105
+ public function testNonUtf8CharsetInvalid ()
106
+ {
107
+ $ constraint = new NotCompromisedPassword ();
108
+
109
+ $ this ->context = $ this ->createContext ();
110
+
111
+ $ validator = new NotCompromisedPasswordValidator ($ this ->createHttpClientStub (), 'ISO-8859-5 ' );
112
+ $ validator ->initialize ($ this ->context );
113
+ $ validator ->validate (mb_convert_encoding (self ::PASSWORD_NON_UTF8_LEAKED , 'ISO-8859-5 ' , 'UTF-8 ' ), $ constraint );
114
+
115
+ $ this ->buildViolation ($ constraint ->message )
116
+ ->setCode (NotCompromisedPassword::COMPROMISED_PASSWORD_ERROR )
117
+ ->assertRaised ();
118
+ }
119
+
115
120
/**
116
121
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
117
122
*/
@@ -142,4 +147,30 @@ public function testApiErrorSkipped()
142
147
$ this ->validator ->validate (self ::PASSWORD_TRIGGERING_AN_ERROR , new NotCompromisedPassword (['skipOnError ' => true ]));
143
148
$ this ->assertTrue (true ); // No exception have been thrown
144
149
}
150
+
151
+ private function createHttpClientStub (): HttpClientInterface
152
+ {
153
+ $ httpClientStub = $ this ->createMock (HttpClientInterface::class);
154
+ $ httpClientStub ->method ('request ' )->will (
155
+ $ this ->returnCallback (function (string $ method , string $ url ): ResponseInterface {
156
+ if (self ::PASSWORD_TRIGGERING_AN_ERROR_RANGE_URL === $ url ) {
157
+ throw new class ('Problem contacting the Have I been Pwned API. ' ) extends \Exception implements ServerExceptionInterface {
158
+ public function getResponse (): ResponseInterface
159
+ {
160
+ throw new \RuntimeException ('Not implemented ' );
161
+ }
162
+ };
163
+ }
164
+
165
+ $ responseStub = $ this ->createMock (ResponseInterface::class);
166
+ $ responseStub
167
+ ->method ('getContent ' )
168
+ ->willReturn (implode ("\r\n" , self ::RETURN ));
169
+
170
+ return $ responseStub ;
171
+ })
172
+ );
173
+
174
+ return $ httpClientStub ;
175
+ }
145
176
}
0 commit comments