Merge "phpunit: Avoid use of deprecated getMock for PHPUnit 5 compat"
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / LocalPasswordPrimaryAuthenticationProviderTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
5 use MediaWiki\MediaWikiServices;
6
7 /**
8 * @group AuthManager
9 * @group Database
10 * @covers MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider
11 */
12 class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
13
14 private $manager = null;
15 private $config = null;
16 private $validity = null;
17
18 /**
19 * Get an instance of the provider
20 *
21 * $provider->checkPasswordValidity is mocked to return $this->validity,
22 * because we don't need to test that here.
23 *
24 * @param bool $loginOnly
25 * @return LocalPasswordPrimaryAuthenticationProvider
26 */
27 protected function getProvider( $loginOnly = false ) {
28 if ( !$this->config ) {
29 $this->config = new \HashConfig();
30 }
31 $config = new \MultiConfig( [
32 $this->config,
33 MediaWikiServices::getInstance()->getMainConfig()
34 ] );
35
36 if ( !$this->manager ) {
37 $this->manager = new AuthManager( new \FauxRequest(), $config );
38 }
39 $this->validity = \Status::newGood();
40
41 $provider = $this->getMockBuilder( LocalPasswordPrimaryAuthenticationProvider::class )
42 ->setMethods( [ 'checkPasswordValidity' ] )
43 ->setConstructorArgs( [ [ 'loginOnly' => $loginOnly ] ] )
44 ->getMock();
45 $provider->expects( $this->any() )->method( 'checkPasswordValidity' )
46 ->will( $this->returnCallback( function () {
47 return $this->validity;
48 } ) );
49 $provider->setConfig( $config );
50 $provider->setLogger( new \Psr\Log\NullLogger() );
51 $provider->setManager( $this->manager );
52
53 return $provider;
54 }
55
56 public function testBasics() {
57 $user = $this->getMutableTestUser()->getUser();
58 $userName = $user->getName();
59 $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
60
61 $provider = new LocalPasswordPrimaryAuthenticationProvider();
62
63 $this->assertSame(
64 PrimaryAuthenticationProvider::TYPE_CREATE,
65 $provider->accountCreationType()
66 );
67
68 $this->assertTrue( $provider->testUserExists( $userName ) );
69 $this->assertTrue( $provider->testUserExists( $lowerInitialUserName ) );
70 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
71 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
72
73 $provider = new LocalPasswordPrimaryAuthenticationProvider( [ 'loginOnly' => true ] );
74
75 $this->assertSame(
76 PrimaryAuthenticationProvider::TYPE_NONE,
77 $provider->accountCreationType()
78 );
79
80 $this->assertTrue( $provider->testUserExists( $userName ) );
81 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
82
83 $req = new PasswordAuthenticationRequest;
84 $req->action = AuthManager::ACTION_CHANGE;
85 $req->username = '<invalid>';
86 $provider->providerChangeAuthenticationData( $req );
87 }
88
89 public function testTestUserCanAuthenticate() {
90 $user = $this->getMutableTestUser()->getUser();
91 $userName = $user->getName();
92 $dbw = wfGetDB( DB_MASTER );
93
94 $provider = $this->getProvider();
95
96 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
97
98 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
99
100 $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
101 $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
102 $this->assertTrue( $provider->testUserCanAuthenticate( $lowerInitialUserName ) );
103
104 $dbw->update(
105 'user',
106 [ 'user_password' => \PasswordFactory::newInvalidPassword()->toString() ],
107 [ 'user_name' => $userName ]
108 );
109 $this->assertFalse( $provider->testUserCanAuthenticate( $userName ) );
110
111 // Really old format
112 $dbw->update(
113 'user',
114 [ 'user_password' => '0123456789abcdef0123456789abcdef' ],
115 [ 'user_name' => $userName ]
116 );
117 $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
118 }
119
120 public function testSetPasswordResetFlag() {
121 // Set instance vars
122 $this->getProvider();
123
124 /// @todo: Because we're currently using User, which uses the global config...
125 $this->setMwGlobals( [ 'wgPasswordExpireGrace' => 100 ] );
126
127 $this->config->set( 'PasswordExpireGrace', 100 );
128 $this->config->set( 'InvalidPasswordReset', true );
129
130 $provider = new LocalPasswordPrimaryAuthenticationProvider();
131 $provider->setConfig( $this->config );
132 $provider->setLogger( new \Psr\Log\NullLogger() );
133 $provider->setManager( $this->manager );
134 $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
135
136 $user = $this->getMutableTestUser()->getUser();
137 $userName = $user->getName();
138 $dbw = wfGetDB( DB_MASTER );
139 $row = $dbw->selectRow(
140 'user',
141 '*',
142 [ 'user_name' => $userName ],
143 __METHOD__
144 );
145
146 $this->manager->removeAuthenticationSessionData( null );
147 $row->user_password_expires = wfTimestamp( TS_MW, time() + 200 );
148 $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
149 $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
150
151 $this->manager->removeAuthenticationSessionData( null );
152 $row->user_password_expires = wfTimestamp( TS_MW, time() - 200 );
153 $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
154 $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
155 $this->assertNotNull( $ret );
156 $this->assertSame( 'resetpass-expired', $ret->msg->getKey() );
157 $this->assertTrue( $ret->hard );
158
159 $this->manager->removeAuthenticationSessionData( null );
160 $row->user_password_expires = wfTimestamp( TS_MW, time() - 1 );
161 $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
162 $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
163 $this->assertNotNull( $ret );
164 $this->assertSame( 'resetpass-expired-soft', $ret->msg->getKey() );
165 $this->assertFalse( $ret->hard );
166
167 $this->manager->removeAuthenticationSessionData( null );
168 $row->user_password_expires = null;
169 $status = \Status::newGood();
170 $status->error( 'testing' );
171 $providerPriv->setPasswordResetFlag( $userName, $status, $row );
172 $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
173 $this->assertNotNull( $ret );
174 $this->assertSame( 'resetpass-validity-soft', $ret->msg->getKey() );
175 $this->assertFalse( $ret->hard );
176 }
177
178 public function testAuthentication() {
179 $testUser = $this->getMutableTestUser();
180 $userName = $testUser->getUser()->getName();
181
182 $dbw = wfGetDB( DB_MASTER );
183 $id = \User::idFromName( $userName );
184
185 $req = new PasswordAuthenticationRequest();
186 $req->action = AuthManager::ACTION_LOGIN;
187 $reqs = [ PasswordAuthenticationRequest::class => $req ];
188
189 $provider = $this->getProvider();
190
191 // General failures
192 $this->assertEquals(
193 AuthenticationResponse::newAbstain(),
194 $provider->beginPrimaryAuthentication( [] )
195 );
196
197 $req->username = 'foo';
198 $req->password = null;
199 $this->assertEquals(
200 AuthenticationResponse::newAbstain(),
201 $provider->beginPrimaryAuthentication( $reqs )
202 );
203
204 $req->username = null;
205 $req->password = 'bar';
206 $this->assertEquals(
207 AuthenticationResponse::newAbstain(),
208 $provider->beginPrimaryAuthentication( $reqs )
209 );
210
211 $req->username = '<invalid>';
212 $req->password = 'WhoCares';
213 $ret = $provider->beginPrimaryAuthentication( $reqs );
214 $this->assertEquals(
215 AuthenticationResponse::newAbstain(),
216 $provider->beginPrimaryAuthentication( $reqs )
217 );
218
219 $req->username = 'DoesNotExist';
220 $req->password = 'DoesNotExist';
221 $ret = $provider->beginPrimaryAuthentication( $reqs );
222 $this->assertEquals(
223 AuthenticationResponse::newAbstain(),
224 $provider->beginPrimaryAuthentication( $reqs )
225 );
226
227 // Validation failure
228 $req->username = $userName;
229 $req->password = $testUser->getPassword();
230 $this->validity = \Status::newFatal( 'arbitrary-failure' );
231 $ret = $provider->beginPrimaryAuthentication( $reqs );
232 $this->assertEquals(
233 AuthenticationResponse::FAIL,
234 $ret->status
235 );
236 $this->assertEquals(
237 'arbitrary-failure',
238 $ret->message->getKey()
239 );
240
241 // Successful auth
242 $this->manager->removeAuthenticationSessionData( null );
243 $this->validity = \Status::newGood();
244 $this->assertEquals(
245 AuthenticationResponse::newPass( $userName ),
246 $provider->beginPrimaryAuthentication( $reqs )
247 );
248 $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
249
250 // Successful auth after normalizing name
251 $this->manager->removeAuthenticationSessionData( null );
252 $this->validity = \Status::newGood();
253 $req->username = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
254 $this->assertEquals(
255 AuthenticationResponse::newPass( $userName ),
256 $provider->beginPrimaryAuthentication( $reqs )
257 );
258 $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
259 $req->username = $userName;
260
261 // Successful auth with reset
262 $this->manager->removeAuthenticationSessionData( null );
263 $this->validity->error( 'arbitrary-warning' );
264 $this->assertEquals(
265 AuthenticationResponse::newPass( $userName ),
266 $provider->beginPrimaryAuthentication( $reqs )
267 );
268 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
269
270 // Wrong password
271 $this->validity = \Status::newGood();
272 $req->password = 'Wrong';
273 $ret = $provider->beginPrimaryAuthentication( $reqs );
274 $this->assertEquals(
275 AuthenticationResponse::FAIL,
276 $ret->status
277 );
278 $this->assertEquals(
279 'wrongpassword',
280 $ret->message->getKey()
281 );
282
283 // Correct handling of legacy encodings
284 $password = ':B:salt:' . md5( 'salt-' . md5( "\xe1\xe9\xed\xf3\xfa" ) );
285 $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
286 $req->password = 'áéíóú';
287 $ret = $provider->beginPrimaryAuthentication( $reqs );
288 $this->assertEquals(
289 AuthenticationResponse::FAIL,
290 $ret->status
291 );
292 $this->assertEquals(
293 'wrongpassword',
294 $ret->message->getKey()
295 );
296
297 $this->config->set( 'LegacyEncoding', true );
298 $this->assertEquals(
299 AuthenticationResponse::newPass( $userName ),
300 $provider->beginPrimaryAuthentication( $reqs )
301 );
302
303 $req->password = 'áéíóú Wrong';
304 $ret = $provider->beginPrimaryAuthentication( $reqs );
305 $this->assertEquals(
306 AuthenticationResponse::FAIL,
307 $ret->status
308 );
309 $this->assertEquals(
310 'wrongpassword',
311 $ret->message->getKey()
312 );
313
314 // Correct handling of really old password hashes
315 $this->config->set( 'PasswordSalt', false );
316 $password = md5( 'FooBar' );
317 $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
318 $req->password = 'FooBar';
319 $this->assertEquals(
320 AuthenticationResponse::newPass( $userName ),
321 $provider->beginPrimaryAuthentication( $reqs )
322 );
323
324 $this->config->set( 'PasswordSalt', true );
325 $password = md5( "$id-" . md5( 'FooBar' ) );
326 $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
327 $req->password = 'FooBar';
328 $this->assertEquals(
329 AuthenticationResponse::newPass( $userName ),
330 $provider->beginPrimaryAuthentication( $reqs )
331 );
332 }
333
334 /**
335 * @dataProvider provideProviderAllowsAuthenticationDataChange
336 * @param string $type
337 * @param string $user
338 * @param \Status $validity Result of the password validity check
339 * @param \StatusValue $expect1 Expected result with $checkData = false
340 * @param \StatusValue $expect2 Expected result with $checkData = true
341 */
342 public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status $validity,
343 \StatusValue $expect1, \StatusValue $expect2
344 ) {
345 if ( $type === PasswordAuthenticationRequest::class ) {
346 $req = new $type();
347 } elseif ( $type === PasswordDomainAuthenticationRequest::class ) {
348 $req = new $type( [] );
349 } else {
350 $req = $this->createMock( $type );
351 }
352 $req->action = AuthManager::ACTION_CHANGE;
353 $req->username = $user;
354 $req->password = 'NewPassword';
355 $req->retype = 'NewPassword';
356
357 $provider = $this->getProvider();
358 $this->validity = $validity;
359 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
360 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
361
362 $req->retype = 'BadRetype';
363 $this->assertEquals(
364 $expect1,
365 $provider->providerAllowsAuthenticationDataChange( $req, false )
366 );
367 $this->assertEquals(
368 $expect2->getValue() === 'ignored' ? $expect2 : \StatusValue::newFatal( 'badretype' ),
369 $provider->providerAllowsAuthenticationDataChange( $req, true )
370 );
371
372 $provider = $this->getProvider( true );
373 $this->assertEquals(
374 \StatusValue::newGood( 'ignored' ),
375 $provider->providerAllowsAuthenticationDataChange( $req, true ),
376 'loginOnly mode should claim to ignore all changes'
377 );
378 }
379
380 public static function provideProviderAllowsAuthenticationDataChange() {
381 $err = \StatusValue::newGood();
382 $err->error( 'arbitrary-warning' );
383
384 return [
385 [ AuthenticationRequest::class, 'UTSysop', \Status::newGood(),
386 \StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
387 [ PasswordAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
388 \StatusValue::newGood(), \StatusValue::newGood() ],
389 [ PasswordAuthenticationRequest::class, 'uTSysop', \Status::newGood(),
390 \StatusValue::newGood(), \StatusValue::newGood() ],
391 [ PasswordAuthenticationRequest::class, 'UTSysop', \Status::wrap( $err ),
392 \StatusValue::newGood(), $err ],
393 [ PasswordAuthenticationRequest::class, 'UTSysop', \Status::newFatal( 'arbitrary-error' ),
394 \StatusValue::newGood(), \StatusValue::newFatal( 'arbitrary-error' ) ],
395 [ PasswordAuthenticationRequest::class, 'DoesNotExist', \Status::newGood(),
396 \StatusValue::newGood(), \StatusValue::newGood( 'ignored' ) ],
397 [ PasswordDomainAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
398 \StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
399 ];
400 }
401
402 /**
403 * @dataProvider provideProviderChangeAuthenticationData
404 * @param callable|bool $usernameTransform
405 * @param string $type
406 * @param bool $loginOnly
407 * @param bool $changed
408 */
409 public function testProviderChangeAuthenticationData(
410 $usernameTransform, $type, $loginOnly, $changed ) {
411 $testUser = $this->getMutableTestUser();
412 $user = $testUser->getUser()->getName();
413 if ( is_callable( $usernameTransform ) ) {
414 $user = call_user_func( $usernameTransform, $user );
415 }
416 $cuser = ucfirst( $user );
417 $oldpass = $testUser->getPassword();
418 $newpass = 'NewPassword';
419
420 $dbw = wfGetDB( DB_MASTER );
421 $oldExpiry = $dbw->selectField( 'user', 'user_password_expires', [ 'user_name' => $cuser ] );
422
423 $this->mergeMwGlobalArrayValue( 'wgHooks', [
424 'ResetPasswordExpiration' => [ function ( $user, &$expires ) {
425 $expires = '30001231235959';
426 } ]
427 ] );
428
429 $provider = $this->getProvider( $loginOnly );
430
431 // Sanity check
432 $loginReq = new PasswordAuthenticationRequest();
433 $loginReq->action = AuthManager::ACTION_LOGIN;
434 $loginReq->username = $user;
435 $loginReq->password = $oldpass;
436 $loginReqs = [ PasswordAuthenticationRequest::class => $loginReq ];
437 $this->assertEquals(
438 AuthenticationResponse::newPass( $cuser ),
439 $provider->beginPrimaryAuthentication( $loginReqs ),
440 'Sanity check'
441 );
442
443 if ( $type === PasswordAuthenticationRequest::class ) {
444 $changeReq = new $type();
445 } else {
446 $changeReq = $this->createMock( $type );
447 }
448 $changeReq->action = AuthManager::ACTION_CHANGE;
449 $changeReq->username = $user;
450 $changeReq->password = $newpass;
451 $provider->providerChangeAuthenticationData( $changeReq );
452
453 if ( $loginOnly && $changed ) {
454 $old = 'fail';
455 $new = 'fail';
456 $expectExpiry = null;
457 } elseif ( $changed ) {
458 $old = 'fail';
459 $new = 'pass';
460 $expectExpiry = '30001231235959';
461 } else {
462 $old = 'pass';
463 $new = 'fail';
464 $expectExpiry = $oldExpiry;
465 }
466
467 $loginReq->password = $oldpass;
468 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
469 if ( $old === 'pass' ) {
470 $this->assertEquals(
471 AuthenticationResponse::newPass( $cuser ),
472 $ret,
473 'old password should pass'
474 );
475 } else {
476 $this->assertEquals(
477 AuthenticationResponse::FAIL,
478 $ret->status,
479 'old password should fail'
480 );
481 $this->assertEquals(
482 'wrongpassword',
483 $ret->message->getKey(),
484 'old password should fail'
485 );
486 }
487
488 $loginReq->password = $newpass;
489 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
490 if ( $new === 'pass' ) {
491 $this->assertEquals(
492 AuthenticationResponse::newPass( $cuser ),
493 $ret,
494 'new password should pass'
495 );
496 } else {
497 $this->assertEquals(
498 AuthenticationResponse::FAIL,
499 $ret->status,
500 'new password should fail'
501 );
502 $this->assertEquals(
503 'wrongpassword',
504 $ret->message->getKey(),
505 'new password should fail'
506 );
507 }
508
509 $this->assertSame(
510 $expectExpiry,
511 $dbw->selectField( 'user', 'user_password_expires', [ 'user_name' => $cuser ] )
512 );
513 }
514
515 public static function provideProviderChangeAuthenticationData() {
516 return [
517 [ false, AuthenticationRequest::class, false, false ],
518 [ false, PasswordAuthenticationRequest::class, false, true ],
519 [ false, AuthenticationRequest::class, true, false ],
520 [ false, PasswordAuthenticationRequest::class, true, true ],
521 [ 'ucfirst', PasswordAuthenticationRequest::class, false, true ],
522 [ 'ucfirst', PasswordAuthenticationRequest::class, true, true ],
523 ];
524 }
525
526 public function testTestForAccountCreation() {
527 $user = \User::newFromName( 'foo' );
528 $req = new PasswordAuthenticationRequest();
529 $req->action = AuthManager::ACTION_CREATE;
530 $req->username = 'Foo';
531 $req->password = 'Bar';
532 $req->retype = 'Bar';
533 $reqs = [ PasswordAuthenticationRequest::class => $req ];
534
535 $provider = $this->getProvider();
536 $this->assertEquals(
537 \StatusValue::newGood(),
538 $provider->testForAccountCreation( $user, $user, [] ),
539 'No password request'
540 );
541
542 $this->assertEquals(
543 \StatusValue::newGood(),
544 $provider->testForAccountCreation( $user, $user, $reqs ),
545 'Password request, validated'
546 );
547
548 $req->retype = 'Baz';
549 $this->assertEquals(
550 \StatusValue::newFatal( 'badretype' ),
551 $provider->testForAccountCreation( $user, $user, $reqs ),
552 'Password request, bad retype'
553 );
554 $req->retype = 'Bar';
555
556 $this->validity->error( 'arbitrary warning' );
557 $expect = \StatusValue::newGood();
558 $expect->error( 'arbitrary warning' );
559 $this->assertEquals(
560 $expect,
561 $provider->testForAccountCreation( $user, $user, $reqs ),
562 'Password request, not validated'
563 );
564
565 $provider = $this->getProvider( true );
566 $this->validity->error( 'arbitrary warning' );
567 $this->assertEquals(
568 \StatusValue::newGood(),
569 $provider->testForAccountCreation( $user, $user, $reqs ),
570 'Password request, not validated, loginOnly'
571 );
572 }
573
574 public function testAccountCreation() {
575 $user = \User::newFromName( 'Foo' );
576
577 $req = new PasswordAuthenticationRequest();
578 $req->action = AuthManager::ACTION_CREATE;
579 $reqs = [ PasswordAuthenticationRequest::class => $req ];
580
581 $provider = $this->getProvider( true );
582 try {
583 $provider->beginPrimaryAccountCreation( $user, $user, [] );
584 $this->fail( 'Expected exception was not thrown' );
585 } catch ( \BadMethodCallException $ex ) {
586 $this->assertSame(
587 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
588 );
589 }
590
591 try {
592 $provider->finishAccountCreation( $user, $user, AuthenticationResponse::newPass() );
593 $this->fail( 'Expected exception was not thrown' );
594 } catch ( \BadMethodCallException $ex ) {
595 $this->assertSame(
596 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
597 );
598 }
599
600 $provider = $this->getProvider( false );
601
602 $this->assertEquals(
603 AuthenticationResponse::newAbstain(),
604 $provider->beginPrimaryAccountCreation( $user, $user, [] )
605 );
606
607 $req->username = 'foo';
608 $req->password = null;
609 $this->assertEquals(
610 AuthenticationResponse::newAbstain(),
611 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
612 );
613
614 $req->username = null;
615 $req->password = 'bar';
616 $this->assertEquals(
617 AuthenticationResponse::newAbstain(),
618 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
619 );
620
621 $req->username = 'foo';
622 $req->password = 'bar';
623
624 $expect = AuthenticationResponse::newPass( 'Foo' );
625 $expect->createRequest = clone( $req );
626 $expect->createRequest->username = 'Foo';
627 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
628
629 // We have to cheat a bit to avoid having to add a new user to
630 // the database to test the actual setting of the password works right
631 $dbw = wfGetDB( DB_MASTER );
632
633 $user = \User::newFromName( 'UTSysop' );
634 $req->username = $user->getName();
635 $req->password = 'NewPassword';
636 $expect = AuthenticationResponse::newPass( 'UTSysop' );
637 $expect->createRequest = $req;
638
639 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
640 $this->assertEquals( $expect, $res2, 'Sanity check' );
641
642 $ret = $provider->beginPrimaryAuthentication( $reqs );
643 $this->assertEquals( AuthenticationResponse::FAIL, $ret->status, 'sanity check' );
644
645 $this->assertNull( $provider->finishAccountCreation( $user, $user, $res2 ) );
646 $ret = $provider->beginPrimaryAuthentication( $reqs );
647 $this->assertEquals( AuthenticationResponse::PASS, $ret->status, 'new password is set' );
648 }
649
650 }