ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
ilBcryptPasswordEncoderTest.php
Go to the documentation of this file.
1<?php declare(strict_types=1);
2/* Copyright (c) 1998-2014 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Services/Password/classes/encoders/class.ilBcryptPasswordEncoder.php';
5require_once 'Services/Password/test/ilPasswordBaseTest.php';
6
7use org\bovigo\vfs;
8
15{
17 const VALID_COSTS = '08';
18
20 const PASSWORD = 'password';
21
23 const WRONG_PASSWORD = 'wrong_password';
24
26 const CLIENT_SALT = 'homer!12345_/';
27
29 const PASSWORD_SALT = 'salt';
30
32 protected $testDirectory;
33
36
40 public function getTestDirectory() : vfs\vfsStreamDirectory
41 {
43 }
44
48 public function setTestDirectory(vfs\vfsStreamDirectory $testDirectory) : void
49 {
50 $this->testDirectory = $testDirectory;
51 }
52
56 public function getTestDirectoryUrl() : string
57 {
59 }
60
64 public function setTestDirectoryUrl(string $testDirectoryUrl) : void
65 {
66 $this->testDirectoryUrl = $testDirectoryUrl;
67 }
68
72 private function skipIfPhpVersionIsNotSupported() : void
73 {
74 if (version_compare(phpversion(), '5.3.7', '<')) {
75 $this->markTestSkipped('Requires PHP >= 5.3.7');
76 }
77 }
78
82 private function isVsfStreamInstalled() : bool
83 {
84 return class_exists('org\bovigo\vfs\vfsStreamWrapper');
85 }
86
90 private function skipIfvfsStreamNotSupported() : void
91 {
92 if (!$this->isVsfStreamInstalled()) {
93 $this->markTestSkipped('Skipped test, vfsStream (http://vfs.bovigo.org) required');
94 } else {
95 vfs\vfsStream::setup();
96 $this->setTestDirectory(vfs\vfsStream::newDirectory('tests')->at(vfs\vfsStreamWrapper::getRoot()));
97 $this->setTestDirectoryUrl(vfs\vfsStream::url('root/tests'));
98 }
99 }
100
104 public function costsProvider() : array
105 {
106 $data = [];
107 for ($i = 4; $i <= 31; $i++) {
108 $data[sprintf("Costs: %s", (string) $i)] = [(string) $i];
109 }
110
111 return $data;
112 }
113
119 {
120 $encoder = new ilBcryptPasswordEncoder(array(
121 'data_directory' => $this->getTestDirectoryUrl()
122 ));
123
124 return $encoder;
125 }
126
132 {
134
135 $security_flaw_ignoring_encoder = new ilBcryptPasswordEncoder([
136 'ignore_security_flaw' => true,
137 'data_directory' => $this->getTestDirectoryUrl()
138 ]);
139 $this->assertTrue($security_flaw_ignoring_encoder->isSecurityFlawIgnored());
140
141 $security_flaw_respecting_encoder = new ilBcryptPasswordEncoder([
142 'ignore_security_flaw' => false,
143 'data_directory' => $this->getTestDirectoryUrl()
144 ]);
145 $this->assertFalse($security_flaw_respecting_encoder->isSecurityFlawIgnored());
146
147 $encoder = new ilBcryptPasswordEncoder([
148 'cost' => self::VALID_COSTS,
149 'data_directory' => $this->getTestDirectoryUrl()
150 ]);
151 $this->assertInstanceOf('ilBcryptPasswordEncoder', $encoder);
152 $this->assertEquals(self::VALID_COSTS, $encoder->getCosts());
153 $this->assertFalse($encoder->isSecurityFlawIgnored());
154 $encoder->setClientSalt(self::CLIENT_SALT);
155
156 return $encoder;
157 }
158
165 {
166 $expected = '04';
167
168 $encoder->setCosts($expected);
169 $this->assertEquals($expected, $encoder->getCosts());
170 }
171
178 {
179 $this->expectException(ilPasswordException::class);
180 $encoder->setCosts('32');
181 }
182
189 {
190 $this->expectException(ilPasswordException::class);
191 $encoder->setCosts('3');
192 }
193
202 public function testCostsCanBeSetInRange(string $costs, ilBcryptPasswordEncoder $encoder) : void
203 {
204 $encoder->setCosts($costs);
205 }
206
216 $encoder->setCosts(self::VALID_COSTS);
217 $encoded_password = $encoder->encodePassword(self::PASSWORD, self::PASSWORD_SALT);
218 $this->assertTrue($encoder->isPasswordValid($encoded_password, self::PASSWORD, self::PASSWORD_SALT));
219 $this->assertFalse($encoder->isPasswordValid($encoded_password, self::WRONG_PASSWORD, self::PASSWORD_SALT));
220 return $encoder;
221 }
222
230 ) : void {
231 $this->expectException(ilPasswordException::class);
232 $encoder->setCosts(self::VALID_COSTS);
233 $encoder->encodePassword(str_repeat('a', 5000), self::PASSWORD_SALT);
234 }
235
243 ) : void {
244 $encoder->setCosts(self::VALID_COSTS);
245 $this->assertFalse($encoder->isPasswordValid('encoded', str_repeat('a', 5000), self::PASSWORD_SALT));
246 }
247
252 public function testEncoderReliesOnSalts(ilBcryptPasswordEncoder $encoder) : void
253 {
254 $this->assertTrue($encoder->requiresSalt());
255 }
256
262 {
263 $this->assertFalse($encoder->requiresReencoding('hello'));
264 }
265
270 public function testNameShouldBeBcrypt(ilBcryptPasswordEncoder $encoder) : void
271 {
272 $this->assertEquals('bcrypt', $encoder->getName());
273 }
274
279 {
280 $this->skipIfvfsStreamNotSupported();
281
282 $this->expectException(ilPasswordException::class);
283 $encoder = $this->getInstanceWithConfiguredDataDirectory();
284 $encoder->setClientSalt(null);
285 $encoder->setCosts(self::VALID_COSTS);
286 $encoder->encodePassword(self::PASSWORD, self::PASSWORD_SALT);
287 }
288
293 {
294 $this->skipIfvfsStreamNotSupported();
295
296 $this->expectException(ilPasswordException::class);
297 $encoder = $this->getInstanceWithConfiguredDataDirectory();
298 $encoder->setClientSalt(null);
299 $encoder->setCosts(self::VALID_COSTS);
300 $encoder->isPasswordValid('12121212', self::PASSWORD, self::PASSWORD_SALT);
301 }
302
307 {
308 $this->skipIfvfsStreamNotSupported();
309
310 $this->getTestDirectory()->chmod(0777);
311 vfs\vfsStream::newFile(ilBcryptPasswordEncoder::SALT_STORAGE_FILENAME)->withContent(self::CLIENT_SALT)->at($this->getTestDirectory());
312
313 $encoder = $this->getInstanceWithConfiguredDataDirectory();
314 $this->assertEquals(self::CLIENT_SALT, $encoder->getClientSalt());
315 }
316
321 {
322 $this->skipIfvfsStreamNotSupported();
323
324 $this->getTestDirectory()->chmod(0777);
325
326 $encoder = $this->getInstanceWithConfiguredDataDirectory();
327 $this->assertNotNull($encoder->getClientSalt());
328 }
329
334 {
335 $this->skipIfvfsStreamNotSupported();
336
337 $this->expectException(ilPasswordException::class);
338 $this->getTestDirectory()->chmod(0000);
339
340 $this->getInstanceWithConfiguredDataDirectory();
341 }
342
347 {
348 $this->skipIfvfsStreamNotSupported();
349
350 $encoder = $this->getInstanceWithConfiguredDataDirectory();
351 $encoder->setBackwardCompatibility(true);
352 $this->assertTrue($encoder->isBackwardCompatibilityEnabled());
353 $encoder->setBackwardCompatibility(false);
354 $this->assertFalse($encoder->isBackwardCompatibilityEnabled());
355 }
356
360 public function testBackwardCompatibility() : void
361 {
362 $this->skipIfPhpVersionIsNotSupported();
363 $this->skipIfvfsStreamNotSupported();
364
365 $encoder = $this->getInstanceWithConfiguredDataDirectory();
366 $encoder->setClientSalt(self::CLIENT_SALT);
367 $encoder->setBackwardCompatibility(true);
368 $encoded_password = $encoder->encodePassword(self::PASSWORD, self::PASSWORD_SALT);
369 $this->assertTrue($encoder->isPasswordValid($encoded_password, self::PASSWORD, self::PASSWORD_SALT));
370 $this->assertEquals('$2a$', substr($encoded_password, 0, 4));
371
372 $another_encoder = $this->getInstanceWithConfiguredDataDirectory();
373 $another_encoder->setClientSalt(self::CLIENT_SALT);
374 $another_encoder->setBackwardCompatibility(false);
375 $another_encoded_password = $another_encoder->encodePassword(self::PASSWORD, self::PASSWORD_SALT);
376 $this->assertEquals('$2y$', substr($another_encoded_password, 0, 4));
377 $this->assertTrue($another_encoder->isPasswordValid($encoded_password, self::PASSWORD, self::PASSWORD_SALT));
378 }
379
384 {
385 $this->skipIfvfsStreamNotSupported();
386
387 $this->expectException(ilPasswordException::class);
388 $encoder = $this->getInstanceWithConfiguredDataDirectory();
389 $encoder->setClientSalt(self::CLIENT_SALT);
390 $encoder->setBackwardCompatibility(true);
391 $encoder->encodePassword(self::PASSWORD . chr(195), self::PASSWORD_SALT);
392 }
393
399 {
400 $this->skipIfvfsStreamNotSupported();
401
402 $encoder = $this->getInstanceWithConfiguredDataDirectory();
403 $encoder->setClientSalt(self::CLIENT_SALT);
404 $encoder->setBackwardCompatibility(true);
405 $encoder->setIsSecurityFlawIgnored(true);
406 $encoder->encodePassword(self::PASSWORD . chr(195), self::PASSWORD_SALT);
407 }
408}
An exception for terminatinating execution or to throw for unit testing.
testCostsCanBeSetInRange(string $costs, ilBcryptPasswordEncoder $encoder)
@doesNotPerformAssertions @depends testInstanceCanBeCreated @dataProvider costsProvider
testPasswordVerificationShouldFailIfTheRawPasswordExceedsTheSupportedLength(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
setTestDirectory(vfs\vfsStreamDirectory $testDirectory)
testPasswordShouldBeCorrectlyEncodedAndVerified(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testCostsCanBeRetrievedWhenCostsAreSet(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testCostsCannotBeSetBelowRange(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testNoExceptionIfPasswordsContainA8BitCharacterAndBackwardCompatibilityIsEnabledWithIgnoredSecurityFlaw()
@doesNotPerformAssertions
setTestDirectoryUrl(string $testDirectoryUrl)
testEncoderReliesOnSalts(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testExceptionIsRaisedIfThePasswordExceedsTheSupportedLengthOnEncoding(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testEncoderDoesNotSupportReencoding(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testCostsCannotBeSetAboveRange(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
testNameShouldBeBcrypt(ilBcryptPasswordEncoder $encoder)
@depends testInstanceCanBeCreated
encodePassword(string $raw, string $salt)
@inheritDoc
setBackwardCompatibility(bool $backward_compatibility)
Set the backward compatibility $2a$ instead of $2y$ for PHP 5.3.7+.
isPasswordValid(string $encoded, string $raw, string $salt)
@inheritDoc
requiresReencoding(string $encoded)
@inheritDoc
setIsSecurityFlawIgnored(bool $is_security_flaw_ignored)
Class for user password exception handling in ILIAS.
$i
Definition: metadata.php:24
$data
Definition: storeScorm.php:23