wiki.techinc.nl/tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php
Reedy 8c39aab84b Remove or replace usages of "sanity"
Bug: T254646
Change-Id: Ib192dc5704a14d02c7c374d0ab29bac55c5df24a
2021-11-21 19:35:49 +00:00

307 lines
9.7 KiB
PHP

<?php
namespace MediaWiki\Session;
use MediaWikiIntegrationTestCase;
use Psr\Log\NullLogger;
use TestLogger;
use User;
use Wikimedia\TestingAccessWrapper;
/**
* @group Session
* @group Database
* @covers MediaWiki\Session\ImmutableSessionProviderWithCookie
*/
class ImmutableSessionProviderWithCookieTest extends MediaWikiIntegrationTestCase {
use SessionProviderTestTrait;
private function getProvider( $name, $prefix = null, $forceHTTPS = false, $logger = null ) {
$config = new \HashConfig();
$config->set( 'CookiePrefix', 'wgCookiePrefix' );
$config->set( 'ForceHTTPS', $forceHTTPS );
$params = [
'sessionCookieName' => $name,
'sessionCookieOptions' => [],
];
if ( $prefix !== null ) {
$params['sessionCookieOptions']['prefix'] = $prefix;
}
$provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
->setConstructorArgs( [ $params ] )
->getMockForAbstractClass();
$this->initProvider( $provider, $logger ?? new TestLogger(), $config, new SessionManager() );
return $provider;
}
public function testConstructor() {
$provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
->getMockForAbstractClass();
$priv = TestingAccessWrapper::newFromObject( $provider );
$this->assertNull( $priv->sessionCookieName );
$this->assertSame( [], $priv->sessionCookieOptions );
$provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
->setConstructorArgs( [ [
'sessionCookieName' => 'Foo',
'sessionCookieOptions' => [ 'Bar' ],
] ] )
->getMockForAbstractClass();
$priv = TestingAccessWrapper::newFromObject( $provider );
$this->assertSame( 'Foo', $priv->sessionCookieName );
$this->assertSame( [ 'Bar' ], $priv->sessionCookieOptions );
try {
$provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
->setConstructorArgs( [ [
'sessionCookieName' => false,
] ] )
->getMockForAbstractClass();
$this->fail( 'Expected exception not thrown' );
} catch ( \InvalidArgumentException $ex ) {
$this->assertSame(
'sessionCookieName must be a string',
$ex->getMessage()
);
}
try {
$provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
->setConstructorArgs( [ [
'sessionCookieOptions' => 'x',
] ] )
->getMockForAbstractClass();
$this->fail( 'Expected exception not thrown' );
} catch ( \InvalidArgumentException $ex ) {
$this->assertSame(
'sessionCookieOptions must be an array',
$ex->getMessage()
);
}
}
public function testBasics() {
$provider = $this->getProvider( null );
$this->assertFalse( $provider->persistsSessionID() );
$this->assertFalse( $provider->canChangeUser() );
$provider = $this->getProvider( 'Foo' );
$this->assertTrue( $provider->persistsSessionID() );
$this->assertFalse( $provider->canChangeUser() );
$msg = $provider->whyNoSession();
$this->assertInstanceOf( \Message::class, $msg );
$this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
}
public function testGetVaryCookies() {
$provider = $this->getProvider( null );
$this->assertSame( [], $provider->getVaryCookies() );
$provider = $this->getProvider( 'Foo' );
$this->assertSame( [ 'wgCookiePrefixFoo' ], $provider->getVaryCookies() );
$provider = $this->getProvider( 'Foo', 'Bar' );
$this->assertSame( [ 'BarFoo' ], $provider->getVaryCookies() );
$provider = $this->getProvider( 'Foo', '' );
$this->assertSame( [ 'Foo' ], $provider->getVaryCookies() );
}
public function testGetSessionIdFromCookie() {
$this->setMwGlobals( 'wgCookiePrefix', 'wgCookiePrefix' );
$request = new \FauxRequest();
$request->setCookies( [
'' => 'empty---------------------------',
'Foo' => 'foo-----------------------------',
'wgCookiePrefixFoo' => 'wgfoo---------------------------',
'BarFoo' => 'foobar--------------------------',
'bad' => 'bad',
], '' );
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( null ) );
try {
$provider->getSessionIdFromCookie( $request );
$this->fail( 'Expected exception not thrown' );
} catch ( \BadMethodCallException $ex ) {
$this->assertSame(
'MediaWiki\\Session\\ImmutableSessionProviderWithCookie::getSessionIdFromCookie ' .
'may not be called when $this->sessionCookieName === null',
$ex->getMessage()
);
}
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo' ) );
$this->assertSame(
'wgfoo---------------------------',
$provider->getSessionIdFromCookie( $request )
);
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', 'Bar' ) );
$this->assertSame(
'foobar--------------------------',
$provider->getSessionIdFromCookie( $request )
);
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', '' ) );
$this->assertSame(
'foo-----------------------------',
$provider->getSessionIdFromCookie( $request )
);
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'bad', '' ) );
$this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
$provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'none', '' ) );
$this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
}
protected function getSentRequest() {
$sentResponse = $this->getMockBuilder( \FauxResponse::class )
->onlyMethods( [ 'headersSent', 'setCookie', 'header' ] )
->getMock();
$sentResponse->method( 'headersSent' )
->willReturn( true );
$sentResponse->expects( $this->never() )->method( 'setCookie' );
$sentResponse->expects( $this->never() )->method( 'header' );
$sentRequest = $this->getMockBuilder( \FauxRequest::class )
->onlyMethods( [ 'response' ] )->getMock();
$sentRequest->method( 'response' )
->willReturn( $sentResponse );
return $sentRequest;
}
/**
* @dataProvider providePersistSession
* @param bool $secure
* @param bool $remember
* @param bool $forceHTTPS
*/
public function testPersistSession( $secure, $remember, $forceHTTPS ) {
$this->setMwGlobals( [
'wgCookieExpiration' => 100,
'wgSecureLogin' => false,
'wgForceHTTPS' => $forceHTTPS,
] );
$provider = $this->getProvider( 'session', null, $forceHTTPS, new NullLogger() );
$priv = TestingAccessWrapper::newFromObject( $provider );
$priv->sessionCookieOptions = [
'prefix' => 'x',
'path' => 'CookiePath',
'domain' => 'CookieDomain',
'secure' => false,
'httpOnly' => true,
];
$sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
$user = User::newFromName( 'UTSysop' );
$this->assertSame( $forceHTTPS, $user->requiresHTTPS() );
$backend = new SessionBackend(
new SessionId( $sessionId ),
new SessionInfo( SessionInfo::MIN_PRIORITY, [
'provider' => $provider,
'id' => $sessionId,
'persisted' => true,
'userInfo' => UserInfo::newFromUser( $user, true ),
'idIsSafe' => true,
] ),
new TestBagOStuff(),
new NullLogger(),
$this->createHookContainer(),
10
);
TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
$backend->setRememberUser( $remember );
$backend->setForceHTTPS( $secure );
// No cookie
$priv->sessionCookieName = null;
$request = new \FauxRequest();
$provider->persistSession( $backend, $request );
$this->assertSame( [], $request->response()->getCookies() );
// Cookie
$priv->sessionCookieName = 'session';
$request = new \FauxRequest();
$time = time();
$provider->persistSession( $backend, $request );
$cookie = $request->response()->getCookieData( 'xsession' );
$this->assertIsArray( $cookie );
if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
// Round expiry so we don't randomly fail if the seconds ticked during the test.
$cookie['expire'] = round( $cookie['expire'] - $time, -2 );
}
$this->assertEquals( [
'value' => $sessionId,
'expire' => null,
'path' => 'CookiePath',
'domain' => 'CookieDomain',
'secure' => $secure || $forceHTTPS,
'httpOnly' => true,
'raw' => false,
], $cookie );
$cookie = $request->response()->getCookieData( 'forceHTTPS' );
if ( $secure && !$forceHTTPS ) {
$this->assertIsArray( $cookie );
if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
// Round expiry so we don't randomly fail if the seconds ticked during the test.
$cookie['expire'] = round( $cookie['expire'] - $time, -2 );
}
$this->assertEquals( [
'value' => 'true',
'expire' => null,
'path' => 'CookiePath',
'domain' => 'CookieDomain',
'secure' => false,
'httpOnly' => true,
'raw' => false,
], $cookie );
} else {
$this->assertNull( $cookie );
}
// Headers sent
$request = $this->getSentRequest();
$provider->persistSession( $backend, $request );
$this->assertSame( [], $request->response()->getCookies() );
}
public static function providePersistSession() {
return \ArrayUtils::cartesianProduct(
[ false, true ], // $secure
[ false, true ], // $remember
[ false, true ] // $forceHTTPS
);
}
public function testUnpersistSession() {
$provider = $this->getProvider( 'session', '', false, new NullLogger() );
$priv = TestingAccessWrapper::newFromObject( $provider );
// No cookie
$priv->sessionCookieName = null;
$request = new \FauxRequest();
$provider->unpersistSession( $request );
$this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
// Cookie
$priv->sessionCookieName = 'session';
$request = new \FauxRequest();
$provider->unpersistSession( $request );
$this->assertSame( '', $request->response()->getCookie( 'session', '' ) );
// Headers sent
$request = $this->getSentRequest();
$provider->unpersistSession( $request );
$this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
}
}