ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
ilObjUserPasswordTest.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 use org\bovigo\vfs;
22 
23 require_once 'libs/composer/vendor/autoload.php';
24 require_once 'Services/User/classes/class.ilUserPasswordManager.php';
25 require_once 'Services/User/classes/class.ilUserPasswordEncoderFactory.php';
26 require_once 'Services/Password/classes/class.ilBasePasswordEncoder.php';
27 require_once 'Services/Utilities/classes/class.ilUtil.php';
28 require_once 'Services/User/classes/class.ilObjUser.php';
29 require_once 'Services/User/exceptions/class.ilUserException.php';
30 require_once 'Services/User/test/ilUserBaseTest.php';
31 
33 {
34  private const PASSWORD = 'password';
35  private const ENCODED_PASSWORD = 'encoded';
36 
37  protected vfs\vfsStreamDirectory $testDirectory;
38  protected string $testDirectoryUrl;
39 
40  public function getTestDirectory(): vfs\vfsStreamDirectory
41  {
42  return $this->testDirectory;
43  }
44 
45  public function setTestDirectory(vfs\vfsStreamDirectory $testDirectory): void
46  {
47  $this->testDirectory = $testDirectory;
48  }
49 
50  public function getTestDirectoryUrl(): string
51  {
53  }
54 
55  public function setTestDirectoryUrl(string $testDirectoryUrl): void
56  {
57  $this->testDirectoryUrl = $testDirectoryUrl;
58  }
59 
60  protected function setUp(): void
61  {
62  vfs\vfsStream::setup();
63  $this->setTestDirectory(vfs\vfsStream::newDirectory('tests')->at(vfs\vfsStreamWrapper::getRoot()));
64  $this->setTestDirectoryUrl(vfs\vfsStream::url('root/tests'));
65 
66  parent::setUp();
67  }
68 
73  {
74  $this->assertException(ilUserException::class);
75  new ilUserPasswordManager(['data_directory' => $this->getTestDirectoryUrl()]);
76  }
77 
82  {
83  $this->assertException(ilUserException::class);
85  'password_encoder' => 'md5',
86  'data_directory' => $this->getTestDirectoryUrl()
87  ]);
88  }
89 
94  {
95  $this->expectException(TypeError::class);
96  $this->expectExceptionMessageMatches('/' . ilUserPasswordEncoderFactory::class . '/');
97 
99  'password_encoder' => 'md5',
100  'encoder_factory' => 'test',
101  'data_directory' => $this->getTestDirectoryUrl()
102  ]);
103  }
104 
109  public function testInstanceCanBeCreated(): void
110  {
111  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
112  $factory_mock->expects($this->exactly(2))->method('getSupportedEncoderNames')->will($this->onConsecutiveCalls(
113  [
114  'mockencoder',
115  'second_mockencoder'
116  ],
117  [
118  'mockencoder'
119  ]
120  ));
121 
122  $password_manager = new ilUserPasswordManager([
123  'password_encoder' => 'md5',
124  'encoder_factory' => $factory_mock,
125  'data_directory' => $this->getTestDirectoryUrl()
126  ]);
127  $this->assertInstanceOf('ilUserPasswordManager', $password_manager);
128  $this->assertEquals('md5', $password_manager->getEncoderName());
129  $this->assertEquals($factory_mock, $password_manager->getEncoderFactory());
130 
131  $this->assertTrue($password_manager->isEncodingTypeSupported('second_mockencoder'));
132  $this->assertFalse($password_manager->isEncodingTypeSupported('second_mockencoder'));
133  }
134 
140  {
141  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
142  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
143  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
144 
145  $user_mock->expects($this->once())->method('setPasswordSalt')->with($this->isType('string'));
146  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
147  $user_mock->expects($this->once())->method('setPasswordEncodingType')->with($this->equalTo('mockencoder'));
148  $user_mock->expects($this->once())->method('setPasswd')->with(
149  $this->equalTo(self::ENCODED_PASSWORD),
150  $this->equalTo(ilObjUser::PASSWD_CRYPTED)
151  );
152 
153  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
154  $encoder->expects($this->once())->method('requiresSalt')->willReturn(true);
155  $encoder->expects($this->once())->method('encodePassword')
156  ->with(
157  $this->equalTo(self::PASSWORD),
158  $this->isType('string')
159  )->willReturn(self::ENCODED_PASSWORD);
160 
161  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
162 
163  $password_manager = new ilUserPasswordManager([
164  'password_encoder' => 'mockencoder',
165  'encoder_factory' => $factory_mock,
166  'data_directory' => $this->getTestDirectoryUrl()
167  ]);
168 
169  $password_manager->encodePassword($user_mock, self::PASSWORD);
170  }
171 
177  {
178  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
179  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
180  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
181 
182  $user_mock->expects($this->once())->method('setPasswordSalt')->with($this->equalTo(null));
183  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn(null);
184  $user_mock->expects($this->once())->method('setPasswordEncodingType')->with($this->equalTo('mockencoder'));
185  $user_mock->expects($this->once())->method('setPasswd')->with(
186  $this->equalTo(self::ENCODED_PASSWORD),
187  $this->equalTo(ilObjUser::PASSWD_CRYPTED)
188  );
189 
190  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
191  $encoder->expects($this->once())->method('requiresSalt')->willReturn(false);
192  $encoder->expects($this->once())->method('encodePassword')->with(
193  $this->equalTo(self::PASSWORD),
194  $this->equalTo(null)
195  )->willReturn(self::ENCODED_PASSWORD);
196 
197  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
198 
199  $password_manager = new ilUserPasswordManager([
200  'password_encoder' => 'mockencoder',
201  'encoder_factory' => $factory_mock,
202  'data_directory' => $this->getTestDirectoryUrl()
203  ]);
204 
205  $password_manager->encodePassword($user_mock, self::PASSWORD);
206  }
207 
212  public function testPasswordManagerVerifiesPassword(): void
213  {
214  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
215  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
216  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
217 
218  $user_mock->expects($this->atLeast(1))->method('getPasswordSalt')->willReturn('asuperrandomsalt');
219  $user_mock->expects($this->atLeast(1))->method('getPasswordEncodingType')->willReturn('mockencoder');
220  $user_mock->expects($this->atLeast(1))->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
221  $user_mock->expects($this->never())->method('resetPassword');
222 
223  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
224  $encoder->expects($this->once())->method('isPasswordValid')->with(
225  $this->equalTo(self::ENCODED_PASSWORD),
226  $this->equalTo(self::PASSWORD),
227  $this->isType('string')
228  )->willReturn(true);
229  $encoder->expects($this->once())->method('requiresReencoding')
230  ->with($this->equalTo(self::ENCODED_PASSWORD))
231  ->willReturn(false);
232 
233  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
234 
235  $password_manager = new ilUserPasswordManager([
236  'password_encoder' => 'mockencoder',
237  'encoder_factory' => $factory_mock,
238  'data_directory' => $this->getTestDirectoryUrl()
239  ]);
240 
241  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
242  }
243 
249  {
250  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
251  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
252  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
253 
254  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
255  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('second_mockencoder');
256  $user_mock->expects($this->once())->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
257  $user_mock->expects($this->once())->method('resetPassword')->with(
258  $this->equalTo(self::PASSWORD),
259  $this->equalTo(self::PASSWORD)
260  );
261 
262  $encoder->expects($this->once())->method('getName')->willReturn('second_mockencoder');
263  $encoder->expects($this->once())->method('isPasswordValid')->with(
264  $this->equalTo(self::ENCODED_PASSWORD),
265  $this->equalTo(self::PASSWORD),
266  $this->isType('string')
267  )->willReturn(true);
268  $encoder->expects($this->never())->method('requiresReencoding')
269  ->with($this->equalTo(self::ENCODED_PASSWORD))
270  ->willReturn(false);
271 
272  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
273 
274  $password_manager = new ilUserPasswordManager([
275  'password_encoder' => 'mockencoder',
276  'encoder_factory' => $factory_mock,
277  'data_directory' => $this->getTestDirectoryUrl()
278  ]);
279 
280  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
281  }
282 
288  {
289  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
290  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
291  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
292 
293  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
294  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('mockencoder');
295  $user_mock->expects($this->exactly(2))->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
296  $user_mock->expects($this->once())->method('resetPassword')->with(
297  $this->equalTo(self::PASSWORD),
298  $this->equalTo(self::PASSWORD)
299  );
300 
301  $encoder->expects($this->once())->method('getName')->willReturn('mockencoder');
302  $encoder->expects($this->once())->method('isPasswordValid')->with(
303  $this->equalTo(self::ENCODED_PASSWORD),
304  $this->equalTo(self::PASSWORD),
305  $this->isType('string')
306  )->willReturn(true);
307  $encoder->expects($this->once())->method('requiresReencoding')
308  ->with($this->equalTo(self::ENCODED_PASSWORD))
309  ->willReturn(true);
310 
311  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
312 
313  $password_manager = new ilUserPasswordManager([
314  'password_encoder' => 'mockencoder',
315  'encoder_factory' => $factory_mock,
316  'data_directory' => $this->getTestDirectoryUrl()
317  ]);
318 
319  $this->assertTrue($password_manager->verifyPassword($user_mock, self::PASSWORD));
320  }
321 
327  {
328  $user_mock = $this->getMockBuilder(ilObjUser::class)->disableOriginalConstructor()->getMock();
329  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
330  $factory_mock = $this->getMockBuilder(ilUserPasswordEncoderFactory::class)->disableOriginalConstructor()->getMock();
331 
332  $user_mock->expects($this->once())->method('getPasswordSalt')->willReturn('asuperrandomsalt');
333  $user_mock->expects($this->once())->method('getPasswordEncodingType')->willReturn('second_mockencoder');
334  $user_mock->expects($this->once())->method('getPasswd')->willReturn(self::ENCODED_PASSWORD);
335  $user_mock->expects($this->never())->method('resetPassword');
336 
337  $encoder->expects($this->once())->method('getName')->willReturn('second_mockencoder');
338  $encoder->expects($this->never())->method('requiresReencoding');
339  $encoder->expects($this->once())->method('isPasswordValid')
340  ->with(
341  $this->equalTo(self::ENCODED_PASSWORD),
342  $this->equalTo(self::PASSWORD),
343  $this->isType('string')
344  )->willReturn(false);
345 
346  $factory_mock->expects($this->once())->method('getEncoderByName')->willReturn($encoder);
347 
348  $password_manager = new ilUserPasswordManager([
349  'password_encoder' => 'mockencoder',
350  'encoder_factory' => $factory_mock,
351  'data_directory' => $this->getTestDirectoryUrl()
352  ]);
353 
354  $this->assertFalse($password_manager->verifyPassword($user_mock, self::PASSWORD));
355  }
356 
360  public function testFactoryCanBeCreated(): void
361  {
362  $factory = new ilUserPasswordEncoderFactory([
363  'data_directory' => $this->getTestDirectoryUrl()
364  ]);
365  $this->assertInstanceOf(ilUserPasswordEncoderFactory::class, $factory);
366  }
367 
374  {
375  $factory = new ilUserPasswordEncoderFactory([
376  'default_password_encoder' => 'md5',
377  'data_directory' => $this->getTestDirectoryUrl()
378  ]);
379  $this->assertEquals('md5', $factory->getDefaultEncoder());
380 
381  $encoder = $this->createMock(ilPasswordEncoder::class);
382  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
383  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
384 
385  $second_mockencoder = $this->createMock(ilPasswordEncoder::class);
386  $second_mockencoder->expects($this->atLeastOnce())->method('getName')->willReturn('second_mockencoder');
387  $second_mockencoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
388 
389  $factory->setSupportedEncoders([$encoder, $second_mockencoder]);
390  $this->assertCount(2, $factory->getSupportedEncoders());
391  $this->assertCount(2, $factory->getSupportedEncoderNames());
392  $this->assertCount(
393  0,
394  array_diff(['mockencoder', 'second_mockencoder'], $factory->getSupportedEncoderNames())
395  );
396  $this->assertCount(
397  0,
398  array_diff($factory->getSupportedEncoderNames(), ['mockencoder', 'second_mockencoder'])
399  );
400  }
401 
407  {
408  $this->assertException(ilUserException::class);
409  $factory = new ilUserPasswordEncoderFactory([
410  'data_directory' => $this->getTestDirectoryUrl()
411  ]);
412  $factory->setSupportedEncoders(['phpunit']);
413  }
414 
420  {
421  $this->assertException(ilUserException::class);
422  $factory = new ilUserPasswordEncoderFactory([
423  'data_directory' => $this->getTestDirectoryUrl()
424  ]);
425  $factory->getEncoderByName('phpunit');
426  }
427 
433  {
434  $this->assertException(ilUserException::class);
435  $factory = new ilUserPasswordEncoderFactory([
436  'default_password_encoder' => 'phpunit',
437  'data_directory' => $this->getTestDirectoryUrl()
438  ]);
439  $factory->getEncoderByName('phpunit');
440  }
441 
448  {
449  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
450  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
451  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
452 
453  $factory = new ilUserPasswordEncoderFactory([
454  'default_password_encoder' => $encoder->getName(),
455  'data_directory' => $this->getTestDirectoryUrl()
456  ]);
457  $factory->setSupportedEncoders([$encoder]);
458  $this->assertEquals($encoder, $factory->getEncoderByName('phpunit'));
459  }
460 
467  {
468  $encoder = $this->getMockBuilder(ilBasePasswordEncoder::class)->disableOriginalConstructor()->getMock();
469  $encoder->expects($this->atLeastOnce())->method('getName')->willReturn('mockencoder');
470  $encoder->expects($this->atLeastOnce())->method('isSupportedByRuntime')->willReturn(true);
471 
472  $factory = new ilUserPasswordEncoderFactory([
473  'default_password_encoder' => $encoder->getName(),
474  'data_directory' => $this->getTestDirectoryUrl()
475  ]);
476  $factory->setSupportedEncoders([$encoder]);
477  $this->assertEquals($encoder, $factory->getEncoderByName('mockencoder'));
478  }
479 }
testPasswordManagerNeverMigratesPasswordOnFailedVerificationWithVariantEncoders()
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutValidFactory()
testFactoryReturnsTheDefaultEncoderIfAnUnsupportedEncoderIsRequestedAndASupportedDefaultEncoderWasSpecifiedInFallbackMode()
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderWasInjected()
setTestDirectoryUrl(string $testDirectoryUrl)
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutEncoderInformation()
assertException(string $exception_class)
vfs vfsStreamDirectory $testDirectory
setTestDirectory(vfs\vfsStreamDirectory $testDirectory)
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderIsRequestedAndNoDefaultEncoderWasSpecifiedInFallbackMode()
const PASSWD_CRYPTED
testFactoryRaisesAnExceptionIfAnUnsupportedEncoderIsRequestedAndTheDefaultEncoderDoesNotMatchOneOfTheSupportedEncodersInFallbackMode()
testPasswordManagerMigratesPasswordOnVerificationWithVariantEncoders()
testExceptionIsRaisedIfPasswordManagerIsCreatedWithoutFactory()