wiki.techinc.nl/tests/phpunit/includes/auth/ConfirmLinkSecondaryAuthenticationProviderTest.php
Umherirrender de47d93928 Get the MainConfig from MediaWikiServices instead of RequestContext
The getConfig of a ContextSource should only be used, if the
ContextSource is available. Getting the global context just for the
config looks harder to fix/inject as using the MainConfig from
MediaWikiServices

Change-Id: Iaf14bfc7bd68cc315672e1c256887faf87e22542
2022-05-24 21:54:21 +00:00

332 lines
11 KiB
PHP

<?php
namespace MediaWiki\Auth;
use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait;
use MediaWiki\User\UserNameUtils;
use Psr\Container\ContainerInterface;
use Wikimedia\TestingAccessWrapper;
/**
* @group AuthManager
* @covers \MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
*/
class ConfirmLinkSecondaryAuthenticationProviderTest extends \MediaWikiIntegrationTestCase {
use AuthenticationProviderTestTrait;
/**
* @dataProvider provideGetAuthenticationRequests
* @param string $action
* @param array $response
*/
public function testGetAuthenticationRequests( $action, $response ) {
$provider = new ConfirmLinkSecondaryAuthenticationProvider();
$this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
}
public static function provideGetAuthenticationRequests() {
return [
[ AuthManager::ACTION_LOGIN, [] ],
[ AuthManager::ACTION_CREATE, [] ],
[ AuthManager::ACTION_LINK, [] ],
[ AuthManager::ACTION_CHANGE, [] ],
[ AuthManager::ACTION_REMOVE, [] ],
];
}
public function testBeginSecondaryAuthentication() {
$user = \User::newFromName( 'UTSysop' );
$obj = new \stdClass;
$mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
->getMock();
$mock->expects( $this->once() )->method( 'beginLinkAttempt' )
->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::authnState' ) )
->willReturn( $obj );
$mock->expects( $this->never() )->method( 'continueLinkAttempt' );
$this->assertSame( $obj, $mock->beginSecondaryAuthentication( $user, [] ) );
}
public function testContinueSecondaryAuthentication() {
$user = \User::newFromName( 'UTSysop' );
$obj = new \stdClass;
$reqs = [ new \stdClass ];
$mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
->getMock();
$mock->expects( $this->never() )->method( 'beginLinkAttempt' );
$mock->expects( $this->once() )->method( 'continueLinkAttempt' )
->with(
$this->identicalTo( $user ),
$this->identicalTo( 'AuthManager::authnState' ),
$this->identicalTo( $reqs )
)
->willReturn( $obj );
$this->assertSame( $obj, $mock->continueSecondaryAuthentication( $user, $reqs ) );
}
public function testBeginSecondaryAccountCreation() {
$user = \User::newFromName( 'UTSysop' );
$obj = new \stdClass;
$mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
->getMock();
$mock->expects( $this->once() )->method( 'beginLinkAttempt' )
->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::accountCreationState' ) )
->willReturn( $obj );
$mock->expects( $this->never() )->method( 'continueLinkAttempt' );
$this->assertSame( $obj, $mock->beginSecondaryAccountCreation( $user, $user, [] ) );
}
public function testContinueSecondaryAccountCreation() {
$user = \User::newFromName( 'UTSysop' );
$obj = new \stdClass;
$reqs = [ new \stdClass ];
$mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
->getMock();
$mock->expects( $this->never() )->method( 'beginLinkAttempt' );
$mock->expects( $this->once() )->method( 'continueLinkAttempt' )
->with(
$this->identicalTo( $user ),
$this->identicalTo( 'AuthManager::accountCreationState' ),
$this->identicalTo( $reqs )
)
->willReturn( $obj );
$this->assertSame( $obj, $mock->continueSecondaryAccountCreation( $user, $user, $reqs ) );
}
/**
* Get requests for testing
* @return AuthenticationRequest[]
*/
private function getLinkRequests() {
$reqs = [];
$mb = $this->getMockBuilder( AuthenticationRequest::class )
->onlyMethods( [ 'getUniqueId' ] );
for ( $i = 1; $i <= 3; $i++ ) {
$req = $mb->getMockForAbstractClass();
$req->method( 'getUniqueId' )
->willReturn( "Request$i" );
$req->id = $i - 1;
$reqs[$req->getUniqueId()] = $req;
}
return $reqs;
}
public function testBeginLinkAttempt() {
$badReq = $this->getMockBuilder( AuthenticationRequest::class )
->onlyMethods( [ 'getUniqueId' ] )
->getMockForAbstractClass();
$badReq->method( 'getUniqueId' )
->willReturn( "BadReq" );
$user = \User::newFromName( 'UTSysop' );
$provider = new ConfirmLinkSecondaryAuthenticationProvider;
$providerPriv = TestingAccessWrapper::newFromObject( $provider );
$request = new \FauxRequest();
$mwServices = $this->getServiceContainer();
$manager = $this->getMockBuilder( AuthManager::class )
->onlyMethods( [ 'allowsAuthenticationDataChange' ] )
->setConstructorArgs( [
$request,
$mwServices->getMainConfig(),
$mwServices->getObjectFactory(),
$mwServices->getHookContainer(),
$mwServices->getReadOnlyMode(),
$this->createNoOpMock( UserNameUtils::class ),
$mwServices->getBlockManager(),
$mwServices->getWatchlistManager(),
$mwServices->getDBLoadBalancer(),
$mwServices->getContentLanguage(),
$mwServices->getLanguageConverterFactory(),
$mwServices->getBotPasswordStore(),
$mwServices->getUserFactory(),
$mwServices->getUserIdentityLookup(),
$mwServices->getUserOptionsManager()
] )
->getMock();
$manager->method( 'allowsAuthenticationDataChange' )
->will( $this->returnCallback( static function ( $req ) {
return $req->getUniqueId() !== 'BadReq'
? \StatusValue::newGood()
: \StatusValue::newFatal( 'no' );
} ) );
$this->initProvider( $provider, null, null, $manager );
$this->assertEquals(
AuthenticationResponse::newAbstain(),
$providerPriv->beginLinkAttempt( $user, 'state' )
);
$request->getSession()->setSecret( 'state', [
'maybeLink' => [],
] );
$this->assertEquals(
AuthenticationResponse::newAbstain(),
$providerPriv->beginLinkAttempt( $user, 'state' )
);
$reqs = $this->getLinkRequests();
$request->getSession()->setSecret( 'state', [
'maybeLink' => $reqs + [ 'BadReq' => $badReq ]
] );
$res = $providerPriv->beginLinkAttempt( $user, 'state' );
$this->assertInstanceOf( AuthenticationResponse::class, $res );
$this->assertSame( AuthenticationResponse::UI, $res->status );
$this->assertSame( 'authprovider-confirmlink-message', $res->message->getKey() );
$this->assertCount( 1, $res->neededRequests );
$req = $res->neededRequests[0];
$this->assertInstanceOf( ConfirmLinkAuthenticationRequest::class, $req );
$expectReqs = $this->getLinkRequests();
foreach ( $expectReqs as $r ) {
$r->action = AuthManager::ACTION_CHANGE;
$r->username = $user->getName();
}
$this->assertEquals( $expectReqs, TestingAccessWrapper::newFromObject( $req )->linkRequests );
}
public function testContinueLinkAttempt() {
$user = \User::newFromName( 'UTSysop' );
$obj = new \stdClass;
$reqs = $this->getLinkRequests();
$done = [ false, false, false ];
// First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
$mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'beginLinkAttempt' ] )
->getMock();
$mock->expects( $this->once() )->method( 'beginLinkAttempt' )
->with( $this->identicalTo( $user ), $this->identicalTo( 'state' ) )
->willReturn( $obj );
$this->assertSame(
$obj,
TestingAccessWrapper::newFromObject( $mock )->continueLinkAttempt( $user, 'state', $reqs )
);
// Now test the actual functioning
$provider = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider::class )
->onlyMethods( [
'beginLinkAttempt', 'providerAllowsAuthenticationDataChange',
'providerChangeAuthenticationData'
] )
->getMock();
$provider->expects( $this->never() )->method( 'beginLinkAttempt' );
$provider->method( 'providerAllowsAuthenticationDataChange' )
->will( $this->returnCallback( static function ( $req ) {
return $req->getUniqueId() === 'Request3'
? \StatusValue::newFatal( 'foo' ) : \StatusValue::newGood();
} ) );
$provider->method( 'providerChangeAuthenticationData' )
->will( $this->returnCallback( static function ( $req ) use ( &$done ) {
$done[$req->id] = true;
} ) );
$config = new \HashConfig( [
'AuthManagerConfig' => [
'preauth' => [],
'primaryauth' => [],
'secondaryauth' => [
[ 'factory' => static function () use ( $provider ) {
return $provider;
} ],
],
],
] );
$request = new \FauxRequest();
$services = $this->createNoOpMock( ContainerInterface::class );
$objectFactory = new \Wikimedia\ObjectFactory\ObjectFactory( $services );
$mwServices = $this->getServiceContainer();
$hookContainer = $mwServices->getHookContainer();
$readOnlyMode = $mwServices->getReadOnlyMode();
$userNameUtils = $mwServices->getUserNameUtils();
$manager = new AuthManager(
$request,
$config,
$objectFactory,
$hookContainer,
$readOnlyMode,
$userNameUtils,
$mwServices->getBlockManager(),
$mwServices->getWatchlistManager(),
$mwServices->getDBLoadBalancer(),
$mwServices->getContentLanguage(),
$mwServices->getLanguageConverterFactory(),
$mwServices->getBotPasswordStore(),
$mwServices->getUserFactory(),
$mwServices->getUserIdentityLookup(),
$mwServices->getUserOptionsManager()
);
$this->initProvider( $provider, null, null, $manager );
$provider = TestingAccessWrapper::newFromObject( $provider );
$req = new ConfirmLinkAuthenticationRequest( $reqs );
$this->assertEquals(
AuthenticationResponse::newAbstain(),
$provider->continueLinkAttempt( $user, 'state', [ $req ] )
);
$request->getSession()->setSecret( 'state', [
'maybeLink' => [],
] );
$this->assertEquals(
AuthenticationResponse::newAbstain(),
$provider->continueLinkAttempt( $user, 'state', [ $req ] )
);
$request->getSession()->setSecret( 'state', [
'maybeLink' => $reqs
] );
$this->assertEquals(
AuthenticationResponse::newPass(),
$res = $provider->continueLinkAttempt( $user, 'state', [ $req ] )
);
$this->assertSame( [ false, false, false ], $done );
$request->getSession()->setSecret( 'state', [
'maybeLink' => [ $reqs['Request2'] ],
] );
$req->confirmedLinkIDs = [ 'Request1', 'Request2' ];
$res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
$this->assertEquals( AuthenticationResponse::newPass(), $res );
$this->assertSame( [ false, true, false ], $done );
$done = [ false, false, false ];
$request->getSession()->setSecret( 'state', [
'maybeLink' => $reqs,
] );
$req->confirmedLinkIDs = [ 'Request1', 'Request2' ];
$res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
$this->assertEquals( AuthenticationResponse::newPass(), $res );
$this->assertSame( [ true, true, false ], $done );
$done = [ false, false, false ];
$request->getSession()->setSecret( 'state', [
'maybeLink' => $reqs,
] );
$req->confirmedLinkIDs = [ 'Request1', 'Request3' ];
$res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
$this->assertEquals( AuthenticationResponse::UI, $res->status );
$this->assertCount( 1, $res->neededRequests );
$this->assertInstanceOf( ButtonAuthenticationRequest::class, $res->neededRequests[0] );
$this->assertSame( [ true, false, false ], $done );
$done = [ false, false, false ];
$res = $provider->continueLinkAttempt( $user, 'state', [ $res->neededRequests[0] ] );
$this->assertEquals( AuthenticationResponse::newPass(), $res );
$this->assertSame( [ false, false, false ], $done );
}
}