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