Page MenuHomeWickedGov Phorge

CentralAuthTokenSessionProviderTestBase.php
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

CentralAuthTokenSessionProviderTestBase.php

<?php
use MediaWiki\Context\RequestContext;
use MediaWiki\Extension\CentralAuth\CentralAuthServices;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\MainConfigNames;
use MediaWiki\Output\OutputPage;
use MediaWiki\Request\FauxRequest;
use MediaWiki\Request\WebRequest;
use MediaWiki\ResourceLoader\Module;
use MediaWiki\Session\Session;
use MediaWiki\Session\SessionInfo;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserIdentity;
use PHPUnit\Framework\MockObject\MockObject;
use Wikimedia\LightweightObjectStore\ExpirationAwareness;
use Wikimedia\ObjectCache\BagOStuff;
use Wikimedia\ObjectCache\HashBagOStuff;
use Wikimedia\Rdbms\IDBAccessObject;
use Wikimedia\TestingAccessWrapper;
/**
* @covers CentralAuthTokenSessionProvider
* @group medium
* @group Database
*/
abstract class CentralAuthTokenSessionProviderTestBase extends MediaWikiIntegrationTestCase {
protected BagOStuff $sessionStore;
/** @var int */
private $idCounter = 0;
/**
* @var User[]|null
*/
private $userObjects;
public function setUp(): void {
parent::setUp();
$this->setUserLang( 'qqx' );
$this->sessionStore = new HashBagOStuff();
$this->patchStores();
// CentralAuthTokenSessionProvider registers hooks dynamically.
// Make sure the original hooks are restored before the next test.
$this->overrideConfigValue( MainConfigNames::Hooks, $GLOBALS['wgHooks'] );
}
/**
* Patch our modified session store into CentralAuthSessionManager
*/
private function patchStores() {
$sessionManager = CentralAuthServices::getSessionManager( $this->getServiceContainer() );
$wrappedSessionManager = TestingAccessWrapper::newFromObject( $sessionManager );
$wrappedSessionManager->sessionStore = $this->sessionStore;
$tokenManager = CentralAuthServices::getTokenManager( $this->getServiceContainer() );
$wrappedTokenManager = TestingAccessWrapper::newFromObject( $tokenManager );
$wrappedTokenManager->tokenStore = $this->sessionStore;
}
protected function assertSessionInfoError(
WebRequest $request,
?SessionInfo $result,
?string $error = null,
?string $code = null
) {
$this->assertNotNull( $result );
$data = $result->getProviderMetadata();
$this->assertIsArray( $data );
$this->assertArrayHasKey( 'error', $data );
$this->assertArrayHasKey( 'error-code', $data );
if ( $code ) {
$this->assertSame( $code, $data['error-code'] );
}
if ( $error ) {
$this->assertSame( $error, $data['error'] );
}
}
protected function assertSessionInfoOk( ?SessionInfo $result ) {
$data = $result->getProviderMetadata();
if ( $data !== null ) {
$this->assertArrayNotHasKey( 'error', $data, $data['error'] ?? '' );
$this->assertArrayNotHasKey( 'error-code', $data, $data['error-code'] ?? '' );
}
$this->assertNotNull( $result->getUserInfo() );
}
abstract protected function newSessionProvider();
protected function makeValidToken( $data = [] ) {
// NOTE: logic stolen from ApiCentralAuthToken
$data += [
'userName' => 'Frank',
// not the login token
'token' => 'CATOKEN',
'origin' => 'test',
'originSessionId' => 1337,
];
$loginToken = 'testtoken' . ++$this->idCounter;
$tokenManager = CentralAuthServices::getTokenManager( $this->getServiceContainer() );
$tokenManager->tokenize(
$data,
'api-token',
[ 'token' => $loginToken, 'expiry' => ExpirationAwareness::TTL_HOUR ]
);
return $loginToken;
}
/**
* @param int $id
* @param string $name
*
* @return MockObject|User
*/
protected function makeUser( $id, $name ) {
/** @var MockObject|CentralAuthUser $caUser */
$user = $this->createNoOpMock( User::class, [
'getName', 'getId', 'isAnon'
] );
$user->method( 'getId' )->willReturn( $id );
$user->method( 'getName' )->willReturn( $name );
$user->method( 'isAnon' )->willReturn( $id === 0 );
if ( $this->userObjects === null ) {
// set up a fake user factory
$this->userObjects = [];
$userFactory = $this->createNoOpMock( UserFactory::class, [
'newFromName'
] );
$userFactory->method( 'newFromName' )->willReturnCallback( function ( $name ) {
return $this->userObjects[ $name ] ?? null;
} );
$this->setService( 'UserFactory', $userFactory );
// setService resets existing objects, so make sure our sessionStore patch will be there
$this->patchStores();
}
$this->userObjects[ $name ] = $user;
return $user;
}
/**
* @param string $name
* @param array $return
* @param array $methods
*
* @return MockObject|CentralAuthUser
*/
protected function makeCentralAuthUser( $name, $return = [], $methods = [] ) {
$id = ++$this->idCounter;
$methods += [
'getId',
'getName',
'renameInProgress',
'exists',
'isAttached',
'authenticateWithToken'
];
/** @var MockObject|CentralAuthUser $caUser */
$caUser = $this->getMockBuilder( CentralAuthUser::class )
->setConstructorArgs( [ $name, IDBAccessObject::READ_LATEST ] )
->getMock();
$caUser->expects( $this->never() )->method( $this->anythingBut( '__destruct', ...$methods ) );
$return += [
'getId' => $id,
'getName' => $name,
'renameInProgress' => false,
'exists' => true,
'isAttached' => true,
'authenticateWithToken' => 'ok',
];
foreach ( $return as $method => $value ) {
$caUser->method( $method )->willReturn( $value );
}
$user = $this->createMock( UserIdentity::class );
$user->method( 'getName' )->willReturn( $name );
CentralAuthUser::setInstance( $user, $caUser );
$this->makeUser( $id, $name );
return $caUser;
}
abstract protected function makeRequest( $token );
public function testProvideSessionInfo_noToken() {
$provider = $this->newSessionProvider();
$request = new FauxRequest();
$result = $provider->provideSessionInfo( $request );
$this->assertNull( $result );
}
public function testProvideSessionInfo_unknownToken() {
$provider = $this->newSessionProvider();
$request = $this->makeRequest( 'bogus' );
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoError( $request, $result, 'apierror-centralauth-badtoken', 'badtoken' );
}
public function testProvideSessionInfo() {
$user = $this->makeCentralAuthUser( 'Frank' );
$token = $this->makeValidToken( [ 'userName' => $user->getName() ] );
$provider = $this->newSessionProvider();
$request = $this->makeRequest( $token );
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoOk( $result );
$this->assertSame( $user->getName(), $result->getUserInfo()->getName() );
$this->assertSame( $user->getId(), $result->getUserInfo()->getId() );
}
public static function provideBadUsers() {
yield 'bad user name' => [ '_', [], 'apierror-centralauth-badtoken', 'badtoken' ];
yield 'rename in progress' => [
'Fran',
[ 'renameInProgress' => true ],
'apierror-centralauth-renameinprogress',
'renameinprogress'
];
yield 'nonexisting user' => [
'Macy',
[ 'exists' => false ],
'apierror-centralauth-badtoken',
'badtoken'
];
yield 'authentication failed' => [
'Shlob',
[ 'authenticateWithToken' => 'failed' ],
'apierror-centralauth-badtoken',
'badtoken'
];
}
/**
* @dataProvider provideBadUsers
*/
public function testProvideSessionInfo_badUser( $name, $return, $error, $code ) {
// NOTE: the user gets registered in a fake service,
// we can't construct it in the data provider!
$user = $this->makeCentralAuthUser( $name, $return );
$token = $this->makeValidToken( [ 'userName' => $user->getName() ] );
$provider = $this->newSessionProvider();
$request = $this->makeRequest( $token );
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoError( $request, $result, $error, $code );
}
public function testCannotReuseToken() {
$user = $this->makeCentralAuthUser( 'Frank' );
$token = $this->makeValidToken( [ 'userName' => $user->getName() ] );
$provider = $this->newSessionProvider();
$request = $this->makeRequest( $token );
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoOk( $result );
// the token should now be unknown
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoError( $request, $result, 'apierror-centralauth-badtoken', 'badtoken' );
}
public function testInvalidateSessionForUser() {
$caUser = $this->makeCentralAuthUser( 'Frank', [], [ 'resetAuthToken' ] );
$caUser->expects( $this->once() )->method( 'resetAuthToken' );
$user = $this->getServiceContainer()->getUserFactory()->newFromName( $caUser->getName() );
$provider = $this->newSessionProvider();
$provider->invalidateSessionsForUser( $user );
}
public function testPreventSessionForUser() {
$user = $this->makeCentralAuthUser( 'Donald' );
$token = $this->makeValidToken( [ 'userName' => $user->getName() ] );
$provider = $this->newSessionProvider();
$provider->preventSessionsForUser( $user->getName() );
$request = $this->makeRequest( $token );
$result = $provider->provideSessionInfo( $request );
$this->assertSessionInfoError( $request, $result, 'apierror-centralauth-badtoken', 'badtoken' );
}
public function testUserScriptsDisabled() {
$provider = $this->newSessionProvider();
$session = $this->createNoOpMock(
Session::class,
[ 'getProvider', 'getSessionId', 'get', 'canSetUser', 'isPersistent', 'getUser' ]
);
$session->method( 'getProvider' )->willReturn( $provider );
$session->method( 'getSessionId' )->willReturn( 23 );
$session->method( 'get' )->willReturn( null );
$session->method( 'isPersistent' )->willReturn( true );
$session->method( 'getUser' )->willReturn( $this->getTestUser()->getUser() );
/** @var MockObject|WebRequest $request */
$request = $this->getMockBuilder( FauxRequest::class )
->setConstructorArgs( [], false, $session )
->onlyMethods( [ 'getSession' ] )
->getMock();
$request->method( 'getSession' )->willReturn( $session );
$context = new RequestContext();
$context->setRequest( $request );
$context->setTitle( Title::newMainPage() );
$context->setUser( $this->getTestUser()->getUser() );
$out = new OutputPage( $context );
$out->output( true );
$this->assertSame(
Module::ORIGIN_USER_SITEWIDE,
$out->getAllowedModules( Module::TYPE_SCRIPTS )
);
$this->assertSame(
Module::ORIGIN_USER_SITEWIDE,
$out->getAllowedModules( Module::TYPE_STYLES )
);
}
}

File Metadata

Mime Type
text/x-php
Expires
Fri, Jul 3, 17:52 (1 d, 3 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
91/75/292b64a9a5ff66dbd8050a3bea34
Default Alt Text
CentralAuthTokenSessionProviderTestBase.php (10 KB)

Event Timeline