Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / includes / session / ImmutableSessionProviderWithCookieTest.php
blobc9d1edff4ae15e8f450bcc4ea5d5952be34581e3
1 <?php
3 namespace MediaWiki\Tests\Session;
5 use ArrayUtils;
6 use BadMethodCallException;
7 use InvalidArgumentException;
8 use MediaWiki\Config\HashConfig;
9 use MediaWiki\MainConfigNames;
10 use MediaWiki\Message\Message;
11 use MediaWiki\Request\FauxRequest;
12 use MediaWiki\Request\FauxResponse;
13 use MediaWiki\Session\ImmutableSessionProviderWithCookie;
14 use MediaWiki\Session\SessionBackend;
15 use MediaWiki\Session\SessionId;
16 use MediaWiki\Session\SessionInfo;
17 use MediaWiki\Session\SessionManager;
18 use MediaWiki\Session\UserInfo;
19 use MediaWikiIntegrationTestCase;
20 use Psr\Log\NullLogger;
21 use TestLogger;
22 use Wikimedia\TestingAccessWrapper;
24 /**
25 * @group Session
26 * @group Database
27 * @covers \MediaWiki\Session\ImmutableSessionProviderWithCookie
29 class ImmutableSessionProviderWithCookieTest extends MediaWikiIntegrationTestCase {
30 use SessionProviderTestTrait;
32 private function getProvider( $name, $prefix = null, $forceHTTPS = false, $logger = null ) {
33 $config = new HashConfig();
34 $config->set( MainConfigNames::CookiePrefix, 'wgCookiePrefix' );
35 $config->set( MainConfigNames::ForceHTTPS, $forceHTTPS );
37 $params = [
38 'sessionCookieName' => $name,
39 'sessionCookieOptions' => [],
41 if ( $prefix !== null ) {
42 $params['sessionCookieOptions']['prefix'] = $prefix;
45 $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
46 ->setConstructorArgs( [ $params ] )
47 ->getMockForAbstractClass();
48 $this->initProvider( $provider, $logger ?? new TestLogger(), $config, new SessionManager() );
50 return $provider;
53 public function testConstructor() {
54 $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
55 ->getMockForAbstractClass();
56 $priv = TestingAccessWrapper::newFromObject( $provider );
57 $this->assertNull( $priv->sessionCookieName );
58 $this->assertSame( [], $priv->sessionCookieOptions );
60 $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
61 ->setConstructorArgs( [ [
62 'sessionCookieName' => 'Foo',
63 'sessionCookieOptions' => [ 'Bar' ],
64 ] ] )
65 ->getMockForAbstractClass();
66 $priv = TestingAccessWrapper::newFromObject( $provider );
67 $this->assertSame( 'Foo', $priv->sessionCookieName );
68 $this->assertSame( [ 'Bar' ], $priv->sessionCookieOptions );
70 try {
71 $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
72 ->setConstructorArgs( [ [
73 'sessionCookieName' => false,
74 ] ] )
75 ->getMockForAbstractClass();
76 $this->fail( 'Expected exception not thrown' );
77 } catch ( InvalidArgumentException $ex ) {
78 $this->assertSame(
79 'sessionCookieName must be a string',
80 $ex->getMessage()
84 try {
85 $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
86 ->setConstructorArgs( [ [
87 'sessionCookieOptions' => 'x',
88 ] ] )
89 ->getMockForAbstractClass();
90 $this->fail( 'Expected exception not thrown' );
91 } catch ( InvalidArgumentException $ex ) {
92 $this->assertSame(
93 'sessionCookieOptions must be an array',
94 $ex->getMessage()
99 public function testBasics() {
100 $provider = $this->getProvider( null );
101 $this->assertFalse( $provider->persistsSessionId() );
102 $this->assertFalse( $provider->canChangeUser() );
104 $provider = $this->getProvider( 'Foo' );
105 $this->assertTrue( $provider->persistsSessionId() );
106 $this->assertFalse( $provider->canChangeUser() );
108 $msg = $provider->whyNoSession();
109 $this->assertInstanceOf( Message::class, $msg );
110 $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
113 public function testGetVaryCookies() {
114 $provider = $this->getProvider( null );
115 $this->assertSame( [], $provider->getVaryCookies() );
117 $provider = $this->getProvider( 'Foo' );
118 $this->assertSame( [ 'wgCookiePrefixFoo' ], $provider->getVaryCookies() );
120 $provider = $this->getProvider( 'Foo', 'Bar' );
121 $this->assertSame( [ 'BarFoo' ], $provider->getVaryCookies() );
123 $provider = $this->getProvider( 'Foo', '' );
124 $this->assertSame( [ 'Foo' ], $provider->getVaryCookies() );
127 public function testGetSessionIdFromCookie() {
128 $this->overrideConfigValue( MainConfigNames::CookiePrefix, 'wgCookiePrefix' );
129 $request = new FauxRequest();
130 $request->setCookies( [
131 '' => 'empty---------------------------',
132 'Foo' => 'foo-----------------------------',
133 'wgCookiePrefixFoo' => 'wgfoo---------------------------',
134 'BarFoo' => 'foobar--------------------------',
135 'bad' => 'bad',
136 ], '' );
138 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( null ) );
139 try {
140 $provider->getSessionIdFromCookie( $request );
141 $this->fail( 'Expected exception not thrown' );
142 } catch ( BadMethodCallException $ex ) {
143 $this->assertSame(
144 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie::getSessionIdFromCookie ' .
145 'may not be called when $this->sessionCookieName === null',
146 $ex->getMessage()
150 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo' ) );
151 $this->assertSame(
152 'wgfoo---------------------------',
153 $provider->getSessionIdFromCookie( $request )
156 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', 'Bar' ) );
157 $this->assertSame(
158 'foobar--------------------------',
159 $provider->getSessionIdFromCookie( $request )
162 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', '' ) );
163 $this->assertSame(
164 'foo-----------------------------',
165 $provider->getSessionIdFromCookie( $request )
168 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'bad', '' ) );
169 $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
171 $provider = TestingAccessWrapper::newFromObject( $this->getProvider( 'none', '' ) );
172 $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
175 protected function getSentRequest() {
176 $sentResponse = $this->getMockBuilder( FauxResponse::class )
177 ->onlyMethods( [ 'headersSent', 'setCookie', 'header' ] )
178 ->getMock();
179 $sentResponse->method( 'headersSent' )
180 ->willReturn( true );
181 $sentResponse->expects( $this->never() )->method( 'setCookie' );
182 $sentResponse->expects( $this->never() )->method( 'header' );
184 $sentRequest = $this->getMockBuilder( FauxRequest::class )
185 ->onlyMethods( [ 'response' ] )->getMock();
186 $sentRequest->method( 'response' )
187 ->willReturn( $sentResponse );
188 return $sentRequest;
192 * @dataProvider providePersistSession
193 * @param bool $secure
194 * @param bool $remember
195 * @param bool $forceHTTPS
197 public function testPersistSession( $secure, $remember, $forceHTTPS ) {
198 $this->overrideConfigValues( [
199 MainConfigNames::CookieExpiration => 100,
200 MainConfigNames::SecureLogin => false,
201 MainConfigNames::ForceHTTPS => $forceHTTPS,
202 ] );
204 $provider = $this->getProvider( 'session', null, $forceHTTPS, new NullLogger() );
205 $priv = TestingAccessWrapper::newFromObject( $provider );
206 $priv->sessionCookieOptions = [
207 'prefix' => 'x',
208 'path' => 'CookiePath',
209 'domain' => 'CookieDomain',
210 'secure' => false,
211 'httpOnly' => true,
214 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
215 $user = $this->getTestSysop()->getUser();
216 $this->assertSame( $forceHTTPS, $user->requiresHTTPS() );
218 $backend = new SessionBackend(
219 new SessionId( $sessionId ),
220 new SessionInfo( SessionInfo::MIN_PRIORITY, [
221 'provider' => $provider,
222 'id' => $sessionId,
223 'persisted' => true,
224 'userInfo' => UserInfo::newFromUser( $user, true ),
225 'idIsSafe' => true,
226 ] ),
227 new TestBagOStuff(),
228 new NullLogger(),
229 $this->createHookContainer(),
232 TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
233 $backend->setRememberUser( $remember );
234 $backend->setForceHTTPS( $secure );
236 // No cookie
237 $priv->sessionCookieName = null;
238 $request = new FauxRequest();
239 $provider->persistSession( $backend, $request );
240 $this->assertSame( [], $request->response()->getCookies() );
242 // Cookie
243 $priv->sessionCookieName = 'session';
244 $request = new FauxRequest();
245 $time = time();
246 $provider->persistSession( $backend, $request );
248 $cookie = $request->response()->getCookieData( 'xsession' );
249 $this->assertIsArray( $cookie );
250 if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
251 // Round expiry so we don't randomly fail if the seconds ticked during the test.
252 $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
254 $this->assertEquals( [
255 'value' => $sessionId,
256 'expire' => null,
257 'path' => 'CookiePath',
258 'domain' => 'CookieDomain',
259 'secure' => $secure || $forceHTTPS,
260 'httpOnly' => true,
261 'raw' => false,
262 ], $cookie );
264 $cookie = $request->response()->getCookieData( 'forceHTTPS' );
265 if ( $secure && !$forceHTTPS ) {
266 $this->assertIsArray( $cookie );
267 if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
268 // Round expiry so we don't randomly fail if the seconds ticked during the test.
269 $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
271 $this->assertEquals( [
272 'value' => 'true',
273 'expire' => null,
274 'path' => 'CookiePath',
275 'domain' => 'CookieDomain',
276 'secure' => false,
277 'httpOnly' => true,
278 'raw' => false,
279 ], $cookie );
280 } else {
281 $this->assertNull( $cookie );
284 // Headers sent
285 $request = $this->getSentRequest();
286 $provider->persistSession( $backend, $request );
287 $this->assertSame( [], $request->response()->getCookies() );
290 public static function providePersistSession() {
291 return ArrayUtils::cartesianProduct(
292 [ false, true ], // $secure
293 [ false, true ], // $remember
294 [ false, true ] // $forceHTTPS
298 public function testUnpersistSession() {
299 $provider = $this->getProvider( 'session', '', false, new NullLogger() );
300 $priv = TestingAccessWrapper::newFromObject( $provider );
302 // No cookie
303 $priv->sessionCookieName = null;
304 $request = new FauxRequest();
305 $provider->unpersistSession( $request );
306 $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
308 // Cookie
309 $priv->sessionCookieName = 'session';
310 $request = new FauxRequest();
311 $provider->unpersistSession( $request );
312 $this->assertSame( '', $request->response()->getCookie( 'session', '' ) );
314 // Headers sent
315 $request = $this->getSentRequest();
316 $provider->unpersistSession( $request );
317 $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );