ILIAS  trunk Revision v11.0_alpha-1689-g66c127b4ae8
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
LocalUserPasswordTest.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
23 use ILIAS\User\Tests\BaseTestCase as ilUserBaseTestCase;
24 use org\bovigo\vfs;
25 
26 class LocalUserPasswordTest extends ilUserBaseTestCase
27 {
28  private const PASSWORD = 'password';
29  private const ENCODED_PASSWORD = 'encoded';
30  protected vfs\vfsStreamDirectory $testDirectory;
31  protected string $testDirectoryUrl;
32 
33  public function getTestDirectory(): vfs\vfsStreamDirectory
34  {
35  return $this->testDirectory;
36  }
37 
38  public function setTestDirectory(vfs\vfsStreamDirectory $testDirectory): void
39  {
40  $this->testDirectory = $testDirectory;
41  }
42 
43  public function getTestDirectoryUrl(): string
44  {
46  }
47 
48  public function setTestDirectoryUrl(string $testDirectoryUrl): void
49  {
50  $this->testDirectoryUrl = $testDirectoryUrl;
51  }
52 
53  protected function setUp(): void
54  {
55  vfs\vfsStream::setup();
56  $this->setTestDirectory(vfs\vfsStream::newDirectory('tests')->at(vfs\vfsStreamWrapper::getRoot()));
57  $this->setTestDirectoryUrl(vfs\vfsStream::url('root/tests'));
58 
59  parent::setUp();
60  }
61 
66  {
67  $this->expectException(ilUserException::class);
68  new LocalUserPasswordManager(['data_directory' => $this->getTestDirectoryUrl()]);
69  }
70 
75  {
76  $this->expectException(ilUserException::class);
78  'password_encoder' => 'md5',
79  'data_directory' => $this->getTestDirectoryUrl()
80  ]);
81  }
82 
87  {
88  $this->expectException(TypeError::class);
89  $this->expectExceptionMessageMatches('/' . preg_quote(LocalUserPasswordManager::class, '/') . '/');
90 
92  'password_encoder' => 'md5',
93  'encoder_factory' => 'test',
94  'data_directory' => $this->getTestDirectoryUrl()
95  ]);
96  }
97 
102  public function testInstanceCanBeCreated(): void
103  {
104  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
105  $factory_mock->expects($this->exactly(2))->method('getSupportedEncoderNames')->will($this->onConsecutiveCalls(
106  [
107  'mockencoder',
108  'second_mockencoder'
109  ],
110  [
111  'mockencoder'
112  ]
113  ));
114 
115  $password_manager = new LocalUserPasswordManager([
116  'password_encoder' => 'md5',
117  'encoder_factory' => $factory_mock,
118  'data_directory' => $this->getTestDirectoryUrl()
119  ]);
120  $this->assertInstanceOf(LocalUserPasswordManager::class, $password_manager);
121  $this->assertEquals('md5', $password_manager->getEncoderName());
122  $this->assertEquals($factory_mock, $password_manager->getEncoderFactory());
123 
124  $this->assertTrue($password_manager->isEncodingTypeSupported('second_mockencoder'));
125  $this->assertFalse($password_manager->isEncodingTypeSupported('second_mockencoder'));
126  }
127 
133  {
134  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
135  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
136  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
137 
138  $user_mock->expects($this->once())->method('setPasswordSalt')->with($this->isType('string'));
139  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
140  $user_mock->expects($this->once())->method('setPasswordEncodingType')->with($this->equalTo('mockencoder'));
141  $user_mock->expects($this->once())->method('setPasswd')->with(
142  $this->equalTo(self::ENCODED_PASSWORD),
143  $this->equalTo(ilObjUser::PASSWD_CRYPTED)
144  );
145 
146  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
147  $encoder->expects($this->once())->method('requiresSalt')->willReturn(true);
148  $encoder->expects($this->once())->method('encodePassword')
149  ->with(
150  $this->equalTo(self::PASSWORD),
151  $this->isType('string')
152  )->willReturn(self::ENCODED_PASSWORD)
153  ;
154 
155  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
156 
157  $password_manager = new LocalUserPasswordManager([
158  'password_encoder' => 'mockencoder',
159  'encoder_factory' => $factory_mock,
160  'data_directory' => $this->getTestDirectoryUrl()
161  ]);
162 
163  $password_manager->encodePassword($user_mock, self::PASSWORD);
164  }
165 
171  {
172  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
173  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
174  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
175 
176  $user_mock->expects($this->once())->method('setPasswordSalt')->with($this->equalTo(null));
177  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn(null);
178  $user_mock->expects($this->once())->method('setPasswordEncodingType')->with($this->equalTo('mockencoder'));
179  $user_mock->expects($this->once())->method('setPasswd')->with(
180  $this->equalTo(self::ENCODED_PASSWORD),
181  $this->equalTo(ilObjUser::PASSWD_CRYPTED)
182  );
183 
184  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
185  $encoder->expects($this->once())->method('requiresSalt')->willReturn(false);
186  $encoder->expects($this->once())->method('encodePassword')->with(
187  $this->equalTo(self::PASSWORD),
188  $this->equalTo(null)
189  )->willReturn(self::ENCODED_PASSWORD);
190 
191  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
192 
193  $password_manager = new LocalUserPasswordManager([
194  'password_encoder' => 'mockencoder',
195  'encoder_factory' => $factory_mock,
196  'data_directory' => $this->getTestDirectoryUrl()
197  ]);
198 
199  $password_manager->encodePassword($user_mock, self::PASSWORD);
200  }
201 
206  public function testPasswordManagerVerifiesPassword(): void
207  {
208  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
209  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
210  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
211 
212  $user_mock->expects($this->atLeast(1))->method('getPasswordSalt')->willReturn('asuperrandomsalt');
213  $user_mock->expects($this->atLeast(1))->method('getPasswordEncodingType')->willReturn('mockencoder');
214  $user_mock->expects($this->atLeast(1))->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
215  $user_mock->expects($this->never())->method('resetPassword');
216 
217  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
218  $encoder->expects($this->once())->method('isPasswordValid')->with(
219  $this->equalTo(self::ENCODED_PASSWORD),
220  $this->equalTo(self::PASSWORD),
221  $this->isType('string')
222  )->willReturn(true);
223  $encoder->expects($this->once())->method('requiresReencoding')
224  ->with($this->equalTo(self::ENCODED_PASSWORD))
225  ->willReturn(false)
226  ;
227 
228  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
229 
230  $password_manager = new LocalUserPasswordManager([
231  'password_encoder' => 'mockencoder',
232  'encoder_factory' => $factory_mock,
233  'data_directory' => $this->getTestDirectoryUrl()
234  ]);
235 
236  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
237  }
238 
244  {
245  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
246  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
247  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
248 
249  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
250  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('second_mockencoder');
251  $user_mock->expects($this->once())->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
252  $user_mock->expects($this->once())->method('resetPassword')->with(
253  $this->equalTo(self::PASSWORD),
254  $this->equalTo(self::PASSWORD)
255  );
256 
257  $encoder->expects($this->once())->method('getName')->willReturn('second_mockencoder');
258  $encoder->expects($this->once())->method('isPasswordValid')->with(
259  $this->equalTo(self::ENCODED_PASSWORD),
260  $this->equalTo(self::PASSWORD),
261  $this->isType('string')
262  )->willReturn(true);
263  $encoder->expects($this->never())->method('requiresReencoding')
264  ->with($this->equalTo(self::ENCODED_PASSWORD))
265  ->willReturn(false)
266  ;
267 
268  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
269 
270  $password_manager = new LocalUserPasswordManager([
271  'password_encoder' => 'mockencoder',
272  'encoder_factory' => $factory_mock,
273  'data_directory' => $this->getTestDirectoryUrl()
274  ]);
275 
276  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
277  }
278 
284  {
285  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
286  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
287  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
288 
289  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
290  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('mockencoder');
291  $user_mock->expects($this->exactly(2))->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
292  $user_mock->expects($this->once())->method('resetPassword')->with(
293  $this->equalTo(self::PASSWORD),
294  $this->equalTo(self::PASSWORD)
295  );
296 
297  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
298  $encoder->expects($this->once())->method('isPasswordValid')->with(
299  $this->equalTo(self::ENCODED_PASSWORD),
300  $this->equalTo(self::PASSWORD),
301  $this->isType('string')
302  )->willReturn(true);
303  $encoder->expects($this->once())->method('requiresReencoding')
304  ->with($this->equalTo(self::ENCODED_PASSWORD))
305  ->willReturn(true)
306  ;
307 
308  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
309 
310  $password_manager = new LocalUserPasswordManager([
311  'password_encoder' => 'mockencoder',
312  'encoder_factory' => $factory_mock,
313  'data_directory' => $this->getTestDirectoryUrl()
314  ]);
315 
316  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
317  }
318 
324  {
325  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
326  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
327  $factory_mock = $this->getMockBuilder(LocalUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
328 
329  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
330  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('second_mockencoder');
331  $user_mock->expects($this->once())->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
332  $user_mock->expects($this->never())->method('resetPassword');
333 
334  $encoder->expects($this->once())->method('getName')->willReturn('second_mockencoder');
335  $encoder->expects($this->never())->method('requiresReencoding');
336  $encoder->expects($this->once())->method('isPasswordValid')
337  ->with(
338  $this->equalTo(self::ENCODED_PASSWORD),
339  $this->equalTo(self::PASSWORD),
340  $this->isType('string')
341  )->willReturn(false)
342  ;
343 
344  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
345 
346  $password_manager = new LocalUserPasswordManager([
347  'password_encoder' => 'mockencoder',
348  'encoder_factory' => $factory_mock,
349  'data_directory' => $this->getTestDirectoryUrl()
350  ]);
351 
352  $this->assertFalse($password_manager->verifyPassword($user_mock, self::PASSWORD));
353  }
354 
358  public function testFactoryCanBeCreated(): void
359  {
360  $factory = new LocalUserPasswordEncoderFactory([
361  'data_directory' => $this->getTestDirectoryUrl()
362  ]);
363  $this->assertInstanceOf(LocalUserPasswordEncoderFactory::class, $factory);
364  }
365 
372  {
373  $factory = new LocalUserPasswordEncoderFactory([
374  'default_password_encoder' => 'md5',
375  'data_directory' => $this->getTestDirectoryUrl()
376  ]);
377  $this->assertEquals('md5', $factory->getDefaultEncoder());
378 
379  $encoder = $this->createMock(ilPasswordEncoder::class);
380  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
381  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
382 
383  $second_mockencoder = $this->createMock(ilPasswordEncoder::class);
384  $second_mockencoder->expects($this->atLeastOnce())->method('getName')->willReturn('second_mockencoder');
385  $second_mockencoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
386 
387  $factory->setSupportedEncoders([$encoder, $second_mockencoder]);
388  $this->assertCount(2, $factory->getSupportedEncoders());
389  $this->assertCount(2, $factory->getSupportedEncoderNames());
390  $this->assertCount(
391  0,
392  array_diff(['mockencoder', 'second_mockencoder'], $factory->getSupportedEncoderNames())
393  );
394  $this->assertCount(
395  0,
396  array_diff($factory->getSupportedEncoderNames(), ['mockencoder', 'second_mockencoder'])
397  );
398  }
399 
405  {
406  $this->expectException(ilUserException::class);
407  $factory = new LocalUserPasswordEncoderFactory([
408  'data_directory' => $this->getTestDirectoryUrl()
409  ]);
410  $factory->setSupportedEncoders(['phpunit']);
411  }
412 
418  {
419  $this->expectException(ilUserException::class);
420  $factory = new LocalUserPasswordEncoderFactory([
421  'data_directory' => $this->getTestDirectoryUrl()
422  ]);
423  $factory->getEncoderByName('phpunit');
424  }
425 
431  {
432  $this->expectException(ilUserException::class);
433  $factory = new LocalUserPasswordEncoderFactory([
434  'default_password_encoder' => 'phpunit',
435  'data_directory' => $this->getTestDirectoryUrl()
436  ]);
437  $factory->getEncoderByName('phpunit');
438  }
439 
446  {
447  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
448  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
449  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
450 
451  $factory = new LocalUserPasswordEncoderFactory([
452  'default_password_encoder' => $encoder->getName(),
453  'data_directory' => $this->getTestDirectoryUrl()
454  ]);
455  $factory->setSupportedEncoders([$encoder]);
456  $this->assertEquals($encoder, $factory->getEncoderByName('phpunit'));
457  }
458 
465  {
466  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
467  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
468  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
469 
470  $factory = new LocalUserPasswordEncoderFactory([
471  'default_password_encoder' => $encoder->getName(),
472  'data_directory' => $this->getTestDirectoryUrl()
473  ]);
474  $factory->setSupportedEncoders([$encoder]);
475  $this->assertEquals($encoder, $factory->getEncoderByName('mockencoder'));
476  }
477 }
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutFactory()
testPasswordManagerMigratesPasswordOnVerificationWithVariantEncoders()
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
vfs vfsStreamDirectory $testDirectory
testFactoryReturnsTheDefaultEncoderIfAnUnsupportedEncoderIsRequestedAndASupportedDefaultEncoderWasSpecifiedInFallbackMode()
setTestDirectory(vfs\vfsStreamDirectory $testDirectory)
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderIsRequestedAndNoDefaultEncoderWasSpecifiedInFallbackMode()
const PASSWD_CRYPTED
setTestDirectoryUrl(string $testDirectoryUrl)
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutValidFactory()
testPasswordManagerNeverMigratesPasswordOnFailedVerificationWithVariantEncoders()
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutEncoderInformation()
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderWasInjected()
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderIsRequestedAndTheDefaultEncoderDoesNotMatchOneOfTheSupportedEncodersInFallbackMode()