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