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