@@ -28,40 +28,22 @@ class NotCompromisedPasswordValidatorTest extends ConstraintValidatorTestCase
2828 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"
2929 private const PASSWORD_LEAKED = 'maman ' ;
3030 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 ' ;
3133
3234 private const RETURN = [
3335 '35E033023A46402F94CFB4F654C5BFE44A1:1 ' ,
3436 '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: мама
3639 '3686792BBC66A72D40D928ED15621124CFE:7 ' ,
3740 '36EEC709091B810AA240179A44317ED415C:2 ' ,
3841 ];
3942
4043 protected function createValidator ()
4144 {
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 ());
6547 }
6648
6749 public function testNullIsValid ()
@@ -112,6 +94,29 @@ public function testValidPassword()
11294 $ this ->assertNoViolation ();
11395 }
11496
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+
115120 /**
116121 * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
117122 */
@@ -142,4 +147,30 @@ public function testApiErrorSkipped()
142147 $ this ->validator ->validate (self ::PASSWORD_TRIGGERING_AN_ERROR , new NotCompromisedPassword (['skipOnError ' => true ]));
143148 $ this ->assertTrue (true ); // No exception have been thrown
144149 }
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+ }
145176}
0 commit comments