254 $this->logger = new \Psr\Log\NullLogger();
255 $user = \User::newFromName(
'UTSysop' );
260 $mutableSession, [
'provideSessionInfo' ]
262 $provider->expects( $this->
any() )->method(
'provideSessionInfo' )
263 ->will( $this->returnCallback(
function () use ( $provider, &$provideUser ) {
264 return new SessionInfo( SessionInfo::MIN_PRIORITY, [
265 'provider' => $provider,
273 $this->config->set(
'ReauthenticateTime', [] );
274 $this->config->set(
'AllowSecuritySensitiveOperationIfCannotReauthenticate', [] );
275 $provideUser = new \User;
276 $session = $provider->getManager()->getSessionForRequest( $this->request );
277 $this->assertSame( 0, $session->getUser()->getId(),
'sanity check' );
280 $session->set(
'AuthManager:lastAuthId', 0 );
281 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
282 $this->assertSame( $reauth, $this->manager->securitySensitiveOperationStatus(
'foo' ) );
284 $provideUser =
$user;
285 $session = $provider->getManager()->getSessionForRequest( $this->request );
286 $this->assertSame( $user->getId(), $session->getUser()->getId(),
'sanity check' );
289 $session->set(
'AuthManager:lastAuthId', $user->getId() + 1 );
290 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
292 $this->manager->securitySensitiveOperationStatus(
'foo' );
293 $this->fail(
'Expected exception not thrown' );
294 }
catch ( \UnexpectedValueException $ex ) {
297 ?
'$wgReauthenticateTime lacks a default'
298 :
'$wgAllowSecuritySensitiveOperationIfCannotReauthenticate lacks a default',
303 if ( $mutableSession ) {
304 $this->config->set(
'ReauthenticateTime', [
311 $session->set(
'AuthManager:lastAuthId', $user->getId() + 1 );
312 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
324 $session->set(
'AuthManager:lastAuthId', $user->getId() );
325 $session->set(
'AuthManager:lastAuthTimestamp',
null );
337 $session->set(
'AuthManager:lastAuthTimestamp', time() - 5 );
343 $session->set(
'AuthManager:lastAuthTimestamp', time() - 20 );
352 $this->config->set(
'AllowSecuritySensitiveOperationIfCannotReauthenticate', [
371 ] as $hook => $expect ) {
372 $this->
hook(
'SecuritySensitiveOperationStatus', $this->exactly( 2 ) )
376 $this->callback(
function (
$s ) use ( $session ) {
377 return $s->getId() === $session->getId();
379 $mutableSession ? $this->equalTo( 500, 1 ) : $this->equalTo( -1 )
381 ->
will( $this->returnCallback(
function ( &$v ) use ( $hook ) {
385 $session->set(
'AuthManager:lastAuthTimestamp', time() - 500 );
387 $expect, $this->manager->securitySensitiveOperationStatus(
'test' ),
"hook $hook"
390 $expect, $this->manager->securitySensitiveOperationStatus(
'test2' ),
"hook $hook"
392 $this->
unhook(
'SecuritySensitiveOperationStatus' );
395 ScopedCallback::consume( $reset );
464 'pre' => $this->getMockForAbstractClass( PreAuthenticationProvider::class ),
465 'primary' => $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class ),
466 'secondary' => $this->getMockForAbstractClass( SecondaryAuthenticationProvider::class ),
468 foreach ( $mocks as $key => $mock ) {
469 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue( $key ) );
470 $mock->expects( $this->once() )->method(
'setLogger' );
471 $mock->expects( $this->once() )->method(
'setManager' );
472 $mock->expects( $this->once() )->method(
'setConfig' );
474 $this->preauthMocks = [ $mocks[
'pre'] ];
475 $this->primaryauthMocks = [ $mocks[
'primary'] ];
476 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
482 $this->managerPriv->getAuthenticationProvider(
'primary' )
486 $this->managerPriv->getAuthenticationProvider(
'secondary' )
490 $this->managerPriv->getAuthenticationProvider(
'pre' )
493 [
'pre' => $mocks[
'pre'] ],
494 $this->managerPriv->getPreAuthenticationProviders()
497 [
'primary' => $mocks[
'primary'] ],
498 $this->managerPriv->getPrimaryAuthenticationProviders()
501 [
'secondary' => $mocks[
'secondary'] ],
502 $this->managerPriv->getSecondaryAuthenticationProviders()
506 $mock1 = $this->getMockForAbstractClass( PreAuthenticationProvider::class );
507 $mock2 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
508 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
509 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
510 $this->preauthMocks = [ $mock1 ];
511 $this->primaryauthMocks = [ $mock2 ];
512 $this->secondaryauthMocks = [];
515 $this->managerPriv->getAuthenticationProvider(
'Y' );
516 $this->fail(
'Expected exception not thrown' );
517 }
catch ( \RuntimeException $ex ) {
518 $class1 = get_class( $mock1 );
519 $class2 = get_class( $mock2 );
521 "Duplicate specifications for id X (classes $class1 and $class2)", $ex->getMessage()
526 $mock = $this->getMockForAbstractClass( AuthenticationProvider::class );
527 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
528 $class = get_class( $mock );
529 $this->preauthMocks = [ $mock ];
530 $this->primaryauthMocks = [ $mock ];
531 $this->secondaryauthMocks = [ $mock ];
534 $this->managerPriv->getPreAuthenticationProviders();
535 $this->fail(
'Expected exception not thrown' );
536 }
catch ( \RuntimeException $ex ) {
538 "Expected instance of MediaWiki\\Auth\\PreAuthenticationProvider, got $class",
543 $this->managerPriv->getPrimaryAuthenticationProviders();
544 $this->fail(
'Expected exception not thrown' );
545 }
catch ( \RuntimeException $ex ) {
547 "Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got $class",
552 $this->managerPriv->getSecondaryAuthenticationProviders();
553 $this->fail(
'Expected exception not thrown' );
554 }
catch ( \RuntimeException $ex ) {
556 "Expected instance of MediaWiki\\Auth\\SecondaryAuthenticationProvider, got $class",
562 $mock1 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
563 $mock2 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
564 $mock3 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
565 $mock1->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'A' ) );
566 $mock2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
567 $mock3->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'C' ) );
568 $this->preauthMocks = [];
569 $this->primaryauthMocks = [ $mock1, $mock2, $mock3 ];
570 $this->secondaryauthMocks = [];
572 $config = $this->config->
get(
'AuthManagerConfig' );
576 [
'A' => $mock1,
'B' => $mock2,
'C' => $mock3 ],
577 $this->managerPriv->getPrimaryAuthenticationProviders(),
581 $config[
'primaryauth'][
'A'][
'sort'] = 100;
582 $config[
'primaryauth'][
'C'][
'sort'] = -1;
583 $this->config->set(
'AuthManagerConfig',
$config );
586 [
'C' => $mock3,
'B' => $mock2,
'A' => $mock1 ],
587 $this->managerPriv->getPrimaryAuthenticationProviders()
630 $mockA = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
631 $mockB = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
632 $mockB2 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
633 $mockA->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'A' ) );
634 $mockB->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
635 $mockB2->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'B' ) );
636 $this->primaryauthMocks = [ $mockA ];
638 $this->logger = new \TestLogger(
true );
642 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB ],
'testing' );
644 [
'B' => $mockB ], $this->managerPriv->getPrimaryAuthenticationProviders()
646 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'A' ) );
647 $this->assertSame( $mockB, $this->managerPriv->getAuthenticationProvider(
'B' ) );
649 [ LogLevel::WARNING,
'Overriding AuthManager primary authn because testing' ],
650 ], $this->logger->getBuffer() );
651 $this->logger->clearBuffer();
655 $this->assertSame( $mockA, $this->managerPriv->getAuthenticationProvider(
'A' ) );
656 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'B' ) );
657 $this->request->getSession()->setSecret(
'AuthManager::authnState',
'test' );
658 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
'test' );
659 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB ],
'testing' );
661 [
'B' => $mockB ], $this->managerPriv->getPrimaryAuthenticationProviders()
663 $this->assertSame(
null, $this->managerPriv->getAuthenticationProvider(
'A' ) );
664 $this->assertSame( $mockB, $this->managerPriv->getAuthenticationProvider(
'B' ) );
665 $this->assertNull( $this->request->getSession()->getSecret(
'AuthManager::authnState' ) );
667 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
670 [ LogLevel::WARNING,
'Overriding AuthManager primary authn because testing' ],
673 'PrimaryAuthenticationProviders have already been accessed! I hope nothing breaks.'
675 ], $this->logger->getBuffer() );
676 $this->logger->clearBuffer();
681 $this->manager->forcePrimaryAuthenticationProviders( [ $mockB, $mockB2 ],
'testing' );
682 $this->fail(
'Expected exception not thrown' );
683 }
catch ( \RuntimeException $ex ) {
684 $class1 = get_class( $mockB );
685 $class2 = get_class( $mockB2 );
687 "Duplicate specifications for id B (classes $class2 and $class1)", $ex->getMessage()
692 $mock = $this->getMockForAbstractClass( AuthenticationProvider::class );
693 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
694 $class = get_class( $mock );
696 $this->manager->forcePrimaryAuthenticationProviders( [ $mock ],
'testing' );
697 $this->fail(
'Expected exception not thrown' );
698 }
catch ( \RuntimeException $ex ) {
700 "Expected instance of MediaWiki\\Auth\\PrimaryAuthenticationProvider, got $class",
770 $user = \User::newFromName(
'UTSysop' );
771 $req1 = $this->createMock( AuthenticationRequest::class );
772 $req2 = $this->createMock( AuthenticationRequest::class );
773 $req3 = $this->createMock( AuthenticationRequest::class );
775 $userReq->username =
'UTDummy';
777 $req1->returnToUrl =
'http://localhost/';
778 $req2->returnToUrl =
'http://localhost/';
779 $req3->returnToUrl =
'http://localhost/';
780 $req3->username =
'UTDummy';
781 $userReq->returnToUrl =
'http://localhost/';
784 $primary = $this->getMockForAbstractClass( AbstractPrimaryAuthenticationProvider::class );
785 $this->primaryauthMocks = [ $primary ];
788 $res->createRequest = $req1;
789 $primary->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
790 ->will( $this->returnValue(
$res ) );
792 null, [ $req2->getUniqueId() => $req2 ]
794 $this->logger->setCollect(
true );
795 $ret = $this->manager->beginAuthentication( [ $createReq ],
'http://localhost/' );
796 $this->logger->setCollect(
false );
798 $this->assertInstanceOf( CreateFromLoginAuthenticationRequest::class,
$ret->createRequest );
799 $this->assertSame( $req1,
$ret->createRequest->createRequest );
800 $this->assertEquals( [ $req2->getUniqueId() => $req2 ],
$ret->createRequest->maybeLink );
803 $primary = $this->getMockBuilder( AbstractPrimaryAuthenticationProvider::class )
804 ->setMethods( [
'continuePrimaryAuthentication' ] )
805 ->getMockForAbstractClass();
806 $this->primaryauthMocks = [ $primary ];
808 $primary->expects( $this->
any() )->method(
'beginPrimaryAuthentication' )
809 ->will( $this->returnValue(
813 $res->createRequest = $req2;
814 $primary->expects( $this->
any() )->method(
'continuePrimaryAuthentication' )
815 ->will( $this->returnValue(
$res ) );
816 $this->logger->setCollect(
true );
817 $ret = $this->manager->beginAuthentication( [],
'http://localhost/' );
819 $ret = $this->manager->continueAuthentication( [] );
820 $this->logger->setCollect(
false );
822 $this->assertInstanceOf( CreateFromLoginAuthenticationRequest::class,
$ret->createRequest );
823 $this->assertSame( $req2,
$ret->createRequest->createRequest );
824 $this->assertEquals( [],
$ret->createRequest->maybeLink );
827 $primary = $this->getMockForAbstractClass( AbstractPrimaryAuthenticationProvider::class );
828 $this->primaryauthMocks = [ $primary ];
831 $createReq->returnToUrl =
'http://localhost/';
832 $createReq->username =
'UTDummy';
834 $primary->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )
835 ->with( $this->
anything(), $this->
anything(), [ $userReq, $createReq, $req3 ] )
836 ->will( $this->returnValue(
$res ) );
837 $primary->expects( $this->
any() )->method(
'accountCreationType' )
839 $this->logger->setCollect(
true );
840 $ret = $this->manager->beginAccountCreation(
841 $user, [ $userReq, $createReq ],
'http://localhost/'
843 $this->logger->setCollect(
false );
845 $state = $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' );
846 $this->assertNotNull( $state );
847 $this->assertEquals( [ $userReq, $createReq, $req3 ], $state[
'reqs'] );
848 $this->assertEquals( [ $req2 ], $state[
'maybeLink'] );
860 StatusValue $preResponse, array $primaryResponses, array $secondaryResponses,
861 array $managerResponses,
$link =
false
864 $user = \User::newFromName(
'UTSysop' );
865 $id = $user->getId();
866 $name = $user->getName();
870 $req->rememberMe = (bool)rand( 0, 1 );
871 $req->pre = $preResponse;
872 $req->primary = $primaryResponses;
873 $req->secondary = $secondaryResponses;
875 foreach ( [
'pre',
'primary',
'secondary' ] as $key ) {
876 $class = ucfirst( $key ) .
'AuthenticationProvider';
877 $mocks[$key] = $this->getMockForAbstractClass(
878 "MediaWiki\\Auth\\$class", [],
"Mock$class"
880 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
881 ->will( $this->returnValue( $key ) );
882 $mocks[$key .
'2'] = $this->getMockForAbstractClass(
"MediaWiki\\Auth\\$class" );
883 $mocks[$key .
'2']->expects( $this->
any() )->method(
'getUniqueId' )
884 ->will( $this->returnValue( $key .
'2' ) );
885 $mocks[$key .
'3'] = $this->getMockForAbstractClass(
"MediaWiki\\Auth\\$class" );
886 $mocks[$key .
'3']->expects( $this->
any() )->method(
'getUniqueId' )
887 ->will( $this->returnValue( $key .
'3' ) );
889 foreach ( $mocks as $mock ) {
890 $mock->expects( $this->
any() )->method(
'getAuthenticationRequests' )
891 ->will( $this->returnValue( [] ) );
894 $mocks[
'pre']->expects( $this->once() )->method(
'testForAuthentication' )
895 ->will( $this->returnCallback(
function ( $reqs ) use (
$req ) {
896 $this->assertContains(
$req, $reqs );
900 $ct = count(
$req->primary );
901 $callback = $this->returnCallback(
function ( $reqs ) use (
$req ) {
902 $this->assertContains(
$req, $reqs );
903 return array_shift(
$req->primary );
905 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
906 ->method(
'beginPrimaryAuthentication' )
908 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
909 ->method(
'continuePrimaryAuthentication' )
912 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
916 $ct = count(
$req->secondary );
917 $callback = $this->returnCallback(
function ( $user, $reqs ) use ( $id, $name,
$req ) {
918 $this->assertSame( $id, $user->getId() );
919 $this->assertSame( $name, $user->getName() );
920 $this->assertContains(
$req, $reqs );
921 return array_shift(
$req->secondary );
923 $mocks[
'secondary']->expects( $this->exactly( min( 1, $ct ) ) )
924 ->method(
'beginSecondaryAuthentication' )
926 $mocks[
'secondary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
927 ->method(
'continueSecondaryAuthentication' )
931 $mocks[
'pre2']->expects( $this->atMost( 1 ) )->method(
'testForAuthentication' )
932 ->will( $this->returnValue( StatusValue::newGood() ) );
933 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAuthentication' )
934 ->will( $this->returnValue( $abstain ) );
935 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAuthentication' );
936 $mocks[
'secondary2']->expects( $this->atMost( 1 ) )->method(
'beginSecondaryAuthentication' )
937 ->will( $this->returnValue( $abstain ) );
938 $mocks[
'secondary2']->expects( $this->never() )->method(
'continueSecondaryAuthentication' );
939 $mocks[
'secondary3']->expects( $this->atMost( 1 ) )->method(
'beginSecondaryAuthentication' )
940 ->will( $this->returnValue( $abstain ) );
941 $mocks[
'secondary3']->expects( $this->never() )->method(
'continueSecondaryAuthentication' );
943 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
944 $this->primaryauthMocks = [ $mocks[
'primary'], $mocks[
'primary2'] ];
945 $this->secondaryauthMocks = [
946 $mocks[
'secondary3'], $mocks[
'secondary'], $mocks[
'secondary2'],
951 $this->logger->setCollect(
true );
953 $constraint = \PHPUnit_Framework_Assert::logicalOr(
957 $providers = array_filter(
959 $this->preauthMocks, $this->primaryauthMocks, $this->secondaryauthMocks
962 return is_callable( [ $p,
'expects' ] );
965 foreach ( $providers as $p ) {
966 $p->postCalled =
false;
967 $p->expects( $this->atMost( 1 ) )->method(
'postAuthentication' )
968 ->willReturnCallback(
function ( $user,
$response )
use ( $constraint, $p ) {
969 if ( $user !==
null ) {
970 $this->assertInstanceOf( \User::class, $user );
971 $this->assertSame(
'UTSysop', $user->getName() );
973 $this->assertInstanceOf( AuthenticationResponse::class,
$response );
974 $this->assertThat(
$response->status, $constraint );
979 $session = $this->request->getSession();
980 $session->setRememberUser( !
$req->rememberMe );
982 foreach ( $managerResponses as $i =>
$response ) {
986 $this->
hook(
'UserLoggedIn', $this->once() )
987 ->with( $this->callback(
function ( $user ) use ( $id, $name ) {
988 return $user->getId() === $id && $user->getName() === $name;
991 $this->
hook(
'UserLoggedIn', $this->never() );
996 $response->message->getKey() !==
'authmanager-authn-not-in-progress' &&
997 $response->message->getKey() !==
'authmanager-authn-no-primary'
1000 $this->
hook(
'AuthManagerLoginAuthenticateAudit', $this->once() );
1002 $this->
hook(
'AuthManagerLoginAuthenticateAudit', $this->never() );
1008 $ret = $this->manager->beginAuthentication( [
$req ],
'http://localhost/' );
1010 $ret = $this->manager->continueAuthentication( [
$req ] );
1012 if (
$response instanceof \Exception ) {
1013 $this->fail(
'Expected exception not thrown',
"Response $i" );
1015 }
catch ( \Exception $ex ) {
1016 if ( !
$response instanceof \Exception ) {
1019 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
1020 $this->assertNull( $session->getSecret(
'AuthManager::authnState' ),
1021 "Response $i, exception, session state" );
1022 $this->
unhook(
'UserLoggedIn' );
1023 $this->
unhook(
'AuthManagerLoginAuthenticateAudit' );
1027 $this->
unhook(
'UserLoggedIn' );
1028 $this->
unhook(
'AuthManagerLoginAuthenticateAudit' );
1030 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
1033 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
1035 $this->assertSame( $id, $session->getUser()->getId(),
1036 "Response $i, authn" );
1038 $this->assertSame( 0, $session->getUser()->getId(),
1039 "Response $i, authn" );
1042 $this->assertNull( $session->getSecret(
'AuthManager::authnState' ),
1043 "Response $i, session state" );
1044 foreach ( $providers as $p ) {
1045 $this->assertSame(
$response->status, $p->postCalled,
1046 "Response $i, post-auth callback called" );
1049 $this->assertNotNull( $session->getSecret(
'AuthManager::authnState' ),
1050 "Response $i, session state" );
1051 foreach (
$ret->neededRequests as $neededReq ) {
1053 "Response $i, neededRequest action" );
1055 $this->assertEquals(
1056 $ret->neededRequests,
1058 "Response $i, continuation check"
1060 foreach ( $providers as $p ) {
1061 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
1065 $state = $session->getSecret(
'AuthManager::authnState' );
1066 $maybeLink = isset( $state[
'maybeLink'] ) ? $state[
'maybeLink'] : [];
1068 $this->assertEquals(
1071 "Response $i, maybeLink"
1074 $this->assertEquals( [], $maybeLink,
"Response $i, maybeLink" );
1079 $this->assertSame(
$req->rememberMe, $session->shouldRememberUser(),
1080 'rememberMe checkbox had effect' );
1082 $this->assertNotSame(
$req->rememberMe, $session->shouldRememberUser(),
1083 'rememberMe checkbox wasn\'t applied' );
1554 $creator = \User::newFromName(
'UTSysop' );
1556 $this->logger = new \TestLogger(
false,
function ( $message, $level ) {
1557 return $level === LogLevel::DEBUG ? null : $message;
1561 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
'test' );
1562 $this->
hook(
'LocalUserCreated', $this->never() );
1564 $this->manager->beginAccountCreation(
1565 $creator, [],
'http://localhost/'
1567 $this->fail(
'Expected exception not thrown' );
1568 }
catch ( \LogicException $ex ) {
1569 $this->assertEquals(
'Account creation is not possible', $ex->getMessage() );
1571 $this->
unhook(
'LocalUserCreated' );
1573 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1576 $mock = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
1577 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1578 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1580 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
true ) );
1581 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1582 ->will( $this->returnValue( StatusValue::newGood() ) );
1583 $this->primaryauthMocks = [ $mock ];
1586 $this->
hook(
'LocalUserCreated', $this->never() );
1587 $ret = $this->manager->beginAccountCreation( $creator, [],
'http://localhost/' );
1588 $this->
unhook(
'LocalUserCreated' );
1590 $this->assertSame(
'noname',
$ret->message->getKey() );
1592 $this->
hook(
'LocalUserCreated', $this->never() );
1595 $userReq2->username = $userReq->username .
'X';
1596 $ret = $this->manager->beginAccountCreation(
1597 $creator, [ $userReq, $userReq2 ],
'http://localhost/'
1599 $this->
unhook(
'LocalUserCreated' );
1601 $this->assertSame(
'noname',
$ret->message->getKey() );
1603 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
1604 $readOnlyMode->setReason(
'Because' );
1605 $this->
hook(
'LocalUserCreated', $this->never() );
1607 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1608 $this->
unhook(
'LocalUserCreated' );
1610 $this->assertSame(
'readonlytext',
$ret->message->getKey() );
1611 $this->assertSame( [
'Because' ],
$ret->message->getParams() );
1612 $readOnlyMode->setReason(
false );
1614 $this->
hook(
'LocalUserCreated', $this->never() );
1616 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1617 $this->
unhook(
'LocalUserCreated' );
1619 $this->assertSame(
'userexists',
$ret->message->getKey() );
1621 $mock = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
1622 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1623 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1625 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1626 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1627 ->will( $this->returnValue( StatusValue::newFatal(
'fail' ) ) );
1628 $this->primaryauthMocks = [ $mock ];
1631 $this->
hook(
'LocalUserCreated', $this->never() );
1633 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1634 $this->
unhook(
'LocalUserCreated' );
1636 $this->assertSame(
'fail',
$ret->message->getKey() );
1638 $mock = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
1639 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1640 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1642 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1643 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1644 ->will( $this->returnValue( StatusValue::newGood() ) );
1645 $this->primaryauthMocks = [ $mock ];
1648 $this->
hook(
'LocalUserCreated', $this->never() );
1650 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1651 $this->
unhook(
'LocalUserCreated' );
1653 $this->assertSame(
'noname',
$ret->message->getKey() );
1655 $this->
hook(
'LocalUserCreated', $this->never() );
1656 $userReq->username = $creator->getName();
1657 $ret = $this->manager->beginAccountCreation( $creator, [ $userReq ],
'http://localhost/' );
1658 $this->
unhook(
'LocalUserCreated' );
1660 $this->assertSame(
'userexists',
$ret->message->getKey() );
1662 $mock = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
1663 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1664 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1666 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1667 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
1668 ->will( $this->returnValue( StatusValue::newGood() ) );
1669 $mock->expects( $this->
any() )->method(
'testForAccountCreation' )
1670 ->will( $this->returnValue( StatusValue::newFatal(
'fail' ) ) );
1671 $this->primaryauthMocks = [ $mock ];
1674 $req = $this->getMockBuilder( UserDataAuthenticationRequest::class )
1675 ->setMethods( [
'populateUser' ] )
1677 $req->expects( $this->
any() )->method(
'populateUser' )
1678 ->willReturn( \StatusValue::newFatal(
'populatefail' ) );
1680 $ret = $this->manager->beginAccountCreation(
1681 $creator, [ $userReq,
$req ],
'http://localhost/'
1684 $this->assertSame(
'populatefail',
$ret->message->getKey() );
1689 $ret = $this->manager->beginAccountCreation(
1690 $creator, [ $userReq,
$req ],
'http://localhost/'
1693 $this->assertSame(
'fail',
$ret->message->getKey() );
1695 $this->manager->beginAccountCreation(
1699 $this->assertSame(
'fail',
$ret->message->getKey() );
1703 $creator = \User::newFromName(
'UTSysop' );
1705 $this->logger = new \TestLogger(
false,
function ( $message, $level ) {
1706 return $level === LogLevel::DEBUG ? null : $message;
1717 'primaryResponse' =>
null,
1719 'ranPreTests' =>
true,
1722 $this->
hook(
'LocalUserCreated', $this->never() );
1724 $this->manager->continueAccountCreation( [] );
1725 $this->fail(
'Expected exception not thrown' );
1726 }
catch ( \LogicException $ex ) {
1727 $this->assertEquals(
'Account creation is not possible', $ex->getMessage() );
1729 $this->
unhook(
'LocalUserCreated' );
1731 $mock = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
1732 $mock->expects( $this->
any() )->method(
'getUniqueId' )->will( $this->returnValue(
'X' ) );
1733 $mock->expects( $this->
any() )->method(
'accountCreationType' )
1735 $mock->expects( $this->
any() )->method(
'testUserExists' )->will( $this->returnValue(
false ) );
1736 $mock->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )->will(
1739 $this->primaryauthMocks = [ $mock ];
1742 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
null );
1743 $this->
hook(
'LocalUserCreated', $this->never() );
1744 $ret = $this->manager->continueAccountCreation( [] );
1745 $this->
unhook(
'LocalUserCreated' );
1747 $this->assertSame(
'authmanager-create-not-in-progress',
$ret->message->getKey() );
1749 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1750 [
'username' =>
"$username<>" ] + $session );
1751 $this->
hook(
'LocalUserCreated', $this->never() );
1752 $ret = $this->manager->continueAccountCreation( [] );
1753 $this->
unhook(
'LocalUserCreated' );
1755 $this->assertSame(
'noname',
$ret->message->getKey() );
1757 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1760 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState', $session );
1761 $this->
hook(
'LocalUserCreated', $this->never() );
1762 $cache = \ObjectCache::getLocalClusterInstance();
1764 $ret = $this->manager->continueAccountCreation( [] );
1766 $this->
unhook(
'LocalUserCreated' );
1768 $this->assertSame(
'usernameinprogress',
$ret->message->getKey() );
1772 $session, $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1775 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1776 [
'username' => $creator->getName() ] + $session );
1777 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
1778 $readOnlyMode->setReason(
'Because' );
1779 $this->
hook(
'LocalUserCreated', $this->never() );
1780 $ret = $this->manager->continueAccountCreation( [] );
1781 $this->
unhook(
'LocalUserCreated' );
1783 $this->assertSame(
'readonlytext',
$ret->message->getKey() );
1784 $this->assertSame( [
'Because' ],
$ret->message->getParams() );
1785 $readOnlyMode->setReason(
false );
1787 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1788 [
'username' => $creator->getName() ] + $session );
1789 $this->
hook(
'LocalUserCreated', $this->never() );
1790 $ret = $this->manager->continueAccountCreation( [] );
1791 $this->
unhook(
'LocalUserCreated' );
1793 $this->assertSame(
'userexists',
$ret->message->getKey() );
1795 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1798 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1799 [
'userid' => $creator->getId() ] + $session );
1800 $this->
hook(
'LocalUserCreated', $this->never() );
1802 $ret = $this->manager->continueAccountCreation( [] );
1803 $this->fail(
'Expected exception not thrown' );
1804 }
catch ( \UnexpectedValueException $ex ) {
1805 $this->assertEquals(
"User \"{$username}\" should exist now, but doesn't!", $ex->getMessage() );
1807 $this->
unhook(
'LocalUserCreated' );
1809 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1812 $id = $creator->getId();
1813 $name = $creator->getName();
1814 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1815 [
'username' => $name,
'userid' => $id + 1 ] + $session );
1816 $this->
hook(
'LocalUserCreated', $this->never() );
1818 $ret = $this->manager->continueAccountCreation( [] );
1819 $this->fail(
'Expected exception not thrown' );
1820 }
catch ( \UnexpectedValueException $ex ) {
1821 $this->assertEquals(
1822 "User \"{$name}\" exists, but ID $id != " . ( $id + 1 ) .
'!', $ex->getMessage()
1825 $this->
unhook(
'LocalUserCreated' );
1827 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1830 $req = $this->getMockBuilder( UserDataAuthenticationRequest::class )
1831 ->setMethods( [
'populateUser' ] )
1833 $req->expects( $this->
any() )->method(
'populateUser' )
1834 ->willReturn( \StatusValue::newFatal(
'populatefail' ) );
1835 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState',
1836 [
'reqs' => [
$req ] ] + $session );
1837 $ret = $this->manager->continueAccountCreation( [] );
1839 $this->assertSame(
'populatefail',
$ret->message->getKey() );
1841 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' )
1855 StatusValue $preTest, $primaryTest, $secondaryTest,
1856 array $primaryResponses, array $secondaryResponses, array $managerResponses
1858 $creator = \User::newFromName(
'UTSysop' );
1864 $req = $this->getMockForAbstractClass( AuthenticationRequest::class );
1865 $req->preTest = $preTest;
1866 $req->primaryTest = $primaryTest;
1867 $req->secondaryTest = $secondaryTest;
1868 $req->primary = $primaryResponses;
1869 $req->secondary = $secondaryResponses;
1871 foreach ( [
'pre',
'primary',
'secondary' ] as $key ) {
1872 $class = ucfirst( $key ) .
'AuthenticationProvider';
1873 $mocks[$key] = $this->getMockForAbstractClass(
1874 "MediaWiki\\Auth\\$class", [],
"Mock$class"
1876 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
1877 ->will( $this->returnValue( $key ) );
1878 $mocks[$key]->expects( $this->
any() )->method(
'testUserForCreation' )
1879 ->will( $this->returnValue( StatusValue::newGood() ) );
1880 $mocks[$key]->expects( $this->
any() )->method(
'testForAccountCreation' )
1881 ->will( $this->returnCallback(
1882 function ( $user, $creatorIn, $reqs )
1885 $this->assertSame(
$username, $user->getName() );
1886 $this->assertSame( $creator->getId(), $creatorIn->getId() );
1887 $this->assertSame( $creator->getName(), $creatorIn->getName() );
1889 foreach ( $reqs as $r ) {
1890 $this->assertSame(
$username, $r->username );
1891 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1893 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1899 for ( $i = 2; $i <= 3; $i++ ) {
1900 $mocks[$key . $i] = $this->getMockForAbstractClass(
"MediaWiki\\Auth\\$class" );
1901 $mocks[$key . $i]->expects( $this->
any() )->method(
'getUniqueId' )
1902 ->will( $this->returnValue( $key . $i ) );
1903 $mocks[$key . $i]->expects( $this->
any() )->method(
'testUserForCreation' )
1904 ->will( $this->returnValue( StatusValue::newGood() ) );
1905 $mocks[$key . $i]->expects( $this->atMost( 1 ) )->method(
'testForAccountCreation' )
1906 ->will( $this->returnValue( StatusValue::newGood() ) );
1910 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
1912 $mocks[
'primary']->expects( $this->
any() )->method(
'testUserExists' )
1913 ->will( $this->returnValue(
false ) );
1914 $ct = count(
$req->primary );
1915 $callback = $this->returnCallback(
function ( $user, $creator, $reqs ) use (
$username,
$req ) {
1916 $this->assertSame(
$username, $user->getName() );
1917 $this->assertSame(
'UTSysop', $creator->getName() );
1919 foreach ( $reqs as $r ) {
1920 $this->assertSame(
$username, $r->username );
1921 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1923 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1924 return array_shift(
$req->primary );
1926 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
1927 ->method(
'beginPrimaryAccountCreation' )
1928 ->will( $callback );
1929 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
1930 ->method(
'continuePrimaryAccountCreation' )
1931 ->will( $callback );
1933 $ct = count(
$req->secondary );
1934 $callback = $this->returnCallback(
function ( $user, $creator, $reqs ) use (
$username,
$req ) {
1935 $this->assertSame(
$username, $user->getName() );
1936 $this->assertSame(
'UTSysop', $creator->getName() );
1938 foreach ( $reqs as $r ) {
1939 $this->assertSame(
$username, $r->username );
1940 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
1942 $this->assertTrue( $foundReq,
'$reqs contains $req' );
1943 return array_shift(
$req->secondary );
1945 $mocks[
'secondary']->expects( $this->exactly( min( 1, $ct ) ) )
1946 ->method(
'beginSecondaryAccountCreation' )
1947 ->will( $callback );
1948 $mocks[
'secondary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
1949 ->method(
'continueSecondaryAccountCreation' )
1950 ->will( $callback );
1953 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
1955 $mocks[
'primary2']->expects( $this->
any() )->method(
'testUserExists' )
1956 ->will( $this->returnValue(
false ) );
1957 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAccountCreation' )
1958 ->will( $this->returnValue( $abstain ) );
1959 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAccountCreation' );
1960 $mocks[
'primary3']->expects( $this->
any() )->method(
'accountCreationType' )
1962 $mocks[
'primary3']->expects( $this->
any() )->method(
'testUserExists' )
1963 ->will( $this->returnValue(
false ) );
1964 $mocks[
'primary3']->expects( $this->never() )->method(
'beginPrimaryAccountCreation' );
1965 $mocks[
'primary3']->expects( $this->never() )->method(
'continuePrimaryAccountCreation' );
1966 $mocks[
'secondary2']->expects( $this->atMost( 1 ) )
1967 ->method(
'beginSecondaryAccountCreation' )
1968 ->will( $this->returnValue( $abstain ) );
1969 $mocks[
'secondary2']->expects( $this->never() )->method(
'continueSecondaryAccountCreation' );
1970 $mocks[
'secondary3']->expects( $this->atMost( 1 ) )
1971 ->method(
'beginSecondaryAccountCreation' )
1972 ->will( $this->returnValue( $abstain ) );
1973 $mocks[
'secondary3']->expects( $this->never() )->method(
'continueSecondaryAccountCreation' );
1975 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
1976 $this->primaryauthMocks = [ $mocks[
'primary3'], $mocks[
'primary'], $mocks[
'primary2'] ];
1977 $this->secondaryauthMocks = [
1978 $mocks[
'secondary3'], $mocks[
'secondary'], $mocks[
'secondary2']
1981 $this->logger = new \TestLogger(
true,
function ( $message, $level ) {
1982 return $level === LogLevel::DEBUG ? null : $message;
1987 $constraint = \PHPUnit_Framework_Assert::logicalOr(
1991 $providers = array_merge(
1992 $this->preauthMocks, $this->primaryauthMocks, $this->secondaryauthMocks
1994 foreach ( $providers as $p ) {
1995 $p->postCalled =
false;
1996 $p->expects( $this->atMost( 1 ) )->method(
'postAccountCreation' )
1997 ->willReturnCallback(
function ( $user, $creator,
$response )
2000 $this->assertInstanceOf( \User::class, $user );
2001 $this->assertSame(
$username, $user->getName() );
2002 $this->assertSame(
'UTSysop', $creator->getName() );
2003 $this->assertInstanceOf( AuthenticationResponse::class,
$response );
2004 $this->assertThat(
$response->status, $constraint );
2011 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2015 foreach ( $managerResponses as $i =>
$response ) {
2018 if ( $i ===
'created' ) {
2020 $this->
hook(
'LocalUserCreated', $this->once() )
2022 $this->callback(
function ( $user ) use (
$username ) {
2025 $this->equalTo(
false )
2027 $expectLog[] = [ LogLevel::INFO,
"Creating user {user} during account creation" ];
2029 $this->
hook(
'LocalUserCreated', $this->never() );
2037 $ret = $this->manager->beginAccountCreation(
2038 $creator, [ $userReq,
$req ],
'http://localhost/'
2041 $ret = $this->manager->continueAccountCreation( [
$req ] );
2043 if (
$response instanceof \Exception ) {
2044 $this->fail(
'Expected exception not thrown',
"Response $i" );
2046 }
catch ( \Exception $ex ) {
2047 if ( !
$response instanceof \Exception ) {
2050 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
2052 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2053 "Response $i, exception, session state"
2055 $this->
unhook(
'LocalUserCreated' );
2059 $this->
unhook(
'LocalUserCreated' );
2061 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
2064 $this->assertNotNull(
$ret->loginRequest,
"Response $i, login marker" );
2065 $this->assertContains(
2066 $ret->loginRequest, $this->managerPriv->createdAccountAuthenticationRequests,
2067 "Response $i, login marker"
2072 "MediaWiki\Auth\AuthManager::continueAccountCreation: Account creation succeeded for {user}"
2080 $this->assertNull(
$ret->loginRequest,
"Response $i, login marker" );
2081 $this->assertSame( [], $this->managerPriv->createdAccountAuthenticationRequests,
2082 "Response $i, login marker" );
2085 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
2088 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2089 "Response $i, session state"
2091 foreach ( $providers as $p ) {
2092 $this->assertSame(
$response->status, $p->postCalled,
2093 "Response $i, post-auth callback called" );
2096 $this->assertNotNull(
2097 $this->request->getSession()->getSecret(
'AuthManager::accountCreationState' ),
2098 "Response $i, session state"
2100 foreach (
$ret->neededRequests as $neededReq ) {
2102 "Response $i, neededRequest action" );
2104 $this->assertEquals(
2105 $ret->neededRequests,
2107 "Response $i, continuation check"
2109 foreach ( $providers as $p ) {
2110 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
2123 $this->assertSame( $expectLog, $this->logger->getBuffer() );
2127 $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] )
2257 $creator = $isAnon ? new \User : \User::newFromName(
'UTSysop' );
2263 $mock = $this->getMockForAbstractClass(
2264 \
MediaWiki\Auth\PrimaryAuthenticationProvider::class, []
2266 $mock->expects( $this->
any() )->method(
'getUniqueId' )
2267 ->will( $this->returnValue(
'primary' ) );
2268 $mock->expects( $this->
any() )->method(
'testUserForCreation' )
2269 ->will( $this->returnValue( StatusValue::newGood() ) );
2270 $mock->expects( $this->
any() )->method(
'testForAccountCreation' )
2271 ->will( $this->returnValue( StatusValue::newGood() ) );
2272 $mock->expects( $this->
any() )->method(
'accountCreationType' )
2274 $mock->expects( $this->
any() )->method(
'testUserExists' )
2275 ->will( $this->returnValue(
false ) );
2276 $mock->expects( $this->
any() )->method(
'beginPrimaryAccountCreation' )
2278 $mock->expects( $this->
any() )->method(
'finishAccountCreation' )
2279 ->will( $this->returnValue( $logSubtype ) );
2281 $this->primaryauthMocks = [ $mock ];
2283 $this->logger->setCollect(
true );
2285 $this->config->set(
'NewUserLog',
true );
2288 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2293 $reasonReq->reason = $this->toString();
2294 $ret = $this->manager->beginAccountCreation(
2295 $creator, [ $userReq, $reasonReq ],
'http://localhost/'
2300 $user = \User::newFromName(
$username );
2301 $this->assertNotEquals( 0, $user->getId(),
'sanity check' );
2302 $this->assertNotEquals( $creator->getId(), $user->getId(),
'sanity check' );
2304 $data = \DatabaseLogEntry::getSelectQueryData();
2305 $rows = iterator_to_array( $dbw->select(
2309 'log_id > ' . (
int)$maxLogId,
2310 'log_type' =>
'newusers'
2316 $this->assertCount( 1,
$rows );
2317 $entry = \DatabaseLogEntry::newFromRow( reset(
$rows ) );
2319 $this->assertSame( $logSubtype ?: ( $isAnon ?
'create' :
'create2' ), $entry->getSubtype() );
2321 $isAnon ? $user->getId() : $creator->getId(),
2322 $entry->getPerformer()->getId()
2325 $isAnon ? $user->getName() : $creator->getName(),
2326 $entry->getPerformer()->getName()
2328 $this->assertSame( $user->getUserPage()->getFullText(), $entry->getTarget()->getFullText() );
2329 $this->assertSame( [
'4::userid' => $user->getId() ], $entry->getParameters() );
2330 $this->assertSame( $this->toString(), $entry->getComment() );
2349 $workaroundPHPUnitBug =
false;
2355 $wgGroupPermissions[
'*'][
'createaccount'] =
true;
2359 $this->
setMwGlobals( [
'wgMainCacheType' => __METHOD__ ] );
2363 foreach ( [
'pre',
'primary',
'secondary' ] as $key ) {
2364 $class = ucfirst( $key ) .
'AuthenticationProvider';
2365 $mocks[$key] = $this->getMockForAbstractClass(
"MediaWiki\\Auth\\$class" );
2366 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
2367 ->will( $this->returnValue( $key ) );
2370 $good = StatusValue::newGood();
2371 $callback = $this->callback(
function ( $user ) use ( &
$username, &$workaroundPHPUnitBug ) {
2372 return $workaroundPHPUnitBug || $user->getName() ===
$username;
2375 $mocks[
'pre']->expects( $this->exactly( 12 ) )->method(
'testUserForCreation' )
2377 ->will( $this->onConsecutiveCalls(
2378 StatusValue::newFatal(
'ok' ), StatusValue::newFatal(
'ok' ),
2379 StatusValue::newFatal(
'fail-in-pre' ), $good, $good,
2387 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
2389 $mocks[
'primary']->expects( $this->
any() )->method(
'testUserExists' )
2390 ->will( $this->returnValue(
true ) );
2391 $mocks[
'primary']->expects( $this->exactly( 9 ) )->method(
'testUserForCreation' )
2393 ->will( $this->onConsecutiveCalls(
2394 StatusValue::newFatal(
'fail-in-primary' ), $good,
2401 $mocks[
'primary']->expects( $this->exactly( 3 ) )->method(
'autoCreatedAccount' )
2404 $mocks[
'secondary']->expects( $this->exactly( 8 ) )->method(
'testUserForCreation' )
2406 ->will( $this->onConsecutiveCalls(
2407 StatusValue::newFatal(
'fail-in-secondary' ),
2414 $mocks[
'secondary']->expects( $this->exactly( 3 ) )->method(
'autoCreatedAccount' )
2417 $this->preauthMocks = [ $mocks[
'pre'] ];
2418 $this->primaryauthMocks = [ $mocks[
'primary'] ];
2419 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
2421 $session = $this->request->getSession();
2423 $logger = new \TestLogger(
true,
function ( $m ) {
2424 $m = str_replace(
'MediaWiki\\Auth\\AuthManager::autoCreateUser: ',
'', $m );
2427 $this->manager->setLogger(
$logger );
2430 $user = \User::newFromName(
'UTSysop' );
2431 $this->manager->autoCreateUser( $user,
'InvalidSource',
true );
2432 $this->fail(
'Expected exception not thrown' );
2433 }
catch ( \InvalidArgumentException $ex ) {
2434 $this->assertSame(
'Unknown auto-creation source: InvalidSource', $ex->getMessage() );
2439 $user = \User::newFromName(
'UTSysop' );
2440 $this->
hook(
'LocalUserCreated', $this->never() );
2442 $this->
unhook(
'LocalUserCreated' );
2443 $expect = \Status::newGood();
2444 $expect->warning(
'userexists' );
2445 $this->assertEquals( $expect,
$ret );
2446 $this->assertNotEquals( 0, $user->getId() );
2447 $this->assertSame(
'UTSysop', $user->getName() );
2448 $this->assertEquals( $user->getId(), $session->getUser()->getId() );
2449 $this->assertSame( [
2450 [ LogLevel::DEBUG,
'{username} already exists locally' ],
2455 $user = \User::newFromName(
'UTSysop' );
2456 $this->
hook(
'LocalUserCreated', $this->never() );
2458 $this->
unhook(
'LocalUserCreated' );
2459 $expect = \Status::newGood();
2460 $expect->warning(
'userexists' );
2461 $this->assertEquals( $expect,
$ret );
2462 $this->assertNotEquals( 0, $user->getId() );
2463 $this->assertSame(
'UTSysop', $user->getName() );
2464 $this->assertEquals( 0, $session->getUser()->getId() );
2465 $this->assertSame( [
2466 [ LogLevel::DEBUG,
'{username} already exists locally' ],
2472 $readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
2473 $readOnlyMode->setReason(
'Because' );
2474 $user = \User::newFromName(
$username );
2475 $this->
hook(
'LocalUserCreated', $this->never() );
2477 $this->
unhook(
'LocalUserCreated' );
2478 $this->assertEquals( \Status::newFatal(
wfMessage(
'readonlytext',
'Because' ) ),
$ret );
2479 $this->assertEquals( 0, $user->getId() );
2480 $this->assertNotEquals(
$username, $user->getName() );
2481 $this->assertEquals( 0, $session->getUser()->getId() );
2482 $this->assertSame( [
2483 [ LogLevel::DEBUG,
'denied by wfReadOnly(): {reason}' ],
2486 $readOnlyMode->setReason(
false );
2490 $session->set(
'AuthManager::AutoCreateBlacklist',
'test' );
2491 $user = \User::newFromName(
$username );
2492 $this->
hook(
'LocalUserCreated', $this->never() );
2494 $this->
unhook(
'LocalUserCreated' );
2495 $this->assertEquals( \Status::newFatal(
'test' ),
$ret );
2496 $this->assertEquals( 0, $user->getId() );
2497 $this->assertNotEquals(
$username, $user->getName() );
2498 $this->assertEquals( 0, $session->getUser()->getId() );
2499 $this->assertSame( [
2500 [ LogLevel::DEBUG,
'blacklisted in session {sessionid}' ],
2505 $session->set(
'AuthManager::AutoCreateBlacklist', StatusValue::newFatal(
'test2' ) );
2506 $user = \User::newFromName(
$username );
2507 $this->
hook(
'LocalUserCreated', $this->never() );
2509 $this->
unhook(
'LocalUserCreated' );
2510 $this->assertEquals( \Status::newFatal(
'test2' ),
$ret );
2511 $this->assertEquals( 0, $user->getId() );
2512 $this->assertNotEquals(
$username, $user->getName() );
2513 $this->assertEquals( 0, $session->getUser()->getId() );
2514 $this->assertSame( [
2515 [ LogLevel::DEBUG,
'blacklisted in session {sessionid}' ],
2521 $user = \User::newFromName(
$username .
'@' );
2522 $this->
hook(
'LocalUserCreated', $this->never() );
2524 $this->
unhook(
'LocalUserCreated' );
2525 $this->assertEquals( \Status::newFatal(
'noname' ),
$ret );
2526 $this->assertEquals( 0, $user->getId() );
2527 $this->assertNotEquals(
$username .
'@', $user->getId() );
2528 $this->assertEquals( 0, $session->getUser()->getId() );
2529 $this->assertSame( [
2530 [ LogLevel::DEBUG,
'name "{username}" is not creatable' ],
2533 $this->assertSame(
'noname', $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2539 $user = \User::newFromName(
$username );
2540 $this->
hook(
'LocalUserCreated', $this->never() );
2542 $this->
unhook(
'LocalUserCreated' );
2543 $this->assertEquals( \Status::newFatal(
'authmanager-autocreate-noperm' ),
$ret );
2544 $this->assertEquals( 0, $user->getId() );
2545 $this->assertNotEquals(
$username, $user->getName() );
2546 $this->assertEquals( 0, $session->getUser()->getId() );
2547 $this->assertSame( [
2548 [ LogLevel::DEBUG,
'IP lacks the ability to create or autocreate accounts' ],
2552 'authmanager-autocreate-noperm', $session->get(
'AuthManager::AutoCreateBlacklist' )
2560 $user = \User::newFromName(
$username );
2561 $this->
hook(
'LocalUserCreated', $this->never() );
2563 $this->
unhook(
'LocalUserCreated' );
2564 $this->assertEquals( \Status::newFatal(
'ok' ),
$ret );
2569 $user = \User::newFromName(
$username );
2570 $this->
hook(
'LocalUserCreated', $this->never() );
2572 $this->
unhook(
'LocalUserCreated' );
2573 $this->assertEquals( \Status::newFatal(
'ok' ),
$ret );
2578 $user = \User::newFromName(
$username );
2579 $this->
hook(
'LocalUserCreated', $this->never() );
2580 $cache = \ObjectCache::getLocalClusterInstance();
2584 $this->
unhook(
'LocalUserCreated' );
2585 $this->assertEquals( \Status::newFatal(
'usernameinprogress' ),
$ret );
2586 $this->assertEquals( 0, $user->getId() );
2587 $this->assertNotEquals(
$username, $user->getName() );
2588 $this->assertEquals( 0, $session->getUser()->getId() );
2589 $this->assertSame( [
2590 [ LogLevel::DEBUG,
'Could not acquire account creation lock' ],
2596 $user = \User::newFromName(
$username );
2597 $this->
hook(
'LocalUserCreated', $this->never() );
2599 $this->
unhook(
'LocalUserCreated' );
2600 $this->assertEquals( \Status::newFatal(
'fail-in-pre' ),
$ret );
2601 $this->assertEquals( 0, $user->getId() );
2602 $this->assertNotEquals(
$username, $user->getName() );
2603 $this->assertEquals( 0, $session->getUser()->getId() );
2604 $this->assertSame( [
2605 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2608 $this->assertEquals(
2609 StatusValue::newFatal(
'fail-in-pre' ), $session->get(
'AuthManager::AutoCreateBlacklist' )
2613 $user = \User::newFromName(
$username );
2614 $this->
hook(
'LocalUserCreated', $this->never() );
2616 $this->
unhook(
'LocalUserCreated' );
2617 $this->assertEquals( \Status::newFatal(
'fail-in-primary' ),
$ret );
2618 $this->assertEquals( 0, $user->getId() );
2619 $this->assertNotEquals(
$username, $user->getName() );
2620 $this->assertEquals( 0, $session->getUser()->getId() );
2621 $this->assertSame( [
2622 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2625 $this->assertEquals(
2626 StatusValue::newFatal(
'fail-in-primary' ), $session->get(
'AuthManager::AutoCreateBlacklist' )
2630 $user = \User::newFromName(
$username );
2631 $this->
hook(
'LocalUserCreated', $this->never() );
2633 $this->
unhook(
'LocalUserCreated' );
2634 $this->assertEquals( \Status::newFatal(
'fail-in-secondary' ),
$ret );
2635 $this->assertEquals( 0, $user->getId() );
2636 $this->assertNotEquals(
$username, $user->getName() );
2637 $this->assertEquals( 0, $session->getUser()->getId() );
2638 $this->assertSame( [
2639 [ LogLevel::DEBUG,
'Provider denied creation of {username}: {reason}' ],
2642 $this->assertEquals(
2643 StatusValue::newFatal(
'fail-in-secondary' ), $session->get(
'AuthManager::AutoCreateBlacklist' )
2647 $cache = \ObjectCache::getLocalClusterInstance();
2649 $cache->set( $backoffKey,
true );
2651 $user = \User::newFromName(
$username );
2652 $this->
hook(
'LocalUserCreated', $this->never() );
2654 $this->
unhook(
'LocalUserCreated' );
2655 $this->assertEquals( \Status::newFatal(
'authmanager-autocreate-exception' ),
$ret );
2656 $this->assertEquals( 0, $user->getId() );
2657 $this->assertNotEquals(
$username, $user->getName() );
2658 $this->assertEquals( 0, $session->getUser()->getId() );
2659 $this->assertSame( [
2660 [ LogLevel::DEBUG,
'{username} denied by prior creation attempt failures' ],
2663 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2664 $cache->delete( $backoffKey );
2668 $user = $this->getMockBuilder( \User::class )
2669 ->setMethods( [
'addToDatabase' ] )->getMock();
2670 $user->expects( $this->once() )->method(
'addToDatabase' )
2671 ->will( $this->returnValue( \Status::newFatal(
'because' ) ) );
2674 $this->assertEquals( \Status::newFatal(
'because' ),
$ret );
2675 $this->assertEquals( 0, $user->getId() );
2676 $this->assertNotEquals(
$username, $user->getName() );
2677 $this->assertEquals( 0, $session->getUser()->getId() );
2678 $this->assertSame( [
2679 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2680 [ LogLevel::ERROR,
'{username} failed with message {msg}' ],
2683 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2686 $cache = \ObjectCache::getLocalClusterInstance();
2688 $this->assertFalse(
$cache->get( $backoffKey ),
'sanity check' );
2690 $user = $this->getMockBuilder( \User::class )
2691 ->setMethods( [
'addToDatabase' ] )->getMock();
2692 $user->expects( $this->once() )->method(
'addToDatabase' )
2693 ->will( $this->throwException(
new \Exception(
'Excepted' ) ) );
2697 $this->fail(
'Expected exception not thrown' );
2698 }
catch ( \Exception $ex ) {
2699 $this->assertSame(
'Excepted', $ex->getMessage() );
2701 $this->assertEquals( 0, $user->getId() );
2702 $this->assertEquals( 0, $session->getUser()->getId() );
2703 $this->assertSame( [
2704 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2705 [ LogLevel::ERROR,
'{username} failed with exception {exception}' ],
2708 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2709 $this->assertNotEquals(
false,
$cache->get( $backoffKey ) );
2710 $cache->delete( $backoffKey );
2714 $user = $this->getMockBuilder( \User::class )
2715 ->setMethods( [
'addToDatabase' ] )->getMock();
2716 $user->expects( $this->once() )->method(
'addToDatabase' )
2717 ->will( $this->returnCallback(
function () use (
$username, &$user ) {
2718 $oldUser = \User::newFromName(
$username );
2719 $status = $oldUser->addToDatabase();
2720 $this->assertTrue(
$status->isOK(),
'sanity check' );
2721 $user->setId( $oldUser->getId() );
2722 return \Status::newFatal(
'userexists' );
2726 $expect = \Status::newGood();
2727 $expect->warning(
'userexists' );
2728 $this->assertEquals( $expect,
$ret );
2729 $this->assertNotEquals( 0, $user->getId() );
2730 $this->assertEquals(
$username, $user->getName() );
2731 $this->assertEquals( $user->getId(), $session->getUser()->getId() );
2732 $this->assertSame( [
2733 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2734 [ LogLevel::INFO,
'{username} already exists locally (race)' ],
2737 $this->assertSame(
null, $session->get(
'AuthManager::AutoCreateBlacklist' ) );
2742 $user = \User::newFromName(
$username );
2743 $this->
hook(
'AuthPluginAutoCreate', $this->once() )
2744 ->with( $callback );
2746 get_class(
$wgHooks[
'AuthPluginAutoCreate'][0] ) .
'::onAuthPluginAutoCreate)' );
2747 $this->
hook(
'LocalUserCreated', $this->once() )
2748 ->with( $callback, $this->equalTo(
true ) );
2750 $this->
unhook(
'LocalUserCreated' );
2751 $this->
unhook(
'AuthPluginAutoCreate' );
2752 $this->assertEquals( \Status::newGood(),
$ret );
2753 $this->assertNotEquals( 0, $user->getId() );
2754 $this->assertEquals(
$username, $user->getName() );
2755 $this->assertEquals( $user->getId(), $session->getUser()->getId() );
2756 $this->assertSame( [
2757 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2762 $maxLogId = $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] );
2765 $user = \User::newFromName(
$username );
2766 $this->
hook(
'LocalUserCreated', $this->once() )
2767 ->with( $callback, $this->equalTo(
true ) );
2769 $this->
unhook(
'LocalUserCreated' );
2770 $this->assertEquals( \Status::newGood(),
$ret );
2771 $this->assertNotEquals( 0, $user->getId() );
2772 $this->assertEquals(
$username, $user->getName() );
2773 $this->assertEquals( 0, $session->getUser()->getId() );
2774 $this->assertSame( [
2775 [ LogLevel::INFO,
'creating new user ({username}) - from: {from}' ],
2780 $dbw->selectField(
'logging',
'MAX(log_id)', [
'log_type' =>
'newusers' ] )
2783 $this->config->set(
'NewUserLog',
true );
2786 $user = \User::newFromName(
$username );
2788 $this->assertEquals( \Status::newGood(),
$ret );
2791 $data = \DatabaseLogEntry::getSelectQueryData();
2792 $rows = iterator_to_array( $dbw->select(
2796 'log_id > ' . (
int)$maxLogId,
2797 'log_type' =>
'newusers'
2803 $this->assertCount( 1,
$rows );
2804 $entry = \DatabaseLogEntry::newFromRow( reset(
$rows ) );
2806 $this->assertSame(
'autocreate', $entry->getSubtype() );
2807 $this->assertSame( $user->getId(), $entry->getPerformer()->getId() );
2808 $this->assertSame( $user->getName(), $entry->getPerformer()->getName() );
2809 $this->assertSame( $user->getUserPage()->getFullText(), $entry->getTarget()->getFullText() );
2810 $this->assertSame( [
'4::userid' => $user->getId() ], $entry->getParameters() );
2812 $workaroundPHPUnitBug =
true;
2822 $makeReq =
function ( $key ) use ( $action ) {
2823 $req = $this->createMock( AuthenticationRequest::class );
2824 $req->expects( $this->
any() )->method(
'getUniqueId' )
2825 ->will( $this->returnValue( $key ) );
2830 $cmpReqs =
function ( $a, $b ) {
2831 $ret = strcmp( get_class( $a ), get_class( $b ) );
2833 $ret = strcmp( $a->key, $b->key );
2838 $good = StatusValue::newGood();
2841 foreach ( [
'pre',
'primary',
'secondary' ] as $key ) {
2842 $class = ucfirst( $key ) .
'AuthenticationProvider';
2843 $mocks[$key] = $this->getMockBuilder(
"MediaWiki\\Auth\\$class" )
2845 'getUniqueId',
'getAuthenticationRequests',
'providerAllowsAuthenticationDataChange',
2847 ->getMockForAbstractClass();
2848 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
2849 ->will( $this->returnValue( $key ) );
2850 $mocks[$key]->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2851 ->will( $this->returnCallback(
function ( $action ) use ( $key, $makeReq ) {
2852 return [ $makeReq(
"$key-$action" ), $makeReq(
'generic' ) ];
2854 $mocks[$key]->expects( $this->
any() )->method(
'providerAllowsAuthenticationDataChange' )
2855 ->will( $this->returnValue( $good ) );
2864 $class =
'PrimaryAuthenticationProvider';
2865 $mocks[
"primary-$type"] = $this->getMockBuilder(
"MediaWiki\\Auth\\$class" )
2867 'getUniqueId',
'accountCreationType',
'getAuthenticationRequests',
2868 'providerAllowsAuthenticationDataChange',
2870 ->getMockForAbstractClass();
2871 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'getUniqueId' )
2872 ->will( $this->returnValue(
"primary-$type" ) );
2873 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'accountCreationType' )
2874 ->will( $this->returnValue(
$type ) );
2875 $mocks[
"primary-$type"]->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2876 ->will( $this->returnCallback(
function ( $action ) use (
$type, $makeReq ) {
2877 return [ $makeReq(
"primary-$type-$action" ), $makeReq(
'generic' ) ];
2879 $mocks[
"primary-$type"]->expects( $this->
any() )
2880 ->method(
'providerAllowsAuthenticationDataChange' )
2881 ->will( $this->returnValue( $good ) );
2882 $this->primaryauthMocks[] = $mocks[
"primary-$type"];
2885 $mocks[
'primary2'] = $this->getMockBuilder( PrimaryAuthenticationProvider::class )
2887 'getUniqueId',
'accountCreationType',
'getAuthenticationRequests',
2888 'providerAllowsAuthenticationDataChange',
2890 ->getMockForAbstractClass();
2891 $mocks[
'primary2']->expects( $this->
any() )->method(
'getUniqueId' )
2892 ->will( $this->returnValue(
'primary2' ) );
2893 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
2895 $mocks[
'primary2']->expects( $this->
any() )->method(
'getAuthenticationRequests' )
2896 ->will( $this->returnValue( [] ) );
2897 $mocks[
'primary2']->expects( $this->
any() )
2898 ->method(
'providerAllowsAuthenticationDataChange' )
2899 ->will( $this->returnCallback(
function (
$req ) use ( $good ) {
2900 return $req->key ===
'generic' ? StatusValue::newFatal(
'no' ) : $good;
2902 $this->primaryauthMocks[] = $mocks[
'primary2'];
2904 $this->preauthMocks = [ $mocks[
'pre'] ];
2905 $this->secondaryauthMocks = [ $mocks[
'secondary'] ];
2909 if ( isset( $state[
'continueRequests'] ) ) {
2910 $state[
'continueRequests'] = array_map( $makeReq, $state[
'continueRequests'] );
2913 $this->request->getSession()->setSecret(
'AuthManager::authnState', $state );
2915 $this->request->getSession()->setSecret(
'AuthManager::accountCreationState', $state );
2917 $this->request->getSession()->setSecret(
'AuthManager::accountLinkState', $state );
2921 $expectReqs = array_map( $makeReq, $expect );
2924 $req->action = $action;
2926 $expectReqs[] =
$req;
2929 $req->action = $action;
2930 $expectReqs[] =
$req;
2932 $req->action = $action;
2934 $expectReqs[] =
$req;
2936 usort( $expectReqs, $cmpReqs );
2938 $actual = $this->manager->getAuthenticationRequests( $action );
2939 foreach ( $actual as
$req ) {
2943 usort( $actual, $cmpReqs );
2945 $this->assertEquals( $expectReqs, $actual );
2950 $req->action = $action;
2952 $expectReqs[] =
$req;
2953 usort( $expectReqs, $cmpReqs );
2955 $actual = $this->manager->getAuthenticationRequests( $action, \
User::newFromName(
'UTSysop' ) );
2956 foreach ( $actual as
$req ) {
2960 usort( $actual, $cmpReqs );
2962 $this->assertEquals( $expectReqs, $actual );
3033 $makeReq =
function ( $key, $required ) {
3034 $req = $this->createMock( AuthenticationRequest::class );
3035 $req->expects( $this->
any() )->method(
'getUniqueId' )
3036 ->will( $this->returnValue( $key ) );
3039 $req->required = $required;
3042 $cmpReqs =
function ( $a, $b ) {
3043 $ret = strcmp( get_class( $a ), get_class( $b ) );
3045 $ret = strcmp( $a->key, $b->key );
3050 $good = StatusValue::newGood();
3052 $primary1 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
3053 $primary1->expects( $this->
any() )->method(
'getUniqueId' )
3054 ->will( $this->returnValue(
'primary1' ) );
3055 $primary1->expects( $this->
any() )->method(
'accountCreationType' )
3057 $primary1->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3058 ->will( $this->returnCallback(
function ( $action ) use ( $makeReq ) {
3069 $primary2 = $this->getMockForAbstractClass( PrimaryAuthenticationProvider::class );
3070 $primary2->expects( $this->
any() )->method(
'getUniqueId' )
3071 ->will( $this->returnValue(
'primary2' ) );
3072 $primary2->expects( $this->
any() )->method(
'accountCreationType' )
3074 $primary2->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3075 ->will( $this->returnCallback(
function ( $action ) use ( $makeReq ) {
3083 $secondary = $this->getMockForAbstractClass( SecondaryAuthenticationProvider::class );
3084 $secondary->expects( $this->
any() )->method(
'getUniqueId' )
3085 ->will( $this->returnValue(
'secondary' ) );
3086 $secondary->expects( $this->
any() )->method(
'getAuthenticationRequests' )
3087 ->will( $this->returnCallback(
function ( $action ) use ( $makeReq ) {
3098 $this->primaryauthMocks = [ $primary1, $primary2 ];
3099 $this->secondaryauthMocks = [ $secondary ];
3114 usort( $actual, $cmpReqs );
3115 usort( $expected, $cmpReqs );
3116 $this->assertEquals( $expected, $actual );
3118 $this->primaryauthMocks = [ $primary1 ];
3119 $this->secondaryauthMocks = [ $secondary ];
3132 usort( $actual, $cmpReqs );
3133 usort( $expected, $cmpReqs );
3134 $this->assertEquals( $expected, $actual );
3389 StatusValue $preTest, array $primaryResponses, array $managerResponses
3391 $user = \User::newFromName(
'UTSysop' );
3396 $req = $this->getMockForAbstractClass( AuthenticationRequest::class );
3397 $req->primary = $primaryResponses;
3400 foreach ( [
'pre',
'primary' ] as $key ) {
3401 $class = ucfirst( $key ) .
'AuthenticationProvider';
3402 $mocks[$key] = $this->getMockForAbstractClass(
3403 "MediaWiki\\Auth\\$class", [],
"Mock$class"
3405 $mocks[$key]->expects( $this->
any() )->method(
'getUniqueId' )
3406 ->will( $this->returnValue( $key ) );
3408 for ( $i = 2; $i <= 3; $i++ ) {
3409 $mocks[$key . $i] = $this->getMockForAbstractClass(
3410 "MediaWiki\\Auth\\$class", [],
"Mock$class"
3412 $mocks[$key . $i]->expects( $this->
any() )->method(
'getUniqueId' )
3413 ->will( $this->returnValue( $key . $i ) );
3417 $mocks[
'pre']->expects( $this->
any() )->method(
'testForAccountLink' )
3418 ->will( $this->returnCallback(
3420 use ( $user, $preTest )
3422 $this->assertSame( $user->getId(), $u->getId() );
3423 $this->assertSame( $user->getName(), $u->getName() );
3428 $mocks[
'pre2']->expects( $this->atMost( 1 ) )->method(
'testForAccountLink' )
3429 ->will( $this->returnValue( StatusValue::newGood() ) );
3431 $mocks[
'primary']->expects( $this->
any() )->method(
'accountCreationType' )
3433 $ct = count(
$req->primary );
3434 $callback = $this->returnCallback(
function ( $u, $reqs ) use ( $user,
$req ) {
3435 $this->assertSame( $user->getId(), $u->getId() );
3436 $this->assertSame( $user->getName(), $u->getName() );
3438 foreach ( $reqs as $r ) {
3439 $this->assertSame( $user->getName(), $r->username );
3440 $foundReq = $foundReq || get_class( $r ) === get_class(
$req );
3442 $this->assertTrue( $foundReq,
'$reqs contains $req' );
3443 return array_shift(
$req->primary );
3445 $mocks[
'primary']->expects( $this->exactly( min( 1, $ct ) ) )
3446 ->method(
'beginPrimaryAccountLink' )
3447 ->will( $callback );
3448 $mocks[
'primary']->expects( $this->exactly( max( 0, $ct - 1 ) ) )
3449 ->method(
'continuePrimaryAccountLink' )
3450 ->will( $callback );
3453 $mocks[
'primary2']->expects( $this->
any() )->method(
'accountCreationType' )
3455 $mocks[
'primary2']->expects( $this->atMost( 1 ) )->method(
'beginPrimaryAccountLink' )
3456 ->will( $this->returnValue( $abstain ) );
3457 $mocks[
'primary2']->expects( $this->never() )->method(
'continuePrimaryAccountLink' );
3458 $mocks[
'primary3']->expects( $this->
any() )->method(
'accountCreationType' )
3460 $mocks[
'primary3']->expects( $this->never() )->method(
'beginPrimaryAccountLink' );
3461 $mocks[
'primary3']->expects( $this->never() )->method(
'continuePrimaryAccountLink' );
3463 $this->preauthMocks = [ $mocks[
'pre'], $mocks[
'pre2'] ];
3464 $this->primaryauthMocks = [ $mocks[
'primary3'], $mocks[
'primary2'], $mocks[
'primary'] ];
3465 $this->logger = new \TestLogger(
true,
function ( $message, $level ) {
3466 return $level === LogLevel::DEBUG ? null : $message;
3470 $constraint = \PHPUnit_Framework_Assert::logicalOr(
3474 $providers = array_merge( $this->preauthMocks, $this->primaryauthMocks );
3475 foreach ( $providers as $p ) {
3476 $p->postCalled =
false;
3477 $p->expects( $this->atMost( 1 ) )->method(
'postAccountLink' )
3478 ->willReturnCallback(
function ( $user,
$response ) use ( $constraint, $p ) {
3479 $this->assertInstanceOf( \User::class, $user );
3480 $this->assertSame(
'UTSysop', $user->getName() );
3481 $this->assertInstanceOf( AuthenticationResponse::class,
$response );
3482 $this->assertThat(
$response->status, $constraint );
3490 foreach ( $managerResponses as $i =>
$response ) {
3494 $expectLog[] = [ LogLevel::INFO,
'Account linked to {user} by primary' ];
3500 $ret = $this->manager->beginAccountLink( $user, [
$req ],
'http://localhost/' );
3502 $ret = $this->manager->continueAccountLink( [
$req ] );
3504 if (
$response instanceof \Exception ) {
3505 $this->fail(
'Expected exception not thrown',
"Response $i" );
3507 }
catch ( \Exception $ex ) {
3508 if ( !
$response instanceof \Exception ) {
3511 $this->assertEquals(
$response->getMessage(), $ex->getMessage(),
"Response $i, exception" );
3512 $this->assertNull( $this->request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3513 "Response $i, exception, session state" );
3517 $this->assertSame(
'http://localhost/',
$req->returnToUrl );
3520 $this->assertEquals(
$response,
$ret,
"Response $i, response" );
3524 $this->assertNull( $this->request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3525 "Response $i, session state" );
3526 foreach ( $providers as $p ) {
3527 $this->assertSame(
$response->status, $p->postCalled,
3528 "Response $i, post-auth callback called" );
3531 $this->assertNotNull(
3532 $this->request->getSession()->getSecret(
'AuthManager::accountLinkState' ),
3533 "Response $i, session state"
3535 foreach (
$ret->neededRequests as $neededReq ) {
3537 "Response $i, neededRequest action" );
3539 $this->assertEquals(
3540 $ret->neededRequests,
3542 "Response $i, continuation check"
3544 foreach ( $providers as $p ) {
3545 $this->assertFalse( $p->postCalled,
"Response $i, post-auth callback not called" );
3552 $this->assertSame( $expectLog, $this->logger->getBuffer() );