3 namespace MediaWiki\Session
;
8 use Wikimedia\TestingAccessWrapper
;
13 * @covers MediaWiki\Session\SessionManager
15 class SessionManagerTest
extends MediaWikiTestCase
{
17 /** @var \HashConfig */
20 /** @var \TestLogger */
23 /** @var TestBagOStuff */
26 protected function getManager() {
27 \ObjectCache
::$instances['testSessionStore'] = new TestBagOStuff();
28 $this->config
= new \
HashConfig( [
29 'LanguageCode' => 'en',
30 'SessionCacheType' => 'testSessionStore',
31 'ObjectCacheSessionExpiry' => 100,
32 'SessionProviders' => [
33 [ 'class' => \DummySessionProvider
::class ],
36 $this->logger
= new \
TestLogger( false, function ( $m ) {
37 return substr( $m, 0, 15 ) === 'SessionBackend ' ?
null : $m;
39 $this->store
= new TestBagOStuff();
41 return new SessionManager( [
42 'config' => $this->config
,
43 'logger' => $this->logger
,
44 'store' => $this->store
,
48 protected function objectCacheDef( $object ) {
49 return [ 'factory' => function () use ( $object ) {
54 public function testSingleton() {
55 $reset = TestUtils
::setSessionManagerSingleton( null );
57 $singleton = SessionManager
::singleton();
58 $this->assertInstanceOf( SessionManager
::class, $singleton );
59 $this->assertSame( $singleton, SessionManager
::singleton() );
62 public function testGetGlobalSession() {
63 $context = \RequestContext
::getMain();
65 if ( !PHPSessionHandler
::isInstalled() ) {
66 PHPSessionHandler
::install( SessionManager
::singleton() );
68 $rProp = new \
ReflectionProperty( PHPSessionHandler
::class, 'instance' );
69 $rProp->setAccessible( true );
70 $handler = TestingAccessWrapper
::newFromObject( $rProp->getValue() );
71 $oldEnable = $handler->enable
;
72 $reset[] = new \Wikimedia\
ScopedCallback( function () use ( $handler, $oldEnable ) {
73 if ( $handler->enable
) {
74 session_write_close();
76 $handler->enable
= $oldEnable;
78 $reset[] = TestUtils
::setSessionManagerSingleton( $this->getManager() );
80 $handler->enable
= true;
81 $request = new \
FauxRequest();
82 $context->setRequest( $request );
83 $id = $request->getSession()->getId();
85 session_write_close();
87 $session = SessionManager
::getGlobalSession();
88 $this->assertSame( $id, $session->getId() );
90 session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
91 $session = SessionManager
::getGlobalSession();
92 $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $session->getId() );
93 $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $request->getSession()->getId() );
95 session_write_close();
96 $handler->enable
= false;
97 $request = new \
FauxRequest();
98 $context->setRequest( $request );
99 $id = $request->getSession()->getId();
102 $session = SessionManager
::getGlobalSession();
103 $this->assertSame( $id, $session->getId() );
105 session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
106 $session = SessionManager
::getGlobalSession();
107 $this->assertSame( $id, $session->getId() );
108 $this->assertSame( $id, $request->getSession()->getId() );
111 public function testConstructor() {
112 $manager = TestingAccessWrapper
::newFromObject( $this->getManager() );
113 $this->assertSame( $this->config
, $manager->config
);
114 $this->assertSame( $this->logger
, $manager->logger
);
115 $this->assertSame( $this->store
, $manager->store
);
117 $manager = TestingAccessWrapper
::newFromObject( new SessionManager() );
118 $this->assertSame( \RequestContext
::getMain()->getConfig(), $manager->config
);
120 $manager = TestingAccessWrapper
::newFromObject( new SessionManager( [
121 'config' => $this->config
,
123 $this->assertSame( \ObjectCache
::$instances['testSessionStore'], $manager->store
);
126 'config' => '$options[\'config\'] must be an instance of Config',
127 'logger' => '$options[\'logger\'] must be an instance of LoggerInterface',
128 'store' => '$options[\'store\'] must be an instance of BagOStuff',
129 ] as $key => $error ) {
131 new SessionManager( [ $key => new \stdClass
] );
132 $this->fail( 'Expected exception not thrown' );
133 } catch ( \InvalidArgumentException
$ex ) {
134 $this->assertSame( $error, $ex->getMessage() );
139 public function testGetSessionForRequest() {
140 $manager = $this->getManager();
141 $request = new \
FauxRequest();
142 $request->unpersist1
= false;
143 $request->unpersist2
= false;
147 $idEmpty = 'empty-session-------------------';
149 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
151 [ 'provideSessionInfo', 'newSessionInfo', '__toString', 'describe', 'unpersistSession' ]
154 $provider1 = $providerBuilder->getMock();
155 $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
156 ->with( $this->identicalTo( $request ) )
157 ->will( $this->returnCallback( function ( $request ) {
158 return $request->info1
;
160 $provider1->expects( $this->any() )->method( 'newSessionInfo' )
161 ->will( $this->returnCallback( function () use ( $idEmpty, $provider1 ) {
162 return new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
163 'provider' => $provider1,
169 $provider1->expects( $this->any() )->method( '__toString' )
170 ->will( $this->returnValue( 'Provider1' ) );
171 $provider1->expects( $this->any() )->method( 'describe' )
172 ->will( $this->returnValue( '#1 sessions' ) );
173 $provider1->expects( $this->any() )->method( 'unpersistSession' )
174 ->will( $this->returnCallback( function ( $request ) {
175 $request->unpersist1
= true;
178 $provider2 = $providerBuilder->getMock();
179 $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
180 ->with( $this->identicalTo( $request ) )
181 ->will( $this->returnCallback( function ( $request ) {
182 return $request->info2
;
184 $provider2->expects( $this->any() )->method( '__toString' )
185 ->will( $this->returnValue( 'Provider2' ) );
186 $provider2->expects( $this->any() )->method( 'describe' )
187 ->will( $this->returnValue( '#2 sessions' ) );
188 $provider2->expects( $this->any() )->method( 'unpersistSession' )
189 ->will( $this->returnCallback( function ( $request ) {
190 $request->unpersist2
= true;
193 $this->config
->set( 'SessionProviders', [
194 $this->objectCacheDef( $provider1 ),
195 $this->objectCacheDef( $provider2 ),
198 // No provider returns info
199 $request->info1
= null;
200 $request->info2
= null;
201 $session = $manager->getSessionForRequest( $request );
202 $this->assertInstanceOf( Session
::class, $session );
203 $this->assertSame( $idEmpty, $session->getId() );
204 $this->assertFalse( $request->unpersist1
);
205 $this->assertFalse( $request->unpersist2
);
207 // Both providers return info, picks best one
208 $request->info1
= new SessionInfo( SessionInfo
::MIN_PRIORITY +
1, [
209 'provider' => $provider1,
210 'id' => ( $id1 = $manager->generateSessionId() ),
214 $request->info2
= new SessionInfo( SessionInfo
::MIN_PRIORITY +
2, [
215 'provider' => $provider2,
216 'id' => ( $id2 = $manager->generateSessionId() ),
220 $session = $manager->getSessionForRequest( $request );
221 $this->assertInstanceOf( Session
::class, $session );
222 $this->assertSame( $id2, $session->getId() );
223 $this->assertFalse( $request->unpersist1
);
224 $this->assertFalse( $request->unpersist2
);
226 $request->info1
= new SessionInfo( SessionInfo
::MIN_PRIORITY +
2, [
227 'provider' => $provider1,
228 'id' => ( $id1 = $manager->generateSessionId() ),
232 $request->info2
= new SessionInfo( SessionInfo
::MIN_PRIORITY +
1, [
233 'provider' => $provider2,
234 'id' => ( $id2 = $manager->generateSessionId() ),
238 $session = $manager->getSessionForRequest( $request );
239 $this->assertInstanceOf( Session
::class, $session );
240 $this->assertSame( $id1, $session->getId() );
241 $this->assertFalse( $request->unpersist1
);
242 $this->assertFalse( $request->unpersist2
);
245 $request->info1
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
246 'provider' => $provider1,
247 'id' => ( $id1 = $manager->generateSessionId() ),
249 'userInfo' => UserInfo
::newAnonymous(),
252 $request->info2
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
253 'provider' => $provider2,
254 'id' => ( $id2 = $manager->generateSessionId() ),
256 'userInfo' => UserInfo
::newAnonymous(),
260 $manager->getSessionForRequest( $request );
261 $this->fail( 'Expcected exception not thrown' );
262 } catch ( \OverflowException
$ex ) {
263 $this->assertStringStartsWith(
264 'Multiple sessions for this request tied for top priority: ',
267 $this->assertCount( 2, $ex->sessionInfos
);
268 $this->assertContains( $request->info1
, $ex->sessionInfos
);
269 $this->assertContains( $request->info2
, $ex->sessionInfos
);
271 $this->assertFalse( $request->unpersist1
);
272 $this->assertFalse( $request->unpersist2
);
275 $request->info1
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
276 'provider' => $provider2,
277 'id' => ( $id1 = $manager->generateSessionId() ),
281 $request->info2
= null;
283 $manager->getSessionForRequest( $request );
284 $this->fail( 'Expcected exception not thrown' );
285 } catch ( \UnexpectedValueException
$ex ) {
287 'Provider1 returned session info for a different provider: ' . $request->info1
,
291 $this->assertFalse( $request->unpersist1
);
292 $this->assertFalse( $request->unpersist2
);
294 // Unusable session info
295 $this->logger
->setCollect( true );
296 $request->info1
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
297 'provider' => $provider1,
298 'id' => ( $id1 = $manager->generateSessionId() ),
300 'userInfo' => UserInfo
::newFromName( 'UTSysop', false ),
303 $request->info2
= new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
304 'provider' => $provider2,
305 'id' => ( $id2 = $manager->generateSessionId() ),
309 $session = $manager->getSessionForRequest( $request );
310 $this->assertInstanceOf( Session
::class, $session );
311 $this->assertSame( $id2, $session->getId() );
312 $this->logger
->setCollect( false );
313 $this->assertTrue( $request->unpersist1
);
314 $this->assertFalse( $request->unpersist2
);
315 $request->unpersist1
= false;
317 $this->logger
->setCollect( true );
318 $request->info1
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
319 'provider' => $provider1,
320 'id' => ( $id1 = $manager->generateSessionId() ),
324 $request->info2
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
325 'provider' => $provider2,
326 'id' => ( $id2 = $manager->generateSessionId() ),
328 'userInfo' => UserInfo
::newFromName( 'UTSysop', false ),
331 $session = $manager->getSessionForRequest( $request );
332 $this->assertInstanceOf( Session
::class, $session );
333 $this->assertSame( $id1, $session->getId() );
334 $this->logger
->setCollect( false );
335 $this->assertFalse( $request->unpersist1
);
336 $this->assertTrue( $request->unpersist2
);
337 $request->unpersist2
= false;
339 // Unpersisted session ID
340 $request->info1
= new SessionInfo( SessionInfo
::MAX_PRIORITY
, [
341 'provider' => $provider1,
342 'id' => ( $id1 = $manager->generateSessionId() ),
343 'persisted' => false,
344 'userInfo' => UserInfo
::newFromName( 'UTSysop', true ),
347 $request->info2
= null;
348 $session = $manager->getSessionForRequest( $request );
349 $this->assertInstanceOf( Session
::class, $session );
350 $this->assertSame( $id1, $session->getId() );
351 $this->assertTrue( $request->unpersist1
); // The saving of the session does it
352 $this->assertFalse( $request->unpersist2
);
354 $this->assertTrue( $session->isPersistent(), 'sanity check' );
357 public function testGetSessionById() {
358 $manager = $this->getManager();
360 $manager->getSessionById( 'bad' );
361 $this->fail( 'Expected exception not thrown' );
362 } catch ( \InvalidArgumentException
$ex ) {
363 $this->assertSame( 'Invalid session ID', $ex->getMessage() );
366 // Unknown session ID
367 $id = $manager->generateSessionId();
368 $session = $manager->getSessionById( $id, true );
369 $this->assertInstanceOf( Session
::class, $session );
370 $this->assertSame( $id, $session->getId() );
372 $id = $manager->generateSessionId();
373 $this->assertNull( $manager->getSessionById( $id, false ) );
375 // Known but unloadable session ID
376 $this->logger
->setCollect( true );
377 $id = $manager->generateSessionId();
378 $this->store
->setSession( $id, [ 'metadata' => [
379 'userId' => User
::idFromName( 'UTSysop' ),
380 'userToken' => 'bad',
383 $this->assertNull( $manager->getSessionById( $id, true ) );
384 $this->assertNull( $manager->getSessionById( $id, false ) );
385 $this->logger
->setCollect( false );
388 $this->store
->setSession( $id, [] );
389 $session = $manager->getSessionById( $id, false );
390 $this->assertInstanceOf( Session
::class, $session );
391 $this->assertSame( $id, $session->getId() );
393 // Store isn't checked if the session is already loaded
394 $this->store
->setSession( $id, [ 'metadata' => [
395 'userId' => User
::idFromName( 'UTSysop' ),
396 'userToken' => 'bad',
398 $session2 = $manager->getSessionById( $id, false );
399 $this->assertInstanceOf( Session
::class, $session2 );
400 $this->assertSame( $id, $session2->getId() );
401 unset( $session, $session2 );
402 $this->logger
->setCollect( true );
403 $this->assertNull( $manager->getSessionById( $id, true ) );
404 $this->logger
->setCollect( false );
406 // Failure to create an empty session
407 $manager = $this->getManager();
408 $provider = $this->getMockBuilder( \DummySessionProvider
::class )
409 ->setMethods( [ 'provideSessionInfo', 'newSessionInfo', '__toString' ] )
411 $provider->expects( $this->any() )->method( 'provideSessionInfo' )
412 ->will( $this->returnValue( null ) );
413 $provider->expects( $this->any() )->method( 'newSessionInfo' )
414 ->will( $this->returnValue( null ) );
415 $provider->expects( $this->any() )->method( '__toString' )
416 ->will( $this->returnValue( 'MockProvider' ) );
417 $this->config
->set( 'SessionProviders', [
418 $this->objectCacheDef( $provider ),
420 $this->logger
->setCollect( true );
421 $this->assertNull( $manager->getSessionById( $id, true ) );
422 $this->logger
->setCollect( false );
424 [ LogLevel
::ERROR
, 'Failed to create empty session: {exception}' ]
425 ], $this->logger
->getBuffer() );
428 public function testGetEmptySession() {
429 $manager = $this->getManager();
430 $pmanager = TestingAccessWrapper
::newFromObject( $manager );
431 $request = new \
FauxRequest();
433 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
434 ->setMethods( [ 'provideSessionInfo', 'newSessionInfo', '__toString' ] );
440 $provider1 = $providerBuilder->getMock();
441 $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
442 ->will( $this->returnValue( null ) );
443 $provider1->expects( $this->any() )->method( 'newSessionInfo' )
444 ->with( $this->callback( function ( $id ) use ( &$expectId ) {
445 return $id === $expectId;
447 ->will( $this->returnCallback( function () use ( &$info1 ) {
450 $provider1->expects( $this->any() )->method( '__toString' )
451 ->will( $this->returnValue( 'MockProvider1' ) );
453 $provider2 = $providerBuilder->getMock();
454 $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
455 ->will( $this->returnValue( null ) );
456 $provider2->expects( $this->any() )->method( 'newSessionInfo' )
457 ->with( $this->callback( function ( $id ) use ( &$expectId ) {
458 return $id === $expectId;
460 ->will( $this->returnCallback( function () use ( &$info2 ) {
463 $provider1->expects( $this->any() )->method( '__toString' )
464 ->will( $this->returnValue( 'MockProvider2' ) );
466 $this->config
->set( 'SessionProviders', [
467 $this->objectCacheDef( $provider1 ),
468 $this->objectCacheDef( $provider2 ),
476 $manager->getEmptySession();
477 $this->fail( 'Expected exception not thrown' );
478 } catch ( \UnexpectedValueException
$ex ) {
480 'No provider could provide an empty session!',
487 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
488 'provider' => $provider1,
489 'id' => 'empty---------------------------',
494 $session = $manager->getEmptySession();
495 $this->assertInstanceOf( Session
::class, $session );
496 $this->assertSame( 'empty---------------------------', $session->getId() );
499 $expectId = 'expected------------------------';
500 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
501 'provider' => $provider1,
507 $session = $pmanager->getEmptySessionInternal( null, $expectId );
508 $this->assertInstanceOf( Session
::class, $session );
509 $this->assertSame( $expectId, $session->getId() );
512 $expectId = 'expected-----------------------2';
513 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
514 'provider' => $provider1,
515 'id' => "un$expectId",
521 $pmanager->getEmptySessionInternal( null, $expectId );
522 $this->fail( 'Expected exception not thrown' );
523 } catch ( \UnexpectedValueException
$ex ) {
525 'MockProvider1 returned empty session info with a wrong id: ' .
526 "un$expectId != $expectId",
532 $expectId = 'expected-----------------------2';
533 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
534 'provider' => $provider1,
540 $pmanager->getEmptySessionInternal( null, $expectId );
541 $this->fail( 'Expected exception not thrown' );
542 } catch ( \UnexpectedValueException
$ex ) {
544 'MockProvider1 returned empty session info with id flagged unsafe',
551 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
552 'provider' => $provider2,
553 'id' => 'empty---------------------------',
559 $manager->getEmptySession();
560 $this->fail( 'Expected exception not thrown' );
561 } catch ( \UnexpectedValueException
$ex ) {
563 'MockProvider1 returned an empty session info for a different provider: ' . $info1,
568 // Highest priority wins
570 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY +
1, [
571 'provider' => $provider1,
572 'id' => 'empty1--------------------------',
576 $info2 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
577 'provider' => $provider2,
578 'id' => 'empty2--------------------------',
582 $session = $manager->getEmptySession();
583 $this->assertInstanceOf( Session
::class, $session );
584 $this->assertSame( 'empty1--------------------------', $session->getId() );
587 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY +
1, [
588 'provider' => $provider1,
589 'id' => 'empty1--------------------------',
593 $info2 = new SessionInfo( SessionInfo
::MIN_PRIORITY +
2, [
594 'provider' => $provider2,
595 'id' => 'empty2--------------------------',
599 $session = $manager->getEmptySession();
600 $this->assertInstanceOf( Session
::class, $session );
601 $this->assertSame( 'empty2--------------------------', $session->getId() );
603 // Tied priorities throw an exception
605 $info1 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
606 'provider' => $provider1,
607 'id' => 'empty1--------------------------',
609 'userInfo' => UserInfo
::newAnonymous(),
612 $info2 = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
613 'provider' => $provider2,
614 'id' => 'empty2--------------------------',
616 'userInfo' => UserInfo
::newAnonymous(),
620 $manager->getEmptySession();
621 $this->fail( 'Expected exception not thrown' );
622 } catch ( \UnexpectedValueException
$ex ) {
623 $this->assertStringStartsWith(
624 'Multiple empty sessions tied for top priority: ',
631 $pmanager->getEmptySessionInternal( null, 'bad' );
632 $this->fail( 'Expected exception not thrown' );
633 } catch ( \InvalidArgumentException
$ex ) {
634 $this->assertSame( 'Invalid session ID', $ex->getMessage() );
637 // Session already exists
638 $expectId = 'expected-----------------------3';
639 $this->store
->setSessionMeta( $expectId, [
640 'provider' => 'MockProvider2',
646 $pmanager->getEmptySessionInternal( null, $expectId );
647 $this->fail( 'Expected exception not thrown' );
648 } catch ( \InvalidArgumentException
$ex ) {
649 $this->assertSame( 'Session ID already exists', $ex->getMessage() );
653 public function testInvalidateSessionsForUser() {
654 $user = User
::newFromName( 'UTSysop' );
655 $manager = $this->getManager();
657 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
658 ->setMethods( [ 'invalidateSessionsForUser', '__toString' ] );
660 $provider1 = $providerBuilder->getMock();
661 $provider1->expects( $this->once() )->method( 'invalidateSessionsForUser' )
662 ->with( $this->identicalTo( $user ) );
663 $provider1->expects( $this->any() )->method( '__toString' )
664 ->will( $this->returnValue( 'MockProvider1' ) );
666 $provider2 = $providerBuilder->getMock();
667 $provider2->expects( $this->once() )->method( 'invalidateSessionsForUser' )
668 ->with( $this->identicalTo( $user ) );
669 $provider2->expects( $this->any() )->method( '__toString' )
670 ->will( $this->returnValue( 'MockProvider2' ) );
672 $this->config
->set( 'SessionProviders', [
673 $this->objectCacheDef( $provider1 ),
674 $this->objectCacheDef( $provider2 ),
677 $oldToken = $user->getToken( true );
678 $manager->invalidateSessionsForUser( $user );
679 $this->assertNotEquals( $oldToken, $user->getToken() );
682 public function testGetVaryHeaders() {
683 $manager = $this->getManager();
685 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
686 ->setMethods( [ 'getVaryHeaders', '__toString' ] );
688 $provider1 = $providerBuilder->getMock();
689 $provider1->expects( $this->once() )->method( 'getVaryHeaders' )
690 ->will( $this->returnValue( [
692 'Bar' => [ 'X', 'Bar1' ],
695 $provider1->expects( $this->any() )->method( '__toString' )
696 ->will( $this->returnValue( 'MockProvider1' ) );
698 $provider2 = $providerBuilder->getMock();
699 $provider2->expects( $this->once() )->method( 'getVaryHeaders' )
700 ->will( $this->returnValue( [
702 'Bar' => [ 'X', 'Bar2' ],
703 'Quux' => [ 'Quux' ],
705 $provider2->expects( $this->any() )->method( '__toString' )
706 ->will( $this->returnValue( 'MockProvider2' ) );
708 $this->config
->set( 'SessionProviders', [
709 $this->objectCacheDef( $provider1 ),
710 $this->objectCacheDef( $provider2 ),
715 'Bar' => [ 'X', 'Bar1', 3 => 'Bar2' ],
716 'Quux' => [ 'Quux' ],
720 $this->assertEquals( $expect, $manager->getVaryHeaders() );
722 // Again, to ensure it's cached
723 $this->assertEquals( $expect, $manager->getVaryHeaders() );
726 public function testGetVaryCookies() {
727 $manager = $this->getManager();
729 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
730 ->setMethods( [ 'getVaryCookies', '__toString' ] );
732 $provider1 = $providerBuilder->getMock();
733 $provider1->expects( $this->once() )->method( 'getVaryCookies' )
734 ->will( $this->returnValue( [ 'Foo', 'Bar' ] ) );
735 $provider1->expects( $this->any() )->method( '__toString' )
736 ->will( $this->returnValue( 'MockProvider1' ) );
738 $provider2 = $providerBuilder->getMock();
739 $provider2->expects( $this->once() )->method( 'getVaryCookies' )
740 ->will( $this->returnValue( [ 'Foo', 'Baz' ] ) );
741 $provider2->expects( $this->any() )->method( '__toString' )
742 ->will( $this->returnValue( 'MockProvider2' ) );
744 $this->config
->set( 'SessionProviders', [
745 $this->objectCacheDef( $provider1 ),
746 $this->objectCacheDef( $provider2 ),
749 $expect = [ 'Foo', 'Bar', 'Baz' ];
751 $this->assertEquals( $expect, $manager->getVaryCookies() );
753 // Again, to ensure it's cached
754 $this->assertEquals( $expect, $manager->getVaryCookies() );
757 public function testGetProviders() {
758 $realManager = $this->getManager();
759 $manager = TestingAccessWrapper
::newFromObject( $realManager );
761 $this->config
->set( 'SessionProviders', [
762 [ 'class' => \DummySessionProvider
::class ],
764 $providers = $manager->getProviders();
765 $this->assertArrayHasKey( 'DummySessionProvider', $providers );
766 $provider = TestingAccessWrapper
::newFromObject( $providers['DummySessionProvider'] );
767 $this->assertSame( $manager->logger
, $provider->logger
);
768 $this->assertSame( $manager->config
, $provider->config
);
769 $this->assertSame( $realManager, $provider->getManager() );
771 $this->config
->set( 'SessionProviders', [
772 [ 'class' => \DummySessionProvider
::class ],
773 [ 'class' => \DummySessionProvider
::class ],
775 $manager->sessionProviders
= null;
777 $manager->getProviders();
778 $this->fail( 'Expected exception not thrown' );
779 } catch ( \UnexpectedValueException
$ex ) {
781 'Duplicate provider name "DummySessionProvider"',
787 public function testShutdown() {
788 $manager = TestingAccessWrapper
::newFromObject( $this->getManager() );
789 $manager->setLogger( new \Psr\Log\
NullLogger() );
791 $mock = $this->getMockBuilder( stdClass
::class )
792 ->setMethods( [ 'shutdown' ] )->getMock();
793 $mock->expects( $this->once() )->method( 'shutdown' );
795 $manager->allSessionBackends
= [ $mock ];
796 $manager->shutdown();
799 public function testGetSessionFromInfo() {
800 $manager = TestingAccessWrapper
::newFromObject( $this->getManager() );
801 $request = new \
FauxRequest();
803 $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
805 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
806 'provider' => $manager->getProvider( 'DummySessionProvider' ),
809 'userInfo' => UserInfo
::newFromName( 'UTSysop', true ),
812 TestingAccessWrapper
::newFromObject( $info )->idIsSafe
= true;
813 $session1 = TestingAccessWrapper
::newFromObject(
814 $manager->getSessionFromInfo( $info, $request )
816 $session2 = TestingAccessWrapper
::newFromObject(
817 $manager->getSessionFromInfo( $info, $request )
820 $this->assertSame( $session1->backend
, $session2->backend
);
821 $this->assertNotEquals( $session1->index
, $session2->index
);
822 $this->assertSame( $session1->getSessionId(), $session2->getSessionId() );
823 $this->assertSame( $id, $session1->getId() );
825 TestingAccessWrapper
::newFromObject( $info )->idIsSafe
= false;
826 $session3 = $manager->getSessionFromInfo( $info, $request );
827 $this->assertNotSame( $id, $session3->getId() );
830 public function testBackendRegistration() {
831 $manager = $this->getManager();
833 $session = $manager->getSessionForRequest( new \FauxRequest
);
834 $backend = TestingAccessWrapper
::newFromObject( $session )->backend
;
835 $sessionId = $session->getSessionId();
836 $id = (string)$sessionId;
838 $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
840 $manager->changeBackendId( $backend );
841 $this->assertSame( $sessionId, $session->getSessionId() );
842 $this->assertNotEquals( $id, (string)$sessionId );
843 $id = (string)$sessionId;
845 $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
847 // Destruction of the session here causes the backend to be deregistered
851 $manager->changeBackendId( $backend );
852 $this->fail( 'Expected exception not thrown' );
853 } catch ( \InvalidArgumentException
$ex ) {
855 'Backend was not registered with this SessionManager', $ex->getMessage()
860 $manager->deregisterSessionBackend( $backend );
861 $this->fail( 'Expected exception not thrown' );
862 } catch ( \InvalidArgumentException
$ex ) {
864 'Backend was not registered with this SessionManager', $ex->getMessage()
868 $session = $manager->getSessionById( $id, true );
869 $this->assertSame( $sessionId, $session->getSessionId() );
872 public function testGenerateSessionId() {
873 $manager = $this->getManager();
875 $id = $manager->generateSessionId();
876 $this->assertTrue( SessionManager
::validateSessionId( $id ), "Generated ID: $id" );
879 public function testPreventSessionsForUser() {
880 $manager = $this->getManager();
882 $providerBuilder = $this->getMockBuilder( \DummySessionProvider
::class )
883 ->setMethods( [ 'preventSessionsForUser', '__toString' ] );
885 $provider1 = $providerBuilder->getMock();
886 $provider1->expects( $this->once() )->method( 'preventSessionsForUser' )
887 ->with( $this->equalTo( 'UTSysop' ) );
888 $provider1->expects( $this->any() )->method( '__toString' )
889 ->will( $this->returnValue( 'MockProvider1' ) );
891 $this->config
->set( 'SessionProviders', [
892 $this->objectCacheDef( $provider1 ),
895 $this->assertFalse( $manager->isUserSessionPrevented( 'UTSysop' ) );
896 $manager->preventSessionsForUser( 'UTSysop' );
897 $this->assertTrue( $manager->isUserSessionPrevented( 'UTSysop' ) );
900 public function testLoadSessionInfoFromStore() {
901 $manager = $this->getManager();
902 $logger = new \
TestLogger( true );
903 $manager->setLogger( $logger );
904 $request = new \
FauxRequest();
906 // TestingAccessWrapper can't handle methods with reference arguments, sigh.
907 $rClass = new \
ReflectionClass( $manager );
908 $rMethod = $rClass->getMethod( 'loadSessionInfoFromStore' );
909 $rMethod->setAccessible( true );
910 $loadSessionInfoFromStore = function ( &$info ) use ( $rMethod, $manager, $request ) {
911 return $rMethod->invokeArgs( $manager, [ &$info, $request ] );
914 $userInfo = UserInfo
::newFromName( 'UTSysop', true );
915 $unverifiedUserInfo = UserInfo
::newFromName( 'UTSysop', false );
917 $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
919 'userId' => $userInfo->getId(),
920 'userName' => $userInfo->getName(),
921 'userToken' => $userInfo->getToken( true ),
922 'provider' => 'Mock',
925 $builder = $this->getMockBuilder( SessionProvider
::class )
926 ->setMethods( [ '__toString', 'mergeMetadata', 'refreshSessionInfo' ] );
928 $provider = $builder->getMockForAbstractClass();
929 $provider->setManager( $manager );
930 $provider->expects( $this->any() )->method( 'persistsSessionId' )
931 ->will( $this->returnValue( true ) );
932 $provider->expects( $this->any() )->method( 'canChangeUser' )
933 ->will( $this->returnValue( true ) );
934 $provider->expects( $this->any() )->method( 'refreshSessionInfo' )
935 ->will( $this->returnValue( true ) );
936 $provider->expects( $this->any() )->method( '__toString' )
937 ->will( $this->returnValue( 'Mock' ) );
938 $provider->expects( $this->any() )->method( 'mergeMetadata' )
939 ->will( $this->returnCallback( function ( $a, $b ) {
940 if ( $b === [ 'Throw' ] ) {
941 throw new MetadataMergeException( 'no merge!' );
946 $provider2 = $builder->getMockForAbstractClass();
947 $provider2->setManager( $manager );
948 $provider2->expects( $this->any() )->method( 'persistsSessionId' )
949 ->will( $this->returnValue( false ) );
950 $provider2->expects( $this->any() )->method( 'canChangeUser' )
951 ->will( $this->returnValue( false ) );
952 $provider2->expects( $this->any() )->method( '__toString' )
953 ->will( $this->returnValue( 'Mock2' ) );
954 $provider2->expects( $this->any() )->method( 'refreshSessionInfo' )
955 ->will( $this->returnCallback( function ( $info, $request, &$metadata ) {
956 $metadata['changed'] = true;
960 $provider3 = $builder->getMockForAbstractClass();
961 $provider3->setManager( $manager );
962 $provider3->expects( $this->any() )->method( 'persistsSessionId' )
963 ->will( $this->returnValue( true ) );
964 $provider3->expects( $this->any() )->method( 'canChangeUser' )
965 ->will( $this->returnValue( true ) );
966 $provider3->expects( $this->once() )->method( 'refreshSessionInfo' )
967 ->will( $this->returnValue( false ) );
968 $provider3->expects( $this->any() )->method( '__toString' )
969 ->will( $this->returnValue( 'Mock3' ) );
971 TestingAccessWrapper
::newFromObject( $manager )->sessionProviders
= [
972 (string)$provider => $provider,
973 (string)$provider2 => $provider2,
974 (string)$provider3 => $provider3,
977 // No metadata, basic usage
978 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
979 'provider' => $provider,
981 'userInfo' => $userInfo
983 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
984 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
985 $this->assertFalse( $info->isIdSafe() );
986 $this->assertSame( [], $logger->getBuffer() );
988 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
989 'provider' => $provider,
990 'userInfo' => $userInfo
992 $this->assertTrue( $info->isIdSafe(), 'sanity check' );
993 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
994 $this->assertTrue( $info->isIdSafe() );
995 $this->assertSame( [], $logger->getBuffer() );
997 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
998 'provider' => $provider2,
1000 'userInfo' => $userInfo
1002 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1003 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1004 $this->assertTrue( $info->isIdSafe() );
1005 $this->assertSame( [], $logger->getBuffer() );
1007 // Unverified user, no metadata
1008 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1009 'provider' => $provider,
1011 'userInfo' => $unverifiedUserInfo
1013 $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
1014 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1015 $this->assertSame( [
1018 'Session "{session}": Unverified user provided and no metadata to auth it',
1020 ], $logger->getBuffer() );
1021 $logger->clearBuffer();
1023 // No metadata, missing data
1024 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1026 'userInfo' => $userInfo
1028 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1029 $this->assertSame( [
1030 [ LogLevel
::WARNING
, 'Session "{session}": Null provider and no metadata' ],
1031 ], $logger->getBuffer() );
1032 $logger->clearBuffer();
1034 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1035 'provider' => $provider,
1038 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1039 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1040 $this->assertInstanceOf( UserInfo
::class, $info->getUserInfo() );
1041 $this->assertTrue( $info->getUserInfo()->isVerified() );
1042 $this->assertTrue( $info->getUserInfo()->isAnon() );
1043 $this->assertFalse( $info->isIdSafe() );
1044 $this->assertSame( [], $logger->getBuffer() );
1046 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1047 'provider' => $provider2,
1050 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1051 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1052 $this->assertSame( [
1053 [ LogLevel
::INFO
, 'Session "{session}": No user provided and provider cannot set user' ]
1054 ], $logger->getBuffer() );
1055 $logger->clearBuffer();
1057 // Incomplete/bad metadata
1058 $this->store
->setRawSession( $id, true );
1059 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1060 $this->assertSame( [
1061 [ LogLevel
::WARNING
, 'Session "{session}": Bad data' ],
1062 ], $logger->getBuffer() );
1063 $logger->clearBuffer();
1065 $this->store
->setRawSession( $id, [ 'data' => [] ] );
1066 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1067 $this->assertSame( [
1068 [ LogLevel
::WARNING
, 'Session "{session}": Bad data structure' ],
1069 ], $logger->getBuffer() );
1070 $logger->clearBuffer();
1072 $this->store
->deleteSession( $id );
1073 $this->store
->setRawSession( $id, [ 'metadata' => $metadata ] );
1074 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1075 $this->assertSame( [
1076 [ LogLevel
::WARNING
, 'Session "{session}": Bad data structure' ],
1077 ], $logger->getBuffer() );
1078 $logger->clearBuffer();
1080 $this->store
->setRawSession( $id, [ 'metadata' => $metadata, 'data' => true ] );
1081 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1082 $this->assertSame( [
1083 [ LogLevel
::WARNING
, 'Session "{session}": Bad data structure' ],
1084 ], $logger->getBuffer() );
1085 $logger->clearBuffer();
1087 $this->store
->setRawSession( $id, [ 'metadata' => true, 'data' => [] ] );
1088 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1089 $this->assertSame( [
1090 [ LogLevel
::WARNING
, 'Session "{session}": Bad data structure' ],
1091 ], $logger->getBuffer() );
1092 $logger->clearBuffer();
1094 foreach ( $metadata as $key => $dummy ) {
1096 unset( $tmp[$key] );
1097 $this->store
->setRawSession( $id, [ 'metadata' => $tmp, 'data' => [] ] );
1098 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1099 $this->assertSame( [
1100 [ LogLevel
::WARNING
, 'Session "{session}": Bad metadata' ],
1101 ], $logger->getBuffer() );
1102 $logger->clearBuffer();
1105 // Basic usage with metadata
1106 $this->store
->setRawSession( $id, [ 'metadata' => $metadata, 'data' => [] ] );
1107 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1108 'provider' => $provider,
1110 'userInfo' => $userInfo
1112 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1113 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1114 $this->assertTrue( $info->isIdSafe() );
1115 $this->assertSame( [], $logger->getBuffer() );
1117 // Mismatched provider
1118 $this->store
->setSessionMeta( $id, [ 'provider' => 'Bad' ] +
$metadata );
1119 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1120 'provider' => $provider,
1122 'userInfo' => $userInfo
1124 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1125 $this->assertSame( [
1126 [ LogLevel
::WARNING
, 'Session "{session}": Wrong provider Bad !== Mock' ],
1127 ], $logger->getBuffer() );
1128 $logger->clearBuffer();
1131 $this->store
->setSessionMeta( $id, [ 'provider' => 'Bad' ] +
$metadata );
1132 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1134 'userInfo' => $userInfo
1136 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1137 $this->assertSame( [
1138 [ LogLevel
::WARNING
, 'Session "{session}": Unknown provider Bad' ],
1139 ], $logger->getBuffer() );
1140 $logger->clearBuffer();
1143 $this->store
->setSessionMeta( $id, $metadata );
1144 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1146 'userInfo' => $userInfo
1148 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1149 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1150 $this->assertTrue( $info->isIdSafe() );
1151 $this->assertSame( [], $logger->getBuffer() );
1153 // Bad user metadata
1154 $this->store
->setSessionMeta( $id, [ 'userId' => -1, 'userToken' => null ] +
$metadata );
1155 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1156 'provider' => $provider,
1159 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1160 $this->assertSame( [
1161 [ LogLevel
::ERROR
, 'Session "{session}": {exception}' ],
1162 ], $logger->getBuffer() );
1163 $logger->clearBuffer();
1165 $this->store
->setSessionMeta(
1166 $id, [ 'userId' => 0, 'userName' => '<X>', 'userToken' => null ] +
$metadata
1168 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1169 'provider' => $provider,
1172 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1173 $this->assertSame( [
1174 [ LogLevel
::ERROR
, 'Session "{session}": {exception}', ],
1175 ], $logger->getBuffer() );
1176 $logger->clearBuffer();
1178 // Mismatched user by ID
1179 $this->store
->setSessionMeta(
1180 $id, [ 'userId' => $userInfo->getId() +
1, 'userToken' => null ] +
$metadata
1182 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1183 'provider' => $provider,
1185 'userInfo' => $userInfo
1187 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1188 $this->assertSame( [
1189 [ LogLevel
::WARNING
, 'Session "{session}": User ID mismatch, {uid_a} !== {uid_b}' ],
1190 ], $logger->getBuffer() );
1191 $logger->clearBuffer();
1193 // Mismatched user by name
1194 $this->store
->setSessionMeta(
1195 $id, [ 'userId' => 0, 'userName' => 'X', 'userToken' => null ] +
$metadata
1197 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1198 'provider' => $provider,
1200 'userInfo' => $userInfo
1202 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1203 $this->assertSame( [
1204 [ LogLevel
::WARNING
, 'Session "{session}": User name mismatch, {uname_a} !== {uname_b}' ],
1205 ], $logger->getBuffer() );
1206 $logger->clearBuffer();
1208 // ID matches, name doesn't
1209 $this->store
->setSessionMeta(
1210 $id, [ 'userId' => $userInfo->getId(), 'userName' => 'X', 'userToken' => null ] +
$metadata
1212 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1213 'provider' => $provider,
1215 'userInfo' => $userInfo
1217 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1218 $this->assertSame( [
1221 'Session "{session}": User ID matched but name didn\'t (rename?), {uname_a} !== {uname_b}'
1223 ], $logger->getBuffer() );
1224 $logger->clearBuffer();
1226 // Mismatched anon user
1227 $this->store
->setSessionMeta(
1228 $id, [ 'userId' => 0, 'userName' => null, 'userToken' => null ] +
$metadata
1230 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1231 'provider' => $provider,
1233 'userInfo' => $userInfo
1235 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1236 $this->assertSame( [
1239 'Session "{session}": Metadata has an anonymous user, ' .
1240 'but a non-anon user was provided',
1242 ], $logger->getBuffer() );
1243 $logger->clearBuffer();
1245 // Lookup user by ID
1246 $this->store
->setSessionMeta( $id, [ 'userToken' => null ] +
$metadata );
1247 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1248 'provider' => $provider,
1251 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1252 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1253 $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
1254 $this->assertTrue( $info->isIdSafe() );
1255 $this->assertSame( [], $logger->getBuffer() );
1257 // Lookup user by name
1258 $this->store
->setSessionMeta(
1259 $id, [ 'userId' => 0, 'userName' => 'UTSysop', 'userToken' => null ] +
$metadata
1261 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1262 'provider' => $provider,
1265 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1266 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1267 $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
1268 $this->assertTrue( $info->isIdSafe() );
1269 $this->assertSame( [], $logger->getBuffer() );
1271 // Lookup anonymous user
1272 $this->store
->setSessionMeta(
1273 $id, [ 'userId' => 0, 'userName' => null, 'userToken' => null ] +
$metadata
1275 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1276 'provider' => $provider,
1279 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1280 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1281 $this->assertTrue( $info->getUserInfo()->isAnon() );
1282 $this->assertTrue( $info->isIdSafe() );
1283 $this->assertSame( [], $logger->getBuffer() );
1285 // Unverified user with metadata
1286 $this->store
->setSessionMeta( $id, $metadata );
1287 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1288 'provider' => $provider,
1290 'userInfo' => $unverifiedUserInfo
1292 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1293 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1294 $this->assertTrue( $info->getUserInfo()->isVerified() );
1295 $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
1296 $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
1297 $this->assertTrue( $info->isIdSafe() );
1298 $this->assertSame( [], $logger->getBuffer() );
1300 // Unverified user with metadata
1301 $this->store
->setSessionMeta( $id, $metadata );
1302 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1303 'provider' => $provider,
1305 'userInfo' => $unverifiedUserInfo
1307 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1308 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1309 $this->assertTrue( $info->getUserInfo()->isVerified() );
1310 $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
1311 $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
1312 $this->assertTrue( $info->isIdSafe() );
1313 $this->assertSame( [], $logger->getBuffer() );
1316 $this->store
->setSessionMeta( $id, [ 'userToken' => 'Bad' ] +
$metadata );
1317 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1318 'provider' => $provider,
1320 'userInfo' => $userInfo
1322 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1323 $this->assertSame( [
1324 [ LogLevel
::WARNING
, 'Session "{session}": User token mismatch' ],
1325 ], $logger->getBuffer() );
1326 $logger->clearBuffer();
1328 // Provider metadata
1329 $this->store
->setSessionMeta( $id, [ 'provider' => 'Mock2' ] +
$metadata );
1330 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1331 'provider' => $provider2,
1333 'userInfo' => $userInfo,
1334 'metadata' => [ 'Info' ],
1336 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1337 $this->assertSame( [ 'Info', 'changed' => true ], $info->getProviderMetadata() );
1338 $this->assertSame( [], $logger->getBuffer() );
1340 $this->store
->setSessionMeta( $id, [ 'providerMetadata' => [ 'Saved' ] ] +
$metadata );
1341 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1342 'provider' => $provider,
1344 'userInfo' => $userInfo,
1346 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1347 $this->assertSame( [ 'Saved' ], $info->getProviderMetadata() );
1348 $this->assertSame( [], $logger->getBuffer() );
1350 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1351 'provider' => $provider,
1353 'userInfo' => $userInfo,
1354 'metadata' => [ 'Info' ],
1356 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1357 $this->assertSame( [ 'Merged' ], $info->getProviderMetadata() );
1358 $this->assertSame( [], $logger->getBuffer() );
1360 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1361 'provider' => $provider,
1363 'userInfo' => $userInfo,
1364 'metadata' => [ 'Throw' ],
1366 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1367 $this->assertSame( [
1370 'Session "{session}": Metadata merge failed: {exception}',
1372 ], $logger->getBuffer() );
1373 $logger->clearBuffer();
1375 // Remember from session
1376 $this->store
->setSessionMeta( $id, $metadata );
1377 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1378 'provider' => $provider,
1381 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1382 $this->assertFalse( $info->wasRemembered() );
1383 $this->assertSame( [], $logger->getBuffer() );
1385 $this->store
->setSessionMeta( $id, [ 'remember' => true ] +
$metadata );
1386 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1387 'provider' => $provider,
1390 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1391 $this->assertTrue( $info->wasRemembered() );
1392 $this->assertSame( [], $logger->getBuffer() );
1394 $this->store
->setSessionMeta( $id, [ 'remember' => false ] +
$metadata );
1395 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1396 'provider' => $provider,
1398 'userInfo' => $userInfo
1400 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1401 $this->assertTrue( $info->wasRemembered() );
1402 $this->assertSame( [], $logger->getBuffer() );
1404 // forceHTTPS from session
1405 $this->store
->setSessionMeta( $id, $metadata );
1406 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1407 'provider' => $provider,
1409 'userInfo' => $userInfo
1411 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1412 $this->assertFalse( $info->forceHTTPS() );
1413 $this->assertSame( [], $logger->getBuffer() );
1415 $this->store
->setSessionMeta( $id, [ 'forceHTTPS' => true ] +
$metadata );
1416 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1417 'provider' => $provider,
1419 'userInfo' => $userInfo
1421 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1422 $this->assertTrue( $info->forceHTTPS() );
1423 $this->assertSame( [], $logger->getBuffer() );
1425 $this->store
->setSessionMeta( $id, [ 'forceHTTPS' => false ] +
$metadata );
1426 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1427 'provider' => $provider,
1429 'userInfo' => $userInfo,
1430 'forceHTTPS' => true
1432 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1433 $this->assertTrue( $info->forceHTTPS() );
1434 $this->assertSame( [], $logger->getBuffer() );
1436 // "Persist" flag from session
1437 $this->store
->setSessionMeta( $id, $metadata );
1438 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1439 'provider' => $provider,
1441 'userInfo' => $userInfo
1443 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1444 $this->assertFalse( $info->wasPersisted() );
1445 $this->assertSame( [], $logger->getBuffer() );
1447 $this->store
->setSessionMeta( $id, [ 'persisted' => true ] +
$metadata );
1448 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1449 'provider' => $provider,
1451 'userInfo' => $userInfo
1453 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1454 $this->assertTrue( $info->wasPersisted() );
1455 $this->assertSame( [], $logger->getBuffer() );
1457 $this->store
->setSessionMeta( $id, [ 'persisted' => false ] +
$metadata );
1458 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1459 'provider' => $provider,
1461 'userInfo' => $userInfo,
1464 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1465 $this->assertTrue( $info->wasPersisted() );
1466 $this->assertSame( [], $logger->getBuffer() );
1468 // Provider refreshSessionInfo() returning false
1469 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1470 'provider' => $provider3,
1472 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1473 $this->assertSame( [], $logger->getBuffer() );
1477 $data = [ 'foo' => 1 ];
1478 $this->store
->setSession( $id, [ 'metadata' => $metadata, 'data' => $data ] );
1479 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1480 'provider' => $provider,
1482 'userInfo' => $userInfo
1484 $this->mergeMwGlobalArrayValue( 'wgHooks', [
1485 'SessionCheckInfo' => [ function ( &$reason, $i, $r, $m, $d ) use (
1486 $info, $metadata, $data, $request, &$called
1488 $this->assertSame( $info->getId(), $i->getId() );
1489 $this->assertSame( $info->getProvider(), $i->getProvider() );
1490 $this->assertSame( $info->getUserInfo(), $i->getUserInfo() );
1491 $this->assertSame( $request, $r );
1492 $this->assertEquals( $metadata, $m );
1493 $this->assertEquals( $data, $d );
1498 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1499 $this->assertTrue( $called );
1500 $this->assertSame( [
1501 [ LogLevel
::WARNING
, 'Session "{session}": Hook aborted' ],
1502 ], $logger->getBuffer() );
1503 $logger->clearBuffer();
1504 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionCheckInfo' => [] ] );
1506 // forceUse deletes bad backend data
1507 $this->store
->setSessionMeta( $id, [ 'userToken' => 'Bad' ] +
$metadata );
1508 $info = new SessionInfo( SessionInfo
::MIN_PRIORITY
, [
1509 'provider' => $provider,
1511 'userInfo' => $userInfo,
1514 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1515 $this->assertFalse( $this->store
->getSession( $id ) );
1516 $this->assertSame( [
1517 [ LogLevel
::WARNING
, 'Session "{session}": User token mismatch' ],
1518 ], $logger->getBuffer() );
1519 $logger->clearBuffer();