Merge "Increase LocalFile lock() timeout and improve error message"
[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 'ExtendedLoginCookies' => [ 'UserID', 'Token' ],
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 $msg = $provider->whyNoSession();
152 $this->assertInstanceOf( 'Message', $msg );
153 $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
154 }
155
156 public function testProvideSessionInfo() {
157 $params = [
158 'priority' => 20,
159 'sessionName' => 'session',
160 'cookieOptions' => [ 'prefix' => 'x' ],
161 ];
162 $provider = new CookieSessionProvider( $params );
163 $logger = new \TestLogger( true );
164 $provider->setLogger( $logger );
165 $provider->setConfig( $this->getConfig() );
166 $provider->setManager( new SessionManager() );
167
168 $user = User::newFromName( 'UTSysop' );
169 $id = $user->getId();
170 $name = $user->getName();
171 $token = $user->getToken( true );
172
173 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
174
175 // No data
176 $request = new \FauxRequest();
177 $info = $provider->provideSessionInfo( $request );
178 $this->assertNull( $info );
179 $this->assertSame( [], $logger->getBuffer() );
180 $logger->clearBuffer();
181
182 // Session key only
183 $request = new \FauxRequest();
184 $request->setCookies( [
185 'session' => $sessionId,
186 ], '' );
187 $info = $provider->provideSessionInfo( $request );
188 $this->assertNotNull( $info );
189 $this->assertSame( $params['priority'], $info->getPriority() );
190 $this->assertSame( $sessionId, $info->getId() );
191 $this->assertNotNull( $info->getUserInfo() );
192 $this->assertSame( 0, $info->getUserInfo()->getId() );
193 $this->assertNull( $info->getUserInfo()->getName() );
194 $this->assertFalse( $info->forceHTTPS() );
195 $this->assertSame( [
196 [
197 LogLevel::DEBUG,
198 'Session "{session}" requested without UserID cookie',
199 ],
200 ], $logger->getBuffer() );
201 $logger->clearBuffer();
202
203 // User, no session key
204 $request = new \FauxRequest();
205 $request->setCookies( [
206 'xUserID' => $id,
207 'xToken' => $token,
208 ], '' );
209 $info = $provider->provideSessionInfo( $request );
210 $this->assertNotNull( $info );
211 $this->assertSame( $params['priority'], $info->getPriority() );
212 $this->assertNotSame( $sessionId, $info->getId() );
213 $this->assertNotNull( $info->getUserInfo() );
214 $this->assertSame( $id, $info->getUserInfo()->getId() );
215 $this->assertSame( $name, $info->getUserInfo()->getName() );
216 $this->assertFalse( $info->forceHTTPS() );
217 $this->assertSame( [], $logger->getBuffer() );
218 $logger->clearBuffer();
219
220 // User and session key
221 $request = new \FauxRequest();
222 $request->setCookies( [
223 'session' => $sessionId,
224 'xUserID' => $id,
225 'xToken' => $token,
226 ], '' );
227 $info = $provider->provideSessionInfo( $request );
228 $this->assertNotNull( $info );
229 $this->assertSame( $params['priority'], $info->getPriority() );
230 $this->assertSame( $sessionId, $info->getId() );
231 $this->assertNotNull( $info->getUserInfo() );
232 $this->assertSame( $id, $info->getUserInfo()->getId() );
233 $this->assertSame( $name, $info->getUserInfo()->getName() );
234 $this->assertFalse( $info->forceHTTPS() );
235 $this->assertSame( [], $logger->getBuffer() );
236 $logger->clearBuffer();
237
238 // User with bad token
239 $request = new \FauxRequest();
240 $request->setCookies( [
241 'session' => $sessionId,
242 'xUserID' => $id,
243 'xToken' => 'BADTOKEN',
244 ], '' );
245 $info = $provider->provideSessionInfo( $request );
246 $this->assertNull( $info );
247 $this->assertSame( [
248 [
249 LogLevel::WARNING,
250 'Session "{session}" requested with invalid Token cookie.'
251 ],
252 ], $logger->getBuffer() );
253 $logger->clearBuffer();
254
255 // User id with no token
256 $request = new \FauxRequest();
257 $request->setCookies( [
258 'session' => $sessionId,
259 'xUserID' => $id,
260 ], '' );
261 $info = $provider->provideSessionInfo( $request );
262 $this->assertNotNull( $info );
263 $this->assertSame( $params['priority'], $info->getPriority() );
264 $this->assertSame( $sessionId, $info->getId() );
265 $this->assertNotNull( $info->getUserInfo() );
266 $this->assertFalse( $info->getUserInfo()->isVerified() );
267 $this->assertSame( $id, $info->getUserInfo()->getId() );
268 $this->assertSame( $name, $info->getUserInfo()->getName() );
269 $this->assertFalse( $info->forceHTTPS() );
270 $this->assertSame( [], $logger->getBuffer() );
271 $logger->clearBuffer();
272
273 $request = new \FauxRequest();
274 $request->setCookies( [
275 'xUserID' => $id,
276 ], '' );
277 $info = $provider->provideSessionInfo( $request );
278 $this->assertNull( $info );
279 $this->assertSame( [], $logger->getBuffer() );
280 $logger->clearBuffer();
281
282 // User and session key, with forceHTTPS flag
283 $request = new \FauxRequest();
284 $request->setCookies( [
285 'session' => $sessionId,
286 'xUserID' => $id,
287 'xToken' => $token,
288 'forceHTTPS' => true,
289 ], '' );
290 $info = $provider->provideSessionInfo( $request );
291 $this->assertNotNull( $info );
292 $this->assertSame( $params['priority'], $info->getPriority() );
293 $this->assertSame( $sessionId, $info->getId() );
294 $this->assertNotNull( $info->getUserInfo() );
295 $this->assertSame( $id, $info->getUserInfo()->getId() );
296 $this->assertSame( $name, $info->getUserInfo()->getName() );
297 $this->assertTrue( $info->forceHTTPS() );
298 $this->assertSame( [], $logger->getBuffer() );
299 $logger->clearBuffer();
300
301 // Invalid user id
302 $request = new \FauxRequest();
303 $request->setCookies( [
304 'session' => $sessionId,
305 'xUserID' => '-1',
306 ], '' );
307 $info = $provider->provideSessionInfo( $request );
308 $this->assertNull( $info );
309 $this->assertSame( [], $logger->getBuffer() );
310 $logger->clearBuffer();
311
312 // User id with matching name
313 $request = new \FauxRequest();
314 $request->setCookies( [
315 'session' => $sessionId,
316 'xUserID' => $id,
317 'xUserName' => $name,
318 ], '' );
319 $info = $provider->provideSessionInfo( $request );
320 $this->assertNotNull( $info );
321 $this->assertSame( $params['priority'], $info->getPriority() );
322 $this->assertSame( $sessionId, $info->getId() );
323 $this->assertNotNull( $info->getUserInfo() );
324 $this->assertFalse( $info->getUserInfo()->isVerified() );
325 $this->assertSame( $id, $info->getUserInfo()->getId() );
326 $this->assertSame( $name, $info->getUserInfo()->getName() );
327 $this->assertFalse( $info->forceHTTPS() );
328 $this->assertSame( [], $logger->getBuffer() );
329 $logger->clearBuffer();
330
331 // User id with wrong name
332 $request = new \FauxRequest();
333 $request->setCookies( [
334 'session' => $sessionId,
335 'xUserID' => $id,
336 'xUserName' => 'Wrong',
337 ], '' );
338 $info = $provider->provideSessionInfo( $request );
339 $this->assertNull( $info );
340 $this->assertSame( [
341 [
342 LogLevel::WARNING,
343 'Session "{session}" requested with mismatched UserID and UserName cookies.',
344 ],
345 ], $logger->getBuffer() );
346 $logger->clearBuffer();
347 }
348
349 public function testGetVaryCookies() {
350 $provider = new CookieSessionProvider( [
351 'priority' => 1,
352 'sessionName' => 'MySessionName',
353 'cookieOptions' => [ 'prefix' => 'MyCookiePrefix' ],
354 ] );
355 $this->assertArrayEquals( [
356 'MyCookiePrefixToken',
357 'MyCookiePrefixLoggedOut',
358 'MySessionName',
359 'forceHTTPS',
360 ], $provider->getVaryCookies() );
361 }
362
363 public function testSuggestLoginUsername() {
364 $provider = new CookieSessionProvider( [
365 'priority' => 1,
366 'sessionName' => 'MySessionName',
367 'cookieOptions' => [ 'prefix' => 'x' ],
368 ] );
369
370 $request = new \FauxRequest();
371 $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
372
373 $request->setCookies( [
374 'xUserName' => 'Example',
375 ], '' );
376 $this->assertEquals( 'Example', $provider->suggestLoginUsername( $request ) );
377 }
378
379 public function testPersistSession() {
380 $provider = new CookieSessionProvider( [
381 'priority' => 1,
382 'sessionName' => 'MySessionName',
383 'callUserSetCookiesHook' => false,
384 'cookieOptions' => [ 'prefix' => 'x' ],
385 ] );
386 $config = $this->getConfig();
387 $provider->setLogger( new \TestLogger() );
388 $provider->setConfig( $config );
389 $provider->setManager( SessionManager::singleton() );
390
391 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
392 $store = new TestBagOStuff();
393 $user = User::newFromName( 'UTSysop' );
394 $anon = new User;
395
396 $backend = new SessionBackend(
397 new SessionId( $sessionId ),
398 new SessionInfo( SessionInfo::MIN_PRIORITY, [
399 'provider' => $provider,
400 'id' => $sessionId,
401 'persisted' => true,
402 'idIsSafe' => true,
403 ] ),
404 $store,
405 new \Psr\Log\NullLogger(),
406 10
407 );
408 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
409
410 $mock = $this->getMock( 'stdClass', [ 'onUserSetCookies' ] );
411 $mock->expects( $this->never() )->method( 'onUserSetCookies' );
412 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
413
414 // Anonymous user
415 $backend->setUser( $anon );
416 $backend->setRememberUser( true );
417 $backend->setForceHTTPS( false );
418 $request = new \FauxRequest();
419 $provider->persistSession( $backend, $request );
420 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
421 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
422 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
423 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
424 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
425 $this->assertSame( [], $backend->getData() );
426
427 // Logged-in user, no remember
428 $backend->setUser( $user );
429 $backend->setRememberUser( false );
430 $backend->setForceHTTPS( false );
431 $request = new \FauxRequest();
432 $provider->persistSession( $backend, $request );
433 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
434 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
435 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
436 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
437 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
438 $this->assertSame( [], $backend->getData() );
439
440 // Logged-in user, remember
441 $backend->setUser( $user );
442 $backend->setRememberUser( true );
443 $backend->setForceHTTPS( true );
444 $request = new \FauxRequest();
445 $time = time();
446 $provider->persistSession( $backend, $request );
447 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
448 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
449 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
450 $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
451 $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
452 $this->assertSame( [], $backend->getData() );
453 }
454
455 /**
456 * @dataProvider provideCookieData
457 * @param bool $secure
458 * @param bool $remember
459 */
460 public function testCookieData( $secure, $remember ) {
461 $this->setMwGlobals( [
462 'wgSecureLogin' => false,
463 ] );
464
465 $provider = new CookieSessionProvider( [
466 'priority' => 1,
467 'sessionName' => 'MySessionName',
468 'callUserSetCookiesHook' => false,
469 'cookieOptions' => [ 'prefix' => 'x' ],
470 ] );
471 $config = $this->getConfig();
472 $config->set( 'CookieSecure', $secure );
473 $provider->setLogger( new \TestLogger() );
474 $provider->setConfig( $config );
475 $provider->setManager( SessionManager::singleton() );
476
477 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
478 $user = User::newFromName( 'UTSysop' );
479 $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
480
481 $backend = new SessionBackend(
482 new SessionId( $sessionId ),
483 new SessionInfo( SessionInfo::MIN_PRIORITY, [
484 'provider' => $provider,
485 'id' => $sessionId,
486 'persisted' => true,
487 'idIsSafe' => true,
488 ] ),
489 new TestBagOStuff(),
490 new \Psr\Log\NullLogger(),
491 10
492 );
493 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
494 $backend->setUser( $user );
495 $backend->setRememberUser( $remember );
496 $backend->setForceHTTPS( $secure );
497 $request = new \FauxRequest();
498 $time = time();
499 $provider->persistSession( $backend, $request );
500
501 $defaults = [
502 'expire' => (int)100,
503 'path' => $config->get( 'CookiePath' ),
504 'domain' => $config->get( 'CookieDomain' ),
505 'secure' => $secure,
506 'httpOnly' => $config->get( 'CookieHttpOnly' ),
507 'raw' => false,
508 ];
509 $extendedExpiry = $config->get( 'ExtendedLoginCookieExpiration' );
510 $extendedExpiry = (int)( $extendedExpiry === null ? 0 : $extendedExpiry );
511 $this->assertEquals( [ 'UserID', 'Token' ], $config->get( 'ExtendedLoginCookies' ),
512 'sanity check' );
513 $expect = [
514 'MySessionName' => [
515 'value' => (string)$sessionId,
516 'expire' => 0,
517 ] + $defaults,
518 'xUserID' => [
519 'value' => (string)$user->getId(),
520 'expire' => $extendedExpiry,
521 ] + $defaults,
522 'xUserName' => [
523 'value' => $user->getName(),
524 ] + $defaults,
525 'xToken' => [
526 'value' => $remember ? $user->getToken() : '',
527 'expire' => $remember ? $extendedExpiry : -31536000,
528 ] + $defaults,
529 'forceHTTPS' => [
530 'value' => $secure ? 'true' : '',
531 'secure' => false,
532 'expire' => $secure ? $remember ? $defaults['expire'] : 0 : -31536000,
533 ] + $defaults,
534 ];
535 foreach ( $expect as $key => $value ) {
536 $actual = $request->response()->getCookieData( $key );
537 if ( $actual && $actual['expire'] > 0 ) {
538 // Round expiry so we don't randomly fail if the seconds ticked during the test.
539 $actual['expire'] = round( $actual['expire'] - $time, -2 );
540 }
541 $this->assertEquals( $value, $actual, "Cookie $key" );
542 }
543 }
544
545 public static function provideCookieData() {
546 return [
547 [ false, false ],
548 [ false, true ],
549 [ true, false ],
550 [ true, true ],
551 ];
552 }
553
554 protected function getSentRequest() {
555 $sentResponse = $this->getMock( 'FauxResponse', [ 'headersSent', 'setCookie', 'header' ] );
556 $sentResponse->expects( $this->any() )->method( 'headersSent' )
557 ->will( $this->returnValue( true ) );
558 $sentResponse->expects( $this->never() )->method( 'setCookie' );
559 $sentResponse->expects( $this->never() )->method( 'header' );
560
561 $sentRequest = $this->getMock( 'FauxRequest', [ 'response' ] );
562 $sentRequest->expects( $this->any() )->method( 'response' )
563 ->will( $this->returnValue( $sentResponse ) );
564 return $sentRequest;
565 }
566
567 public function testPersistSessionWithHook() {
568 $provider = new CookieSessionProvider( [
569 'priority' => 1,
570 'sessionName' => 'MySessionName',
571 'callUserSetCookiesHook' => true,
572 'cookieOptions' => [ 'prefix' => 'x' ],
573 ] );
574 $provider->setLogger( new \Psr\Log\NullLogger() );
575 $provider->setConfig( $this->getConfig() );
576 $provider->setManager( SessionManager::singleton() );
577
578 $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
579 $store = new TestBagOStuff();
580 $user = User::newFromName( 'UTSysop' );
581 $anon = new User;
582
583 $backend = new SessionBackend(
584 new SessionId( $sessionId ),
585 new SessionInfo( SessionInfo::MIN_PRIORITY, [
586 'provider' => $provider,
587 'id' => $sessionId,
588 'persisted' => true,
589 'idIsSafe' => true,
590 ] ),
591 $store,
592 new \Psr\Log\NullLogger(),
593 10
594 );
595 \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
596
597 // Anonymous user
598 $mock = $this->getMock( 'stdClass', [ 'onUserSetCookies' ] );
599 $mock->expects( $this->never() )->method( 'onUserSetCookies' );
600 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
601 $backend->setUser( $anon );
602 $backend->setRememberUser( true );
603 $backend->setForceHTTPS( false );
604 $request = new \FauxRequest();
605 $provider->persistSession( $backend, $request );
606 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
607 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
608 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
609 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
610 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
611 $this->assertSame( [], $backend->getData() );
612
613 $provider->persistSession( $backend, $this->getSentRequest() );
614
615 // Logged-in user, no remember
616 $mock = $this->getMock( __CLASS__, [ 'onUserSetCookies' ] );
617 $mock->expects( $this->once() )->method( 'onUserSetCookies' )
618 ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
619 $this->assertSame( $user, $u );
620 $this->assertEquals( [
621 'wsUserID' => $user->getId(),
622 'wsUserName' => $user->getName(),
623 'wsToken' => $user->getToken(),
624 ], $sessionData );
625 $this->assertEquals( [
626 'UserID' => $user->getId(),
627 'UserName' => $user->getName(),
628 'Token' => false,
629 ], $cookies );
630
631 $sessionData['foo'] = 'foo!';
632 $cookies['bar'] = 'bar!';
633 return true;
634 } ) );
635 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
636 $backend->setUser( $user );
637 $backend->setRememberUser( false );
638 $backend->setForceHTTPS( false );
639 $backend->setLoggedOutTimestamp( $loggedOut = time() );
640 $request = new \FauxRequest();
641 $provider->persistSession( $backend, $request );
642 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
643 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
644 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
645 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
646 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
647 $this->assertSame( 'bar!', $request->response()->getCookie( 'xbar' ) );
648 $this->assertSame( (string)$loggedOut, $request->response()->getCookie( 'xLoggedOut' ) );
649 $this->assertEquals( [
650 'wsUserID' => $user->getId(),
651 'wsUserName' => $user->getName(),
652 'wsToken' => $user->getToken(),
653 'foo' => 'foo!',
654 ], $backend->getData() );
655
656 $provider->persistSession( $backend, $this->getSentRequest() );
657
658 // Logged-in user, remember
659 $mock = $this->getMock( __CLASS__, [ 'onUserSetCookies' ] );
660 $mock->expects( $this->once() )->method( 'onUserSetCookies' )
661 ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
662 $this->assertSame( $user, $u );
663 $this->assertEquals( [
664 'wsUserID' => $user->getId(),
665 'wsUserName' => $user->getName(),
666 'wsToken' => $user->getToken(),
667 ], $sessionData );
668 $this->assertEquals( [
669 'UserID' => $user->getId(),
670 'UserName' => $user->getName(),
671 'Token' => $user->getToken(),
672 ], $cookies );
673
674 $sessionData['foo'] = 'foo 2!';
675 $cookies['bar'] = 'bar 2!';
676 return true;
677 } ) );
678 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserSetCookies' => [ $mock ] ] );
679 $backend->setUser( $user );
680 $backend->setRememberUser( true );
681 $backend->setForceHTTPS( true );
682 $backend->setLoggedOutTimestamp( 0 );
683 $request = new \FauxRequest();
684 $provider->persistSession( $backend, $request );
685 $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
686 $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
687 $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
688 $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
689 $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
690 $this->assertSame( 'bar 2!', $request->response()->getCookie( 'xbar' ) );
691 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
692 $this->assertEquals( [
693 'wsUserID' => $user->getId(),
694 'wsUserName' => $user->getName(),
695 'wsToken' => $user->getToken(),
696 'foo' => 'foo 2!',
697 ], $backend->getData() );
698
699 $provider->persistSession( $backend, $this->getSentRequest() );
700 }
701
702 public function testUnpersistSession() {
703 $provider = new CookieSessionProvider( [
704 'priority' => 1,
705 'sessionName' => 'MySessionName',
706 'cookieOptions' => [ 'prefix' => 'x' ],
707 ] );
708 $provider->setLogger( new \Psr\Log\NullLogger() );
709 $provider->setConfig( $this->getConfig() );
710 $provider->setManager( SessionManager::singleton() );
711
712 $request = new \FauxRequest();
713 $provider->unpersistSession( $request );
714 $this->assertSame( '', $request->response()->getCookie( 'MySessionName' ) );
715 $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
716 $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
717 $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
718 $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
719
720 $provider->unpersistSession( $this->getSentRequest() );
721 }
722
723 public function testSetLoggedOutCookie() {
724 $provider = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
725 'priority' => 1,
726 'sessionName' => 'MySessionName',
727 'cookieOptions' => [ 'prefix' => 'x' ],
728 ] ) );
729 $provider->setLogger( new \Psr\Log\NullLogger() );
730 $provider->setConfig( $this->getConfig() );
731 $provider->setManager( SessionManager::singleton() );
732
733 $t1 = time();
734 $t2 = time() - 86400 * 2;
735
736 // Set it
737 $request = new \FauxRequest();
738 $provider->setLoggedOutCookie( $t1, $request );
739 $this->assertSame( (string)$t1, $request->response()->getCookie( 'xLoggedOut' ) );
740
741 // Too old
742 $request = new \FauxRequest();
743 $provider->setLoggedOutCookie( $t2, $request );
744 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
745
746 // Don't reset if it's already set
747 $request = new \FauxRequest();
748 $request->setCookies( [
749 'xLoggedOut' => $t1,
750 ], '' );
751 $provider->setLoggedOutCookie( $t1, $request );
752 $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
753 }
754
755 /**
756 * To be mocked for hooks, since PHPUnit can't otherwise mock methods that
757 * take references.
758 */
759 public function onUserSetCookies( $user, &$sessionData, &$cookies ) {
760 }
761
762 public function testGetCookie() {
763 $provider = new CookieSessionProvider( [
764 'priority' => 1,
765 'sessionName' => 'MySessionName',
766 'cookieOptions' => [ 'prefix' => 'x' ],
767 ] );
768 $provider->setLogger( new \Psr\Log\NullLogger() );
769 $provider->setConfig( $this->getConfig() );
770 $provider->setManager( SessionManager::singleton() );
771 $provider = \TestingAccessWrapper::newFromObject( $provider );
772
773 $request = new \FauxRequest();
774 $request->setCookies( [
775 'xFoo' => 'foo!',
776 'xBar' => 'deleted',
777 ], '' );
778 $this->assertSame( 'foo!', $provider->getCookie( $request, 'Foo', 'x' ) );
779 $this->assertNull( $provider->getCookie( $request, 'Bar', 'x' ) );
780 $this->assertNull( $provider->getCookie( $request, 'Baz', 'x' ) );
781 }
782
783 public function testGetRememberUserDuration() {
784 $config = $this->getConfig();
785 $provider = new CookieSessionProvider( [ 'priority' => 10 ] );
786 $provider->setLogger( new \Psr\Log\NullLogger() );
787 $provider->setConfig( $config );
788 $provider->setManager( SessionManager::singleton() );
789
790 $this->assertSame( 200, $provider->getRememberUserDuration() );
791
792 $config->set( 'ExtendedLoginCookieExpiration', null );
793
794 $this->assertSame( 100, $provider->getRememberUserDuration() );
795
796 $config->set( 'ExtendedLoginCookieExpiration', 0 );
797
798 $this->assertSame( null, $provider->getRememberUserDuration() );
799 }
800
801 public function testGetLoginCookieExpiration() {
802 $config = $this->getConfig();
803 $provider = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( [
804 'priority' => 10
805 ] ) );
806 $provider->setLogger( new \Psr\Log\NullLogger() );
807 $provider->setConfig( $config );
808 $provider->setManager( SessionManager::singleton() );
809
810 $this->assertSame( 200, $provider->getLoginCookieExpiration( 'Token' ) );
811 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User' ) );
812
813 $config->set( 'ExtendedLoginCookieExpiration', null );
814
815 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'Token' ) );
816 $this->assertSame( 100, $provider->getLoginCookieExpiration( 'User' ) );
817 }
818 }