Merge "phpunit: Avoid use of deprecated getMock for PHPUnit 5 compat"
[lhc/web/wiklou.git] / tests / phpunit / includes / session / CookieSessionProviderTest.php
1 <?php
2
3 namespace MediaWiki\Session;
4
5 use MediaWikiTestCase;
6 use User;
7 use Psr\Log\LogLevel;
8
9 /**
10 * @group Session
11 * @group Database
12 * @covers MediaWiki\Session\CookieSessionProvider
13 */
14 class CookieSessionProviderTest extends MediaWikiTestCase {
15
16 private function getConfig() {
17 return new \HashConfig( [
18 'CookiePrefix' => 'CookiePrefix',
19 'CookiePath' => 'CookiePath',
20 'CookieDomain' => 'CookieDomain',
21 'CookieSecure' => true,
22 'CookieHttpOnly' => true,
23 'SessionName' => false,
24 'CookieExpiration' => 100,
25 'ExtendedLoginCookieExpiration' => 200,
26 ] );
27 }
28
29 public function testConstructor() {
30 try {
31 new CookieSessionProvider();
32 $this->fail( 'Expected exception not thrown' );
33 } catch ( \InvalidArgumentException $ex ) {
34 $this->assertSame(
35 'MediaWiki\\Session\\CookieSessionProvider::__construct: priority must be specified',
36 $ex->getMessage()
37 );
38 }
39
40 try {
41 new CookieSessionProvider( [ 'priority' => 'foo' ] );
42 $this->fail( 'Expected exception not thrown' );
43 } catch ( \InvalidArgumentException $ex ) {
44 $this->assertSame(
45 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
46 $ex->getMessage()
47 );
48 }
49 try {
50 new CookieSessionProvider( [ 'priority' => SessionInfo::MIN_PRIORITY - 1 ] );
51 $this->fail( 'Expected exception not thrown' );
52 } catch ( \InvalidArgumentException $ex ) {
53 $this->assertSame(
54 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
55 $ex->getMessage()
56 );
57 }
58 try {
59 new CookieSessionProvider( [ 'priority' => SessionInfo::MAX_PRIORITY + 1 ] );
60 $this->fail( 'Expected exception not thrown' );
61 } catch ( \InvalidArgumentException $ex ) {
62 $this->assertSame(
63 'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
64 $ex->getMessage()
65 );
66 }
67
68 try {
69 new CookieSessionProvider( [ 'priority' => 1, 'cookieOptions' => null ] );
70 $this->fail( 'Expected exception not thrown' );
71 } catch ( \InvalidArgumentException $ex ) {
72 $this->assertSame(
73 'MediaWiki\\Session\\CookieSessionProvider::__construct: cookieOptions must be an array',
74 $ex->getMessage()
75 );
76 }
77
78 $config = $this->getConfig();
79 $p = \TestingAccessWrapper::newFromObject(
80 new CookieSessionProvider( [ 'priority' => 1 ] )
81 );
82 $p->setLogger( new \TestLogger() );
83 $p->setConfig( $config );
84 $this->assertEquals( 1, $p->priority );
85 $this->assertEquals( [
86 'callUserSetCookiesHook' => false,
87 'sessionName' => 'CookiePrefix_session',
88 ], $p->params );
89 $this->assertEquals( [
90 'prefix' => 'CookiePrefix',
91 'path' => 'CookiePath',
92 'domain' => 'CookieDomain',
93 'secure' => true,
94 'httpOnly' => true,
95 ], $p->cookieOptions );
96
97 $config->set( 'SessionName', 'SessionName' );
98 $p = \TestingAccessWrapper::newFromObject(
99 new CookieSessionProvider( [ 'priority' => 3 ] )
100 );
101 $p->setLogger( new \TestLogger() );
102 $p->setConfig( $config );
103 $this->assertEquals( 3, $p->priority );
104 $this->assertEquals( [
105 'callUserSetCookiesHook' => false,
106 'sessionName' => 'SessionName',
107 ], $p->params );
108 $this->assertEquals( [
109 'prefix' => 'CookiePrefix',
110 'path' => 'CookiePath',
111 'domain' => 'CookieDomain',
112 'secure' => true,
113 'httpOnly' => true,
114 ], $p->cookieOptions );
115
116 $p = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
117 'priority' => 10,
118 'callUserSetCookiesHook' => true,
119 'cookieOptions' => [
120 'prefix' => 'XPrefix',
121 'path' => 'XPath',
122 'domain' => 'XDomain',
123 'secure' => 'XSecure',
124 'httpOnly' => 'XHttpOnly',
125 ],
126 'sessionName' => 'XSession',
127 ] ) );
128 $p->setLogger( new \TestLogger() );
129 $p->setConfig( $config );
130 $this->assertEquals( 10, $p->priority );
131 $this->assertEquals( [
132 'callUserSetCookiesHook' => true,
133 'sessionName' => 'XSession',
134 ], $p->params );
135 $this->assertEquals( [
136 'prefix' => 'XPrefix',
137 'path' => 'XPath',
138 'domain' => 'XDomain',
139 'secure' => 'XSecure',
140 'httpOnly' => 'XHttpOnly',
141 ], $p->cookieOptions );
142 }
143
144 public function testBasics() {
145 $provider = new CookieSessionProvider( [ 'priority' => 10 ] );
146
147 $this->assertTrue( $provider->persistsSessionId() );
148 $this->assertTrue( $provider->canChangeUser() );
149
150 $extendedCookies = [ 'UserID', 'UserName', 'Token' ];
151
152 $this->assertEquals(
153 $extendedCookies,
154 \TestingAccessWrapper::newFromObject( $provider )->getExtendedLoginCookies(),
155 'List of extended cookies (subclasses can add values, but we\'re calling the core one here)'
156 );
157
158 $msg = $provider->whyNoSession();
159 $this->assertInstanceOf( 'Message', $msg );
160 $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
161 }
162
163 public function testProvideSessionInfo() {
164 $params = [
165 'priority' => 20,
166 'sessionName' => 'session',
167 'cookieOptions' => [ 'prefix' => 'x' ],
168 ];
169 $provider = new CookieSessionProvider( $params );
170 $logger = new \TestLogger( true );
171 $provider->setLogger( $logger );
172 $provider->setConfig( $this->getConfig() );
173 $provider->setManager( new SessionManager() );
174
175 $user = static::getTestSysop()->getUser();
176 $id = $user->getId();
177 $name = $user->getName();
178 $token = $user->getToken( true );
179
180 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
181
182 // No data
183 $request = new \FauxRequest();
184 $info = $provider->provideSessionInfo( $request );
185 $this->assertNull( $info );
186 $this->assertSame( [], $logger->getBuffer() );
187 $logger->clearBuffer();
188
189 // Session key only
190 $request = new \FauxRequest();
191 $request->setCookies( [
192 'session' => $sessionId,
193 ], '' );
194 $info = $provider->provideSessionInfo( $request );
195 $this->assertNotNull( $info );
196 $this->assertSame( $params['priority'], $info->getPriority() );
197 $this->assertSame( $sessionId, $info->getId() );
198 $this->assertNotNull( $info->getUserInfo() );
199 $this->assertSame( 0, $info->getUserInfo()->getId() );
200 $this->assertNull( $info->getUserInfo()->getName() );
201 $this->assertFalse( $info->forceHTTPS() );
202 $this->assertSame( [
203 [
204 LogLevel::DEBUG,
205 'Session "{session}" requested without UserID cookie',
206 ],
207 ], $logger->getBuffer() );
208 $logger->clearBuffer();
209
210 // User, no session key
211 $request = new \FauxRequest();
212 $request->setCookies( [
213 'xUserID' => $id,
214 'xToken' => $token,
215 ], '' );
216 $info = $provider->provideSessionInfo( $request );
217 $this->assertNotNull( $info );
218 $this->assertSame( $params['priority'], $info->getPriority() );
219 $this->assertNotSame( $sessionId, $info->getId() );
220 $this->assertNotNull( $info->getUserInfo() );
221 $this->assertSame( $id, $info->getUserInfo()->getId() );
222 $this->assertSame( $name, $info->getUserInfo()->getName() );
223 $this->assertFalse( $info->forceHTTPS() );
224 $this->assertSame( [], $logger->getBuffer() );
225 $logger->clearBuffer();
226
227 // User and session key
228 $request = new \FauxRequest();
229 $request->setCookies( [
230 'session' => $sessionId,
231 'xUserID' => $id,
232 'xToken' => $token,
233 ], '' );
234 $info = $provider->provideSessionInfo( $request );
235 $this->assertNotNull( $info );
236 $this->assertSame( $params['priority'], $info->getPriority() );
237 $this->assertSame( $sessionId, $info->getId() );
238 $this->assertNotNull( $info->getUserInfo() );
239 $this->assertSame( $id, $info->getUserInfo()->getId() );
240 $this->assertSame( $name, $info->getUserInfo()->getName() );
241 $this->assertFalse( $info->forceHTTPS() );
242 $this->assertSame( [], $logger->getBuffer() );
243 $logger->clearBuffer();
244
245 // User with bad token
246 $request = new \FauxRequest();
247 $request->setCookies( [
248 'session' => $sessionId,
249 'xUserID' => $id,
250 'xToken' => 'BADTOKEN',
251 ], '' );
252 $info = $provider->provideSessionInfo( $request );
253 $this->assertNull( $info );
254 $this->assertSame( [
255 [
256 LogLevel::WARNING,
257 'Session "{session}" requested with invalid Token cookie.'
258 ],
259 ], $logger->getBuffer() );
260 $logger->clearBuffer();
261
262 // User id with no token
263 $request = new \FauxRequest();
264 $request->setCookies( [
265 'session' => $sessionId,
266 'xUserID' => $id,
267 ], '' );
268 $info = $provider->provideSessionInfo( $request );
269 $this->assertNotNull( $info );
270 $this->assertSame( $params['priority'], $info->getPriority() );
271 $this->assertSame( $sessionId, $info->getId() );
272 $this->assertNotNull( $info->getUserInfo() );
273 $this->assertFalse( $info->getUserInfo()->isVerified() );
274 $this->assertSame( $id, $info->getUserInfo()->getId() );
275 $this->assertSame( $name, $info->getUserInfo()->getName() );
276 $this->assertFalse( $info->forceHTTPS() );
277 $this->assertSame( [], $logger->getBuffer() );
278 $logger->clearBuffer();
279
280 $request = new \FauxRequest();
281 $request->setCookies( [
282 'xUserID' => $id,
283 ], '' );
284 $info = $provider->provideSessionInfo( $request );
285 $this->assertNull( $info );
286 $this->assertSame( [], $logger->getBuffer() );
287 $logger->clearBuffer();
288
289 // User and session key, with forceHTTPS flag
290 $request = new \FauxRequest();
291 $request->setCookies( [
292 'session' => $sessionId,
293 'xUserID' => $id,
294 'xToken' => $token,
295 'forceHTTPS' => true,
296 ], '' );
297 $info = $provider->provideSessionInfo( $request );
298 $this->assertNotNull( $info );
299 $this->assertSame( $params['priority'], $info->getPriority() );
300 $this->assertSame( $sessionId, $info->getId() );
301 $this->assertNotNull( $info->getUserInfo() );
302 $this->assertSame( $id, $info->getUserInfo()->getId() );
303 $this->assertSame( $name, $info->getUserInfo()->getName() );
304 $this->assertTrue( $info->forceHTTPS() );
305 $this->assertSame( [], $logger->getBuffer() );
306 $logger->clearBuffer();
307
308 // Invalid user id
309 $request = new \FauxRequest();
310 $request->setCookies( [
311 'session' => $sessionId,
312 'xUserID' => '-1',
313 ], '' );
314 $info = $provider->provideSessionInfo( $request );
315 $this->assertNull( $info );
316 $this->assertSame( [], $logger->getBuffer() );
317 $logger->clearBuffer();
318
319 // User id with matching name
320 $request = new \FauxRequest();
321 $request->setCookies( [
322 'session' => $sessionId,
323 'xUserID' => $id,
324 'xUserName' => $name,
325 ], '' );
326 $info = $provider->provideSessionInfo( $request );
327 $this->assertNotNull( $info );
328 $this->assertSame( $params['priority'], $info->getPriority() );
329 $this->assertSame( $sessionId, $info->getId() );
330 $this->assertNotNull( $info->getUserInfo() );
331 $this->assertFalse( $info->getUserInfo()->isVerified() );
332 $this->assertSame( $id, $info->getUserInfo()->getId() );
333 $this->assertSame( $name, $info->getUserInfo()->getName() );
334 $this->assertFalse( $info->forceHTTPS() );
335 $this->assertSame( [], $logger->getBuffer() );
336 $logger->clearBuffer();
337
338 // User id with wrong name
339 $request = new \FauxRequest();
340 $request->setCookies( [
341 'session' => $sessionId,
342 'xUserID' => $id,
343 'xUserName' => 'Wrong',
344 ], '' );
345 $info = $provider->provideSessionInfo( $request );
346 $this->assertNull( $info );
347 $this->assertSame( [
348 [
349 LogLevel::WARNING,
350 'Session "{session}" requested with mismatched UserID and UserName cookies.',
351 ],
352 ], $logger->getBuffer() );
353 $logger->clearBuffer();
354 }
355
356 public function testGetVaryCookies() {
357 $provider = new CookieSessionProvider( [
358 'priority' => 1,
359 'sessionName' => 'MySessionName',
360 'cookieOptions' => [ 'prefix' => 'MyCookiePrefix' ],
361 ] );
362 $this->assertArrayEquals( [
363 'MyCookiePrefixToken',
364 'MyCookiePrefixLoggedOut',
365 'MySessionName',
366 'forceHTTPS',
367 ], $provider->getVaryCookies() );
368 }
369
370 public function testSuggestLoginUsername() {
371 $provider = new CookieSessionProvider( [
372 'priority' => 1,
373 'sessionName' => 'MySessionName',
374 'cookieOptions' => [ 'prefix' => 'x' ],
375 ] );
376
377 $request = new \FauxRequest();
378 $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
379
380 $request->setCookies( [
381 'xUserName' => 'Example',
382 ], '' );
383 $this->assertEquals( 'Example', $provider->suggestLoginUsername( $request ) );
384 }
385
386 public function testPersistSession() {
387 $provider = new CookieSessionProvider( [
388 'priority' => 1,
389 'sessionName' => 'MySessionName',
390 'callUserSetCookiesHook' => false,
391 'cookieOptions' => [ 'prefix' => 'x' ],
392 ] );
393 $config = $this->getConfig();
394 $provider->setLogger( new \TestLogger() );
395 $provider->setConfig( $config );
396 $provider->setManager( SessionManager::singleton() );
397
398 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
399 $store = new TestBagOStuff();
400 $user = static::getTestSysop()->getUser();
401 $anon = new User;
402
403 $backend = new SessionBackend(
404 new SessionId( $sessionId ),
405 new SessionInfo( SessionInfo::MIN_PRIORITY, [
406 'provider' => $provider,
407 'id' => $sessionId,
408 'persisted' => true,
409 'idIsSafe' => true,
410 ] ),
411 $store,
412 new \Psr\Log\NullLogger(),
413 10
414 );
415 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
416
417 $mock = $this->getMockBuilder( 'stdClass' )
418 ->setMethods( [ 'onUserSetCookies' ] )
419 ->getMock();
420 $mock->expects( $this->never() )->method( 'onUserSetCookies' );
421 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
422
423 // Anonymous user
424 $backend->setUser( $anon );
425 $backend->setRememberUser( true );
426 $backend->setForceHTTPS( false );
427 $request = new \FauxRequest();
428 $provider->persistSession( $backend, $request );
429 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
430 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
431 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
432 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
433 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
434 $this->assertSame( [], $backend->getData() );
435
436 // Logged-in user, no remember
437 $backend->setUser( $user );
438 $backend->setRememberUser( false );
439 $backend->setForceHTTPS( false );
440 $request = new \FauxRequest();
441 $provider->persistSession( $backend, $request );
442 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
443 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
444 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
445 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
446 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
447 $this->assertSame( [], $backend->getData() );
448
449 // Logged-in user, remember
450 $backend->setUser( $user );
451 $backend->setRememberUser( true );
452 $backend->setForceHTTPS( true );
453 $request = new \FauxRequest();
454 $time = time();
455 $provider->persistSession( $backend, $request );
456 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
457 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
458 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
459 $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
460 $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
461 $this->assertSame( [], $backend->getData() );
462 }
463
464 /**
465 * @dataProvider provideCookieData
466 * @param bool $secure
467 * @param bool $remember
468 */
469 public function testCookieData( $secure, $remember ) {
470 $this->setMwGlobals( [
471 'wgSecureLogin' => false,
472 ] );
473
474 $provider = new CookieSessionProvider( [
475 'priority' => 1,
476 'sessionName' => 'MySessionName',
477 'callUserSetCookiesHook' => false,
478 'cookieOptions' => [ 'prefix' => 'x' ],
479 ] );
480 $config = $this->getConfig();
481 $config->set( 'CookieSecure', $secure );
482 $provider->setLogger( new \TestLogger() );
483 $provider->setConfig( $config );
484 $provider->setManager( SessionManager::singleton() );
485
486 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
487 $user = static::getTestSysop()->getUser();
488 $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
489
490 $backend = new SessionBackend(
491 new SessionId( $sessionId ),
492 new SessionInfo( SessionInfo::MIN_PRIORITY, [
493 'provider' => $provider,
494 'id' => $sessionId,
495 'persisted' => true,
496 'idIsSafe' => true,
497 ] ),
498 new TestBagOStuff(),
499 new \Psr\Log\NullLogger(),
500 10
501 );
502 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
503 $backend->setUser( $user );
504 $backend->setRememberUser( $remember );
505 $backend->setForceHTTPS( $secure );
506 $request = new \FauxRequest();
507 $time = time();
508 $provider->persistSession( $backend, $request );
509
510 $defaults = [
511 'expire' => (int)100,
512 'path' => $config->get( 'CookiePath' ),
513 'domain' => $config->get( 'CookieDomain' ),
514 'secure' => $secure,
515 'httpOnly' => $config->get( 'CookieHttpOnly' ),
516 'raw' => false,
517 ];
518
519 $normalExpiry = $config->get( 'CookieExpiration' );
520 $extendedExpiry = $config->get( 'ExtendedLoginCookieExpiration' );
521 $extendedExpiry = (int)( $extendedExpiry === null ? 0 : $extendedExpiry );
522 $expect = [
523 'MySessionName' => [
524 'value' => (string)$sessionId,
525 'expire' => 0,
526 ] + $defaults,
527 'xUserID' => [
528 'value' => (string)$user->getId(),
529 'expire' => $remember ? $extendedExpiry : $normalExpiry,
530 ] + $defaults,
531 'xUserName' => [
532 'value' => $user->getName(),
533 'expire' => $remember ? $extendedExpiry : $normalExpiry
534 ] + $defaults,
535 'xToken' => [
536 'value' => $remember ? $user->getToken() : '',
537 'expire' => $remember ? $extendedExpiry : -31536000,
538 ] + $defaults,
539 'forceHTTPS' => [
540 'value' => $secure ? 'true' : '',
541 'secure' => false,
542 'expire' => $secure ? $remember ? $defaults['expire'] : 0 : -31536000,
543 ] + $defaults,
544 ];
545 foreach ( $expect as $key => $value ) {
546 $actual = $request->response()->getCookieData( $key );
547 if ( $actual && $actual['expire'] > 0 ) {
548 // Round expiry so we don't randomly fail if the seconds ticked during the test.
549 $actual['expire'] = round( $actual['expire'] - $time, -2 );
550 }
551 $this->assertEquals( $value, $actual, "Cookie $key" );
552 }
553 }
554
555 public static function provideCookieData() {
556 return [
557 [ false, false ],
558 [ false, true ],
559 [ true, false ],
560 [ true, true ],
561 ];
562 }
563
564 protected function getSentRequest() {
565 $sentResponse = $this->getMockBuilder( 'FauxResponse' )
566 ->setMethods( [ 'headersSent', 'setCookie', 'header' ] )->getMock();
567 $sentResponse->expects( $this->any() )->method( 'headersSent' )
568 ->will( $this->returnValue( true ) );
569 $sentResponse->expects( $this->never() )->method( 'setCookie' );
570 $sentResponse->expects( $this->never() )->method( 'header' );
571
572 $sentRequest = $this->getMockBuilder( 'FauxRequest' )
573 ->setMethods( [ 'response' ] )->getMock();
574 $sentRequest->expects( $this->any() )->method( 'response' )
575 ->will( $this->returnValue( $sentResponse ) );
576 return $sentRequest;
577 }
578
579 public function testPersistSessionWithHook() {
580 $provider = new CookieSessionProvider( [
581 'priority' => 1,
582 'sessionName' => 'MySessionName',
583 'callUserSetCookiesHook' => true,
584 'cookieOptions' => [ 'prefix' => 'x' ],
585 ] );
586 $provider->setLogger( new \Psr\Log\NullLogger() );
587 $provider->setConfig( $this->getConfig() );
588 $provider->setManager( SessionManager::singleton() );
589
590 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
591 $store = new TestBagOStuff();
592 $user = static::getTestSysop()->getUser();
593 $anon = new User;
594
595 $backend = new SessionBackend(
596 new SessionId( $sessionId ),
597 new SessionInfo( SessionInfo::MIN_PRIORITY, [
598 'provider' => $provider,
599 'id' => $sessionId,
600 'persisted' => true,
601 'idIsSafe' => true,
602 ] ),
603 $store,
604 new \Psr\Log\NullLogger(),
605 10
606 );
607 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
608
609 // Anonymous user
610 $mock = $this->getMockBuilder( 'stdClass' )
611 ->setMethods( [ 'onUserSetCookies' ] )->getMock();
612 $mock->expects( $this->never() )->method( 'onUserSetCookies' );
613 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
614 $backend->setUser( $anon );
615 $backend->setRememberUser( true );
616 $backend->setForceHTTPS( false );
617 $request = new \FauxRequest();
618 $provider->persistSession( $backend, $request );
619 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
620 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
621 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
622 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
623 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
624 $this->assertSame( [], $backend->getData() );
625
626 $provider->persistSession( $backend, $this->getSentRequest() );
627
628 // Logged-in user, no remember
629 $mock = $this->getMockBuilder( __CLASS__ )
630 ->setMethods( [ 'onUserSetCookies' ] )->getMock();
631 $mock->expects( $this->once() )->method( 'onUserSetCookies' )
632 ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
633 $this->assertSame( $user, $u );
634 $this->assertEquals( [
635 'wsUserID' => $user->getId(),
636 'wsUserName' => $user->getName(),
637 'wsToken' => $user->getToken(),
638 ], $sessionData );
639 $this->assertEquals( [
640 'UserID' => $user->getId(),
641 'UserName' => $user->getName(),
642 'Token' => false,
643 ], $cookies );
644
645 $sessionData['foo'] = 'foo!';
646 $cookies['bar'] = 'bar!';
647 return true;
648 } ) );
649 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
650 $backend->setUser( $user );
651 $backend->setRememberUser( false );
652 $backend->setForceHTTPS( false );
653 $backend->setLoggedOutTimestamp( $loggedOut = time() );
654 $request = new \FauxRequest();
655 $provider->persistSession( $backend, $request );
656 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
657 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
658 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
659 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
660 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
661 $this->assertSame( 'bar!', $request->response()->getCookie( 'xbar' ) );
662 $this->assertSame( (string)$loggedOut, $request->response()->getCookie( 'xLoggedOut' ) );
663 $this->assertEquals( [
664 'wsUserID' => $user->getId(),
665 'wsUserName' => $user->getName(),
666 'wsToken' => $user->getToken(),
667 'foo' => 'foo!',
668 ], $backend->getData() );
669
670 $provider->persistSession( $backend, $this->getSentRequest() );
671
672 // Logged-in user, remember
673 $mock = $this->getMockBuilder( __CLASS__ )
674 ->setMethods( [ 'onUserSetCookies' ] )->getMock();
675 $mock->expects( $this->once() )->method( 'onUserSetCookies' )
676 ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
677 $this->assertSame( $user, $u );
678 $this->assertEquals( [
679 'wsUserID' => $user->getId(),
680 'wsUserName' => $user->getName(),
681 'wsToken' => $user->getToken(),
682 ], $sessionData );
683 $this->assertEquals( [
684 'UserID' => $user->getId(),
685 'UserName' => $user->getName(),
686 'Token' => $user->getToken(),
687 ], $cookies );
688
689 $sessionData['foo'] = 'foo 2!';
690 $cookies['bar'] = 'bar 2!';
691 return true;
692 } ) );
693 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
694 $backend->setUser( $user );
695 $backend->setRememberUser( true );
696 $backend->setForceHTTPS( true );
697 $backend->setLoggedOutTimestamp( 0 );
698 $request = new \FauxRequest();
699 $provider->persistSession( $backend, $request );
700 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
701 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
702 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
703 $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
704 $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
705 $this->assertSame( 'bar 2!', $request->response()->getCookie( 'xbar' ) );
706 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
707 $this->assertEquals( [
708 'wsUserID' => $user->getId(),
709 'wsUserName' => $user->getName(),
710 'wsToken' => $user->getToken(),
711 'foo' => 'foo 2!',
712 ], $backend->getData() );
713
714 $provider->persistSession( $backend, $this->getSentRequest() );
715 }
716
717 public function testUnpersistSession() {
718 $provider = new CookieSessionProvider( [
719 'priority' => 1,
720 'sessionName' => 'MySessionName',
721 'cookieOptions' => [ 'prefix' => 'x' ],
722 ] );
723 $provider->setLogger( new \Psr\Log\NullLogger() );
724 $provider->setConfig( $this->getConfig() );
725 $provider->setManager( SessionManager::singleton() );
726
727 $request = new \FauxRequest();
728 $provider->unpersistSession( $request );
729 $this->assertSame( '', $request->response()->getCookie( 'MySessionName' ) );
730 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
731 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
732 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
733 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
734
735 $provider->unpersistSession( $this->getSentRequest() );
736 }
737
738 public function testSetLoggedOutCookie() {
739 $provider = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
740 'priority' => 1,
741 'sessionName' => 'MySessionName',
742 'cookieOptions' => [ 'prefix' => 'x' ],
743 ] ) );
744 $provider->setLogger( new \Psr\Log\NullLogger() );
745 $provider->setConfig( $this->getConfig() );
746 $provider->setManager( SessionManager::singleton() );
747
748 $t1 = time();
749 $t2 = time() - 86400 * 2;
750
751 // Set it
752 $request = new \FauxRequest();
753 $provider->setLoggedOutCookie( $t1, $request );
754 $this->assertSame( (string)$t1, $request->response()->getCookie( 'xLoggedOut' ) );
755
756 // Too old
757 $request = new \FauxRequest();
758 $provider->setLoggedOutCookie( $t2, $request );
759 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
760
761 // Don't reset if it's already set
762 $request = new \FauxRequest();
763 $request->setCookies( [
764 'xLoggedOut' => $t1,
765 ], '' );
766 $provider->setLoggedOutCookie( $t1, $request );
767 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
768 }
769
770 /**
771 * To be mocked for hooks, since PHPUnit can't otherwise mock methods that
772 * take references.
773 */
774 public function onUserSetCookies( $user, &$sessionData, &$cookies ) {
775 }
776
777 public function testGetCookie() {
778 $provider = new CookieSessionProvider( [
779 'priority' => 1,
780 'sessionName' => 'MySessionName',
781 'cookieOptions' => [ 'prefix' => 'x' ],
782 ] );
783 $provider->setLogger( new \Psr\Log\NullLogger() );
784 $provider->setConfig( $this->getConfig() );
785 $provider->setManager( SessionManager::singleton() );
786 $provider = \TestingAccessWrapper::newFromObject( $provider );
787
788 $request = new \FauxRequest();
789 $request->setCookies( [
790 'xFoo' => 'foo!',
791 'xBar' => 'deleted',
792 ], '' );
793 $this->assertSame( 'foo!', $provider->getCookie( $request, 'Foo', 'x' ) );
794 $this->assertNull( $provider->getCookie( $request, 'Bar', 'x' ) );
795 $this->assertNull( $provider->getCookie( $request, 'Baz', 'x' ) );
796 }
797
798 public function testGetRememberUserDuration() {
799 $config = $this->getConfig();
800 $provider = new CookieSessionProvider( [ 'priority' => 10 ] );
801 $provider->setLogger( new \Psr\Log\NullLogger() );
802 $provider->setConfig( $config );
803 $provider->setManager( SessionManager::singleton() );
804
805 $this->assertSame( 200, $provider->getRememberUserDuration() );
806
807 $config->set( 'ExtendedLoginCookieExpiration', null );
808
809 $this->assertSame( 100, $provider->getRememberUserDuration() );
810
811 $config->set( 'ExtendedLoginCookieExpiration', 0 );
812
813 $this->assertSame( null, $provider->getRememberUserDuration() );
814 }
815
816 public function testGetLoginCookieExpiration() {
817 $config = $this->getConfig();
818 $provider = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
819 'priority' => 10
820 ] ) );
821 $provider->setLogger( new \Psr\Log\NullLogger() );
822 $provider->setConfig( $config );
823 $provider->setManager( SessionManager::singleton() );
824
825 // First cookie is an extended cookie, remember me true
826 $this->assertSame( 200, $provider->getLoginCookieExpiration( 'Token', true ) );
827 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', true ) );
828
829 // First cookie is an extended cookie, remember me false
830 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'UserID', false ) );
831 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', false ) );
832
833 $config->set( 'ExtendedLoginCookieExpiration', null );
834
835 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'Token', true ) );
836 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', true ) );
837
838 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'Token', false ) );
839 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User', false ) );
840 }
841 }