MediaWiki REL1_28
SessionManagerTest.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Session;
4
7use Psr\Log\LogLevel;
9
16
17 protected $config, $logger, $store;
18
19 protected function getManager() {
20 \ObjectCache::$instances['testSessionStore'] = new TestBagOStuff();
21 $this->config = new \HashConfig( [
22 'LanguageCode' => 'en',
23 'SessionCacheType' => 'testSessionStore',
24 'ObjectCacheSessionExpiry' => 100,
25 'SessionProviders' => [
26 [ 'class' => 'DummySessionProvider' ],
27 ]
28 ] );
29 $this->logger = new \TestLogger( false, function ( $m ) {
30 return substr( $m, 0, 15 ) === 'SessionBackend ' ? null : $m;
31 } );
32 $this->store = new TestBagOStuff();
33
34 return new SessionManager( [
35 'config' => $this->config,
36 'logger' => $this->logger,
37 'store' => $this->store,
38 ] );
39 }
40
41 protected function objectCacheDef( $object ) {
42 return [ 'factory' => function () use ( $object ) {
43 return $object;
44 } ];
45 }
46
47 public function testSingleton() {
49
50 $singleton = SessionManager::singleton();
51 $this->assertInstanceOf( SessionManager::class, $singleton );
52 $this->assertSame( $singleton, SessionManager::singleton() );
53 }
54
55 public function testGetGlobalSession() {
56 $context = \RequestContext::getMain();
57
60 }
61 $rProp = new \ReflectionProperty( PHPSessionHandler::class, 'instance' );
62 $rProp->setAccessible( true );
63 $handler = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
64 $oldEnable = $handler->enable;
65 $reset[] = new \Wikimedia\ScopedCallback( function () use ( $handler, $oldEnable ) {
66 if ( $handler->enable ) {
67 session_write_close();
68 }
69 $handler->enable = $oldEnable;
70 } );
72
73 $handler->enable = true;
74 $request = new \FauxRequest();
75 $context->setRequest( $request );
76 $id = $request->getSession()->getId();
77
78 session_id( '' );
80 $this->assertSame( $id, $session->getId() );
81
82 session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
84 $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $session->getId() );
85 $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $request->getSession()->getId() );
86
87 session_write_close();
88 $handler->enable = false;
89 $request = new \FauxRequest();
90 $context->setRequest( $request );
91 $id = $request->getSession()->getId();
92
93 session_id( '' );
95 $this->assertSame( $id, $session->getId() );
96
97 session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
99 $this->assertSame( $id, $session->getId() );
100 $this->assertSame( $id, $request->getSession()->getId() );
101 }
102
103 public function testConstructor() {
104 $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
105 $this->assertSame( $this->config, $manager->config );
106 $this->assertSame( $this->logger, $manager->logger );
107 $this->assertSame( $this->store, $manager->store );
108
109 $manager = \TestingAccessWrapper::newFromObject( new SessionManager() );
110 $this->assertSame( \RequestContext::getMain()->getConfig(), $manager->config );
111
112 $manager = \TestingAccessWrapper::newFromObject( new SessionManager( [
113 'config' => $this->config,
114 ] ) );
115 $this->assertSame( \ObjectCache::$instances['testSessionStore'], $manager->store );
116
117 foreach ( [
118 'config' => '$options[\'config\'] must be an instance of Config',
119 'logger' => '$options[\'logger\'] must be an instance of LoggerInterface',
120 'store' => '$options[\'store\'] must be an instance of BagOStuff',
121 ] as $key => $error ) {
122 try {
123 new SessionManager( [ $key => new \stdClass ] );
124 $this->fail( 'Expected exception not thrown' );
125 } catch ( \InvalidArgumentException $ex ) {
126 $this->assertSame( $error, $ex->getMessage() );
127 }
128 }
129 }
130
131 public function testGetSessionForRequest() {
132 $manager = $this->getManager();
133 $request = new \FauxRequest();
134 $request->unpersist1 = false;
135 $request->unpersist2 = false;
136
137 $id1 = '';
138 $id2 = '';
139 $idEmpty = 'empty-session-------------------';
140
141 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
142 ->setMethods(
143 [ 'provideSessionInfo', 'newSessionInfo', '__toString', 'describe', 'unpersistSession' ]
144 );
145
146 $provider1 = $providerBuilder->getMock();
147 $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
148 ->with( $this->identicalTo( $request ) )
149 ->will( $this->returnCallback( function ( $request ) {
150 return $request->info1;
151 } ) );
152 $provider1->expects( $this->any() )->method( 'newSessionInfo' )
153 ->will( $this->returnCallback( function () use ( $idEmpty, $provider1 ) {
155 'provider' => $provider1,
156 'id' => $idEmpty,
157 'persisted' => true,
158 'idIsSafe' => true,
159 ] );
160 } ) );
161 $provider1->expects( $this->any() )->method( '__toString' )
162 ->will( $this->returnValue( 'Provider1' ) );
163 $provider1->expects( $this->any() )->method( 'describe' )
164 ->will( $this->returnValue( '#1 sessions' ) );
165 $provider1->expects( $this->any() )->method( 'unpersistSession' )
166 ->will( $this->returnCallback( function ( $request ) {
167 $request->unpersist1 = true;
168 } ) );
169
170 $provider2 = $providerBuilder->getMock();
171 $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
172 ->with( $this->identicalTo( $request ) )
173 ->will( $this->returnCallback( function ( $request ) {
174 return $request->info2;
175 } ) );
176 $provider2->expects( $this->any() )->method( '__toString' )
177 ->will( $this->returnValue( 'Provider2' ) );
178 $provider2->expects( $this->any() )->method( 'describe' )
179 ->will( $this->returnValue( '#2 sessions' ) );
180 $provider2->expects( $this->any() )->method( 'unpersistSession' )
181 ->will( $this->returnCallback( function ( $request ) {
182 $request->unpersist2 = true;
183 } ) );
184
185 $this->config->set( 'SessionProviders', [
186 $this->objectCacheDef( $provider1 ),
187 $this->objectCacheDef( $provider2 ),
188 ] );
189
190 // No provider returns info
191 $request->info1 = null;
192 $request->info2 = null;
193 $session = $manager->getSessionForRequest( $request );
194 $this->assertInstanceOf( Session::class, $session );
195 $this->assertSame( $idEmpty, $session->getId() );
196 $this->assertFalse( $request->unpersist1 );
197 $this->assertFalse( $request->unpersist2 );
198
199 // Both providers return info, picks best one
201 'provider' => $provider1,
202 'id' => ( $id1 = $manager->generateSessionId() ),
203 'persisted' => true,
204 'idIsSafe' => true,
205 ] );
207 'provider' => $provider2,
208 'id' => ( $id2 = $manager->generateSessionId() ),
209 'persisted' => true,
210 'idIsSafe' => true,
211 ] );
212 $session = $manager->getSessionForRequest( $request );
213 $this->assertInstanceOf( Session::class, $session );
214 $this->assertSame( $id2, $session->getId() );
215 $this->assertFalse( $request->unpersist1 );
216 $this->assertFalse( $request->unpersist2 );
217
219 'provider' => $provider1,
220 'id' => ( $id1 = $manager->generateSessionId() ),
221 'persisted' => true,
222 'idIsSafe' => true,
223 ] );
225 'provider' => $provider2,
226 'id' => ( $id2 = $manager->generateSessionId() ),
227 'persisted' => true,
228 'idIsSafe' => true,
229 ] );
230 $session = $manager->getSessionForRequest( $request );
231 $this->assertInstanceOf( Session::class, $session );
232 $this->assertSame( $id1, $session->getId() );
233 $this->assertFalse( $request->unpersist1 );
234 $this->assertFalse( $request->unpersist2 );
235
236 // Tied priorities
238 'provider' => $provider1,
239 'id' => ( $id1 = $manager->generateSessionId() ),
240 'persisted' => true,
241 'userInfo' => UserInfo::newAnonymous(),
242 'idIsSafe' => true,
243 ] );
245 'provider' => $provider2,
246 'id' => ( $id2 = $manager->generateSessionId() ),
247 'persisted' => true,
248 'userInfo' => UserInfo::newAnonymous(),
249 'idIsSafe' => true,
250 ] );
251 try {
252 $manager->getSessionForRequest( $request );
253 $this->fail( 'Expcected exception not thrown' );
254 } catch ( \OverflowException $ex ) {
255 $this->assertStringStartsWith(
256 'Multiple sessions for this request tied for top priority: ',
257 $ex->getMessage()
258 );
259 $this->assertCount( 2, $ex->sessionInfos );
260 $this->assertContains( $request->info1, $ex->sessionInfos );
261 $this->assertContains( $request->info2, $ex->sessionInfos );
262 }
263 $this->assertFalse( $request->unpersist1 );
264 $this->assertFalse( $request->unpersist2 );
265
266 // Bad provider
268 'provider' => $provider2,
269 'id' => ( $id1 = $manager->generateSessionId() ),
270 'persisted' => true,
271 'idIsSafe' => true,
272 ] );
273 $request->info2 = null;
274 try {
275 $manager->getSessionForRequest( $request );
276 $this->fail( 'Expcected exception not thrown' );
277 } catch ( \UnexpectedValueException $ex ) {
278 $this->assertSame(
279 'Provider1 returned session info for a different provider: ' . $request->info1,
280 $ex->getMessage()
281 );
282 }
283 $this->assertFalse( $request->unpersist1 );
284 $this->assertFalse( $request->unpersist2 );
285
286 // Unusable session info
287 $this->logger->setCollect( true );
289 'provider' => $provider1,
290 'id' => ( $id1 = $manager->generateSessionId() ),
291 'persisted' => true,
292 'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
293 'idIsSafe' => true,
294 ] );
296 'provider' => $provider2,
297 'id' => ( $id2 = $manager->generateSessionId() ),
298 'persisted' => true,
299 'idIsSafe' => true,
300 ] );
301 $session = $manager->getSessionForRequest( $request );
302 $this->assertInstanceOf( Session::class, $session );
303 $this->assertSame( $id2, $session->getId() );
304 $this->logger->setCollect( false );
305 $this->assertTrue( $request->unpersist1 );
306 $this->assertFalse( $request->unpersist2 );
307 $request->unpersist1 = false;
308
309 $this->logger->setCollect( true );
311 'provider' => $provider1,
312 'id' => ( $id1 = $manager->generateSessionId() ),
313 'persisted' => true,
314 'idIsSafe' => true,
315 ] );
317 'provider' => $provider2,
318 'id' => ( $id2 = $manager->generateSessionId() ),
319 'persisted' => true,
320 'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
321 'idIsSafe' => true,
322 ] );
323 $session = $manager->getSessionForRequest( $request );
324 $this->assertInstanceOf( Session::class, $session );
325 $this->assertSame( $id1, $session->getId() );
326 $this->logger->setCollect( false );
327 $this->assertFalse( $request->unpersist1 );
328 $this->assertTrue( $request->unpersist2 );
329 $request->unpersist2 = false;
330
331 // Unpersisted session ID
333 'provider' => $provider1,
334 'id' => ( $id1 = $manager->generateSessionId() ),
335 'persisted' => false,
336 'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
337 'idIsSafe' => true,
338 ] );
339 $request->info2 = null;
340 $session = $manager->getSessionForRequest( $request );
341 $this->assertInstanceOf( Session::class, $session );
342 $this->assertSame( $id1, $session->getId() );
343 $this->assertTrue( $request->unpersist1 ); // The saving of the session does it
344 $this->assertFalse( $request->unpersist2 );
345 $session->persist();
346 $this->assertTrue( $session->isPersistent(), 'sanity check' );
347 }
348
349 public function testGetSessionById() {
350 $manager = $this->getManager();
351 try {
352 $manager->getSessionById( 'bad' );
353 $this->fail( 'Expected exception not thrown' );
354 } catch ( \InvalidArgumentException $ex ) {
355 $this->assertSame( 'Invalid session ID', $ex->getMessage() );
356 }
357
358 // Unknown session ID
359 $id = $manager->generateSessionId();
360 $session = $manager->getSessionById( $id, true );
361 $this->assertInstanceOf( Session::class, $session );
362 $this->assertSame( $id, $session->getId() );
363
364 $id = $manager->generateSessionId();
365 $this->assertNull( $manager->getSessionById( $id, false ) );
366
367 // Known but unloadable session ID
368 $this->logger->setCollect( true );
369 $id = $manager->generateSessionId();
370 $this->store->setSession( $id, [ 'metadata' => [
371 'userId' => User::idFromName( 'UTSysop' ),
372 'userToken' => 'bad',
373 ] ] );
374
375 $this->assertNull( $manager->getSessionById( $id, true ) );
376 $this->assertNull( $manager->getSessionById( $id, false ) );
377 $this->logger->setCollect( false );
378
379 // Known session ID
380 $this->store->setSession( $id, [] );
381 $session = $manager->getSessionById( $id, false );
382 $this->assertInstanceOf( Session::class, $session );
383 $this->assertSame( $id, $session->getId() );
384
385 // Store isn't checked if the session is already loaded
386 $this->store->setSession( $id, [ 'metadata' => [
387 'userId' => User::idFromName( 'UTSysop' ),
388 'userToken' => 'bad',
389 ] ] );
390 $session2 = $manager->getSessionById( $id, false );
391 $this->assertInstanceOf( Session::class, $session2 );
392 $this->assertSame( $id, $session2->getId() );
393 unset( $session, $session2 );
394 $this->logger->setCollect( true );
395 $this->assertNull( $manager->getSessionById( $id, true ) );
396 $this->logger->setCollect( false );
397
398 // Failure to create an empty session
399 $manager = $this->getManager();
400 $provider = $this->getMockBuilder( 'DummySessionProvider' )
401 ->setMethods( [ 'provideSessionInfo', 'newSessionInfo', '__toString' ] )
402 ->getMock();
403 $provider->expects( $this->any() )->method( 'provideSessionInfo' )
404 ->will( $this->returnValue( null ) );
405 $provider->expects( $this->any() )->method( 'newSessionInfo' )
406 ->will( $this->returnValue( null ) );
407 $provider->expects( $this->any() )->method( '__toString' )
408 ->will( $this->returnValue( 'MockProvider' ) );
409 $this->config->set( 'SessionProviders', [
410 $this->objectCacheDef( $provider ),
411 ] );
412 $this->logger->setCollect( true );
413 $this->assertNull( $manager->getSessionById( $id, true ) );
414 $this->logger->setCollect( false );
415 $this->assertSame( [
416 [ LogLevel::ERROR, 'Failed to create empty session: {exception}' ]
417 ], $this->logger->getBuffer() );
418 }
419
420 public function testGetEmptySession() {
421 $manager = $this->getManager();
422 $pmanager = \TestingAccessWrapper::newFromObject( $manager );
423 $request = new \FauxRequest();
424
425 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
426 ->setMethods( [ 'provideSessionInfo', 'newSessionInfo', '__toString' ] );
427
428 $expectId = null;
429 $info1 = null;
430 $info2 = null;
431
432 $provider1 = $providerBuilder->getMock();
433 $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
434 ->will( $this->returnValue( null ) );
435 $provider1->expects( $this->any() )->method( 'newSessionInfo' )
436 ->with( $this->callback( function ( $id ) use ( &$expectId ) {
437 return $id === $expectId;
438 } ) )
439 ->will( $this->returnCallback( function () use ( &$info1 ) {
440 return $info1;
441 } ) );
442 $provider1->expects( $this->any() )->method( '__toString' )
443 ->will( $this->returnValue( 'MockProvider1' ) );
444
445 $provider2 = $providerBuilder->getMock();
446 $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
447 ->will( $this->returnValue( null ) );
448 $provider2->expects( $this->any() )->method( 'newSessionInfo' )
449 ->with( $this->callback( function ( $id ) use ( &$expectId ) {
450 return $id === $expectId;
451 } ) )
452 ->will( $this->returnCallback( function () use ( &$info2 ) {
453 return $info2;
454 } ) );
455 $provider1->expects( $this->any() )->method( '__toString' )
456 ->will( $this->returnValue( 'MockProvider2' ) );
457
458 $this->config->set( 'SessionProviders', [
459 $this->objectCacheDef( $provider1 ),
460 $this->objectCacheDef( $provider2 ),
461 ] );
462
463 // No info
464 $expectId = null;
465 $info1 = null;
466 $info2 = null;
467 try {
468 $manager->getEmptySession();
469 $this->fail( 'Expected exception not thrown' );
470 } catch ( \UnexpectedValueException $ex ) {
471 $this->assertSame(
472 'No provider could provide an empty session!',
473 $ex->getMessage()
474 );
475 }
476
477 // Info
478 $expectId = null;
480 'provider' => $provider1,
481 'id' => 'empty---------------------------',
482 'persisted' => true,
483 'idIsSafe' => true,
484 ] );
485 $info2 = null;
486 $session = $manager->getEmptySession();
487 $this->assertInstanceOf( Session::class, $session );
488 $this->assertSame( 'empty---------------------------', $session->getId() );
489
490 // Info, explicitly
491 $expectId = 'expected------------------------';
493 'provider' => $provider1,
494 'id' => $expectId,
495 'persisted' => true,
496 'idIsSafe' => true,
497 ] );
498 $info2 = null;
499 $session = $pmanager->getEmptySessionInternal( null, $expectId );
500 $this->assertInstanceOf( Session::class, $session );
501 $this->assertSame( $expectId, $session->getId() );
502
503 // Wrong ID
504 $expectId = 'expected-----------------------2';
506 'provider' => $provider1,
507 'id' => "un$expectId",
508 'persisted' => true,
509 'idIsSafe' => true,
510 ] );
511 $info2 = null;
512 try {
513 $pmanager->getEmptySessionInternal( null, $expectId );
514 $this->fail( 'Expected exception not thrown' );
515 } catch ( \UnexpectedValueException $ex ) {
516 $this->assertSame(
517 'MockProvider1 returned empty session info with a wrong id: ' .
518 "un$expectId != $expectId",
519 $ex->getMessage()
520 );
521 }
522
523 // Unsafe ID
524 $expectId = 'expected-----------------------2';
526 'provider' => $provider1,
527 'id' => $expectId,
528 'persisted' => true,
529 ] );
530 $info2 = null;
531 try {
532 $pmanager->getEmptySessionInternal( null, $expectId );
533 $this->fail( 'Expected exception not thrown' );
534 } catch ( \UnexpectedValueException $ex ) {
535 $this->assertSame(
536 'MockProvider1 returned empty session info with id flagged unsafe',
537 $ex->getMessage()
538 );
539 }
540
541 // Wrong provider
542 $expectId = null;
544 'provider' => $provider2,
545 'id' => 'empty---------------------------',
546 'persisted' => true,
547 'idIsSafe' => true,
548 ] );
549 $info2 = null;
550 try {
551 $manager->getEmptySession();
552 $this->fail( 'Expected exception not thrown' );
553 } catch ( \UnexpectedValueException $ex ) {
554 $this->assertSame(
555 'MockProvider1 returned an empty session info for a different provider: ' . $info1,
556 $ex->getMessage()
557 );
558 }
559
560 // Highest priority wins
561 $expectId = null;
562 $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, [
563 'provider' => $provider1,
564 'id' => 'empty1--------------------------',
565 'persisted' => true,
566 'idIsSafe' => true,
567 ] );
569 'provider' => $provider2,
570 'id' => 'empty2--------------------------',
571 'persisted' => true,
572 'idIsSafe' => true,
573 ] );
574 $session = $manager->getEmptySession();
575 $this->assertInstanceOf( Session::class, $session );
576 $this->assertSame( 'empty1--------------------------', $session->getId() );
577
578 $expectId = null;
579 $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, [
580 'provider' => $provider1,
581 'id' => 'empty1--------------------------',
582 'persisted' => true,
583 'idIsSafe' => true,
584 ] );
585 $info2 = new SessionInfo( SessionInfo::MIN_PRIORITY + 2, [
586 'provider' => $provider2,
587 'id' => 'empty2--------------------------',
588 'persisted' => true,
589 'idIsSafe' => true,
590 ] );
591 $session = $manager->getEmptySession();
592 $this->assertInstanceOf( Session::class, $session );
593 $this->assertSame( 'empty2--------------------------', $session->getId() );
594
595 // Tied priorities throw an exception
596 $expectId = null;
598 'provider' => $provider1,
599 'id' => 'empty1--------------------------',
600 'persisted' => true,
601 'userInfo' => UserInfo::newAnonymous(),
602 'idIsSafe' => true,
603 ] );
605 'provider' => $provider2,
606 'id' => 'empty2--------------------------',
607 'persisted' => true,
608 'userInfo' => UserInfo::newAnonymous(),
609 'idIsSafe' => true,
610 ] );
611 try {
612 $manager->getEmptySession();
613 $this->fail( 'Expected exception not thrown' );
614 } catch ( \UnexpectedValueException $ex ) {
615 $this->assertStringStartsWith(
616 'Multiple empty sessions tied for top priority: ',
617 $ex->getMessage()
618 );
619 }
620
621 // Bad id
622 try {
623 $pmanager->getEmptySessionInternal( null, 'bad' );
624 $this->fail( 'Expected exception not thrown' );
625 } catch ( \InvalidArgumentException $ex ) {
626 $this->assertSame( 'Invalid session ID', $ex->getMessage() );
627 }
628
629 // Session already exists
630 $expectId = 'expected-----------------------3';
631 $this->store->setSessionMeta( $expectId, [
632 'provider' => 'MockProvider2',
633 'userId' => 0,
634 'userName' => null,
635 'userToken' => null,
636 ] );
637 try {
638 $pmanager->getEmptySessionInternal( null, $expectId );
639 $this->fail( 'Expected exception not thrown' );
640 } catch ( \InvalidArgumentException $ex ) {
641 $this->assertSame( 'Session ID already exists', $ex->getMessage() );
642 }
643 }
644
646 $user = User::newFromName( 'UTSysop' );
647 $manager = $this->getManager();
648
649 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
650 ->setMethods( [ 'invalidateSessionsForUser', '__toString' ] );
651
652 $provider1 = $providerBuilder->getMock();
653 $provider1->expects( $this->once() )->method( 'invalidateSessionsForUser' )
654 ->with( $this->identicalTo( $user ) );
655 $provider1->expects( $this->any() )->method( '__toString' )
656 ->will( $this->returnValue( 'MockProvider1' ) );
657
658 $provider2 = $providerBuilder->getMock();
659 $provider2->expects( $this->once() )->method( 'invalidateSessionsForUser' )
660 ->with( $this->identicalTo( $user ) );
661 $provider2->expects( $this->any() )->method( '__toString' )
662 ->will( $this->returnValue( 'MockProvider2' ) );
663
664 $this->config->set( 'SessionProviders', [
665 $this->objectCacheDef( $provider1 ),
666 $this->objectCacheDef( $provider2 ),
667 ] );
668
669 $oldToken = $user->getToken( true );
670 $manager->invalidateSessionsForUser( $user );
671 $this->assertNotEquals( $oldToken, $user->getToken() );
672 }
673
674 public function testGetVaryHeaders() {
675 $manager = $this->getManager();
676
677 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
678 ->setMethods( [ 'getVaryHeaders', '__toString' ] );
679
680 $provider1 = $providerBuilder->getMock();
681 $provider1->expects( $this->once() )->method( 'getVaryHeaders' )
682 ->will( $this->returnValue( [
683 'Foo' => null,
684 'Bar' => [ 'X', 'Bar1' ],
685 'Quux' => null,
686 ] ) );
687 $provider1->expects( $this->any() )->method( '__toString' )
688 ->will( $this->returnValue( 'MockProvider1' ) );
689
690 $provider2 = $providerBuilder->getMock();
691 $provider2->expects( $this->once() )->method( 'getVaryHeaders' )
692 ->will( $this->returnValue( [
693 'Baz' => null,
694 'Bar' => [ 'X', 'Bar2' ],
695 'Quux' => [ 'Quux' ],
696 ] ) );
697 $provider2->expects( $this->any() )->method( '__toString' )
698 ->will( $this->returnValue( 'MockProvider2' ) );
699
700 $this->config->set( 'SessionProviders', [
701 $this->objectCacheDef( $provider1 ),
702 $this->objectCacheDef( $provider2 ),
703 ] );
704
705 $expect = [
706 'Foo' => [],
707 'Bar' => [ 'X', 'Bar1', 3 => 'Bar2' ],
708 'Quux' => [ 'Quux' ],
709 'Baz' => [],
710 ];
711
712 $this->assertEquals( $expect, $manager->getVaryHeaders() );
713
714 // Again, to ensure it's cached
715 $this->assertEquals( $expect, $manager->getVaryHeaders() );
716 }
717
718 public function testGetVaryCookies() {
719 $manager = $this->getManager();
720
721 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
722 ->setMethods( [ 'getVaryCookies', '__toString' ] );
723
724 $provider1 = $providerBuilder->getMock();
725 $provider1->expects( $this->once() )->method( 'getVaryCookies' )
726 ->will( $this->returnValue( [ 'Foo', 'Bar' ] ) );
727 $provider1->expects( $this->any() )->method( '__toString' )
728 ->will( $this->returnValue( 'MockProvider1' ) );
729
730 $provider2 = $providerBuilder->getMock();
731 $provider2->expects( $this->once() )->method( 'getVaryCookies' )
732 ->will( $this->returnValue( [ 'Foo', 'Baz' ] ) );
733 $provider2->expects( $this->any() )->method( '__toString' )
734 ->will( $this->returnValue( 'MockProvider2' ) );
735
736 $this->config->set( 'SessionProviders', [
737 $this->objectCacheDef( $provider1 ),
738 $this->objectCacheDef( $provider2 ),
739 ] );
740
741 $expect = [ 'Foo', 'Bar', 'Baz' ];
742
743 $this->assertEquals( $expect, $manager->getVaryCookies() );
744
745 // Again, to ensure it's cached
746 $this->assertEquals( $expect, $manager->getVaryCookies() );
747 }
748
749 public function testGetProviders() {
750 $realManager = $this->getManager();
751 $manager = \TestingAccessWrapper::newFromObject( $realManager );
752
753 $this->config->set( 'SessionProviders', [
754 [ 'class' => 'DummySessionProvider' ],
755 ] );
756 $providers = $manager->getProviders();
757 $this->assertArrayHasKey( 'DummySessionProvider', $providers );
758 $provider = \TestingAccessWrapper::newFromObject( $providers['DummySessionProvider'] );
759 $this->assertSame( $manager->logger, $provider->logger );
760 $this->assertSame( $manager->config, $provider->config );
761 $this->assertSame( $realManager, $provider->getManager() );
762
763 $this->config->set( 'SessionProviders', [
764 [ 'class' => 'DummySessionProvider' ],
765 [ 'class' => 'DummySessionProvider' ],
766 ] );
767 $manager->sessionProviders = null;
768 try {
769 $manager->getProviders();
770 $this->fail( 'Expected exception not thrown' );
771 } catch ( \UnexpectedValueException $ex ) {
772 $this->assertSame(
773 'Duplicate provider name "DummySessionProvider"',
774 $ex->getMessage()
775 );
776 }
777 }
778
779 public function testShutdown() {
780 $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
781 $manager->setLogger( new \Psr\Log\NullLogger() );
782
783 $mock = $this->getMock( 'stdClass', [ 'shutdown' ] );
784 $mock->expects( $this->once() )->method( 'shutdown' );
785
786 $manager->allSessionBackends = [ $mock ];
787 $manager->shutdown();
788 }
789
790 public function testGetSessionFromInfo() {
791 $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
792 $request = new \FauxRequest();
793
794 $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
795
797 'provider' => $manager->getProvider( 'DummySessionProvider' ),
798 'id' => $id,
799 'persisted' => true,
800 'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
801 'idIsSafe' => true,
802 ] );
803 \TestingAccessWrapper::newFromObject( $info )->idIsSafe = true;
804 $session1 = \TestingAccessWrapper::newFromObject(
805 $manager->getSessionFromInfo( $info, $request )
806 );
807 $session2 = \TestingAccessWrapper::newFromObject(
808 $manager->getSessionFromInfo( $info, $request )
809 );
810
811 $this->assertSame( $session1->backend, $session2->backend );
812 $this->assertNotEquals( $session1->index, $session2->index );
813 $this->assertSame( $session1->getSessionId(), $session2->getSessionId() );
814 $this->assertSame( $id, $session1->getId() );
815
816 \TestingAccessWrapper::newFromObject( $info )->idIsSafe = false;
817 $session3 = $manager->getSessionFromInfo( $info, $request );
818 $this->assertNotSame( $id, $session3->getId() );
819 }
820
821 public function testBackendRegistration() {
822 $manager = $this->getManager();
823
824 $session = $manager->getSessionForRequest( new \FauxRequest );
825 $backend = \TestingAccessWrapper::newFromObject( $session )->backend;
826 $sessionId = $session->getSessionId();
827 $id = (string)$sessionId;
828
829 $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
830
831 $manager->changeBackendId( $backend );
832 $this->assertSame( $sessionId, $session->getSessionId() );
833 $this->assertNotEquals( $id, (string)$sessionId );
834 $id = (string)$sessionId;
835
836 $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
837
838 // Destruction of the session here causes the backend to be deregistered
839 $session = null;
840
841 try {
842 $manager->changeBackendId( $backend );
843 $this->fail( 'Expected exception not thrown' );
844 } catch ( \InvalidArgumentException $ex ) {
845 $this->assertSame(
846 'Backend was not registered with this SessionManager', $ex->getMessage()
847 );
848 }
849
850 try {
851 $manager->deregisterSessionBackend( $backend );
852 $this->fail( 'Expected exception not thrown' );
853 } catch ( \InvalidArgumentException $ex ) {
854 $this->assertSame(
855 'Backend was not registered with this SessionManager', $ex->getMessage()
856 );
857 }
858
859 $session = $manager->getSessionById( $id, true );
860 $this->assertSame( $sessionId, $session->getSessionId() );
861 }
862
863 public function testGenerateSessionId() {
864 $manager = $this->getManager();
865
866 $id = $manager->generateSessionId();
867 $this->assertTrue( SessionManager::validateSessionId( $id ), "Generated ID: $id" );
868 }
869
870 public function testPreventSessionsForUser() {
871 $manager = $this->getManager();
872
873 $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
874 ->setMethods( [ 'preventSessionsForUser', '__toString' ] );
875
876 $provider1 = $providerBuilder->getMock();
877 $provider1->expects( $this->once() )->method( 'preventSessionsForUser' )
878 ->with( $this->equalTo( 'UTSysop' ) );
879 $provider1->expects( $this->any() )->method( '__toString' )
880 ->will( $this->returnValue( 'MockProvider1' ) );
881
882 $this->config->set( 'SessionProviders', [
883 $this->objectCacheDef( $provider1 ),
884 ] );
885
886 $this->assertFalse( $manager->isUserSessionPrevented( 'UTSysop' ) );
887 $manager->preventSessionsForUser( 'UTSysop' );
888 $this->assertTrue( $manager->isUserSessionPrevented( 'UTSysop' ) );
889 }
890
892 $manager = $this->getManager();
893 $logger = new \TestLogger( true );
894 $manager->setLogger( $logger );
895 $request = new \FauxRequest();
896
897 // TestingAccessWrapper can't handle methods with reference arguments, sigh.
898 $rClass = new \ReflectionClass( $manager );
899 $rMethod = $rClass->getMethod( 'loadSessionInfoFromStore' );
900 $rMethod->setAccessible( true );
901 $loadSessionInfoFromStore = function ( &$info ) use ( $rMethod, $manager, $request ) {
902 return $rMethod->invokeArgs( $manager, [ &$info, $request ] );
903 };
904
905 $userInfo = UserInfo::newFromName( 'UTSysop', true );
906 $unverifiedUserInfo = UserInfo::newFromName( 'UTSysop', false );
907
908 $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
909 $metadata = [
910 'userId' => $userInfo->getId(),
911 'userName' => $userInfo->getName(),
912 'userToken' => $userInfo->getToken( true ),
913 'provider' => 'Mock',
914 ];
915
916 $builder = $this->getMockBuilder( SessionProvider::class )
917 ->setMethods( [ '__toString', 'mergeMetadata', 'refreshSessionInfo' ] );
918
919 $provider = $builder->getMockForAbstractClass();
920 $provider->setManager( $manager );
921 $provider->expects( $this->any() )->method( 'persistsSessionId' )
922 ->will( $this->returnValue( true ) );
923 $provider->expects( $this->any() )->method( 'canChangeUser' )
924 ->will( $this->returnValue( true ) );
925 $provider->expects( $this->any() )->method( 'refreshSessionInfo' )
926 ->will( $this->returnValue( true ) );
927 $provider->expects( $this->any() )->method( '__toString' )
928 ->will( $this->returnValue( 'Mock' ) );
929 $provider->expects( $this->any() )->method( 'mergeMetadata' )
930 ->will( $this->returnCallback( function ( $a, $b ) {
931 if ( $b === [ 'Throw' ] ) {
932 throw new MetadataMergeException( 'no merge!' );
933 }
934 return [ 'Merged' ];
935 } ) );
936
937 $provider2 = $builder->getMockForAbstractClass();
938 $provider2->setManager( $manager );
939 $provider2->expects( $this->any() )->method( 'persistsSessionId' )
940 ->will( $this->returnValue( false ) );
941 $provider2->expects( $this->any() )->method( 'canChangeUser' )
942 ->will( $this->returnValue( false ) );
943 $provider2->expects( $this->any() )->method( '__toString' )
944 ->will( $this->returnValue( 'Mock2' ) );
945 $provider2->expects( $this->any() )->method( 'refreshSessionInfo' )
946 ->will( $this->returnCallback( function ( $info, $request, &$metadata ) {
947 $metadata['changed'] = true;
948 return true;
949 } ) );
950
951 $provider3 = $builder->getMockForAbstractClass();
952 $provider3->setManager( $manager );
953 $provider3->expects( $this->any() )->method( 'persistsSessionId' )
954 ->will( $this->returnValue( true ) );
955 $provider3->expects( $this->any() )->method( 'canChangeUser' )
956 ->will( $this->returnValue( true ) );
957 $provider3->expects( $this->once() )->method( 'refreshSessionInfo' )
958 ->will( $this->returnValue( false ) );
959 $provider3->expects( $this->any() )->method( '__toString' )
960 ->will( $this->returnValue( 'Mock3' ) );
961
962 \TestingAccessWrapper::newFromObject( $manager )->sessionProviders = [
963 (string)$provider => $provider,
964 (string)$provider2 => $provider2,
965 (string)$provider3 => $provider3,
966 ];
967
968 // No metadata, basic usage
970 'provider' => $provider,
971 'id' => $id,
972 'userInfo' => $userInfo
973 ] );
974 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
975 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
976 $this->assertFalse( $info->isIdSafe() );
977 $this->assertSame( [], $logger->getBuffer() );
978
980 'provider' => $provider,
981 'userInfo' => $userInfo
982 ] );
983 $this->assertTrue( $info->isIdSafe(), 'sanity check' );
984 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
985 $this->assertTrue( $info->isIdSafe() );
986 $this->assertSame( [], $logger->getBuffer() );
987
989 'provider' => $provider2,
990 'id' => $id,
991 'userInfo' => $userInfo
992 ] );
993 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
994 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
995 $this->assertTrue( $info->isIdSafe() );
996 $this->assertSame( [], $logger->getBuffer() );
997
998 // Unverified user, no metadata
1000 'provider' => $provider,
1001 'id' => $id,
1002 'userInfo' => $unverifiedUserInfo
1003 ] );
1004 $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
1005 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1006 $this->assertSame( [
1007 [
1008 LogLevel::WARNING,
1009 'Session "{session}": Unverified user provided and no metadata to auth it',
1010 ]
1011 ], $logger->getBuffer() );
1012 $logger->clearBuffer();
1013
1014 // No metadata, missing data
1016 'id' => $id,
1017 'userInfo' => $userInfo
1018 ] );
1019 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1020 $this->assertSame( [
1021 [ LogLevel::WARNING, 'Session "{session}": Null provider and no metadata' ],
1022 ], $logger->getBuffer() );
1023 $logger->clearBuffer();
1024
1026 'provider' => $provider,
1027 'id' => $id,
1028 ] );
1029 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1030 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1031 $this->assertInstanceOf( UserInfo::class, $info->getUserInfo() );
1032 $this->assertTrue( $info->getUserInfo()->isVerified() );
1033 $this->assertTrue( $info->getUserInfo()->isAnon() );
1034 $this->assertFalse( $info->isIdSafe() );
1035 $this->assertSame( [], $logger->getBuffer() );
1036
1038 'provider' => $provider2,
1039 'id' => $id,
1040 ] );
1041 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1042 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1043 $this->assertSame( [
1044 [ LogLevel::INFO, 'Session "{session}": No user provided and provider cannot set user' ]
1045 ], $logger->getBuffer() );
1046 $logger->clearBuffer();
1047
1048 // Incomplete/bad metadata
1049 $this->store->setRawSession( $id, true );
1050 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1051 $this->assertSame( [
1052 [ LogLevel::WARNING, 'Session "{session}": Bad data' ],
1053 ], $logger->getBuffer() );
1054 $logger->clearBuffer();
1055
1056 $this->store->setRawSession( $id, [ 'data' => [] ] );
1057 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1058 $this->assertSame( [
1059 [ LogLevel::WARNING, 'Session "{session}": Bad data structure' ],
1060 ], $logger->getBuffer() );
1061 $logger->clearBuffer();
1062
1063 $this->store->deleteSession( $id );
1064 $this->store->setRawSession( $id, [ 'metadata' => $metadata ] );
1065 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1066 $this->assertSame( [
1067 [ LogLevel::WARNING, 'Session "{session}": Bad data structure' ],
1068 ], $logger->getBuffer() );
1069 $logger->clearBuffer();
1070
1071 $this->store->setRawSession( $id, [ 'metadata' => $metadata, 'data' => true ] );
1072 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1073 $this->assertSame( [
1074 [ LogLevel::WARNING, 'Session "{session}": Bad data structure' ],
1075 ], $logger->getBuffer() );
1076 $logger->clearBuffer();
1077
1078 $this->store->setRawSession( $id, [ 'metadata' => true, 'data' => [] ] );
1079 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1080 $this->assertSame( [
1081 [ LogLevel::WARNING, 'Session "{session}": Bad data structure' ],
1082 ], $logger->getBuffer() );
1083 $logger->clearBuffer();
1084
1085 foreach ( $metadata as $key => $dummy ) {
1086 $tmp = $metadata;
1087 unset( $tmp[$key] );
1088 $this->store->setRawSession( $id, [ 'metadata' => $tmp, 'data' => [] ] );
1089 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1090 $this->assertSame( [
1091 [ LogLevel::WARNING, 'Session "{session}": Bad metadata' ],
1092 ], $logger->getBuffer() );
1093 $logger->clearBuffer();
1094 }
1095
1096 // Basic usage with metadata
1097 $this->store->setRawSession( $id, [ 'metadata' => $metadata, 'data' => [] ] );
1099 'provider' => $provider,
1100 'id' => $id,
1101 'userInfo' => $userInfo
1102 ] );
1103 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1104 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1105 $this->assertTrue( $info->isIdSafe() );
1106 $this->assertSame( [], $logger->getBuffer() );
1107
1108 // Mismatched provider
1109 $this->store->setSessionMeta( $id, [ 'provider' => 'Bad' ] + $metadata );
1111 'provider' => $provider,
1112 'id' => $id,
1113 'userInfo' => $userInfo
1114 ] );
1115 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1116 $this->assertSame( [
1117 [ LogLevel::WARNING, 'Session "{session}": Wrong provider Bad !== Mock' ],
1118 ], $logger->getBuffer() );
1119 $logger->clearBuffer();
1120
1121 // Unknown provider
1122 $this->store->setSessionMeta( $id, [ 'provider' => 'Bad' ] + $metadata );
1124 'id' => $id,
1125 'userInfo' => $userInfo
1126 ] );
1127 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1128 $this->assertSame( [
1129 [ LogLevel::WARNING, 'Session "{session}": Unknown provider Bad' ],
1130 ], $logger->getBuffer() );
1131 $logger->clearBuffer();
1132
1133 // Fill in provider
1134 $this->store->setSessionMeta( $id, $metadata );
1136 'id' => $id,
1137 'userInfo' => $userInfo
1138 ] );
1139 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1140 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1141 $this->assertTrue( $info->isIdSafe() );
1142 $this->assertSame( [], $logger->getBuffer() );
1143
1144 // Bad user metadata
1145 $this->store->setSessionMeta( $id, [ 'userId' => -1, 'userToken' => null ] + $metadata );
1147 'provider' => $provider,
1148 'id' => $id,
1149 ] );
1150 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1151 $this->assertSame( [
1152 [ LogLevel::ERROR, 'Session "{session}": {exception}' ],
1153 ], $logger->getBuffer() );
1154 $logger->clearBuffer();
1155
1156 $this->store->setSessionMeta(
1157 $id, [ 'userId' => 0, 'userName' => '<X>', 'userToken' => null ] + $metadata
1158 );
1160 'provider' => $provider,
1161 'id' => $id,
1162 ] );
1163 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1164 $this->assertSame( [
1165 [ LogLevel::ERROR, 'Session "{session}": {exception}', ],
1166 ], $logger->getBuffer() );
1167 $logger->clearBuffer();
1168
1169 // Mismatched user by ID
1170 $this->store->setSessionMeta(
1171 $id, [ 'userId' => $userInfo->getId() + 1, 'userToken' => null ] + $metadata
1172 );
1174 'provider' => $provider,
1175 'id' => $id,
1176 'userInfo' => $userInfo
1177 ] );
1178 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1179 $this->assertSame( [
1180 [ LogLevel::WARNING, 'Session "{session}": User ID mismatch, {uid_a} !== {uid_b}' ],
1181 ], $logger->getBuffer() );
1182 $logger->clearBuffer();
1183
1184 // Mismatched user by name
1185 $this->store->setSessionMeta(
1186 $id, [ 'userId' => 0, 'userName' => 'X', 'userToken' => null ] + $metadata
1187 );
1189 'provider' => $provider,
1190 'id' => $id,
1191 'userInfo' => $userInfo
1192 ] );
1193 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1194 $this->assertSame( [
1195 [ LogLevel::WARNING, 'Session "{session}": User name mismatch, {uname_a} !== {uname_b}' ],
1196 ], $logger->getBuffer() );
1197 $logger->clearBuffer();
1198
1199 // ID matches, name doesn't
1200 $this->store->setSessionMeta(
1201 $id, [ 'userId' => $userInfo->getId(), 'userName' => 'X', 'userToken' => null ] + $metadata
1202 );
1204 'provider' => $provider,
1205 'id' => $id,
1206 'userInfo' => $userInfo
1207 ] );
1208 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1209 $this->assertSame( [
1210 [
1211 LogLevel::WARNING,
1212 'Session "{session}": User ID matched but name didn\'t (rename?), {uname_a} !== {uname_b}'
1213 ],
1214 ], $logger->getBuffer() );
1215 $logger->clearBuffer();
1216
1217 // Mismatched anon user
1218 $this->store->setSessionMeta(
1219 $id, [ 'userId' => 0, 'userName' => null, 'userToken' => null ] + $metadata
1220 );
1222 'provider' => $provider,
1223 'id' => $id,
1224 'userInfo' => $userInfo
1225 ] );
1226 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1227 $this->assertSame( [
1228 [
1229 LogLevel::WARNING,
1230 'Session "{session}": Metadata has an anonymous user, ' .
1231 'but a non-anon user was provided',
1232 ],
1233 ], $logger->getBuffer() );
1234 $logger->clearBuffer();
1235
1236 // Lookup user by ID
1237 $this->store->setSessionMeta( $id, [ 'userToken' => null ] + $metadata );
1239 'provider' => $provider,
1240 'id' => $id,
1241 ] );
1242 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1243 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1244 $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
1245 $this->assertTrue( $info->isIdSafe() );
1246 $this->assertSame( [], $logger->getBuffer() );
1247
1248 // Lookup user by name
1249 $this->store->setSessionMeta(
1250 $id, [ 'userId' => 0, 'userName' => 'UTSysop', 'userToken' => null ] + $metadata
1251 );
1253 'provider' => $provider,
1254 'id' => $id,
1255 ] );
1256 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1257 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1258 $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
1259 $this->assertTrue( $info->isIdSafe() );
1260 $this->assertSame( [], $logger->getBuffer() );
1261
1262 // Lookup anonymous user
1263 $this->store->setSessionMeta(
1264 $id, [ 'userId' => 0, 'userName' => null, 'userToken' => null ] + $metadata
1265 );
1267 'provider' => $provider,
1268 'id' => $id,
1269 ] );
1270 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1271 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1272 $this->assertTrue( $info->getUserInfo()->isAnon() );
1273 $this->assertTrue( $info->isIdSafe() );
1274 $this->assertSame( [], $logger->getBuffer() );
1275
1276 // Unverified user with metadata
1277 $this->store->setSessionMeta( $id, $metadata );
1279 'provider' => $provider,
1280 'id' => $id,
1281 'userInfo' => $unverifiedUserInfo
1282 ] );
1283 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1284 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1285 $this->assertTrue( $info->getUserInfo()->isVerified() );
1286 $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
1287 $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
1288 $this->assertTrue( $info->isIdSafe() );
1289 $this->assertSame( [], $logger->getBuffer() );
1290
1291 // Unverified user with metadata
1292 $this->store->setSessionMeta( $id, $metadata );
1294 'provider' => $provider,
1295 'id' => $id,
1296 'userInfo' => $unverifiedUserInfo
1297 ] );
1298 $this->assertFalse( $info->isIdSafe(), 'sanity check' );
1299 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1300 $this->assertTrue( $info->getUserInfo()->isVerified() );
1301 $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
1302 $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
1303 $this->assertTrue( $info->isIdSafe() );
1304 $this->assertSame( [], $logger->getBuffer() );
1305
1306 // Wrong token
1307 $this->store->setSessionMeta( $id, [ 'userToken' => 'Bad' ] + $metadata );
1309 'provider' => $provider,
1310 'id' => $id,
1311 'userInfo' => $userInfo
1312 ] );
1313 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1314 $this->assertSame( [
1315 [ LogLevel::WARNING, 'Session "{session}": User token mismatch' ],
1316 ], $logger->getBuffer() );
1317 $logger->clearBuffer();
1318
1319 // Provider metadata
1320 $this->store->setSessionMeta( $id, [ 'provider' => 'Mock2' ] + $metadata );
1322 'provider' => $provider2,
1323 'id' => $id,
1324 'userInfo' => $userInfo,
1325 'metadata' => [ 'Info' ],
1326 ] );
1327 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1328 $this->assertSame( [ 'Info', 'changed' => true ], $info->getProviderMetadata() );
1329 $this->assertSame( [], $logger->getBuffer() );
1330
1331 $this->store->setSessionMeta( $id, [ 'providerMetadata' => [ 'Saved' ] ] + $metadata );
1333 'provider' => $provider,
1334 'id' => $id,
1335 'userInfo' => $userInfo,
1336 ] );
1337 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1338 $this->assertSame( [ 'Saved' ], $info->getProviderMetadata() );
1339 $this->assertSame( [], $logger->getBuffer() );
1340
1342 'provider' => $provider,
1343 'id' => $id,
1344 'userInfo' => $userInfo,
1345 'metadata' => [ 'Info' ],
1346 ] );
1347 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1348 $this->assertSame( [ 'Merged' ], $info->getProviderMetadata() );
1349 $this->assertSame( [], $logger->getBuffer() );
1350
1352 'provider' => $provider,
1353 'id' => $id,
1354 'userInfo' => $userInfo,
1355 'metadata' => [ 'Throw' ],
1356 ] );
1357 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1358 $this->assertSame( [
1359 [
1360 LogLevel::WARNING,
1361 'Session "{session}": Metadata merge failed: {exception}',
1362 ],
1363 ], $logger->getBuffer() );
1364 $logger->clearBuffer();
1365
1366 // Remember from session
1367 $this->store->setSessionMeta( $id, $metadata );
1369 'provider' => $provider,
1370 'id' => $id,
1371 ] );
1372 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1373 $this->assertFalse( $info->wasRemembered() );
1374 $this->assertSame( [], $logger->getBuffer() );
1375
1376 $this->store->setSessionMeta( $id, [ 'remember' => true ] + $metadata );
1378 'provider' => $provider,
1379 'id' => $id,
1380 ] );
1381 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1382 $this->assertTrue( $info->wasRemembered() );
1383 $this->assertSame( [], $logger->getBuffer() );
1384
1385 $this->store->setSessionMeta( $id, [ 'remember' => false ] + $metadata );
1387 'provider' => $provider,
1388 'id' => $id,
1389 'userInfo' => $userInfo
1390 ] );
1391 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1392 $this->assertTrue( $info->wasRemembered() );
1393 $this->assertSame( [], $logger->getBuffer() );
1394
1395 // forceHTTPS from session
1396 $this->store->setSessionMeta( $id, $metadata );
1398 'provider' => $provider,
1399 'id' => $id,
1400 'userInfo' => $userInfo
1401 ] );
1402 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1403 $this->assertFalse( $info->forceHTTPS() );
1404 $this->assertSame( [], $logger->getBuffer() );
1405
1406 $this->store->setSessionMeta( $id, [ 'forceHTTPS' => true ] + $metadata );
1408 'provider' => $provider,
1409 'id' => $id,
1410 'userInfo' => $userInfo
1411 ] );
1412 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1413 $this->assertTrue( $info->forceHTTPS() );
1414 $this->assertSame( [], $logger->getBuffer() );
1415
1416 $this->store->setSessionMeta( $id, [ 'forceHTTPS' => false ] + $metadata );
1418 'provider' => $provider,
1419 'id' => $id,
1420 'userInfo' => $userInfo,
1421 'forceHTTPS' => true
1422 ] );
1423 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1424 $this->assertTrue( $info->forceHTTPS() );
1425 $this->assertSame( [], $logger->getBuffer() );
1426
1427 // "Persist" flag from session
1428 $this->store->setSessionMeta( $id, $metadata );
1430 'provider' => $provider,
1431 'id' => $id,
1432 'userInfo' => $userInfo
1433 ] );
1434 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1435 $this->assertFalse( $info->wasPersisted() );
1436 $this->assertSame( [], $logger->getBuffer() );
1437
1438 $this->store->setSessionMeta( $id, [ 'persisted' => true ] + $metadata );
1440 'provider' => $provider,
1441 'id' => $id,
1442 'userInfo' => $userInfo
1443 ] );
1444 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1445 $this->assertTrue( $info->wasPersisted() );
1446 $this->assertSame( [], $logger->getBuffer() );
1447
1448 $this->store->setSessionMeta( $id, [ 'persisted' => false ] + $metadata );
1450 'provider' => $provider,
1451 'id' => $id,
1452 'userInfo' => $userInfo,
1453 'persisted' => true
1454 ] );
1455 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1456 $this->assertTrue( $info->wasPersisted() );
1457 $this->assertSame( [], $logger->getBuffer() );
1458
1459 // Provider refreshSessionInfo() returning false
1461 'provider' => $provider3,
1462 ] );
1463 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1464 $this->assertSame( [], $logger->getBuffer() );
1465
1466 // Hook
1467 $called = false;
1468 $data = [ 'foo' => 1 ];
1469 $this->store->setSession( $id, [ 'metadata' => $metadata, 'data' => $data ] );
1471 'provider' => $provider,
1472 'id' => $id,
1473 'userInfo' => $userInfo
1474 ] );
1475 $this->mergeMwGlobalArrayValue( 'wgHooks', [
1476 'SessionCheckInfo' => [ function ( &$reason, $i, $r, $m, $d ) use (
1477 $info, $metadata, $data, $request, &$called
1478 ) {
1479 $this->assertSame( $info->getId(), $i->getId() );
1480 $this->assertSame( $info->getProvider(), $i->getProvider() );
1481 $this->assertSame( $info->getUserInfo(), $i->getUserInfo() );
1482 $this->assertSame( $request, $r );
1483 $this->assertEquals( $metadata, $m );
1484 $this->assertEquals( $data, $d );
1485 $called = true;
1486 return false;
1487 } ]
1488 ] );
1489 $this->assertFalse( $loadSessionInfoFromStore( $info ) );
1490 $this->assertTrue( $called );
1491 $this->assertSame( [
1492 [ LogLevel::WARNING, 'Session "{session}": Hook aborted' ],
1493 ], $logger->getBuffer() );
1494 $logger->clearBuffer();
1495 $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'SessionCheckInfo' => [] ] );
1496
1497 // forceUse deletes bad backend data
1498 $this->store->setSessionMeta( $id, [ 'userToken' => 'Bad' ] + $metadata );
1500 'provider' => $provider,
1501 'id' => $id,
1502 'userInfo' => $userInfo,
1503 'forceUse' => true,
1504 ] );
1505 $this->assertTrue( $loadSessionInfoFromStore( $info ) );
1506 $this->assertFalse( $this->store->getSession( $id ) );
1507 $this->assertSame( [
1508 [ LogLevel::WARNING, 'Session "{session}": User token mismatch' ],
1509 ], $logger->getBuffer() );
1510 $logger->clearBuffer();
1511 }
1512}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
</td >< td > &</td >< td > t want your writing to be edited mercilessly and redistributed at will
Authentication plugin interface.
WebRequest clone which takes values from a provided array.
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
$called
$called tracks whether the setUp and tearDown method has been called.
Subclass of UnexpectedValueException that can be annotated with additional data for debug logging.
static isInstalled()
Test whether the handler is installed.
static install(SessionManager $manager)
Install a session handler for the current web request.
Value object returned by SessionProvider.
const MIN_PRIORITY
Minimum allowed priority.
const MAX_PRIORITY
Maximum allowed priority.
Session Database MediaWiki\Session\SessionManager.
This serves as the entry point to the MediaWiki session handling system.
static getGlobalSession()
Get the "global" session.
static validateSessionId( $id)
Validate a session ID.
static singleton()
Get the global SessionManager.
BagOStuff with utility functions for MediaWiki\\Session\\* testing.
static setSessionManagerSingleton(SessionManager $manager=null)
Override the singleton for unit testing.
Definition TestUtils.php:17
static newFromName( $name, $verified=false)
Create an instance for a logged-in user by name.
Definition UserInfo.php:102
static newAnonymous()
Create an instance for an anonymous (i.e.
Definition UserInfo.php:74
static BagOStuff[] $instances
Map of (id => BagOStuff)
static getMain()
Static methods.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:48
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:249
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition hooks.txt:183
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2685
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition hooks.txt:925
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
$context
Definition load.php:50
MediaWiki s SiteStore can be cached and stored in a flat in a json format If the SiteStore is frequently the file cache may provide a performance benefit over a database store
Definition sitescache.txt:4