3 namespace MediaWiki\Tests\Auth
;
5 use BadMethodCallException
;
6 use MediaWiki\Auth\AuthenticationRequest
;
7 use MediaWiki\Auth\AuthenticationResponse
;
8 use MediaWiki\Auth\AuthManager
;
9 use MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider
;
10 use MediaWiki\Auth\PasswordAuthenticationRequest
;
11 use MediaWiki\Auth\PasswordDomainAuthenticationRequest
;
12 use MediaWiki\Auth\PrimaryAuthenticationProvider
;
13 use MediaWiki\Config\HashConfig
;
14 use MediaWiki\Config\MultiConfig
;
15 use MediaWiki\MainConfigNames
;
16 use MediaWiki\Password\PasswordFactory
;
17 use MediaWiki\Request\FauxRequest
;
18 use MediaWiki\Status\Status
;
19 use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait
;
20 use MediaWiki\Tests\Unit\DummyServicesTrait
;
21 use MediaWiki\User\User
;
22 use MediaWiki\User\UserNameUtils
;
23 use MediaWikiIntegrationTestCase
;
25 use Wikimedia\TestingAccessWrapper
;
30 * @covers \MediaWiki\Auth\LocalPasswordPrimaryAuthenticationProvider
32 class LocalPasswordPrimaryAuthenticationProviderTest
extends MediaWikiIntegrationTestCase
{
33 use AuthenticationProviderTestTrait
;
34 use DummyServicesTrait
;
36 /** @var AuthManager|null */
37 private $manager = null;
38 /** @var HashConfig|null */
39 private $config = null;
40 /** @var Status|null */
41 private $validity = null;
44 * Get an instance of the provider
46 * $provider->checkPasswordValidity is mocked to return $this->validity,
47 * because we don't need to test that here.
49 * @param bool $loginOnly
50 * @return LocalPasswordPrimaryAuthenticationProvider
52 protected function getProvider( $loginOnly = false ) {
53 $mwServices = $this->getServiceContainer();
54 if ( !$this->config
) {
55 $this->config
= new HashConfig();
57 $config = new MultiConfig( [
59 $mwServices->getMainConfig()
62 // We need a real HookContainer since testProviderChangeAuthenticationData()
64 $hookContainer = $mwServices->getHookContainer();
66 if ( !$this->manager
) {
67 $userNameUtils = $this->createNoOpMock( UserNameUtils
::class );
69 $this->manager
= new AuthManager(
72 $this->getDummyObjectFactory(),
74 $mwServices->getReadOnlyMode(),
76 $mwServices->getBlockManager(),
77 $mwServices->getWatchlistManager(),
78 $mwServices->getDBLoadBalancer(),
79 $mwServices->getContentLanguage(),
80 $mwServices->getLanguageConverterFactory(),
81 $mwServices->getBotPasswordStore(),
82 $mwServices->getUserFactory(),
83 $mwServices->getUserIdentityLookup(),
84 $mwServices->getUserOptionsManager()
87 $this->validity
= Status
::newGood();
88 $provider = $this->getMockBuilder( LocalPasswordPrimaryAuthenticationProvider
::class )
89 ->onlyMethods( [ 'checkPasswordValidity' ] )
90 ->setConstructorArgs( [
91 $mwServices->getConnectionProvider(),
92 [ 'loginOnly' => $loginOnly ]
96 $provider->method( 'checkPasswordValidity' )
97 ->willReturnCallback( function () {
98 return $this->validity
;
101 $provider, $config, null, $this->manager
, $hookContainer, $this->getServiceContainer()->getUserNameUtils()
107 public function testBasics() {
108 $user = $this->getMutableTestUser()->getUser();
109 $userName = $user->getName();
110 $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
112 $provider = $this->getProvider();
115 PrimaryAuthenticationProvider
::TYPE_CREATE
,
116 $provider->accountCreationType()
119 $this->assertTrue( $provider->testUserExists( $userName ) );
120 $this->assertTrue( $provider->testUserExists( $lowerInitialUserName ) );
121 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
122 $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
124 $provider = $this->getProvider( [ 'loginOnly' => true ] );
127 PrimaryAuthenticationProvider
::TYPE_NONE
,
128 $provider->accountCreationType()
131 $this->assertTrue( $provider->testUserExists( $userName ) );
132 $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
134 $req = new PasswordAuthenticationRequest
;
135 $req->action
= AuthManager
::ACTION_CHANGE
;
136 $req->username
= '<invalid>';
137 $provider->providerChangeAuthenticationData( $req );
140 public function testTestUserCanAuthenticate() {
141 $user = $this->getMutableTestUser()->getUser();
142 $userName = $user->getName();
143 $dbw = $this->getDb();
145 $provider = $this->getProvider();
147 $this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
149 $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
151 $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
152 $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
153 $this->assertTrue( $provider->testUserCanAuthenticate( $lowerInitialUserName ) );
155 $dbw->newUpdateQueryBuilder()
157 ->set( [ 'user_password' => PasswordFactory
::newInvalidPassword()->toString() ] )
158 ->where( [ 'user_name' => $userName ] )
159 ->caller( __METHOD__
)
161 $this->assertFalse( $provider->testUserCanAuthenticate( $userName ) );
164 $dbw->newUpdateQueryBuilder()
166 ->set( [ 'user_password' => '0123456789abcdef0123456789abcdef' ] )
167 ->where( [ 'user_name' => $userName ] )
168 ->caller( __METHOD__
)
170 $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
173 public function testSetPasswordResetFlag() {
175 $this->getProvider();
177 // @todo: Because we're currently using User, which uses the global config...
178 $this->overrideConfigValue( MainConfigNames
::PasswordExpireGrace
, 100 );
180 $this->config
->set( MainConfigNames
::PasswordExpireGrace
, 100 );
181 $this->config
->set( MainConfigNames
::InvalidPasswordReset
, true );
183 $provider = new LocalPasswordPrimaryAuthenticationProvider(
184 $this->getServiceContainer()->getConnectionProvider()
186 $this->initProvider( $provider, $this->config
, null, $this->manager
);
187 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
189 $user = $this->getMutableTestUser()->getUser();
190 $userName = $user->getName();
191 $row = $this->getDb()->newSelectQueryBuilder()
194 ->where( [ 'user_name' => $userName ] )
195 ->caller( __METHOD__
)->fetchRow();
197 $this->manager
->removeAuthenticationSessionData( null );
198 $row->user_password_expires
= wfTimestamp( TS_MW
, time() +
200 );
199 $providerPriv->setPasswordResetFlag( $userName, Status
::newGood(), $row );
200 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
202 $this->manager
->removeAuthenticationSessionData( null );
203 $row->user_password_expires
= wfTimestamp( TS_MW
, time() - 200 );
204 $providerPriv->setPasswordResetFlag( $userName, Status
::newGood(), $row );
205 $ret = $this->manager
->getAuthenticationSessionData( 'reset-pass' );
206 $this->assertNotNull( $ret );
207 $this->assertSame( 'resetpass-expired', $ret->msg
->getKey() );
208 $this->assertTrue( $ret->hard
);
210 $this->manager
->removeAuthenticationSessionData( null );
211 $row->user_password_expires
= wfTimestamp( TS_MW
, time() - 1 );
212 $providerPriv->setPasswordResetFlag( $userName, Status
::newGood(), $row );
213 $ret = $this->manager
->getAuthenticationSessionData( 'reset-pass' );
214 $this->assertNotNull( $ret );
215 $this->assertSame( 'resetpass-expired-soft', $ret->msg
->getKey() );
216 $this->assertFalse( $ret->hard
);
218 $this->manager
->removeAuthenticationSessionData( null );
219 $row->user_password_expires
= null;
220 $status = Status
::newGood( [ 'suggestChangeOnLogin' => true ] );
221 $status->error( 'testing' );
222 $providerPriv->setPasswordResetFlag( $userName, $status, $row );
223 $ret = $this->manager
->getAuthenticationSessionData( 'reset-pass' );
224 $this->assertNotNull( $ret );
225 $this->assertSame( 'resetpass-validity-soft', $ret->msg
->getKey() );
226 $this->assertFalse( $ret->hard
);
228 $this->manager
->removeAuthenticationSessionData( null );
229 $row->user_password_expires
= null;
230 $status = Status
::newGood( [ 'forceChange' => true ] );
231 $status->error( 'testing' );
232 $providerPriv->setPasswordResetFlag( $userName, $status, $row );
233 $ret = $this->manager
->getAuthenticationSessionData( 'reset-pass' );
234 $this->assertNotNull( $ret );
235 $this->assertSame( 'resetpass-validity', $ret->msg
->getKey() );
236 $this->assertTrue( $ret->hard
);
238 $this->manager
->removeAuthenticationSessionData( null );
239 $row->user_password_expires
= null;
240 $status = Status
::newGood( [ 'suggestChangeOnLogin' => false, ] );
241 $status->error( 'testing' );
242 $providerPriv->setPasswordResetFlag( $userName, $status, $row );
243 $ret = $this->manager
->getAuthenticationSessionData( 'reset-pass' );
244 $this->assertNull( $ret );
247 public function testAuthentication() {
248 $testUser = $this->getMutableTestUser();
249 $userName = $testUser->getUser()->getName();
251 $dbw = $this->getDb();
252 $id = $testUser->getUser()->getId();
254 $req = new PasswordAuthenticationRequest();
255 $req->action
= AuthManager
::ACTION_LOGIN
;
256 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
258 $provider = $this->getProvider();
262 AuthenticationResponse
::newAbstain(),
263 $provider->beginPrimaryAuthentication( [] )
266 $req->username
= 'foo';
267 $req->password
= null;
269 AuthenticationResponse
::newAbstain(),
270 $provider->beginPrimaryAuthentication( $reqs )
273 $req->username
= null;
274 $req->password
= 'bar';
276 AuthenticationResponse
::newAbstain(),
277 $provider->beginPrimaryAuthentication( $reqs )
280 $req->username
= '<invalid>';
281 $req->password
= 'WhoCares';
282 $ret = $provider->beginPrimaryAuthentication( $reqs );
284 AuthenticationResponse
::newAbstain(),
285 $provider->beginPrimaryAuthentication( $reqs )
288 $req->username
= 'DoesNotExist';
289 $req->password
= 'DoesNotExist';
290 $ret = $provider->beginPrimaryAuthentication( $reqs );
292 AuthenticationResponse
::FAIL
,
297 $ret->message
->getKey()
300 // Validation failure
301 $req->username
= $userName;
302 $req->password
= $testUser->getPassword();
303 $this->validity
= Status
::newFatal( 'arbitrary-failure' );
304 $ret = $provider->beginPrimaryAuthentication( $reqs );
306 AuthenticationResponse
::FAIL
,
309 // AbstractPasswordPrimaryAuthenticationProvider::getFatalPasswordErrorResponse() will
310 // wrap the original message in 'fatalpassworderror'
312 'fatalpassworderror',
313 $ret->message
->getKey()
317 $this->manager
->removeAuthenticationSessionData( null );
318 $this->validity
= Status
::newGood();
320 AuthenticationResponse
::newPass( $userName ),
321 $provider->beginPrimaryAuthentication( $reqs )
323 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
325 // Successful auth after normalizing name
326 $this->manager
->removeAuthenticationSessionData( null );
327 $this->validity
= Status
::newGood();
328 $req->username
= mb_strtolower( $userName[0] ) . substr( $userName, 1 );
330 AuthenticationResponse
::newPass( $userName ),
331 $provider->beginPrimaryAuthentication( $reqs )
333 $this->assertNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
334 $req->username
= $userName;
336 // Successful auth with reset
337 $this->manager
->removeAuthenticationSessionData( null );
338 $this->validity
= Status
::newGood( [ 'suggestChangeOnLogin' => true ] );
339 $this->validity
->error( 'arbitrary-warning' );
341 AuthenticationResponse
::newPass( $userName ),
342 $provider->beginPrimaryAuthentication( $reqs )
344 $this->assertNotNull( $this->manager
->getAuthenticationSessionData( 'reset-pass' ) );
347 $this->validity
= Status
::newGood();
348 $req->password
= 'Wrong';
349 $ret = $provider->beginPrimaryAuthentication( $reqs );
351 AuthenticationResponse
::FAIL
,
356 $ret->message
->getKey()
359 // Correct handling of legacy encodings
360 $password = ':B:salt:' . md5( 'salt-' . md5( "\xe1\xe9\xed\xf3\xfa" ) );
361 $dbw->newUpdateQueryBuilder()
363 ->set( [ 'user_password' => $password ] )
364 ->where( [ 'user_name' => $userName ] )
365 ->caller( __METHOD__
)
367 $req->password
= 'áéÃóú';
368 $ret = $provider->beginPrimaryAuthentication( $reqs );
370 AuthenticationResponse
::FAIL
,
375 $ret->message
->getKey()
378 $this->config
->set( MainConfigNames
::LegacyEncoding
, true );
380 AuthenticationResponse
::newPass( $userName ),
381 $provider->beginPrimaryAuthentication( $reqs )
384 $req->password
= 'áéÃóú Wrong';
385 $ret = $provider->beginPrimaryAuthentication( $reqs );
387 AuthenticationResponse
::FAIL
,
392 $ret->message
->getKey()
395 // Correct handling of really old password hashes
396 $password = md5( "$id-" . md5( 'FooBar' ) );
397 $dbw->newUpdateQueryBuilder()
399 ->set( [ 'user_password' => $password ] )
400 ->where( [ 'user_name' => $userName ] )
401 ->caller( __METHOD__
)
403 $req->password
= 'FooBar';
405 AuthenticationResponse
::newPass( $userName ),
406 $provider->beginPrimaryAuthentication( $reqs )
411 * @dataProvider provideProviderAllowsAuthenticationDataChange
413 * @param string $type
414 * @param callable $usernameGetter Function that takes the username of a sysop user and returns the username to
416 * @param Status $validity Result of the password validity check
417 * @param StatusValue $expect1 Expected result with $checkData = false
418 * @param StatusValue $expect2 Expected result with $checkData = true
420 public function testProviderAllowsAuthenticationDataChange( $type, callable
$usernameGetter, Status
$validity,
421 StatusValue
$expect1,
424 $user = $usernameGetter( $this->getTestSysop()->getUserIdentity()->getName() );
425 if ( $type === PasswordAuthenticationRequest
::class ) {
427 $req->password
= 'NewPassword';
428 $req->retype
= 'NewPassword';
429 } elseif ( $type === PasswordDomainAuthenticationRequest
::class ) {
430 $req = new $type( [] );
432 $req = $this->createMock( $type );
434 $req->action
= AuthManager
::ACTION_CHANGE
;
435 $req->username
= $user;
437 $provider = $this->getProvider();
438 $this->validity
= $validity;
439 $this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
440 $this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
442 if ( $req instanceof PasswordAuthenticationRequest
) {
443 $req->retype
= 'BadRetype';
447 $provider->providerAllowsAuthenticationDataChange( $req, false )
450 $expect2->getValue() === 'ignored' ?
$expect2 : StatusValue
::newFatal( 'badretype' ),
451 $provider->providerAllowsAuthenticationDataChange( $req, true )
454 $provider = $this->getProvider( true );
456 StatusValue
::newGood( 'ignored' ),
457 $provider->providerAllowsAuthenticationDataChange( $req, true ),
458 'loginOnly mode should claim to ignore all changes'
462 public static function provideProviderAllowsAuthenticationDataChange() {
463 $err = StatusValue
::newGood();
464 $err->error( 'arbitrary-warning' );
468 AuthenticationRequest
::class,
469 static fn ( $sysopUsername ) => $sysopUsername,
471 StatusValue
::newGood( 'ignored' ),
472 StatusValue
::newGood( 'ignored' )
475 PasswordAuthenticationRequest
::class,
476 static fn ( $sysopUsername ) => $sysopUsername,
478 StatusValue
::newGood(),
479 StatusValue
::newGood()
482 PasswordAuthenticationRequest
::class,
485 StatusValue
::newGood(),
486 StatusValue
::newGood()
489 PasswordAuthenticationRequest
::class,
490 static fn ( $sysopUsername ) => $sysopUsername,
491 Status
::wrap( $err ),
492 StatusValue
::newGood(),
496 PasswordAuthenticationRequest
::class,
497 static fn ( $sysopUsername ) => $sysopUsername,
498 Status
::newFatal( 'arbitrary-error' ),
499 StatusValue
::newGood(),
500 StatusValue
::newFatal( 'arbitrary-error' )
503 PasswordAuthenticationRequest
::class,
504 static fn () => 'DoesNotExist',
506 StatusValue
::newGood(),
507 StatusValue
::newGood( 'ignored' )
510 PasswordDomainAuthenticationRequest
::class,
511 static fn ( $sysopUsername ) => $sysopUsername,
513 StatusValue
::newGood( 'ignored' ),
514 StatusValue
::newGood( 'ignored' )
520 * @dataProvider provideProviderChangeAuthenticationData
521 * @param callable|false $usernameTransform
522 * @param string $type
523 * @param bool $loginOnly
524 * @param bool $changed
526 public function testProviderChangeAuthenticationData(
527 $usernameTransform, $type, $loginOnly, $changed ) {
528 $testUser = $this->getMutableTestUser();
529 $user = $testUser->getUser()->getName();
530 if ( is_callable( $usernameTransform ) ) {
531 $user = $usernameTransform( $user );
533 $cuser = ucfirst( $user );
534 $oldpass = $testUser->getPassword();
535 $newpass = 'NewPassword';
537 $dbw = $this->getDb();
538 $oldExpiry = $dbw->newSelectQueryBuilder()
539 ->select( 'user_password_expires' )
541 ->where( [ 'user_name' => $cuser ] )
544 $this->mergeMwGlobalArrayValue( 'wgHooks', [
545 'ResetPasswordExpiration' => [ static function ( $user, &$expires ) {
546 $expires = '30001231235959';
550 $provider = $this->getProvider( $loginOnly );
552 $loginReq = new PasswordAuthenticationRequest();
553 $loginReq->action
= AuthManager
::ACTION_LOGIN
;
554 $loginReq->username
= $user;
555 $loginReq->password
= $oldpass;
556 $loginReqs = [ PasswordAuthenticationRequest
::class => $loginReq ];
558 AuthenticationResponse
::newPass( $cuser ),
559 $provider->beginPrimaryAuthentication( $loginReqs )
562 if ( $type === PasswordAuthenticationRequest
::class ) {
563 $changeReq = new $type();
564 $changeReq->password
= $newpass;
566 $changeReq = $this->createMock( $type );
568 $changeReq->action
= AuthManager
::ACTION_CHANGE
;
569 $changeReq->username
= $user;
570 $provider->providerChangeAuthenticationData( $changeReq );
572 if ( $loginOnly && $changed ) {
575 $expectExpiry = null;
576 } elseif ( $changed ) {
579 $expectExpiry = '30001231235959';
583 $expectExpiry = $oldExpiry;
586 $loginReq->password
= $oldpass;
587 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
588 if ( $old === 'pass' ) {
590 AuthenticationResponse
::newPass( $cuser ),
592 'old password should pass'
596 AuthenticationResponse
::FAIL
,
598 'old password should fail'
602 $ret->message
->getKey(),
603 'old password should fail'
607 $loginReq->password
= $newpass;
608 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
609 if ( $new === 'pass' ) {
611 AuthenticationResponse
::newPass( $cuser ),
613 'new password should pass'
617 AuthenticationResponse
::FAIL
,
619 'new password should fail'
623 $ret->message
->getKey(),
624 'new password should fail'
632 $dbw->newSelectQueryBuilder()
633 ->select( 'user_password_expires' )
635 ->where( [ 'user_name' => $cuser ] )
641 public static function provideProviderChangeAuthenticationData() {
643 [ false, AuthenticationRequest
::class, false, false ],
644 [ false, PasswordAuthenticationRequest
::class, false, true ],
645 [ false, AuthenticationRequest
::class, true, false ],
646 [ false, PasswordAuthenticationRequest
::class, true, true ],
647 [ 'ucfirst', PasswordAuthenticationRequest
::class, false, true ],
648 [ 'ucfirst', PasswordAuthenticationRequest
::class, true, true ],
652 public function testTestForAccountCreation() {
653 $user = User
::newFromName( 'foo' );
654 $req = new PasswordAuthenticationRequest();
655 $req->action
= AuthManager
::ACTION_CREATE
;
656 $req->username
= 'Foo';
657 $req->password
= 'Bar';
658 $req->retype
= 'Bar';
659 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
661 $provider = $this->getProvider();
663 StatusValue
::newGood(),
664 $provider->testForAccountCreation( $user, $user, [] ),
665 'No password request'
669 StatusValue
::newGood(),
670 $provider->testForAccountCreation( $user, $user, $reqs ),
671 'Password request, validated'
674 $req->retype
= 'Baz';
676 StatusValue
::newFatal( 'badretype' ),
677 $provider->testForAccountCreation( $user, $user, $reqs ),
678 'Password request, bad retype'
680 $req->retype
= 'Bar';
682 $this->validity
->error( 'arbitrary warning' );
683 $expect = StatusValue
::newGood();
684 $expect->error( 'arbitrary warning' );
687 $provider->testForAccountCreation( $user, $user, $reqs ),
688 'Password request, not validated'
691 $provider = $this->getProvider( true );
692 $this->validity
->error( 'arbitrary warning' );
694 StatusValue
::newGood(),
695 $provider->testForAccountCreation( $user, $user, $reqs ),
696 'Password request, not validated, loginOnly'
700 public function testAccountCreation() {
701 $user = User
::newFromName( 'Foo' );
703 $req = new PasswordAuthenticationRequest();
704 $req->action
= AuthManager
::ACTION_CREATE
;
705 $reqs = [ PasswordAuthenticationRequest
::class => $req ];
707 $provider = $this->getProvider( true );
709 $provider->beginPrimaryAccountCreation( $user, $user, [] );
710 $this->fail( 'Expected exception was not thrown' );
711 } catch ( BadMethodCallException
$ex ) {
713 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
718 $provider->finishAccountCreation( $user, $user, AuthenticationResponse
::newPass() );
719 $this->fail( 'Expected exception was not thrown' );
720 } catch ( BadMethodCallException
$ex ) {
722 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
726 $provider = $this->getProvider( false );
729 AuthenticationResponse
::newAbstain(),
730 $provider->beginPrimaryAccountCreation( $user, $user, [] )
733 $req->username
= 'foo';
734 $req->password
= null;
736 AuthenticationResponse
::newAbstain(),
737 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
740 $req->username
= null;
741 $req->password
= 'bar';
743 AuthenticationResponse
::newAbstain(),
744 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
747 $req->username
= 'foo';
748 $req->password
= 'bar';
750 $expect = AuthenticationResponse
::newPass( 'Foo' );
751 $expect->createRequest
= clone $req;
752 $expect->createRequest
->username
= 'Foo';
753 $this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
755 $user = $this->getTestSysop()->getUser();
756 $req->username
= $user->getName();
757 $req->password
= 'NewPassword';
758 $expect = AuthenticationResponse
::newPass( $user->getName() );
759 $expect->createRequest
= $req;
761 $res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
762 $this->assertEquals( $expect, $res2 );
764 $ret = $provider->beginPrimaryAuthentication( $reqs );
765 $this->assertEquals( AuthenticationResponse
::FAIL
, $ret->status
);
767 $this->assertNull( $provider->finishAccountCreation( $user, $user, $res2 ) );
768 $ret = $provider->beginPrimaryAuthentication( $reqs );
769 $this->assertEquals( AuthenticationResponse
::PASS
, $ret->status
, 'new password is set' );