9 use Wikimedia\ScopedCallback;
10 use Wikimedia\TestingAccessWrapper;
47 protected function hook( $hook, $expect ) {
49 $mock = $this->getMockBuilder( __CLASS__ )
50 ->setMethods( [
"on$hook" ] )
53 return $mock->expects( $expect )->method(
"on$hook" );
72 if ( $key ===
null ) {
77 $key = $key->getKey();
97 foreach ( [
'preauth',
'primaryauth',
'secondaryauth' ]
as $type ) {
98 $key =
$type .
'Mocks';
99 foreach ( $this->$key
as $mock ) {
100 $config[
$type][$mock->getUniqueId()] = [
'factory' =>
function ()
use ( $mock ) {
106 $this->config->set(
'AuthManagerConfig',
$config );
107 $this->config->set(
'LanguageCode',
'en' );
108 $this->config->set(
'NewUserLog',
false );
116 if ( $regen || !$this->config ) {
117 $this->config = new \HashConfig();
119 if ( $regen || !$this->
request ) {
120 $this->
request = new \FauxRequest();
122 if ( !$this->logger ) {
123 $this->logger = new \TestLogger();
126 if ( $regen || !$this->config->has(
'AuthManagerConfig' ) ) {
130 $this->manager->setLogger( $this->logger );
131 $this->managerPriv = TestingAccessWrapper::newFromObject( $this->manager );
141 if ( !$this->config ) {
142 $this->config = new \HashConfig();
145 $this->config->set(
'ObjectCacheSessionExpiry', 100 );
147 $methods[] =
'__toString';
148 $methods[] =
'describe';
149 if ( $canChangeUser !==
null ) {
150 $methods[] =
'canChangeUser';
152 $provider = $this->getMockBuilder(
'DummySessionProvider' )
153 ->setMethods( $methods )
155 $provider->expects( $this->
any() )->method(
'__toString' )
156 ->will( $this->returnValue(
'MockSessionProvider' ) );
157 $provider->expects( $this->
any() )->method(
'describe' )
158 ->will( $this->returnValue(
'MockSessionProvider sessions' ) );
159 if ( $canChangeUser !==
null ) {
160 $provider->expects( $this->
any() )->method(
'canChangeUser' )
161 ->will( $this->returnValue( $canChangeUser ) );
163 $this->config->set(
'SessionProviders', [
164 [
'factory' =>
function ()
use ( $provider ) {
169 $manager = new \MediaWiki\Session\SessionManager( [
170 'config' => $this->config,
171 'logger' =>
new \Psr\Log\NullLogger(),
174 TestingAccessWrapper::newFromObject(
$manager )->getProvider( (
string)$provider );
182 return [ $provider, $reset ];
189 $rProp->setAccessible(
true );
190 $old = $rProp->getValue();
191 $cb =
new ScopedCallback( [ $rProp,
'setValue' ], [ $old ] );
192 $rProp->setValue(
null );
200 TestingAccessWrapper::newFromObject( $singleton )->config
208 $this->assertFalse( $this->manager->canAuthenticateNow() );
209 ScopedCallback::consume( $reset );
212 $this->assertTrue( $this->manager->canAuthenticateNow() );
213 ScopedCallback::consume( $reset );
223 foreach ( $mocks
as $key => $mock ) {
224 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue( $key ) );
226 $mocks[0]->expects( $this->once() )->method(
'providerNormalizeUsername' )
227 ->with( $this->identicalTo(
'XYZ' ) )
228 ->willReturn(
'Foo' );
229 $mocks[1]->expects( $this->once() )->method(
'providerNormalizeUsername' )
230 ->with( $this->identicalTo(
'XYZ' ) )
231 ->willReturn(
'Foo' );
232 $mocks[2]->expects( $this->once() )->method(
'providerNormalizeUsername' )
233 ->with( $this->identicalTo(
'XYZ' ) )
234 ->willReturn(
null );
235 $mocks[3]->expects( $this->once() )->method(
'providerNormalizeUsername' )
236 ->with( $this->identicalTo(
'XYZ' ) )
237 ->willReturn(
'Bar!' );
239 $this->primaryauthMocks = $mocks;
243 $this->assertSame( [
'Foo',
'Bar!' ], $this->manager->normalizeUsername(
'XYZ' ) );
251 $this->logger = new \Psr\Log\NullLogger();
257 $mutableSession, [
'provideSessionInfo' ]
259 $provider->expects( $this->
any() )->method(
'provideSessionInfo' )
260 ->will( $this->returnCallback(
function ()
use ( $provider, &$provideUser ) {
262 'provider' => $provider,
270 $this->config->set(
'ReauthenticateTime', [] );
271 $this->config->set(
'AllowSecuritySensitiveOperationIfCannotReauthenticate', [] );
273 $session = $provider->getManager()->getSessionForRequest( $this->
request );
274 $this->assertSame( 0, $session->getUser()->getId(),
'sanity check' );
277 $session->set(
'AuthManager:lastAuthId', 0 );
278 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
279 $this->assertSame( $reauth, $this->manager->securitySensitiveOperationStatus(
'foo' ) );
281 $provideUser =
$user;
282 $session = $provider->getManager()->getSessionForRequest( $this->
request );
283 $this->assertSame(
$user->getId(), $session->getUser()->getId(),
'sanity check' );
286 $session->set(
'AuthManager:lastAuthId',
$user->getId() + 1 );
287 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
289 $this->manager->securitySensitiveOperationStatus(
'foo' );
290 $this->fail(
'Expected exception not thrown' );
291 }
catch ( \UnexpectedValueException $ex ) {
294 ?
'$wgReauthenticateTime lacks a default'
295 :
'$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default',
300 if ( $mutableSession ) {
301 $this->config->set(
'ReauthenticateTime', [
308 $session->set(
'AuthManager:lastAuthId',
$user->getId() + 1 );
309 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
321 $session->set(
'AuthManager:lastAuthId',
$user->getId() );
322 $session->set(
'AuthManager:lastAuthTimestamp',
null );
334 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
340 $session->set(
'AuthManager:lastAuthTimestamp', time() - 20 );
349 $this->config->set(
'AllowSecuritySensitiveOperationIfCannotReauthenticate', [
368 ]
as $hook => $expect ) {
369 $this->
hook(
'SecuritySensitiveOperationStatus', $this->exactly( 2 ) )
373 $this->callback(
function (
$s )
use ( $session ) {
374 return $s->getId() === $session->getId();
376 $mutableSession ? $this->equalTo( 500, 1 ) : $this->equalTo( -1 )
378 ->
will( $this->returnCallback(
function ( &$v )
use ( $hook ) {
382 $session->set(
'AuthManager:lastAuthTimestamp', time() - 500 );
384 $expect, $this->manager->securitySensitiveOperationStatus(
'test' ),
"hook $hook"
387 $expect, $this->manager->securitySensitiveOperationStatus(
'test2' ),
"hook $hook"
389 $this->
unhook(
'SecuritySensitiveOperationStatus' );
392 ScopedCallback::consume( $reset );
413 $mock1->expects( $this->
any() )->method(
'getUniqueId' )
414 ->will( $this->returnValue(
'primary1' ) );
415 $mock1->expects( $this->
any() )->method(
'testUserCanAuthenticate' )
416 ->with( $this->equalTo(
'UTSysop' ) )
417 ->will( $this->returnValue( $primary1Can ) );
419 $mock2->expects( $this->
any() )->method(
'getUniqueId' )
420 ->will( $this->returnValue(
'primary2' ) );
421 $mock2->expects( $this->
any() )->method(
'testUserCanAuthenticate' )
422 ->with( $this->equalTo(
'UTSysop' ) )
423 ->will( $this->returnValue( $primary2Can ) );
424 $this->primaryauthMocks = [ $mock1, $mock2 ];
427 $this->assertSame( $expect, $this->manager->userCanAuthenticate(
'UTSysop' ) );
432 [
false,
false,
false ],
433 [
true,
false,
true ],
434 [
false,
true,
true ],
435 [
true,
true,
true ],
443 $mock->expects( $this->
any() )->method(
'getUniqueId' )
444 ->will( $this->returnValue(
'primary' ) );
445 $mock->expects( $this->once() )->method(
'providerRevokeAccessForUser' )
446 ->with( $this->equalTo(
'UTSysop' ) );
447 $this->primaryauthMocks = [ $mock ];
450 $this->logger->setCollect(
true );
452 $this->manager->revokeAccessForUser(
'UTSysop' );
455 [ LogLevel::INFO,
'Revoking access for {user}' ],
456 ], $this->logger->getBuffer() );
465 foreach ( $mocks
as $key => $mock ) {
466 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue( $key ) );
467 $mock->expects( $this->once() )->method(
'setLogger' );
468 $mock->expects( $this->once() )->method(
'setManager' );
469 $mock->expects( $this->once() )->method(
'setConfig' );
471 $this->preauthMocks = [ $mocks[
'pre'] ];
472 $this->primaryauthMocks = [ $mocks[
'primary'] ];
473 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
479 $this->managerPriv->getAuthenticationProvider(
'primary' )
483 $this->managerPriv->getAuthenticationProvider(
'secondary' )
487 $this->managerPriv->getAuthenticationProvider(
'pre' )
490 [
'pre' => $mocks[
'pre'] ],
491 $this->managerPriv->getPreAuthenticationProviders()
494 [
'primary' => $mocks[
'primary'] ],
495 $this->managerPriv->getPrimaryAuthenticationProviders()
498 [
'secondary' => $mocks[
'secondary'] ],
499 $this->managerPriv->getSecondaryAuthenticationProviders()
505 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
506 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
507 $this->preauthMocks = [ $mock1 ];
508 $this->primaryauthMocks = [ $mock2 ];
509 $this->secondaryauthMocks = [];
512 $this->managerPriv->getAuthenticationProvider(
'Y' );
513 $this->fail(
'Expected exception not thrown' );
514 }
catch ( \RuntimeException $ex ) {
515 $class1 = get_class( $mock1 );
516 $class2 = get_class( $mock2 );
518 "Duplicate specifications for id X (classes $class1 and $class2)", $ex->getMessage()
524 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
525 $class = get_class( $mock );
526 $this->preauthMocks = [ $mock ];
527 $this->primaryauthMocks = [ $mock ];
528 $this->secondaryauthMocks = [ $mock ];
531 $this->managerPriv->getPreAuthenticationProviders();
532 $this->fail(
'Expected exception not thrown' );
533 }
catch ( \RuntimeException $ex ) {
535 "Expected instance of MediaWiki\\Auth\\PreAuthenticationProvider, got $class",
540 $this->managerPriv->getPrimaryAuthenticationProviders();
541 $this->fail(
'Expected exception not thrown' );
542 }
catch ( \RuntimeException $ex ) {
544 "Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got $class",
549 $this->managerPriv->getSecondaryAuthenticationProviders();
550 $this->fail(
'Expected exception not thrown' );
551 }
catch ( \RuntimeException $ex ) {
553 "Expected instance of MediaWiki\\Auth\\SecondaryAuthenticationProvider, got $class",
562 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'A' ) );
563 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
564 $mock3->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'C' ) );
565 $this->preauthMocks = [];
566 $this->primaryauthMocks = [ $mock1, $mock2, $mock3 ];
567 $this->secondaryauthMocks = [];
569 $config = $this->config->
get(
'AuthManagerConfig' );
573 [
'A' => $mock1,
'B' => $mock2,
'C' => $mock3 ],
574 $this->managerPriv->getPrimaryAuthenticationProviders(),
578 $config[
'primaryauth'][
'A'][
'sort'] = 100;
579 $config[
'primaryauth'][
'C'][
'sort'] = -1;
580 $this->config->set(
'AuthManagerConfig',
$config );
583 [
'C' => $mock3,
'B' => $mock2,
'A' => $mock1 ],
584 $this->managerPriv->getPrimaryAuthenticationProviders()
597 $user->addToDatabase();
598 $oldToken =
$user->getToken();
599 $this->managerPriv->setDefaultUserOptions(
$user,
false );
600 $user->saveSettings();
601 $this->assertNotEquals( $oldToken,
$user->getToken() );
602 $this->assertSame(
'zh',
$user->getOption(
'language' ) );
603 $this->assertSame(
'zh',
$user->getOption(
'variant' ) );
606 $user->addToDatabase();
607 $oldToken =
$user->getToken();
608 $this->managerPriv->setDefaultUserOptions(
$user,
true );
609 $user->saveSettings();
610 $this->assertNotEquals( $oldToken,
$user->getToken() );
611 $this->assertSame(
'de',
$user->getOption(
'language' ) );
612 $this->assertSame(
'zh',
$user->getOption(
'variant' ) );
617 $user->addToDatabase();
618 $oldToken =
$user->getToken();
619 $this->managerPriv->setDefaultUserOptions(
$user,
true );
620 $user->saveSettings();
621 $this->assertNotEquals( $oldToken,
$user->getToken() );
622 $this->assertSame(
'de',
$user->getOption(
'language' ) );
623 $this->assertSame(
null,
$user->getOption(
'variant' ) );
630 $mockA->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'A' ) );
631 $mockB->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
632 $mockB2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
633 $this->primaryauthMocks = [ $mockA ];
635 $this->logger = new \TestLogger(
true );
639 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB ],
'testing' );
641 [
'B' => $mockB ], $this->managerPriv->getPrimaryAuthenticationProviders()
643 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'A' ) );
644 $this->assertSame( $mockB, $this->managerPriv->getAuthenticationProvider(
'B' ) );
646 [ LogLevel::WARNING,
'Overriding AuthManager primary authn because testing' ],
647 ], $this->logger->getBuffer() );
648 $this->logger->clearBuffer();
652 $this->assertSame( $mockA, $this->managerPriv->getAuthenticationProvider(
'A' ) );
653 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'B' ) );
654 $this->
request->getSession()->setSecret(
'AuthManager::authnState',
'test' );
655 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
'test' );
656 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB ],
'testing' );
658 [
'B' => $mockB ], $this->managerPriv->getPrimaryAuthenticationProviders()
660 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'A' ) );
661 $this->assertSame( $mockB, $this->managerPriv->getAuthenticationProvider(
'B' ) );
662 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::authnState' ) );
664 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
667 [ LogLevel::WARNING,
'Overriding AuthManager primary authn because testing' ],
670 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
672 ], $this->logger->getBuffer() );
673 $this->logger->clearBuffer();
678 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB, $mockB2 ],
'testing' );
679 $this->fail(
'Expected exception not thrown' );
680 }
catch ( \RuntimeException $ex ) {
681 $class1 = get_class( $mockB );
682 $class2 = get_class( $mockB2 );
684 "Duplicate specifications for id B (classes $class2 and $class1)", $ex->getMessage()
690 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
691 $class = get_class( $mock );
693 $this->manager->forcePrimaryAuthenticationProviders( [ $mock ],
'testing' );
694 $this->fail(
'Expected exception not thrown' );
695 }
catch ( \RuntimeException $ex ) {
697 "Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got $class",
708 $this->
hook(
'UserLoggedIn', $this->never() );
709 $this->
request->getSession()->setSecret(
'AuthManager::authnState',
'test' );
711 $this->manager->beginAuthentication( [],
'http://localhost/' );
712 $this->fail(
'Expected exception not thrown' );
713 }
catch ( \LogicException $ex ) {
714 $this->assertSame(
'Authentication is not possible now', $ex->getMessage() );
716 $this->
unhook(
'UserLoggedIn' );
717 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::authnState' ) );
718 ScopedCallback::consume( $reset );
726 $this->
hook(
'UserLoggedIn', $this->never() );
728 $this->manager->beginAuthentication( $reqs,
'http://localhost/' );
729 $this->fail(
'Expected exception not thrown' );
730 }
catch ( \LogicException $ex ) {
732 'CreatedAccountAuthenticationRequests are only valid on the same AuthManager ' .
733 'that created the account',
737 $this->
unhook(
'UserLoggedIn' );
739 $this->
request->getSession()->clear();
740 $this->
request->getSession()->setSecret(
'AuthManager::authnState',
'test' );
741 $this->managerPriv->createdAccountAuthenticationRequests = [ $reqs[0] ];
742 $this->
hook(
'UserLoggedIn', $this->once() )
743 ->with( $this->callback(
function ( $u )
use (
$user ) {
744 return $user->getId() === $u->getId() &&
$user->getName() === $u->getName();
746 $this->
hook(
'AuthManagerLoginAuthenticateAudit', $this->once() );
747 $this->logger->setCollect(
true );
748 $ret = $this->manager->beginAuthentication( $reqs,
'http://localhost/' );
749 $this->logger->setCollect(
false );
750 $this->
unhook(
'UserLoggedIn' );
751 $this->
unhook(
'AuthManagerLoginAuthenticateAudit' );
753 $this->assertSame(
$user->getName(),
$ret->username );
754 $this->assertSame(
$user->getId(), $this->
request->getSessionData(
'AuthManager:lastAuthId' ) );
756 time(), $this->
request->getSessionData(
'AuthManager:lastAuthTimestamp' ),
759 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::authnState' ) );
760 $this->assertSame(
$user->getId(), $this->
request->getSession()->getUser()->getId() );
762 [ LogLevel::INFO,
'Logging in {user} after account creation' ],
763 ], $this->logger->getBuffer() );
772 $userReq->username =
'UTDummy';
774 $req1->returnToUrl =
'http://localhost/';
775 $req2->returnToUrl =
'http://localhost/';
776 $req3->returnToUrl =
'http://localhost/';
777 $req3->username =
'UTDummy';
778 $userReq->returnToUrl =
'http://localhost/';
782 $this->primaryauthMocks = [ $primary ];
785 $res->createRequest = $req1;
786 $primary->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
787 ->will( $this->returnValue(
$res ) );
789 null, [ $req2->getUniqueId() => $req2 ]
791 $this->logger->setCollect(
true );
792 $ret = $this->manager->beginAuthentication( [ $createReq ],
'http://localhost/' );
793 $this->logger->setCollect(
false );
796 $this->assertSame( $req1,
$ret->createRequest->createRequest );
797 $this->assertEquals( [ $req2->getUniqueId() => $req2 ],
$ret->createRequest->maybeLink );
801 ->setMethods( [
'continuePrimaryAuthentication' ] )
802 ->getMockForAbstractClass();
803 $this->primaryauthMocks = [ $primary ];
805 $primary->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
806 ->will( $this->returnValue(
810 $res->createRequest = $req2;
811 $primary->expects( $this->
any() )->method(
'continuePrimaryAuthentication' )
812 ->will( $this->returnValue(
$res ) );
813 $this->logger->setCollect(
true );
814 $ret = $this->manager->beginAuthentication( [],
'http://localhost/' );
816 $ret = $this->manager->continueAuthentication( [] );
817 $this->logger->setCollect(
false );
820 $this->assertSame( $req2,
$ret->createRequest->createRequest );
821 $this->assertEquals( [],
$ret->createRequest->maybeLink );
825 $this->primaryauthMocks = [ $primary ];
828 $createReq->returnToUrl =
'http://localhost/';
829 $createReq->username =
'UTDummy';
831 $primary->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )
832 ->with( $this->
anything(), $this->
anything(), [ $userReq, $createReq, $req3 ] )
833 ->will( $this->returnValue(
$res ) );
834 $primary->expects( $this->
any() )->method(
'accountCreationType' )
836 $this->logger->setCollect(
true );
837 $ret = $this->manager->beginAccountCreation(
838 $user, [ $userReq, $createReq ],
'http://localhost/'
840 $this->logger->setCollect(
false );
842 $state = $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' );
843 $this->assertNotNull( $state );
844 $this->assertEquals( [ $userReq, $createReq, $req3 ], $state[
'reqs'] );
845 $this->assertEquals( [ $req2 ], $state[
'maybeLink'] );
862 $id =
$user->getId();
867 $req->rememberMe = (bool)rand( 0, 1 );
868 $req->pre = $preResponse;
869 $req->primary = $primaryResponses;
870 $req->secondary = $secondaryResponses;
872 foreach ( [
'pre',
'primary',
'secondary' ]
as $key ) {
873 $class = ucfirst( $key ) .
'AuthenticationProvider';
874 $mocks[$key] = $this->getMockForAbstractClass(
875 "MediaWiki\\Auth\\$class", [],
"Mock$class"
877 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
878 ->will( $this->returnValue( $key ) );
879 $mocks[$key .
'2'] = $this->getMockForAbstractClass(
880 "MediaWiki\\Auth\\$class", [],
"Mock$class"
882 $mocks[$key .
'2']->expects( $this->
any() )->method(
'getUniqueId' )
883 ->will( $this->returnValue( $key .
'2' ) );
884 $mocks[$key .
'3'] = $this->getMockForAbstractClass(
885 "MediaWiki\\Auth\\$class", [],
"Mock$class"
887 $mocks[$key .
'3']->expects( $this->
any() )->method(
'getUniqueId' )
888 ->will( $this->returnValue( $key .
'3' ) );
890 foreach ( $mocks
as $mock ) {
891 $mock->expects( $this->
any() )->method(
'getAuthenticationRequests' )
892 ->will( $this->returnValue( [] ) );
895 $mocks[
'pre']->expects( $this->once() )->method(
'testForAuthentication' )
896 ->will( $this->returnCallback(
function ( $reqs )
use (
$req ) {
897 $this->assertContains(
$req, $reqs );
902 $callback = $this->returnCallback(
function ( $reqs )
use (
$req ) {
903 $this->assertContains(
$req, $reqs );
904 return array_shift(
$req->primary );
906 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
907 ->method(
'beginPrimaryAuthentication' )
909 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
910 ->method(
'continuePrimaryAuthentication' )
913 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
918 $callback = $this->returnCallback(
function (
$user, $reqs )
use ( $id,
$name,
$req ) {
919 $this->assertSame( $id,
$user->getId() );
921 $this->assertContains(
$req, $reqs );
922 return array_shift(
$req->secondary );
924 $mocks[
'secondary']->expects( $this->exactly( min( 1, $ct ) ) )
925 ->method(
'beginSecondaryAuthentication' )
927 $mocks[
'secondary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
928 ->method(
'continueSecondaryAuthentication' )
932 $mocks[
'pre2']->expects( $this->atMost( 1 ) )->method(
'testForAuthentication' )
934 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAuthentication' )
935 ->will( $this->returnValue( $abstain ) );
936 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAuthentication' );
937 $mocks[
'secondary2']->expects( $this->atMost( 1 ) )->method(
'beginSecondaryAuthentication' )
938 ->will( $this->returnValue( $abstain ) );
939 $mocks[
'secondary2']->expects( $this->never() )->method(
'continueSecondaryAuthentication' );
940 $mocks[
'secondary3']->expects( $this->atMost( 1 ) )->method(
'beginSecondaryAuthentication' )
941 ->will( $this->returnValue( $abstain ) );
942 $mocks[
'secondary3']->expects( $this->never() )->method(
'continueSecondaryAuthentication' );
944 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
945 $this->primaryauthMocks = [ $mocks[
'primary'], $mocks[
'primary2'] ];
946 $this->secondaryauthMocks = [
947 $mocks[
'secondary3'], $mocks[
'secondary'], $mocks[
'secondary2'],
952 $this->logger->setCollect(
true );
954 $constraint = \PHPUnit_Framework_Assert::logicalOr(
958 $providers = array_filter(
960 $this->preauthMocks, $this->primaryauthMocks, $this->secondaryauthMocks
963 return is_callable( [ $p,
'expects' ] );
966 foreach ( $providers
as $p ) {
967 $p->postCalled =
false;
968 $p->expects( $this->atMost( 1 ) )->method(
'postAuthentication' )
970 if (
$user !==
null ) {
971 $this->assertInstanceOf(
'User',
$user );
972 $this->assertSame(
'UTSysop',
$user->getName() );
975 $this->assertThat(
$response->status, $constraint );
980 $session = $this->
request->getSession();
981 $session->setRememberUser( !
$req->rememberMe );
987 $this->
hook(
'UserLoggedIn', $this->once() )
988 ->with( $this->callback(
function (
$user )
use ( $id,
$name ) {
992 $this->
hook(
'UserLoggedIn', $this->never() );
997 $response->message->getKey() !==
'authmanager-authn-not-in-progress' &&
998 $response->message->getKey() !==
'authmanager-authn-no-primary'
1001 $this->
hook(
'AuthManagerLoginAuthenticateAudit', $this->once() );
1003 $this->
hook(
'AuthManagerLoginAuthenticateAudit', $this->never() );
1009 $ret = $this->manager->beginAuthentication( [
$req ],
'http://localhost/' );
1011 $ret = $this->manager->continueAuthentication( [
$req ] );
1013 if (
$response instanceof \Exception ) {
1014 $this->fail(
'Expected exception not thrown',
"Response $i" );
1016 }
catch ( \Exception $ex ) {
1017 if ( !
$response instanceof \Exception ) {
1020 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
1021 $this->assertNull( $session->getSecret(
'AuthManager::authnState' ),
1022 "Response $i, exception, session state" );
1023 $this->
unhook(
'UserLoggedIn' );
1024 $this->
unhook(
'AuthManagerLoginAuthenticateAudit' );
1028 $this->
unhook(
'UserLoggedIn' );
1029 $this->
unhook(
'AuthManagerLoginAuthenticateAudit' );
1031 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
1034 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
1036 $this->assertSame( $id, $session->getUser()->getId(),
1037 "Response $i, authn" );
1039 $this->assertSame( 0, $session->getUser()->getId(),
1040 "Response $i, authn" );
1043 $this->assertNull( $session->getSecret(
'AuthManager::authnState' ),
1044 "Response $i, session state" );
1045 foreach ( $providers
as $p ) {
1046 $this->assertSame(
$response->status, $p->postCalled,
1047 "Response $i, post-auth callback called" );
1050 $this->assertNotNull( $session->getSecret(
'AuthManager::authnState' ),
1051 "Response $i, session state" );
1052 foreach (
$ret->neededRequests
as $neededReq ) {
1054 "Response $i, neededRequest action" );
1056 $this->assertEquals(
1057 $ret->neededRequests,
1059 "Response $i, continuation check"
1061 foreach ( $providers
as $p ) {
1062 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
1066 $state = $session->getSecret(
'AuthManager::authnState' );
1067 $maybeLink = isset( $state[
'maybeLink'] ) ? $state[
'maybeLink'] : [];
1069 $this->assertEquals(
1072 "Response $i, maybeLink"
1075 $this->assertEquals( [], $maybeLink,
"Response $i, maybeLink" );
1080 $this->assertSame(
$req->rememberMe, $session->shouldRememberUser(),
1081 'rememberMe checkbox had effect' );
1083 $this->assertNotSame(
$req->rememberMe, $session->shouldRememberUser(),
1084 'rememberMe checkbox wasn\'t applied' );
1093 $req->foobar =
'baz';
1095 $this->
message(
'authmanager-authn-no-local-user' )
1097 $restartResponse->neededRequests = [ $rememberReq ];
1100 $restartResponse2Pass->linkRequest =
$req;
1102 $this->
message(
'authmanager-authn-no-local-user-link' )
1105 null, [
$req->getUniqueId() =>
$req ]
1108 $restartResponse2->neededRequests = [ $rememberReq, $restartResponse2->createRequest ];
1110 $userName =
'UTSysop';
1113 'Failure in pre-auth' => [
1120 $this->
message(
'authmanager-authn-not-in-progress' )
1124 'Failure in primary' => [
1132 'All primary abstain' => [
1142 'Primary UI, then redirect, then fail' => [
1152 'Primary redirect, then abstain' => [
1156 [
$req ],
'/foo.html', [
'foo' =>
'bar' ]
1163 new \DomainException(
1164 'MockPrimaryAuthenticationProvider::continuePrimaryAuthentication() returned ABSTAIN'
1168 'Primary UI, then pass with no local user' => [
1180 'Primary UI, then pass with no local user (link type)' => [
1184 $restartResponse2Pass,
1193 'Primary pass with invalid username' => [
1200 new \DomainException(
'MockPrimaryAuthenticationProvider returned an invalid username: <>' ),
1203 'Secondary fail' => [
1213 'Secondary UI, then abstain' => [
1227 'Secondary pass' => [
1250 $mock1->expects( $this->
any() )->method(
'getUniqueId' )
1251 ->will( $this->returnValue(
'primary1' ) );
1252 $mock1->expects( $this->
any() )->method(
'testUserExists' )
1253 ->with( $this->equalTo(
'UTSysop' ) )
1254 ->will( $this->returnValue( $primary1Exists ) );
1256 $mock2->expects( $this->
any() )->method(
'getUniqueId' )
1257 ->will( $this->returnValue(
'primary2' ) );
1258 $mock2->expects( $this->
any() )->method(
'testUserExists' )
1259 ->with( $this->equalTo(
'UTSysop' ) )
1260 ->will( $this->returnValue( $primary2Exists ) );
1261 $this->primaryauthMocks = [ $mock1, $mock2 ];
1264 $this->assertSame( $expect, $this->manager->userExists(
'UTSysop' ) );
1269 [
false,
false,
false ],
1270 [
true,
false,
true ],
1271 [
false,
true,
true ],
1272 [
true,
true,
true ],
1286 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'1' ) );
1287 $mock1->expects( $this->
any() )->method(
'providerAllowsAuthenticationDataChange' )
1288 ->with( $this->equalTo(
$req ) )
1289 ->will( $this->returnValue( $primaryReturn ) );
1291 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'2' ) );
1292 $mock2->expects( $this->
any() )->method(
'providerAllowsAuthenticationDataChange' )
1293 ->with( $this->equalTo(
$req ) )
1294 ->will( $this->returnValue( $secondaryReturn ) );
1296 $this->primaryauthMocks = [ $mock1 ];
1297 $this->secondaryauthMocks = [ $mock2 ];
1299 $this->assertEquals( $expect, $this->manager->allowsAuthenticationDataChange(
$req ) );
1304 $ignored->warning(
'authmanager-change-not-supported' );
1307 $okFromPrimary->warning(
'warning-from-primary' );
1309 $okFromSecondary->warning(
'warning-from-secondary' );
1357 $req->username =
'UTSysop';
1360 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'1' ) );
1361 $mock1->expects( $this->once() )->method(
'providerChangeAuthenticationData' )
1362 ->with( $this->equalTo(
$req ) );
1364 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'2' ) );
1365 $mock2->expects( $this->once() )->method(
'providerChangeAuthenticationData' )
1366 ->with( $this->equalTo(
$req ) );
1368 $this->primaryauthMocks = [ $mock1, $mock2 ];
1370 $this->logger->setCollect(
true );
1371 $this->manager->changeAuthenticationData(
$req );
1372 $this->assertSame( [
1373 [ LogLevel::INFO,
'Changing authentication data for {user} class {what}' ],
1374 ], $this->logger->getBuffer() );
1384 foreach ( $types
as $type => $can ) {
1386 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
$type ) );
1387 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1388 ->will( $this->returnValue(
$type ) );
1389 $this->primaryauthMocks = [ $mock ];
1391 $this->assertSame( $can, $this->manager->canCreateAccounts(),
$type );
1402 $wgGroupPermissions[
'*'][
'createaccount'] =
true;
1403 $this->assertEquals(
1405 $this->manager->checkAccountCreatePermissions(
new \
User )
1408 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
1409 $readOnlyMode->setReason(
'Because' );
1410 $this->assertEquals(
1412 $this->manager->checkAccountCreatePermissions(
new \
User )
1414 $readOnlyMode->setReason(
false );
1417 $status = $this->manager->checkAccountCreatePermissions(
new \
User );
1418 $this->assertFalse(
$status->isOK() );
1419 $this->assertTrue(
$status->hasMessage(
'badaccess-groups' ) );
1423 if (
$user->getID() == 0 ) {
1424 $user->addToDatabase();
1426 $user->saveSettings();
1431 $oldBlock->delete();
1434 'address' =>
'UTBlockee',
1435 'user' =>
$user->getID(),
1436 'reason' => __METHOD__,
1437 'expiry' => time() + 100500,
1438 'createAccount' =>
true,
1440 $block = new \Block( $blockOptions );
1442 $status = $this->manager->checkAccountCreatePermissions(
$user );
1443 $this->assertFalse(
$status->isOK() );
1444 $this->assertTrue(
$status->hasMessage(
'cantcreateaccount-text' ) );
1447 'address' =>
'127.0.0.0/24',
1448 'reason' => __METHOD__,
1449 'expiry' => time() + 100500,
1450 'createAccount' =>
true,
1452 $block = new \Block( $blockOptions );
1454 $scopeVariable =
new ScopedCallback( [ $block,
'delete' ] );
1455 $status = $this->manager->checkAccountCreatePermissions(
new \
User );
1456 $this->assertFalse(
$status->isOK() );
1457 $this->assertTrue(
$status->hasMessage(
'cantcreateaccount-range-text' ) );
1458 ScopedCallback::consume( $scopeVariable );
1461 'wgEnableDnsBlacklist' =>
true,
1462 'wgDnsBlacklistUrls' => [
1463 'local.wmftest.net',
1465 'wgProxyWhitelist' => [],
1467 $status = $this->manager->checkAccountCreatePermissions(
new \
User );
1468 $this->assertFalse(
$status->isOK() );
1469 $this->assertTrue(
$status->hasMessage(
'sorbs_create_account_reason' ) );
1470 $this->
setMwGlobals(
'wgProxyWhitelist', [
'127.0.0.1' ] );
1471 $status = $this->manager->checkAccountCreatePermissions(
new \
User );
1472 $this->assertTrue(
$status->isGood() );
1482 $username =
"UTAuthManagerTestAccountCreation" . $uniq . ++$i;
1491 $this->assertEquals(
1493 $this->manager->canCreateAccount(
$username )
1497 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1498 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1500 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
true ) );
1501 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1503 $this->primaryauthMocks = [ $mock ];
1506 $this->assertEquals(
1508 $this->manager->canCreateAccount(
$username )
1512 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1513 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1515 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1516 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1518 $this->primaryauthMocks = [ $mock ];
1521 $this->assertEquals(
1523 $this->manager->canCreateAccount(
$username .
'<>' )
1526 $this->assertEquals(
1528 $this->manager->canCreateAccount(
'UTSysop' )
1531 $this->assertEquals(
1533 $this->manager->canCreateAccount(
$username )
1537 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1538 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1540 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1541 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1543 $this->primaryauthMocks = [ $mock ];
1546 $this->assertEquals(
1548 $this->manager->canCreateAccount(
$username )
1555 $this->logger = new \TestLogger(
false,
function ( $message, $level ) {
1556 return $level === LogLevel::DEBUG ? null : $message;
1560 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
'test' );
1561 $this->
hook(
'LocalUserCreated', $this->never() );
1563 $this->manager->beginAccountCreation(
1564 $creator, [],
'http://localhost/'
1566 $this->fail(
'Expected exception not thrown' );
1567 }
catch ( \LogicException $ex ) {
1568 $this->assertEquals(
'Account creation is not possible', $ex->getMessage() );
1570 $this->
unhook(
'LocalUserCreated' );
1572 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1576 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1577 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1579 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
true ) );
1580 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1582 $this->primaryauthMocks = [ $mock ];
1585 $this->
hook(
'LocalUserCreated', $this->never() );
1586 $ret = $this->manager->beginAccountCreation( $creator, [],
'http://localhost/' );
1587 $this->
unhook(
'LocalUserCreated' );
1589 $this->assertSame(
'noname',
$ret->message->getKey() );
1591 $this->
hook(
'LocalUserCreated', $this->never() );
1594 $userReq2->username = $userReq->username .
'X';
1595 $ret = $this->manager->beginAccountCreation(
1596 $creator, [ $userReq, $userReq2 ],
'http://localhost/'
1598 $this->
unhook(
'LocalUserCreated' );
1600 $this->assertSame(
'noname',
$ret->message->getKey() );
1602 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
1603 $readOnlyMode->setReason(
'Because' );
1604 $this->
hook(
'LocalUserCreated', $this->never() );
1606 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1607 $this->
unhook(
'LocalUserCreated' );
1609 $this->assertSame(
'readonlytext',
$ret->message->getKey() );
1610 $this->assertSame( [
'Because' ],
$ret->message->getParams() );
1611 $readOnlyMode->setReason(
false );
1613 $this->
hook(
'LocalUserCreated', $this->never() );
1615 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1616 $this->
unhook(
'LocalUserCreated' );
1618 $this->assertSame(
'userexists',
$ret->message->getKey() );
1621 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1622 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1624 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1625 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1627 $this->primaryauthMocks = [ $mock ];
1630 $this->
hook(
'LocalUserCreated', $this->never() );
1632 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1633 $this->
unhook(
'LocalUserCreated' );
1635 $this->assertSame(
'fail',
$ret->message->getKey() );
1638 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1639 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1641 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1642 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1644 $this->primaryauthMocks = [ $mock ];
1647 $this->
hook(
'LocalUserCreated', $this->never() );
1649 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1650 $this->
unhook(
'LocalUserCreated' );
1652 $this->assertSame(
'noname',
$ret->message->getKey() );
1654 $this->
hook(
'LocalUserCreated', $this->never() );
1655 $userReq->username = $creator->getName();
1656 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1657 $this->
unhook(
'LocalUserCreated' );
1659 $this->assertSame(
'userexists',
$ret->message->getKey() );
1662 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1663 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1665 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1666 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1668 $mock->expects( $this->
any() )->method(
'testForAccountCreation' )
1670 $this->primaryauthMocks = [ $mock ];
1674 ->setMethods( [
'populateUser' ] )
1676 $req->expects( $this->
any() )->method(
'populateUser' )
1679 $ret = $this->manager->beginAccountCreation(
1680 $creator, [ $userReq,
$req ],
'http://localhost/'
1683 $this->assertSame(
'populatefail',
$ret->message->getKey() );
1688 $ret = $this->manager->beginAccountCreation(
1689 $creator, [ $userReq,
$req ],
'http://localhost/'
1692 $this->assertSame(
'fail',
$ret->message->getKey() );
1694 $this->manager->beginAccountCreation(
1698 $this->assertSame(
'fail',
$ret->message->getKey() );
1704 $this->logger = new \TestLogger(
false,
function ( $message, $level ) {
1705 return $level === LogLevel::DEBUG ? null : $message;
1716 'primaryResponse' =>
null,
1718 'ranPreTests' =>
true,
1721 $this->
hook(
'LocalUserCreated', $this->never() );
1723 $this->manager->continueAccountCreation( [] );
1724 $this->fail(
'Expected exception not thrown' );
1725 }
catch ( \LogicException $ex ) {
1726 $this->assertEquals(
'Account creation is not possible', $ex->getMessage() );
1728 $this->
unhook(
'LocalUserCreated' );
1731 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1732 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1734 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1735 $mock->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )->will(
1738 $this->primaryauthMocks = [ $mock ];
1741 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
null );
1742 $this->
hook(
'LocalUserCreated', $this->never() );
1743 $ret = $this->manager->continueAccountCreation( [] );
1744 $this->
unhook(
'LocalUserCreated' );
1746 $this->assertSame(
'authmanager-create-not-in-progress',
$ret->message->getKey() );
1748 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1749 [
'username' =>
"$username<>" ] + $session );
1750 $this->
hook(
'LocalUserCreated', $this->never() );
1751 $ret = $this->manager->continueAccountCreation( [] );
1752 $this->
unhook(
'LocalUserCreated' );
1754 $this->assertSame(
'noname',
$ret->message->getKey() );
1756 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1759 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState', $session );
1760 $this->
hook(
'LocalUserCreated', $this->never() );
1763 $ret = $this->manager->continueAccountCreation( [] );
1765 $this->
unhook(
'LocalUserCreated' );
1767 $this->assertSame(
'usernameinprogress',
$ret->message->getKey() );
1771 $session, $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1774 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1775 [
'username' => $creator->getName() ] + $session );
1776 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
1777 $readOnlyMode->setReason(
'Because' );
1778 $this->
hook(
'LocalUserCreated', $this->never() );
1779 $ret = $this->manager->continueAccountCreation( [] );
1780 $this->
unhook(
'LocalUserCreated' );
1782 $this->assertSame(
'readonlytext',
$ret->message->getKey() );
1783 $this->assertSame( [
'Because' ],
$ret->message->getParams() );
1784 $readOnlyMode->setReason(
false );
1786 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1787 [
'username' => $creator->getName() ] + $session );
1788 $this->
hook(
'LocalUserCreated', $this->never() );
1789 $ret = $this->manager->continueAccountCreation( [] );
1790 $this->
unhook(
'LocalUserCreated' );
1792 $this->assertSame(
'userexists',
$ret->message->getKey() );
1794 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1797 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1798 [
'userid' => $creator->getId() ] + $session );
1799 $this->
hook(
'LocalUserCreated', $this->never() );
1801 $ret = $this->manager->continueAccountCreation( [] );
1802 $this->fail(
'Expected exception not thrown' );
1803 }
catch ( \UnexpectedValueException $ex ) {
1804 $this->assertEquals(
"User \"{$username}\" should exist now, but doesn't!", $ex->getMessage() );
1806 $this->
unhook(
'LocalUserCreated' );
1808 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1811 $id = $creator->getId();
1812 $name = $creator->getName();
1813 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1814 [
'username' =>
$name,
'userid' => $id + 1 ] + $session );
1815 $this->
hook(
'LocalUserCreated', $this->never() );
1817 $ret = $this->manager->continueAccountCreation( [] );
1818 $this->fail(
'Expected exception not thrown' );
1819 }
catch ( \UnexpectedValueException $ex ) {
1820 $this->assertEquals(
1821 "User \"{$name}\" exists, but ID $id != " . ( $id + 1 ) .
'!', $ex->getMessage()
1824 $this->
unhook(
'LocalUserCreated' );
1826 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1830 ->setMethods( [
'populateUser' ] )
1832 $req->expects( $this->
any() )->method(
'populateUser' )
1834 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState',
1835 [
'reqs' => [ $req ] ] + $session );
1836 $ret = $this->manager->continueAccountCreation( [] );
1838 $this->assertSame(
'populatefail',
$ret->message->getKey() );
1840 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1854 StatusValue $preTest, $primaryTest, $secondaryTest,
1855 array $primaryResponses,
array $secondaryResponses,
array $managerResponses
1864 $req->preTest = $preTest;
1865 $req->primaryTest = $primaryTest;
1866 $req->secondaryTest = $secondaryTest;
1867 $req->primary = $primaryResponses;
1868 $req->secondary = $secondaryResponses;
1870 foreach ( [
'pre',
'primary',
'secondary' ]
as $key ) {
1871 $class = ucfirst( $key ) .
'AuthenticationProvider';
1872 $mocks[$key] = $this->getMockForAbstractClass(
1873 "MediaWiki\\Auth\\$class", [],
"Mock$class"
1875 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
1876 ->will( $this->returnValue( $key ) );
1877 $mocks[$key]->expects( $this->
any() )->method(
'testUserForCreation' )
1879 $mocks[$key]->expects( $this->
any() )->method(
'testForAccountCreation' )
1880 ->will( $this->returnCallback(
1881 function (
$user, $creatorIn, $reqs )
1885 $this->assertSame( $creator->getId(), $creatorIn->getId() );
1886 $this->assertSame( $creator->getName(), $creatorIn->getName() );
1888 foreach ( $reqs
as $r ) {
1889 $this->assertSame(
$username, $r->username );
1890 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1892 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1898 for ( $i = 2; $i <= 3; $i++ ) {
1899 $mocks[$key . $i] = $this->getMockForAbstractClass(
1900 "MediaWiki\\Auth\\$class", [],
"Mock$class"
1902 $mocks[$key . $i]->expects( $this->
any() )->method(
'getUniqueId' )
1903 ->will( $this->returnValue( $key . $i ) );
1904 $mocks[$key . $i]->expects( $this->
any() )->method(
'testUserForCreation' )
1906 $mocks[$key . $i]->expects( $this->atMost( 1 ) )->method(
'testForAccountCreation' )
1911 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
1913 $mocks[
'primary']->expects( $this->
any() )->method(
'testUserExists' )
1914 ->will( $this->returnValue(
false ) );
1918 $this->assertSame(
'UTSysop', $creator->getName() );
1920 foreach ( $reqs
as $r ) {
1921 $this->assertSame(
$username, $r->username );
1922 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1924 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1925 return array_shift(
$req->primary );
1927 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
1928 ->method(
'beginPrimaryAccountCreation' )
1929 ->will( $callback );
1930 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
1931 ->method(
'continuePrimaryAccountCreation' )
1932 ->will( $callback );
1937 $this->assertSame(
'UTSysop', $creator->getName() );
1939 foreach ( $reqs
as $r ) {
1940 $this->assertSame(
$username, $r->username );
1941 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1943 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1944 return array_shift(
$req->secondary );
1946 $mocks[
'secondary']->expects( $this->exactly( min( 1, $ct ) ) )
1947 ->method(
'beginSecondaryAccountCreation' )
1948 ->will( $callback );
1949 $mocks[
'secondary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
1950 ->method(
'continueSecondaryAccountCreation' )
1951 ->will( $callback );
1954 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
1956 $mocks[
'primary2']->expects( $this->
any() )->method(
'testUserExists' )
1957 ->will( $this->returnValue(
false ) );
1958 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAccountCreation' )
1959 ->will( $this->returnValue( $abstain ) );
1960 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAccountCreation' );
1961 $mocks[
'primary3']->expects( $this->
any() )->method(
'accountCreationType' )
1963 $mocks[
'primary3']->expects( $this->
any() )->method(
'testUserExists' )
1964 ->will( $this->returnValue(
false ) );
1965 $mocks[
'primary3']->expects( $this->never() )->method(
'beginPrimaryAccountCreation' );
1966 $mocks[
'primary3']->expects( $this->never() )->method(
'continuePrimaryAccountCreation' );
1967 $mocks[
'secondary2']->expects( $this->atMost( 1 ) )
1968 ->method(
'beginSecondaryAccountCreation' )
1969 ->will( $this->returnValue( $abstain ) );
1970 $mocks[
'secondary2']->expects( $this->never() )->method(
'continueSecondaryAccountCreation' );
1971 $mocks[
'secondary3']->expects( $this->atMost( 1 ) )
1972 ->method(
'beginSecondaryAccountCreation' )
1973 ->will( $this->returnValue( $abstain ) );
1974 $mocks[
'secondary3']->expects( $this->never() )->method(
'continueSecondaryAccountCreation' );
1976 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
1977 $this->primaryauthMocks = [ $mocks[
'primary3'], $mocks[
'primary'], $mocks[
'primary2'] ];
1978 $this->secondaryauthMocks = [
1979 $mocks[
'secondary3'], $mocks[
'secondary'], $mocks[
'secondary2']
1982 $this->logger = new \TestLogger(
true,
function ( $message, $level ) {
1983 return $level === LogLevel::DEBUG ? null : $message;
1988 $constraint = \PHPUnit_Framework_Assert::logicalOr(
1992 $providers = array_merge(
1993 $this->preauthMocks, $this->primaryauthMocks, $this->secondaryauthMocks
1995 foreach ( $providers
as $p ) {
1996 $p->postCalled =
false;
1997 $p->expects( $this->atMost( 1 ) )->method(
'postAccountCreation' )
2001 $this->assertInstanceOf(
'User',
$user );
2003 $this->assertSame(
'UTSysop', $creator->getName() );
2005 $this->assertThat(
$response->status, $constraint );
2012 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2016 foreach ( $managerResponses
as $i =>
$response ) {
2019 if ( $i ===
'created' ) {
2021 $this->
hook(
'LocalUserCreated', $this->once() )
2026 $this->equalTo(
false )
2028 $expectLog[] = [ LogLevel::INFO,
"Creating user {user} during account creation" ];
2030 $this->
hook(
'LocalUserCreated', $this->never() );
2038 $ret = $this->manager->beginAccountCreation(
2039 $creator, [ $userReq,
$req ],
'http://localhost/'
2042 $ret = $this->manager->continueAccountCreation( [
$req ] );
2044 if (
$response instanceof \Exception ) {
2045 $this->fail(
'Expected exception not thrown',
"Response $i" );
2047 }
catch ( \Exception $ex ) {
2048 if ( !
$response instanceof \Exception ) {
2051 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
2053 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2054 "Response $i, exception, session state"
2056 $this->
unhook(
'LocalUserCreated' );
2060 $this->
unhook(
'LocalUserCreated' );
2062 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
2065 $this->assertNotNull(
$ret->loginRequest,
"Response $i, login marker" );
2066 $this->assertContains(
2067 $ret->loginRequest, $this->managerPriv->createdAccountAuthenticationRequests,
2068 "Response $i, login marker"
2073 "MediaWiki\Auth\AuthManager::continueAccountCreation: Account creation succeeded for {user}"
2081 $this->assertNull(
$ret->loginRequest,
"Response $i, login marker" );
2082 $this->assertSame( [], $this->managerPriv->createdAccountAuthenticationRequests,
2083 "Response $i, login marker" );
2086 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
2089 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2090 "Response $i, session state"
2092 foreach ( $providers
as $p ) {
2093 $this->assertSame(
$response->status, $p->postCalled,
2094 "Response $i, post-auth callback called" );
2097 $this->assertNotNull(
2098 $this->
request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2099 "Response $i, session state"
2101 foreach (
$ret->neededRequests
as $neededReq ) {
2103 "Response $i, neededRequest action" );
2105 $this->assertEquals(
2106 $ret->neededRequests,
2108 "Response $i, continuation check"
2110 foreach ( $providers
as $p ) {
2111 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
2124 $this->assertSame( $expectLog, $this->logger->getBuffer() );
2128 $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] )
2137 'Pre-creation test fail in pre' => [
2145 'Pre-creation test fail in primary' => [
2153 'Pre-creation test fail in secondary' => [
2161 'Failure in primary' => [
2162 $good, $good, $good,
2169 'All primary abstain' => [
2170 $good, $good, $good,
2179 'Primary UI, then redirect, then fail' => [
2180 $good, $good, $good,
2189 'Primary redirect, then abstain' => [
2190 $good, $good, $good,
2193 [
$req ],
'/foo.html', [
'foo' =>
'bar' ]
2200 new \DomainException(
2201 'MockPrimaryAuthenticationProvider::continuePrimaryAccountCreation() returned ABSTAIN'
2205 'Primary UI, then pass; secondary abstain' => [
2206 $good, $good, $good,
2219 'Primary pass; secondary UI then pass' => [
2220 $good, $good, $good,
2233 'Primary pass; secondary fail' => [
2234 $good, $good, $good,
2242 'created' => new \DomainException(
2243 'MockSecondaryAuthenticationProvider::beginSecondaryAccountCreation() returned FAIL. ' .
2244 'Secondary providers are not allowed to fail account creation, ' .
2245 'that should have been done via testForAccountCreation().'
2264 $mock = $this->getMockForAbstractClass(
2265 "MediaWiki\\Auth\\PrimaryAuthenticationProvider", []
2267 $mock->expects( $this->
any() )->method(
'getUniqueId' )
2268 ->will( $this->returnValue(
'primary' ) );
2269 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
2271 $mock->expects( $this->
any() )->method(
'testForAccountCreation' )
2273 $mock->expects( $this->
any() )->method(
'accountCreationType' )
2275 $mock->expects( $this->
any() )->method(
'testUserExists' )
2276 ->will( $this->returnValue(
false ) );
2277 $mock->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )
2279 $mock->expects( $this->
any() )->method(
'finishAccountCreation' )
2280 ->will( $this->returnValue( $logSubtype ) );
2282 $this->primaryauthMocks = [ $mock ];
2284 $this->logger->setCollect(
true );
2286 $this->config->set(
'NewUserLog',
true );
2289 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2294 $reasonReq->reason = $this->toString();
2295 $ret = $this->manager->beginAccountCreation(
2296 $creator, [ $userReq, $reasonReq ],
'http://localhost/'
2302 $this->assertNotEquals( 0,
$user->getId(),
'sanity check' );
2303 $this->assertNotEquals( $creator->getId(),
$user->getId(),
'sanity check' );
2306 $rows = iterator_to_array( $dbw->select(
2310 'log_id > ' . (
int)$maxLogId,
2311 'log_type' =>
'newusers'
2317 $this->assertCount( 1,
$rows );
2320 $this->assertSame( $logSubtype ?: ( $isAnon ?
'create' :
'create2' ), $entry->getSubtype() );
2322 $isAnon ?
$user->getId() : $creator->getId(),
2323 $entry->getPerformer()->getId()
2326 $isAnon ?
$user->getName() : $creator->getName(),
2327 $entry->getPerformer()->getName()
2329 $this->assertSame(
$user->getUserPage()->getFullText(), $entry->getTarget()->getFullText() );
2330 $this->assertSame( [
'4::userid' =>
$user->getId() ], $entry->getParameters() );
2331 $this->assertSame( $this->toString(), $entry->getComment() );
2339 [
false,
'byemail' ],
2350 $workaroundPHPUnitBug =
false;
2356 $wgGroupPermissions[
'*'][
'createaccount'] =
true;
2360 $this->
setMwGlobals( [
'wgMainCacheType' => __METHOD__ ] );
2364 foreach ( [
'pre',
'primary',
'secondary' ]
as $key ) {
2365 $class = ucfirst( $key ) .
'AuthenticationProvider';
2366 $mocks[$key] = $this->getMockForAbstractClass(
2367 "MediaWiki\\Auth\\$class", [],
"Mock$class"
2369 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
2370 ->will( $this->returnValue( $key ) );
2374 $callback = $this->callback(
function (
$user )
use ( &
$username, &$workaroundPHPUnitBug ) {
2378 $mocks[
'pre']->expects( $this->exactly( 12 ) )->method(
'testUserForCreation' )
2380 ->will( $this->onConsecutiveCalls(
2390 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
2392 $mocks[
'primary']->expects( $this->
any() )->method(
'testUserExists' )
2393 ->will( $this->returnValue(
true ) );
2394 $mocks[
'primary']->expects( $this->exactly( 9 ) )->method(
'testUserForCreation' )
2396 ->will( $this->onConsecutiveCalls(
2404 $mocks[
'primary']->expects( $this->exactly( 3 ) )->method(
'autoCreatedAccount' )
2407 $mocks[
'secondary']->expects( $this->exactly( 8 ) )->method(
'testUserForCreation' )
2409 ->will( $this->onConsecutiveCalls(
2417 $mocks[
'secondary']->expects( $this->exactly( 3 ) )->method(
'autoCreatedAccount' )
2420 $this->preauthMocks = [ $mocks[
'pre'] ];
2421 $this->primaryauthMocks = [ $mocks[
'primary'] ];
2422 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
2424 $session = $this->
request->getSession();
2426 $logger = new \TestLogger(
true,
function ( $m ) {
2427 $m = str_replace(
'MediaWiki\\Auth\\AuthManager::autoCreateUser: ',
'', $m );
2430 $this->manager->setLogger(
$logger );
2434 $this->manager->autoCreateUser(
$user,
'InvalidSource',
true );
2435 $this->fail(
'Expected exception not thrown' );
2436 }
catch ( \InvalidArgumentException $ex ) {
2437 $this->assertSame(
'Unknown auto-creation source: InvalidSource', $ex->getMessage() );
2443 $this->
hook(
'LocalUserCreated', $this->never() );
2445 $this->
unhook(
'LocalUserCreated' );
2447 $expect->warning(
'userexists' );
2448 $this->assertEquals( $expect,
$ret );
2449 $this->assertNotEquals( 0,
$user->getId() );
2450 $this->assertSame(
'UTSysop',
$user->getName() );
2451 $this->assertEquals(
$user->getId(), $session->getUser()->getId() );
2452 $this->assertSame( [
2453 [ LogLevel::DEBUG,
'{username} already exists locally' ],
2459 $this->
hook(
'LocalUserCreated', $this->never() );
2461 $this->
unhook(
'LocalUserCreated' );
2463 $expect->warning(
'userexists' );
2464 $this->assertEquals( $expect,
$ret );
2465 $this->assertNotEquals( 0,
$user->getId() );
2466 $this->assertSame(
'UTSysop',
$user->getName() );
2467 $this->assertEquals( 0, $session->getUser()->getId() );
2468 $this->assertSame( [
2469 [ LogLevel::DEBUG,
'{username} already exists locally' ],
2475 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
2476 $readOnlyMode->setReason(
'Because' );
2478 $this->
hook(
'LocalUserCreated', $this->never() );
2480 $this->
unhook(
'LocalUserCreated' );
2482 $this->assertEquals( 0,
$user->getId() );
2484 $this->assertEquals( 0, $session->getUser()->getId() );
2485 $this->assertSame( [
2486 [ LogLevel::DEBUG,
'denied by wfReadOnly(): {reason}' ],
2489 $readOnlyMode->setReason(
false );
2493 $session->set(
'AuthManager::AutoCreateBlacklist',
'test' );
2495 $this->
hook(
'LocalUserCreated', $this->never() );
2497 $this->
unhook(
'LocalUserCreated' );
2499 $this->assertEquals( 0,
$user->getId() );
2501 $this->assertEquals( 0, $session->getUser()->getId() );
2502 $this->assertSame( [
2503 [ LogLevel::DEBUG,
'blacklisted in session {sessionid}' ],
2510 $this->
hook(
'LocalUserCreated', $this->never() );
2512 $this->
unhook(
'LocalUserCreated' );
2514 $this->assertEquals( 0,
$user->getId() );
2516 $this->assertEquals( 0, $session->getUser()->getId() );
2517 $this->assertSame( [
2518 [ LogLevel::DEBUG,
'blacklisted in session {sessionid}' ],
2525 $this->
hook(
'LocalUserCreated', $this->never() );
2527 $this->
unhook(
'LocalUserCreated' );
2529 $this->assertEquals( 0,
$user->getId() );
2531 $this->assertEquals( 0, $session->getUser()->getId() );
2532 $this->assertSame( [
2533 [ LogLevel::DEBUG,
'name "{username}" is not creatable' ],
2536 $this->assertSame(
'noname', $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2543 $this->
hook(
'LocalUserCreated', $this->never() );
2545 $this->
unhook(
'LocalUserCreated' );
2547 $this->assertEquals( 0,
$user->getId() );
2549 $this->assertEquals( 0, $session->getUser()->getId() );
2550 $this->assertSame( [
2551 [ LogLevel::DEBUG,
'IP lacks the ability to create or autocreate accounts' ],
2555 'authmanager-autocreate-noperm', $session->get(
'AuthManager::AutoCreateBlacklist' )
2564 $this->
hook(
'LocalUserCreated', $this->never() );
2566 $this->
unhook(
'LocalUserCreated' );
2573 $this->
hook(
'LocalUserCreated', $this->never() );
2575 $this->
unhook(
'LocalUserCreated' );
2582 $this->
hook(
'LocalUserCreated', $this->never() );
2587 $this->
unhook(
'LocalUserCreated' );
2589 $this->assertEquals( 0,
$user->getId() );
2591 $this->assertEquals( 0, $session->getUser()->getId() );
2592 $this->assertSame( [
2593 [ LogLevel::DEBUG,
'Could not acquire account creation lock' ],
2600 $this->
hook(
'LocalUserCreated', $this->never() );
2602 $this->
unhook(
'LocalUserCreated' );
2604 $this->assertEquals( 0,
$user->getId() );
2606 $this->assertEquals( 0, $session->getUser()->getId() );
2607 $this->assertSame( [
2608 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2611 $this->assertEquals(
2617 $this->
hook(
'LocalUserCreated', $this->never() );
2619 $this->
unhook(
'LocalUserCreated' );
2621 $this->assertEquals( 0,
$user->getId() );
2623 $this->assertEquals( 0, $session->getUser()->getId() );
2624 $this->assertSame( [
2625 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2628 $this->assertEquals(
2634 $this->
hook(
'LocalUserCreated', $this->never() );
2636 $this->
unhook(
'LocalUserCreated' );
2638 $this->assertEquals( 0,
$user->getId() );
2640 $this->assertEquals( 0, $session->getUser()->getId() );
2641 $this->assertSame( [
2642 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2645 $this->assertEquals(
2652 $cache->set( $backoffKey,
true );
2655 $this->
hook(
'LocalUserCreated', $this->never() );
2657 $this->
unhook(
'LocalUserCreated' );
2659 $this->assertEquals( 0,
$user->getId() );
2661 $this->assertEquals( 0, $session->getUser()->getId() );
2662 $this->assertSame( [
2663 [ LogLevel::DEBUG,
'{username} denied by prior creation attempt failures' ],
2666 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2667 $cache->delete( $backoffKey );
2671 $user = $this->getMockBuilder(
'User' )
2672 ->setMethods( [
'addToDatabase' ] )->getMock();
2673 $user->expects( $this->once() )->method(
'addToDatabase' )
2678 $this->assertEquals( 0,
$user->getId() );
2680 $this->assertEquals( 0, $session->getUser()->getId() );
2681 $this->assertSame( [
2682 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2683 [ LogLevel::ERROR,
'{username} failed with message {msg}' ],
2686 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2691 $this->assertFalse(
$cache->get( $backoffKey ),
'sanity check' );
2693 $user = $this->getMockBuilder(
'User' )
2694 ->setMethods( [
'addToDatabase' ] )->getMock();
2695 $user->expects( $this->once() )->method(
'addToDatabase' )
2696 ->will( $this->throwException(
new \Exception(
'Excepted' ) ) );
2700 $this->fail(
'Expected exception not thrown' );
2701 }
catch ( \Exception $ex ) {
2702 $this->assertSame(
'Excepted', $ex->getMessage() );
2704 $this->assertEquals( 0,
$user->getId() );
2705 $this->assertEquals( 0, $session->getUser()->getId() );
2706 $this->assertSame( [
2707 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2708 [ LogLevel::ERROR,
'{username} failed with exception {exception}' ],
2711 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2712 $this->assertNotEquals(
false,
$cache->get( $backoffKey ) );
2713 $cache->delete( $backoffKey );
2717 $user = $this->getMockBuilder(
'User' )
2718 ->setMethods( [
'addToDatabase' ] )->getMock();
2719 $user->expects( $this->once() )->method(
'addToDatabase' )
2722 $status = $oldUser->addToDatabase();
2723 $this->assertTrue(
$status->isOK(),
'sanity check' );
2724 $user->setId( $oldUser->getId() );
2725 return \Status::newFatal(
'userexists' );
2730 $expect->warning(
'userexists' );
2731 $this->assertEquals( $expect,
$ret );
2732 $this->assertNotEquals( 0,
$user->getId() );
2734 $this->assertEquals(
$user->getId(), $session->getUser()->getId() );
2735 $this->assertSame( [
2736 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2737 [ LogLevel::INFO,
'{username} already exists locally (race)' ],
2740 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2746 $this->
hook(
'AuthPluginAutoCreate', $this->once() )
2747 ->with( $callback );
2749 get_class(
$wgHooks[
'AuthPluginAutoCreate'][0] ) .
'::onAuthPluginAutoCreate)' );
2750 $this->
hook(
'LocalUserCreated', $this->once() )
2751 ->with( $callback, $this->equalTo(
true ) );
2753 $this->
unhook(
'LocalUserCreated' );
2754 $this->
unhook(
'AuthPluginAutoCreate' );
2756 $this->assertNotEquals( 0,
$user->getId() );
2758 $this->assertEquals(
$user->getId(), $session->getUser()->getId() );
2759 $this->assertSame( [
2760 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2765 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2769 $this->
hook(
'LocalUserCreated', $this->once() )
2770 ->with( $callback, $this->equalTo(
true ) );
2772 $this->
unhook(
'LocalUserCreated' );
2774 $this->assertNotEquals( 0,
$user->getId() );
2776 $this->assertEquals( 0, $session->getUser()->getId() );
2777 $this->assertSame( [
2778 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2783 $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] )
2786 $this->config->set(
'NewUserLog',
true );
2795 $rows = iterator_to_array( $dbw->select(
2799 'log_id > ' . (
int)$maxLogId,
2800 'log_type' =>
'newusers'
2806 $this->assertCount( 1,
$rows );
2809 $this->assertSame(
'autocreate', $entry->getSubtype() );
2810 $this->assertSame(
$user->getId(), $entry->getPerformer()->getId() );
2811 $this->assertSame(
$user->getName(), $entry->getPerformer()->getName() );
2812 $this->assertSame(
$user->getUserPage()->getFullText(), $entry->getTarget()->getFullText() );
2813 $this->assertSame( [
'4::userid' =>
$user->getId() ], $entry->getParameters() );
2815 $workaroundPHPUnitBug =
true;
2825 $makeReq =
function ( $key )
use (
$action ) {
2827 $req->expects( $this->
any() )->method(
'getUniqueId' )
2828 ->will( $this->returnValue( $key ) );
2833 $cmpReqs =
function ( $a, $b ) {
2834 $ret = strcmp( get_class( $a ), get_class( $b ) );
2836 $ret = strcmp( $a->key, $b->key );
2844 foreach ( [
'pre',
'primary',
'secondary' ]
as $key ) {
2845 $class = ucfirst( $key ) .
'AuthenticationProvider';
2846 $mocks[$key] = $this->getMockForAbstractClass(
2847 "MediaWiki\\Auth\\$class", [],
"Mock$class"
2849 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
2850 ->will( $this->returnValue( $key ) );
2851 $mocks[$key]->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2852 ->will( $this->returnCallback(
function (
$action )
use ( $key, $makeReq ) {
2853 return [ $makeReq(
"$key-$action" ), $makeReq(
'generic' ) ];
2855 $mocks[$key]->expects( $this->
any() )->method(
'providerAllowsAuthenticationDataChange' )
2856 ->will( $this->returnValue( $good ) );
2865 $class =
'PrimaryAuthenticationProvider';
2866 $mocks[
"primary-$type"] = $this->getMockForAbstractClass(
2867 "MediaWiki\\Auth\\$class", [],
"Mock$class"
2869 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'getUniqueId' )
2870 ->will( $this->returnValue(
"primary-$type" ) );
2871 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'accountCreationType' )
2872 ->will( $this->returnValue(
$type ) );
2873 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2874 ->will( $this->returnCallback(
function (
$action )
use (
$type, $makeReq ) {
2875 return [ $makeReq(
"primary-$type-$action" ), $makeReq(
'generic' ) ];
2877 $mocks[
"primary-$type"]->expects( $this->
any() )
2878 ->method(
'providerAllowsAuthenticationDataChange' )
2879 ->will( $this->returnValue( $good ) );
2880 $this->primaryauthMocks[] = $mocks[
"primary-$type"];
2883 $mocks[
'primary2'] = $this->getMockForAbstractClass(
2886 $mocks[
'primary2']->expects( $this->
any() )->method(
'getUniqueId' )
2887 ->will( $this->returnValue(
'primary2' ) );
2888 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
2890 $mocks[
'primary2']->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2891 ->will( $this->returnValue( [] ) );
2892 $mocks[
'primary2']->expects( $this->
any() )
2893 ->method(
'providerAllowsAuthenticationDataChange' )
2894 ->will( $this->returnCallback(
function (
$req )
use ( $good ) {
2897 $this->primaryauthMocks[] = $mocks[
'primary2'];
2899 $this->preauthMocks = [ $mocks[
'pre'] ];
2900 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
2904 if ( isset( $state[
'continueRequests'] ) ) {
2905 $state[
'continueRequests'] = array_map( $makeReq, $state[
'continueRequests'] );
2908 $this->
request->getSession()->setSecret(
'AuthManager::authnState', $state );
2910 $this->
request->getSession()->setSecret(
'AuthManager::accountCreationState', $state );
2912 $this->
request->getSession()->setSecret(
'AuthManager::accountLinkState', $state );
2916 $expectReqs = array_map( $makeReq, $expect );
2921 $expectReqs[] =
$req;
2925 $expectReqs[] =
$req;
2929 $expectReqs[] =
$req;
2931 usort( $expectReqs, $cmpReqs );
2933 $actual = $this->manager->getAuthenticationRequests(
$action );
2934 foreach ( $actual
as $req ) {
2938 usort( $actual, $cmpReqs );
2940 $this->assertEquals( $expectReqs, $actual );
2947 $expectReqs[] =
$req;
2948 usort( $expectReqs, $cmpReqs );
2951 foreach ( $actual
as $req ) {
2955 usort( $actual, $cmpReqs );
2957 $this->assertEquals( $expectReqs, $actual );
2965 [
'pre-login',
'primary-none-login',
'primary-create-login',
2966 'primary-link-login',
'secondary-login',
'generic' ],
2970 [
'pre-create',
'primary-none-create',
'primary-create-create',
2971 'primary-link-create',
'secondary-create',
'generic' ],
2975 [
'primary-link-link',
'generic' ],
2979 [
'primary-none-change',
'primary-create-change',
'primary-link-change',
2980 'secondary-change' ],
2984 [
'primary-none-remove',
'primary-create-remove',
'primary-link-remove',
2985 'secondary-remove' ],
2989 [
'primary-link-remove' ],
2997 $reqs = [
'continue-login',
'foo',
'bar' ],
2999 'continueRequests' => $reqs,
3008 $reqs = [
'continue-create',
'foo',
'bar' ],
3010 'continueRequests' => $reqs,
3019 $reqs = [
'continue-link',
'foo',
'bar' ],
3021 'continueRequests' => $reqs,
3028 $makeReq =
function ( $key, $required ) {
3030 $req->expects( $this->
any() )->method(
'getUniqueId' )
3031 ->will( $this->returnValue( $key ) );
3034 $req->required = $required;
3037 $cmpReqs =
function ( $a, $b ) {
3038 $ret = strcmp( get_class( $a ), get_class( $b ) );
3040 $ret = strcmp( $a->key, $b->key );
3048 $primary1->expects( $this->
any() )->method(
'getUniqueId' )
3049 ->will( $this->returnValue(
'primary1' ) );
3050 $primary1->expects( $this->
any() )->method(
'accountCreationType' )
3052 $primary1->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3053 ->will( $this->returnCallback(
function (
$action )
use ( $makeReq ) {
3065 $primary2->expects( $this->
any() )->method(
'getUniqueId' )
3066 ->will( $this->returnValue(
'primary2' ) );
3067 $primary2->expects( $this->
any() )->method(
'accountCreationType' )
3069 $primary2->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3070 ->will( $this->returnCallback(
function (
$action )
use ( $makeReq ) {
3079 $secondary->expects( $this->
any() )->method(
'getUniqueId' )
3080 ->will( $this->returnValue(
'secondary' ) );
3081 $secondary->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3082 ->will( $this->returnCallback(
function (
$action )
use ( $makeReq ) {
3093 $this->primaryauthMocks = [ $primary1, $primary2 ];
3094 $this->secondaryauthMocks = [ $secondary ];
3109 usort( $actual, $cmpReqs );
3110 usort( $expected, $cmpReqs );
3111 $this->assertEquals( $expected, $actual );
3113 $this->primaryauthMocks = [ $primary1 ];
3114 $this->secondaryauthMocks = [ $secondary ];
3127 usort( $actual, $cmpReqs );
3128 usort( $expected, $cmpReqs );
3129 $this->assertEquals( $expected, $actual );
3134 foreach ( [
'primary',
'secondary' ]
as $key ) {
3135 $class = ucfirst( $key ) .
'AuthenticationProvider';
3136 $mocks[$key] = $this->getMockForAbstractClass(
3137 "MediaWiki\\Auth\\$class", [],
"Mock$class"
3139 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
3140 ->will( $this->returnValue( $key ) );
3141 $mocks[$key]->expects( $this->
any() )->method(
'providerAllowsPropertyChange' )
3142 ->will( $this->returnCallback(
function ( $prop )
use ( $key ) {
3143 return $prop !== $key;
3147 $this->primaryauthMocks = [ $mocks[
'primary'] ];
3148 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
3151 $this->assertTrue( $this->manager->allowsPropertyChange(
'foo' ) );
3152 $this->assertFalse( $this->manager->allowsPropertyChange(
'primary' ) );
3153 $this->assertFalse( $this->manager->allowsPropertyChange(
'secondary' ) );
3162 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'primary' ) );
3163 $mock->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
3165 $mock->expects( $this->
any() )->method(
'accountCreationType' )
3167 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
true ) );
3168 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
3172 $mock2->expects( $this->
any() )->method(
'getUniqueId' )
3173 ->will( $this->returnValue(
'secondary' ) );
3174 $mock2->expects( $this->
any() )->method(
'beginSecondaryAuthentication' )->will(
3179 $mock2->expects( $this->
any() )->method(
'continueSecondaryAuthentication' )
3181 $mock2->expects( $this->
any() )->method(
'testUserForCreation' )
3184 $this->primaryauthMocks = [ $mock ];
3185 $this->secondaryauthMocks = [ $mock2 ];
3187 $this->manager->setLogger(
new \Psr\Log\NullLogger() );
3188 $session = $this->
request->getSession();
3198 $this->
hook(
'UserLoggedIn', $this->never() );
3199 $this->
hook(
'LocalUserCreated', $this->once() )->with( $callback, $this->equalTo(
true ) );
3200 $ret = $this->manager->beginAuthentication( [],
'http://localhost/' );
3201 $this->
unhook(
'LocalUserCreated' );
3202 $this->
unhook(
'UserLoggedIn' );
3207 $this->assertSame( 0, $session->getUser()->getId() );
3209 $this->
hook(
'UserLoggedIn', $this->once() )->with( $callback );
3210 $this->
hook(
'LocalUserCreated', $this->never() );
3211 $ret = $this->manager->continueAuthentication( [] );
3212 $this->
unhook(
'LocalUserCreated' );
3213 $this->
unhook(
'UserLoggedIn' );
3216 $this->assertSame( $id, $session->getUser()->getId() );
3222 $mock = $this->getMockForAbstractClass(
3224 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'primary' ) );
3225 $mock->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
3227 $mock->expects( $this->
any() )->method(
'accountCreationType' )
3229 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
true ) );
3230 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
3233 $this->primaryauthMocks = [ $mock ];
3235 $this->manager->setLogger(
new \Psr\Log\NullLogger() );
3236 $session = $this->
request->getSession();
3239 $this->assertSame( 0, $session->getUser()->getId(),
3244 $this->
hook(
'UserLoggedIn', $this->never() );
3245 $this->
hook(
'LocalUserCreated', $this->never() );
3246 $ret = $this->manager->beginAuthentication( [],
'http://localhost/' );
3247 $this->
unhook(
'LocalUserCreated' );
3248 $this->
unhook(
'UserLoggedIn' );
3250 $this->assertSame(
'authmanager-authn-autocreate-failed',
$ret->message->getKey() );
3253 $this->assertSame( 0, $session->getUser()->getId() );
3259 $this->assertNull( $this->manager->getAuthenticationSessionData(
'foo' ) );
3260 $this->manager->setAuthenticationSessionData(
'foo',
'foo!' );
3261 $this->manager->setAuthenticationSessionData(
'bar',
'bar!' );
3262 $this->assertSame(
'foo!', $this->manager->getAuthenticationSessionData(
'foo' ) );
3263 $this->assertSame(
'bar!', $this->manager->getAuthenticationSessionData(
'bar' ) );
3264 $this->manager->removeAuthenticationSessionData(
'foo' );
3265 $this->assertNull( $this->manager->getAuthenticationSessionData(
'foo' ) );
3266 $this->assertSame(
'bar!', $this->manager->getAuthenticationSessionData(
'bar' ) );
3267 $this->manager->removeAuthenticationSessionData(
'bar' );
3268 $this->assertNull( $this->manager->getAuthenticationSessionData(
'bar' ) );
3270 $this->manager->setAuthenticationSessionData(
'foo',
'foo!' );
3271 $this->manager->setAuthenticationSessionData(
'bar',
'bar!' );
3272 $this->manager->removeAuthenticationSessionData(
null );
3273 $this->assertNull( $this->manager->getAuthenticationSessionData(
'foo' ) );
3274 $this->assertNull( $this->manager->getAuthenticationSessionData(
'bar' ) );
3284 foreach ( $types
as $type => $can ) {
3286 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
$type ) );
3287 $mock->expects( $this->
any() )->method(
'accountCreationType' )
3288 ->will( $this->returnValue(
$type ) );
3289 $this->primaryauthMocks = [ $mock ];
3291 $this->assertSame( $can, $this->manager->canCreateAccounts(),
$type );
3299 $this->
request->getSession()->setSecret(
'AuthManager::accountLinkState',
'test' );
3301 $this->manager->beginAccountLink(
$user, [],
'http://localhost/' );
3302 $this->fail(
'Expected exception not thrown' );
3303 }
catch ( \LogicException $ex ) {
3304 $this->assertEquals(
'Account linking is not possible', $ex->getMessage() );
3306 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ) );
3309 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
3310 $mock->expects( $this->
any() )->method(
'accountCreationType' )
3312 $this->primaryauthMocks = [ $mock ];
3315 $ret = $this->manager->beginAccountLink(
new \
User, [],
'http://localhost/' );
3317 $this->assertSame(
'noname',
$ret->message->getKey() );
3319 $ret = $this->manager->beginAccountLink(
3323 $this->assertSame(
'authmanager-userdoesnotexist',
$ret->message->getKey() );
3331 'userid' =>
$user->getId(),
3332 'username' =>
$user->getName(),
3337 $this->manager->continueAccountLink( [] );
3338 $this->fail(
'Expected exception not thrown' );
3339 }
catch ( \LogicException $ex ) {
3340 $this->assertEquals(
'Account linking is not possible', $ex->getMessage() );
3344 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
3345 $mock->expects( $this->
any() )->method(
'accountCreationType' )
3347 $mock->expects( $this->
any() )->method(
'beginPrimaryAccountLink' )->will(
3350 $this->primaryauthMocks = [ $mock ];
3353 $this->
request->getSession()->setSecret(
'AuthManager::accountLinkState',
null );
3354 $ret = $this->manager->continueAccountLink( [] );
3356 $this->assertSame(
'authmanager-link-not-in-progress',
$ret->message->getKey() );
3358 $this->
request->getSession()->setSecret(
'AuthManager::accountLinkState',
3359 [
'username' =>
$user->getName() .
'<>' ] + $session );
3360 $ret = $this->manager->continueAccountLink( [] );
3362 $this->assertSame(
'noname',
$ret->message->getKey() );
3363 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ) );
3365 $id =
$user->getId();
3366 $this->
request->getSession()->setSecret(
'AuthManager::accountLinkState',
3367 [
'userid' => $id + 1 ] + $session );
3369 $ret = $this->manager->continueAccountLink( [] );
3370 $this->fail(
'Expected exception not thrown' );
3371 }
catch ( \UnexpectedValueException $ex ) {
3372 $this->assertEquals(
3373 "User \"{$user->getName()}\" is valid, but ID $id != " . ( $id + 1 ) .
'!',
3377 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ) );
3395 $req->primary = $primaryResponses;
3398 foreach ( [
'pre',
'primary' ]
as $key ) {
3399 $class = ucfirst( $key ) .
'AuthenticationProvider';
3400 $mocks[$key] = $this->getMockForAbstractClass(
3401 "MediaWiki\\Auth\\$class", [],
"Mock$class"
3403 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
3404 ->will( $this->returnValue( $key ) );
3406 for ( $i = 2; $i <= 3; $i++ ) {
3407 $mocks[$key . $i] = $this->getMockForAbstractClass(
3408 "MediaWiki\\Auth\\$class", [],
"Mock$class"
3410 $mocks[$key . $i]->expects( $this->
any() )->method(
'getUniqueId' )
3411 ->will( $this->returnValue( $key . $i ) );
3415 $mocks[
'pre']->expects( $this->
any() )->method(
'testForAccountLink' )
3416 ->will( $this->returnCallback(
3420 $this->assertSame(
$user->getId(), $u->getId() );
3421 $this->assertSame(
$user->getName(), $u->getName() );
3426 $mocks[
'pre2']->expects( $this->atMost( 1 ) )->method(
'testForAccountLink' )
3429 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
3432 $callback = $this->returnCallback(
function ( $u, $reqs )
use (
$user,
$req ) {
3433 $this->assertSame(
$user->getId(), $u->getId() );
3434 $this->assertSame(
$user->getName(), $u->getName() );
3436 foreach ( $reqs
as $r ) {
3437 $this->assertSame(
$user->getName(), $r->username );
3438 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
3440 $this->assertTrue( $foundReq,
'$reqs contains $req' );
3441 return array_shift(
$req->primary );
3443 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
3444 ->method(
'beginPrimaryAccountLink' )
3445 ->will( $callback );
3446 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
3447 ->method(
'continuePrimaryAccountLink' )
3448 ->will( $callback );
3451 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
3453 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAccountLink' )
3454 ->will( $this->returnValue( $abstain ) );
3455 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAccountLink' );
3456 $mocks[
'primary3']->expects( $this->
any() )->method(
'accountCreationType' )
3458 $mocks[
'primary3']->expects( $this->never() )->method(
'beginPrimaryAccountLink' );
3459 $mocks[
'primary3']->expects( $this->never() )->method(
'continuePrimaryAccountLink' );
3461 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
3462 $this->primaryauthMocks = [ $mocks[
'primary3'], $mocks[
'primary2'], $mocks[
'primary'] ];
3463 $this->logger = new \TestLogger(
true,
function ( $message, $level ) {
3464 return $level === LogLevel::DEBUG ? null : $message;
3468 $constraint = \PHPUnit_Framework_Assert::logicalOr(
3472 $providers = array_merge( $this->preauthMocks, $this->primaryauthMocks );
3473 foreach ( $providers
as $p ) {
3474 $p->postCalled =
false;
3475 $p->expects( $this->atMost( 1 ) )->method(
'postAccountLink' )
3477 $this->assertInstanceOf(
'User',
$user );
3478 $this->assertSame(
'UTSysop',
$user->getName() );
3480 $this->assertThat(
$response->status, $constraint );
3488 foreach ( $managerResponses
as $i =>
$response ) {
3492 $expectLog[] = [ LogLevel::INFO,
'Account linked to {user} by primary' ];
3498 $ret = $this->manager->beginAccountLink(
$user, [
$req ],
'http://localhost/' );
3500 $ret = $this->manager->continueAccountLink( [
$req ] );
3502 if (
$response instanceof \Exception ) {
3503 $this->fail(
'Expected exception not thrown',
"Response $i" );
3505 }
catch ( \Exception $ex ) {
3506 if ( !
$response instanceof \Exception ) {
3509 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
3510 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3511 "Response $i, exception, session state" );
3515 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
3518 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
3522 $this->assertNull( $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3523 "Response $i, session state" );
3524 foreach ( $providers
as $p ) {
3525 $this->assertSame(
$response->status, $p->postCalled,
3526 "Response $i, post-auth callback called" );
3529 $this->assertNotNull(
3530 $this->
request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3531 "Response $i, session state"
3533 foreach (
$ret->neededRequests
as $neededReq ) {
3535 "Response $i, neededRequest action" );
3537 $this->assertEquals(
3538 $ret->neededRequests,
3540 "Response $i, continuation check"
3542 foreach ( $providers
as $p ) {
3543 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
3550 $this->assertSame( $expectLog, $this->logger->getBuffer() );
3558 'Pre-link test fail in pre' => [
3565 'Failure in primary' => [
3572 'All primary abstain' => [
3581 'Primary UI, then redirect, then fail' => [
3590 'Primary redirect, then abstain' => [
3594 [
$req ],
'/foo.html', [
'foo' =>
'bar' ]
3600 new \DomainException(
3601 'MockPrimaryAuthenticationProvider::continuePrimaryAccountLink() returned ABSTAIN'
3605 'Primary UI, then pass' => [