summaryrefslogtreecommitdiff
path: root/www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php')
-rw-r--r--www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php842
1 files changed, 842 insertions, 0 deletions
diff --git a/www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php b/www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php
new file mode 100644
index 00000000..c1df365a
--- /dev/null
+++ b/www/wiki/tests/phpunit/includes/session/CookieSessionProviderTest.php
@@ -0,0 +1,842 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+use Psr\Log\LogLevel;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\CookieSessionProvider
+ */
+class CookieSessionProviderTest extends MediaWikiTestCase {
+
+ private function getConfig() {
+ return new \HashConfig( [
+ 'CookiePrefix' => 'CookiePrefix',
+ 'CookiePath' => 'CookiePath',
+ 'CookieDomain' => 'CookieDomain',
+ 'CookieSecure' => true,
+ 'CookieHttpOnly' => true,
+ 'SessionName' => false,
+ 'CookieExpiration' => 100,
+ 'ExtendedLoginCookieExpiration' => 200,
+ ] );
+ }
+
+ public function testConstructor() {
+ try {
+ new CookieSessionProvider();
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( \InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'MediaWiki\\Session\\CookieSessionProvider::__construct: priority must be specified',
+ $ex->getMessage()
+ );
+ }
+
+ try {
+ new CookieSessionProvider( [ 'priority' => 'foo' ] );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( \InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+ $ex->getMessage()
+ );
+ }
+ try {
+ new CookieSessionProvider( [ 'priority' => SessionInfo::MIN_PRIORITY - 1 ] );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( \InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+ $ex->getMessage()
+ );
+ }
+ try {
+ new CookieSessionProvider( [ 'priority' => SessionInfo::MAX_PRIORITY + 1 ] );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( \InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+ $ex->getMessage()
+ );
+ }
+
+ try {
+ new CookieSessionProvider( [ 'priority' => 1, 'cookieOptions' => null ] );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( \InvalidArgumentException $ex ) {
+ $this->assertSame(
+ 'MediaWiki\\Session\\CookieSessionProvider::__construct: cookieOptions must be an array',
+ $ex->getMessage()
+ );
+ }
+
+ $config = $this->getConfig();
+ $p = TestingAccessWrapper::newFromObject(
+ new CookieSessionProvider( [ 'priority' => 1 ] )
+ );
+ $p->setLogger( new \TestLogger() );
+ $p->setConfig( $config );
+ $this->assertEquals( 1, $p->priority );
+ $this->assertEquals( [
+ 'callUserSetCookiesHook' => false,
+ 'sessionName' => 'CookiePrefix_session',
+ ], $p->params );
+ $this->assertEquals( [
+ 'prefix' => 'CookiePrefix',
+ 'path' => 'CookiePath',
+ 'domain' => 'CookieDomain',
+ 'secure' => true,
+ 'httpOnly' => true,
+ ], $p->cookieOptions );
+
+ $config->set( 'SessionName', 'SessionName' );
+ $p = TestingAccessWrapper::newFromObject(
+ new CookieSessionProvider( [ 'priority' => 3 ] )
+ );
+ $p->setLogger( new \TestLogger() );
+ $p->setConfig( $config );
+ $this->assertEquals( 3, $p->priority );
+ $this->assertEquals( [
+ 'callUserSetCookiesHook' => false,
+ 'sessionName' => 'SessionName',
+ ], $p->params );
+ $this->assertEquals( [
+ 'prefix' => 'CookiePrefix',
+ 'path' => 'CookiePath',
+ 'domain' => 'CookieDomain',
+ 'secure' => true,
+ 'httpOnly' => true,
+ ], $p->cookieOptions );
+
+ $p = TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
+ 'priority' => 10,
+ 'callUserSetCookiesHook' => true,
+ 'cookieOptions' => [
+ 'prefix' => 'XPrefix',
+ 'path' => 'XPath',
+ 'domain' => 'XDomain',
+ 'secure' => 'XSecure',
+ 'httpOnly' => 'XHttpOnly',
+ ],
+ 'sessionName' => 'XSession',
+ ] ) );
+ $p->setLogger( new \TestLogger() );
+ $p->setConfig( $config );
+ $this->assertEquals( 10, $p->priority );
+ $this->assertEquals( [
+ 'callUserSetCookiesHook' => true,
+ 'sessionName' => 'XSession',
+ ], $p->params );
+ $this->assertEquals( [
+ 'prefix' => 'XPrefix',
+ 'path' => 'XPath',
+ 'domain' => 'XDomain',
+ 'secure' => 'XSecure',
+ 'httpOnly' => 'XHttpOnly',
+ ], $p->cookieOptions );
+ }
+
+ public function testBasics() {
+ $provider = new CookieSessionProvider( [ 'priority' => 10 ] );
+
+ $this->assertTrue( $provider->persistsSessionId() );
+ $this->assertTrue( $provider->canChangeUser() );
+
+ $extendedCookies = [ 'UserID', 'UserName', 'Token' ];
+
+ $this->assertEquals(
+ $extendedCookies,
+ TestingAccessWrapper::newFromObject( $provider )->getExtendedLoginCookies(),
+ 'List of extended cookies (subclasses can add values, but we\'re calling the core one here)'
+ );
+
+ $msg = $provider->whyNoSession();
+ $this->assertInstanceOf( \Message::class, $msg );
+ $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
+ }
+
+ public function testProvideSessionInfo() {
+ $params = [
+ 'priority' => 20,
+ 'sessionName' => 'session',
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ];
+ $provider = new CookieSessionProvider( $params );
+ $logger = new \TestLogger( true );
+ $provider->setLogger( $logger );
+ $provider->setConfig( $this->getConfig() );
+ $provider->setManager( new SessionManager() );
+
+ $user = static::getTestSysop()->getUser();
+ $id = $user->getId();
+ $name = $user->getName();
+ $token = $user->getToken( true );
+
+ $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+
+ // No data
+ $request = new \FauxRequest();
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNull( $info );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // Session key only
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertSame( 0, $info->getUserInfo()->getId() );
+ $this->assertNull( $info->getUserInfo()->getName() );
+ $this->assertFalse( $info->forceHTTPS() );
+ $this->assertSame( [
+ [
+ LogLevel::DEBUG,
+ 'Session "{session}" requested without UserID cookie',
+ ],
+ ], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User, no session key
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'xUserID' => $id,
+ 'xToken' => $token,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertNotSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertSame( $id, $info->getUserInfo()->getId() );
+ $this->assertSame( $name, $info->getUserInfo()->getName() );
+ $this->assertFalse( $info->forceHTTPS() );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User and session key
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ 'xToken' => $token,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertSame( $id, $info->getUserInfo()->getId() );
+ $this->assertSame( $name, $info->getUserInfo()->getName() );
+ $this->assertFalse( $info->forceHTTPS() );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User with bad token
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ 'xToken' => 'BADTOKEN',
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNull( $info );
+ $this->assertSame( [
+ [
+ LogLevel::WARNING,
+ 'Session "{session}" requested with invalid Token cookie.'
+ ],
+ ], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User id with no token
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertFalse( $info->getUserInfo()->isVerified() );
+ $this->assertSame( $id, $info->getUserInfo()->getId() );
+ $this->assertSame( $name, $info->getUserInfo()->getName() );
+ $this->assertFalse( $info->forceHTTPS() );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'xUserID' => $id,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNull( $info );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User and session key, with forceHTTPS flag
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ 'xToken' => $token,
+ 'forceHTTPS' => true,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertSame( $id, $info->getUserInfo()->getId() );
+ $this->assertSame( $name, $info->getUserInfo()->getName() );
+ $this->assertTrue( $info->forceHTTPS() );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // Invalid user id
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => '-1',
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNull( $info );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User id with matching name
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ 'xUserName' => $name,
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNotNull( $info );
+ $this->assertSame( $params['priority'], $info->getPriority() );
+ $this->assertSame( $sessionId, $info->getId() );
+ $this->assertNotNull( $info->getUserInfo() );
+ $this->assertFalse( $info->getUserInfo()->isVerified() );
+ $this->assertSame( $id, $info->getUserInfo()->getId() );
+ $this->assertSame( $name, $info->getUserInfo()->getName() );
+ $this->assertFalse( $info->forceHTTPS() );
+ $this->assertSame( [], $logger->getBuffer() );
+ $logger->clearBuffer();
+
+ // User id with wrong name
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'session' => $sessionId,
+ 'xUserID' => $id,
+ 'xUserName' => 'Wrong',
+ ], '' );
+ $info = $provider->provideSessionInfo( $request );
+ $this->assertNull( $info );
+ $this->assertSame( [
+ [
+ LogLevel::WARNING,
+ 'Session "{session}" requested with mismatched UserID and UserName cookies.',
+ ],
+ ], $logger->getBuffer() );
+ $logger->clearBuffer();
+ }
+
+ public function testGetVaryCookies() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'cookieOptions' => [ 'prefix' => 'MyCookiePrefix' ],
+ ] );
+ $this->assertArrayEquals( [
+ 'MyCookiePrefixToken',
+ 'MyCookiePrefixLoggedOut',
+ 'MySessionName',
+ 'forceHTTPS',
+ ], $provider->getVaryCookies() );
+ }
+
+ public function testSuggestLoginUsername() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+
+ $request = new \FauxRequest();
+ $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
+
+ $request->setCookies( [
+ 'xUserName' => 'Example',
+ ], '' );
+ $this->assertEquals( 'Example', $provider->suggestLoginUsername( $request ) );
+ }
+
+ public function testPersistSession() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'callUserSetCookiesHook' => false,
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+ $config = $this->getConfig();
+ $provider->setLogger( new \TestLogger() );
+ $provider->setConfig( $config );
+ $provider->setManager( SessionManager::singleton() );
+
+ $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+ $store = new TestBagOStuff();
+ $user = static::getTestSysop()->getUser();
+ $anon = new User;
+
+ $backend = new SessionBackend(
+ new SessionId( $sessionId ),
+ new SessionInfo( SessionInfo::MIN_PRIORITY, [
+ 'provider' => $provider,
+ 'id' => $sessionId,
+ 'persisted' => true,
+ 'idIsSafe' => true,
+ ] ),
+ $store,
+ new \Psr\Log\NullLogger(),
+ 10
+ );
+ TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+
+ $mock = $this->getMockBuilder( stdClass::class )
+ ->setMethods( [ 'onUserSetCookies' ] )
+ ->getMock();
+ $mock->expects( $this->never() )->method( 'onUserSetCookies' );
+ $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
+
+ // Anonymous user
+ $backend->setUser( $anon );
+ $backend->setRememberUser( true );
+ $backend->setForceHTTPS( false );
+ $request = new \FauxRequest();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( [], $backend->getData() );
+
+ // Logged-in user, no remember
+ $backend->setUser( $user );
+ $backend->setRememberUser( false );
+ $backend->setForceHTTPS( false );
+ $request = new \FauxRequest();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( [], $backend->getData() );
+
+ // Logged-in user, remember
+ $backend->setUser( $user );
+ $backend->setRememberUser( true );
+ $backend->setForceHTTPS( true );
+ $request = new \FauxRequest();
+ $time = time();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( [], $backend->getData() );
+ }
+
+ /**
+ * @dataProvider provideCookieData
+ * @param bool $secure
+ * @param bool $remember
+ */
+ public function testCookieData( $secure, $remember ) {
+ $this->setMwGlobals( [
+ 'wgSecureLogin' => false,
+ ] );
+
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'callUserSetCookiesHook' => false,
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+ $config = $this->getConfig();
+ $config->set( 'CookieSecure', $secure );
+ $provider->setLogger( new \TestLogger() );
+ $provider->setConfig( $config );
+ $provider->setManager( SessionManager::singleton() );
+
+ $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+ $user = static::getTestSysop()->getUser();
+ $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
+
+ $backend = new SessionBackend(
+ new SessionId( $sessionId ),
+ new SessionInfo( SessionInfo::MIN_PRIORITY, [
+ 'provider' => $provider,
+ 'id' => $sessionId,
+ 'persisted' => true,
+ 'idIsSafe' => true,
+ ] ),
+ new TestBagOStuff(),
+ new \Psr\Log\NullLogger(),
+ 10
+ );
+ TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+ $backend->setUser( $user );
+ $backend->setRememberUser( $remember );
+ $backend->setForceHTTPS( $secure );
+ $request = new \FauxRequest();
+ $time = time();
+ $provider->persistSession( $backend, $request );
+
+ $defaults = [
+ 'expire' => (int)100,
+ 'path' => $config->get( 'CookiePath' ),
+ 'domain' => $config->get( 'CookieDomain' ),
+ 'secure' => $secure,
+ 'httpOnly' => $config->get( 'CookieHttpOnly' ),
+ 'raw' => false,
+ ];
+
+ $normalExpiry = $config->get( 'CookieExpiration' );
+ $extendedExpiry = $config->get( 'ExtendedLoginCookieExpiration' );
+ $extendedExpiry = (int)( $extendedExpiry === null ? 0 : $extendedExpiry );
+ $expect = [
+ 'MySessionName' => [
+ 'value' => (string)$sessionId,
+ 'expire' => 0,
+ ] + $defaults,
+ 'xUserID' => [
+ 'value' => (string)$user->getId(),
+ 'expire' => $remember ? $extendedExpiry : $normalExpiry,
+ ] + $defaults,
+ 'xUserName' => [
+ 'value' => $user->getName(),
+ 'expire' => $remember ? $extendedExpiry : $normalExpiry
+ ] + $defaults,
+ 'xToken' => [
+ 'value' => $remember ? $user->getToken() : '',
+ 'expire' => $remember ? $extendedExpiry : -31536000,
+ ] + $defaults,
+ 'forceHTTPS' => [
+ 'value' => $secure ? 'true' : '',
+ 'secure' => false,
+ 'expire' => $secure ? $remember ? $defaults['expire'] : 0 : -31536000,
+ ] + $defaults,
+ ];
+ foreach ( $expect as $key => $value ) {
+ $actual = $request->response()->getCookieData( $key );
+ if ( $actual && $actual['expire'] > 0 ) {
+ // Round expiry so we don't randomly fail if the seconds ticked during the test.
+ $actual['expire'] = round( $actual['expire'] - $time, -2 );
+ }
+ $this->assertEquals( $value, $actual, "Cookie $key" );
+ }
+ }
+
+ public static function provideCookieData() {
+ return [
+ [ false, false ],
+ [ false, true ],
+ [ true, false ],
+ [ true, true ],
+ ];
+ }
+
+ protected function getSentRequest() {
+ $sentResponse = $this->getMockBuilder( \FauxResponse::class )
+ ->setMethods( [ 'headersSent', 'setCookie', 'header' ] )->getMock();
+ $sentResponse->expects( $this->any() )->method( 'headersSent' )
+ ->will( $this->returnValue( true ) );
+ $sentResponse->expects( $this->never() )->method( 'setCookie' );
+ $sentResponse->expects( $this->never() )->method( 'header' );
+
+ $sentRequest = $this->getMockBuilder( \FauxRequest::class )
+ ->setMethods( [ 'response' ] )->getMock();
+ $sentRequest->expects( $this->any() )->method( 'response' )
+ ->will( $this->returnValue( $sentResponse ) );
+ return $sentRequest;
+ }
+
+ public function testPersistSessionWithHook() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'callUserSetCookiesHook' => true,
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $this->getConfig() );
+ $provider->setManager( SessionManager::singleton() );
+
+ $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+ $store = new TestBagOStuff();
+ $user = static::getTestSysop()->getUser();
+ $anon = new User;
+
+ $backend = new SessionBackend(
+ new SessionId( $sessionId ),
+ new SessionInfo( SessionInfo::MIN_PRIORITY, [
+ 'provider' => $provider,
+ 'id' => $sessionId,
+ 'persisted' => true,
+ 'idIsSafe' => true,
+ ] ),
+ $store,
+ new \Psr\Log\NullLogger(),
+ 10
+ );
+ TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+
+ // Anonymous user
+ $mock = $this->getMockBuilder( stdClass::class )
+ ->setMethods( [ 'onUserSetCookies' ] )->getMock();
+ $mock->expects( $this->never() )->method( 'onUserSetCookies' );
+ $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
+ $backend->setUser( $anon );
+ $backend->setRememberUser( true );
+ $backend->setForceHTTPS( false );
+ $request = new \FauxRequest();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( [], $backend->getData() );
+
+ $provider->persistSession( $backend, $this->getSentRequest() );
+
+ // Logged-in user, no remember
+ $mock = $this->getMockBuilder( __CLASS__ )
+ ->setMethods( [ 'onUserSetCookies' ] )->getMock();
+ $mock->expects( $this->once() )->method( 'onUserSetCookies' )
+ ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+ $this->assertSame( $user, $u );
+ $this->assertEquals( [
+ 'wsUserID' => $user->getId(),
+ 'wsUserName' => $user->getName(),
+ 'wsToken' => $user->getToken(),
+ ], $sessionData );
+ $this->assertEquals( [
+ 'UserID' => $user->getId(),
+ 'UserName' => $user->getName(),
+ 'Token' => false,
+ ], $cookies );
+
+ $sessionData['foo'] = 'foo!';
+ $cookies['bar'] = 'bar!';
+ return true;
+ } ) );
+ $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
+ $backend->setUser( $user );
+ $backend->setRememberUser( false );
+ $backend->setForceHTTPS( false );
+ $backend->setLoggedOutTimestamp( $loggedOut = time() );
+ $request = new \FauxRequest();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( 'bar!', $request->response()->getCookie( 'xbar' ) );
+ $this->assertSame( (string)$loggedOut, $request->response()->getCookie( 'xLoggedOut' ) );
+ $this->assertEquals( [
+ 'wsUserID' => $user->getId(),
+ 'wsUserName' => $user->getName(),
+ 'wsToken' => $user->getToken(),
+ 'foo' => 'foo!',
+ ], $backend->getData() );
+
+ $provider->persistSession( $backend, $this->getSentRequest() );
+
+ // Logged-in user, remember
+ $mock = $this->getMockBuilder( __CLASS__ )
+ ->setMethods( [ 'onUserSetCookies' ] )->getMock();
+ $mock->expects( $this->once() )->method( 'onUserSetCookies' )
+ ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+ $this->assertSame( $user, $u );
+ $this->assertEquals( [
+ 'wsUserID' => $user->getId(),
+ 'wsUserName' => $user->getName(),
+ 'wsToken' => $user->getToken(),
+ ], $sessionData );
+ $this->assertEquals( [
+ 'UserID' => $user->getId(),
+ 'UserName' => $user->getName(),
+ 'Token' => $user->getToken(),
+ ], $cookies );
+
+ $sessionData['foo'] = 'foo 2!';
+ $cookies['bar'] = 'bar 2!';
+ return true;
+ } ) );
+ $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
+ $backend->setUser( $user );
+ $backend->setRememberUser( true );
+ $backend->setForceHTTPS( true );
+ $backend->setLoggedOutTimestamp( 0 );
+ $request = new \FauxRequest();
+ $provider->persistSession( $backend, $request );
+ $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
+ $this->assertSame( 'bar 2!', $request->response()->getCookie( 'xbar' ) );
+ $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+ $this->assertEquals( [
+ 'wsUserID' => $user->getId(),
+ 'wsUserName' => $user->getName(),
+ 'wsToken' => $user->getToken(),
+ 'foo' => 'foo 2!',
+ ], $backend->getData() );
+
+ $provider->persistSession( $backend, $this->getSentRequest() );
+ }
+
+ public function testUnpersistSession() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $this->getConfig() );
+ $provider->setManager( SessionManager::singleton() );
+
+ $request = new \FauxRequest();
+ $provider->unpersistSession( $request );
+ $this->assertSame( '', $request->response()->getCookie( 'MySessionName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+ $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+ $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+
+ $provider->unpersistSession( $this->getSentRequest() );
+ }
+
+ public function testSetLoggedOutCookie() {
+ $provider = TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] ) );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $this->getConfig() );
+ $provider->setManager( SessionManager::singleton() );
+
+ $t1 = time();
+ $t2 = time() - 86400 * 2;
+
+ // Set it
+ $request = new \FauxRequest();
+ $provider->setLoggedOutCookie( $t1, $request );
+ $this->assertSame( (string)$t1, $request->response()->getCookie( 'xLoggedOut' ) );
+
+ // Too old
+ $request = new \FauxRequest();
+ $provider->setLoggedOutCookie( $t2, $request );
+ $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+
+ // Don't reset if it's already set
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'xLoggedOut' => $t1,
+ ], '' );
+ $provider->setLoggedOutCookie( $t1, $request );
+ $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+ }
+
+ /**
+ * To be mocked for hooks, since PHPUnit can't otherwise mock methods that
+ * take references.
+ */
+ public function onUserSetCookies( $user, &$sessionData, &$cookies ) {
+ }
+
+ public function testGetCookie() {
+ $provider = new CookieSessionProvider( [
+ 'priority' => 1,
+ 'sessionName' => 'MySessionName',
+ 'cookieOptions' => [ 'prefix' => 'x' ],
+ ] );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $this->getConfig() );
+ $provider->setManager( SessionManager::singleton() );
+ $provider = TestingAccessWrapper::newFromObject( $provider );
+
+ $request = new \FauxRequest();
+ $request->setCookies( [
+ 'xFoo' => 'foo!',
+ 'xBar' => 'deleted',
+ ], '' );
+ $this->assertSame( 'foo!', $provider->getCookie( $request, 'Foo', 'x' ) );
+ $this->assertNull( $provider->getCookie( $request, 'Bar', 'x' ) );
+ $this->assertNull( $provider->getCookie( $request, 'Baz', 'x' ) );
+ }
+
+ public function testGetRememberUserDuration() {
+ $config = $this->getConfig();
+ $provider = new CookieSessionProvider( [ 'priority' => 10 ] );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $config );
+ $provider->setManager( SessionManager::singleton() );
+
+ $this->assertSame( 200, $provider->getRememberUserDuration() );
+
+ $config->set( 'ExtendedLoginCookieExpiration', null );
+
+ $this->assertSame( 100, $provider->getRememberUserDuration() );
+
+ $config->set( 'ExtendedLoginCookieExpiration', 0 );
+
+ $this->assertSame( null, $provider->getRememberUserDuration() );
+ }
+
+ public function testGetLoginCookieExpiration() {
+ $config = $this->getConfig();
+ $provider = TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
+ 'priority' => 10
+ ] ) );
+ $provider->setLogger( new \Psr\Log\NullLogger() );
+ $provider->setConfig( $config );
+ $provider->setManager( SessionManager::singleton() );
+
+ // First cookie is an extended cookie, remember me true
+ $this->assertSame( 200, $provider->getLoginCookieExpiration( 'Token', true ) );
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', true ) );
+
+ // First cookie is an extended cookie, remember me false
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'UserID', false ) );
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', false ) );
+
+ $config->set( 'ExtendedLoginCookieExpiration', null );
+
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'Token', true ) );
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', true ) );
+
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'Token', false ) );
+ $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', false ) );
+ }
+}