Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / includes / auth / LocalPasswordPrimaryAuthenticationProviderTest.php
blobb15b04dcd892d22bf0aaec15d478cc890c5f9a03
1 <?php
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;
24 use StatusValue;
25 use Wikimedia\TestingAccessWrapper;
27 /**
28 * @group AuthManager
29 * @group Database
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;
43 /**
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( [
58 $this->config,
59 $mwServices->getMainConfig()
60 ] );
62 // We need a real HookContainer since testProviderChangeAuthenticationData()
63 // modifies $wgHooks
64 $hookContainer = $mwServices->getHookContainer();
66 if ( !$this->manager ) {
67 $userNameUtils = $this->createNoOpMock( UserNameUtils::class );
69 $this->manager = new AuthManager(
70 new FauxRequest(),
71 $config,
72 $this->getDummyObjectFactory(),
73 $hookContainer,
74 $mwServices->getReadOnlyMode(),
75 $userNameUtils,
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 ]
93 ] )
94 ->getMock();
96 $provider->method( 'checkPasswordValidity' )
97 ->willReturnCallback( function () {
98 return $this->validity;
99 } );
100 $this->initProvider(
101 $provider, $config, null, $this->manager, $hookContainer, $this->getServiceContainer()->getUserNameUtils()
104 return $provider;
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();
114 $this->assertSame(
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 ] );
126 $this->assertSame(
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()
156 ->update( 'user' )
157 ->set( [ 'user_password' => PasswordFactory::newInvalidPassword()->toString() ] )
158 ->where( [ 'user_name' => $userName ] )
159 ->caller( __METHOD__ )
160 ->execute();
161 $this->assertFalse( $provider->testUserCanAuthenticate( $userName ) );
163 // Really old format
164 $dbw->newUpdateQueryBuilder()
165 ->update( 'user' )
166 ->set( [ 'user_password' => '0123456789abcdef0123456789abcdef' ] )
167 ->where( [ 'user_name' => $userName ] )
168 ->caller( __METHOD__ )
169 ->execute();
170 $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
173 public function testSetPasswordResetFlag() {
174 // Set instance vars
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()
192 ->select( '*' )
193 ->from( 'user' )
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();
260 // General failures
261 $this->assertEquals(
262 AuthenticationResponse::newAbstain(),
263 $provider->beginPrimaryAuthentication( [] )
266 $req->username = 'foo';
267 $req->password = null;
268 $this->assertEquals(
269 AuthenticationResponse::newAbstain(),
270 $provider->beginPrimaryAuthentication( $reqs )
273 $req->username = null;
274 $req->password = 'bar';
275 $this->assertEquals(
276 AuthenticationResponse::newAbstain(),
277 $provider->beginPrimaryAuthentication( $reqs )
280 $req->username = '<invalid>';
281 $req->password = 'WhoCares';
282 $ret = $provider->beginPrimaryAuthentication( $reqs );
283 $this->assertEquals(
284 AuthenticationResponse::newAbstain(),
285 $provider->beginPrimaryAuthentication( $reqs )
288 $req->username = 'DoesNotExist';
289 $req->password = 'DoesNotExist';
290 $ret = $provider->beginPrimaryAuthentication( $reqs );
291 $this->assertEquals(
292 AuthenticationResponse::FAIL,
293 $ret->status
295 $this->assertEquals(
296 'wrongpassword',
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 );
305 $this->assertEquals(
306 AuthenticationResponse::FAIL,
307 $ret->status
309 // AbstractPasswordPrimaryAuthenticationProvider::getFatalPasswordErrorResponse() will
310 // wrap the original message in 'fatalpassworderror'
311 $this->assertEquals(
312 'fatalpassworderror',
313 $ret->message->getKey()
316 // Successful auth
317 $this->manager->removeAuthenticationSessionData( null );
318 $this->validity = Status::newGood();
319 $this->assertEquals(
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 );
329 $this->assertEquals(
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' );
340 $this->assertEquals(
341 AuthenticationResponse::newPass( $userName ),
342 $provider->beginPrimaryAuthentication( $reqs )
344 $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
346 // Wrong password
347 $this->validity = Status::newGood();
348 $req->password = 'Wrong';
349 $ret = $provider->beginPrimaryAuthentication( $reqs );
350 $this->assertEquals(
351 AuthenticationResponse::FAIL,
352 $ret->status
354 $this->assertEquals(
355 'wrongpassword',
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()
362 ->update( 'user' )
363 ->set( [ 'user_password' => $password ] )
364 ->where( [ 'user_name' => $userName ] )
365 ->caller( __METHOD__ )
366 ->execute();
367 $req->password = 'áéíóú';
368 $ret = $provider->beginPrimaryAuthentication( $reqs );
369 $this->assertEquals(
370 AuthenticationResponse::FAIL,
371 $ret->status
373 $this->assertEquals(
374 'wrongpassword',
375 $ret->message->getKey()
378 $this->config->set( MainConfigNames::LegacyEncoding, true );
379 $this->assertEquals(
380 AuthenticationResponse::newPass( $userName ),
381 $provider->beginPrimaryAuthentication( $reqs )
384 $req->password = 'áéíóú Wrong';
385 $ret = $provider->beginPrimaryAuthentication( $reqs );
386 $this->assertEquals(
387 AuthenticationResponse::FAIL,
388 $ret->status
390 $this->assertEquals(
391 'wrongpassword',
392 $ret->message->getKey()
395 // Correct handling of really old password hashes
396 $password = md5( "$id-" . md5( 'FooBar' ) );
397 $dbw->newUpdateQueryBuilder()
398 ->update( 'user' )
399 ->set( [ 'user_password' => $password ] )
400 ->where( [ 'user_name' => $userName ] )
401 ->caller( __METHOD__ )
402 ->execute();
403 $req->password = 'FooBar';
404 $this->assertEquals(
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
415 * use for testing.
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,
422 StatusValue $expect2
424 $user = $usernameGetter( $this->getTestSysop()->getUserIdentity()->getName() );
425 if ( $type === PasswordAuthenticationRequest::class ) {
426 $req = new $type();
427 $req->password = 'NewPassword';
428 $req->retype = 'NewPassword';
429 } elseif ( $type === PasswordDomainAuthenticationRequest::class ) {
430 $req = new $type( [] );
431 } else {
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';
445 $this->assertEquals(
446 $expect1,
447 $provider->providerAllowsAuthenticationDataChange( $req, false )
449 $this->assertEquals(
450 $expect2->getValue() === 'ignored' ? $expect2 : StatusValue::newFatal( 'badretype' ),
451 $provider->providerAllowsAuthenticationDataChange( $req, true )
454 $provider = $this->getProvider( true );
455 $this->assertEquals(
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' );
466 return [
468 AuthenticationRequest::class,
469 static fn ( $sysopUsername ) => $sysopUsername,
470 Status::newGood(),
471 StatusValue::newGood( 'ignored' ),
472 StatusValue::newGood( 'ignored' )
475 PasswordAuthenticationRequest::class,
476 static fn ( $sysopUsername ) => $sysopUsername,
477 Status::newGood(),
478 StatusValue::newGood(),
479 StatusValue::newGood()
482 PasswordAuthenticationRequest::class,
483 'lcfirst',
484 Status::newGood(),
485 StatusValue::newGood(),
486 StatusValue::newGood()
489 PasswordAuthenticationRequest::class,
490 static fn ( $sysopUsername ) => $sysopUsername,
491 Status::wrap( $err ),
492 StatusValue::newGood(),
493 $err
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',
505 Status::newGood(),
506 StatusValue::newGood(),
507 StatusValue::newGood( 'ignored' )
510 PasswordDomainAuthenticationRequest::class,
511 static fn ( $sysopUsername ) => $sysopUsername,
512 Status::newGood(),
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' )
540 ->from( 'user' )
541 ->where( [ 'user_name' => $cuser ] )
542 ->fetchField();
544 $this->mergeMwGlobalArrayValue( 'wgHooks', [
545 'ResetPasswordExpiration' => [ static function ( $user, &$expires ) {
546 $expires = '30001231235959';
548 ] );
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 ];
557 $this->assertEquals(
558 AuthenticationResponse::newPass( $cuser ),
559 $provider->beginPrimaryAuthentication( $loginReqs )
562 if ( $type === PasswordAuthenticationRequest::class ) {
563 $changeReq = new $type();
564 $changeReq->password = $newpass;
565 } else {
566 $changeReq = $this->createMock( $type );
568 $changeReq->action = AuthManager::ACTION_CHANGE;
569 $changeReq->username = $user;
570 $provider->providerChangeAuthenticationData( $changeReq );
572 if ( $loginOnly && $changed ) {
573 $old = 'fail';
574 $new = 'fail';
575 $expectExpiry = null;
576 } elseif ( $changed ) {
577 $old = 'fail';
578 $new = 'pass';
579 $expectExpiry = '30001231235959';
580 } else {
581 $old = 'pass';
582 $new = 'fail';
583 $expectExpiry = $oldExpiry;
586 $loginReq->password = $oldpass;
587 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
588 if ( $old === 'pass' ) {
589 $this->assertEquals(
590 AuthenticationResponse::newPass( $cuser ),
591 $ret,
592 'old password should pass'
594 } else {
595 $this->assertEquals(
596 AuthenticationResponse::FAIL,
597 $ret->status,
598 'old password should fail'
600 $this->assertEquals(
601 'wrongpassword',
602 $ret->message->getKey(),
603 'old password should fail'
607 $loginReq->password = $newpass;
608 $ret = $provider->beginPrimaryAuthentication( $loginReqs );
609 if ( $new === 'pass' ) {
610 $this->assertEquals(
611 AuthenticationResponse::newPass( $cuser ),
612 $ret,
613 'new password should pass'
615 } else {
616 $this->assertEquals(
617 AuthenticationResponse::FAIL,
618 $ret->status,
619 'new password should fail'
621 $this->assertEquals(
622 'wrongpassword',
623 $ret->message->getKey(),
624 'new password should fail'
628 $this->assertSame(
629 $expectExpiry,
630 wfTimestampOrNull(
631 TS_MW,
632 $dbw->newSelectQueryBuilder()
633 ->select( 'user_password_expires' )
634 ->from( 'user' )
635 ->where( [ 'user_name' => $cuser ] )
636 ->fetchField()
641 public static function provideProviderChangeAuthenticationData() {
642 return [
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();
662 $this->assertEquals(
663 StatusValue::newGood(),
664 $provider->testForAccountCreation( $user, $user, [] ),
665 'No password request'
668 $this->assertEquals(
669 StatusValue::newGood(),
670 $provider->testForAccountCreation( $user, $user, $reqs ),
671 'Password request, validated'
674 $req->retype = 'Baz';
675 $this->assertEquals(
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' );
685 $this->assertEquals(
686 $expect,
687 $provider->testForAccountCreation( $user, $user, $reqs ),
688 'Password request, not validated'
691 $provider = $this->getProvider( true );
692 $this->validity->error( 'arbitrary warning' );
693 $this->assertEquals(
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 );
708 try {
709 $provider->beginPrimaryAccountCreation( $user, $user, [] );
710 $this->fail( 'Expected exception was not thrown' );
711 } catch ( BadMethodCallException $ex ) {
712 $this->assertSame(
713 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
717 try {
718 $provider->finishAccountCreation( $user, $user, AuthenticationResponse::newPass() );
719 $this->fail( 'Expected exception was not thrown' );
720 } catch ( BadMethodCallException $ex ) {
721 $this->assertSame(
722 'Shouldn\'t call this when accountCreationType() is NONE', $ex->getMessage()
726 $provider = $this->getProvider( false );
728 $this->assertEquals(
729 AuthenticationResponse::newAbstain(),
730 $provider->beginPrimaryAccountCreation( $user, $user, [] )
733 $req->username = 'foo';
734 $req->password = null;
735 $this->assertEquals(
736 AuthenticationResponse::newAbstain(),
737 $provider->beginPrimaryAccountCreation( $user, $user, $reqs )
740 $req->username = null;
741 $req->password = 'bar';
742 $this->assertEquals(
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' );