/tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php

https://gitlab.com/link233/bootmw · PHP · 301 lines · 241 code · 42 blank · 18 comment · 7 complexity · bb8d9778fb24f94ea17167af478eea3b MD5 · raw file

  1. <?php
  2. namespace MediaWiki\Session;
  3. use MediaWikiTestCase;
  4. use User;
  5. /**
  6. * @group Session
  7. * @group Database
  8. * @covers MediaWiki\Session\ImmutableSessionProviderWithCookie
  9. */
  10. class ImmutableSessionProviderWithCookieTest extends MediaWikiTestCase {
  11. private function getProvider( $name, $prefix = null ) {
  12. $config = new \HashConfig();
  13. $config->set( 'CookiePrefix', 'wgCookiePrefix' );
  14. $params = [
  15. 'sessionCookieName' => $name,
  16. 'sessionCookieOptions' => [],
  17. ];
  18. if ( $prefix !== null ) {
  19. $params['sessionCookieOptions']['prefix'] = $prefix;
  20. }
  21. $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
  22. ->setConstructorArgs( [ $params ] )
  23. ->getMockForAbstractClass();
  24. $provider->setLogger( new \TestLogger() );
  25. $provider->setConfig( $config );
  26. $provider->setManager( new SessionManager() );
  27. return $provider;
  28. }
  29. public function testConstructor() {
  30. $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
  31. ->getMockForAbstractClass();
  32. $priv = \TestingAccessWrapper::newFromObject( $provider );
  33. $this->assertNull( $priv->sessionCookieName );
  34. $this->assertSame( [], $priv->sessionCookieOptions );
  35. $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
  36. ->setConstructorArgs( [ [
  37. 'sessionCookieName' => 'Foo',
  38. 'sessionCookieOptions' => [ 'Bar' ],
  39. ] ] )
  40. ->getMockForAbstractClass();
  41. $priv = \TestingAccessWrapper::newFromObject( $provider );
  42. $this->assertSame( 'Foo', $priv->sessionCookieName );
  43. $this->assertSame( [ 'Bar' ], $priv->sessionCookieOptions );
  44. try {
  45. $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
  46. ->setConstructorArgs( [ [
  47. 'sessionCookieName' => false,
  48. ] ] )
  49. ->getMockForAbstractClass();
  50. $this->fail( 'Expected exception not thrown' );
  51. } catch ( \InvalidArgumentException $ex ) {
  52. $this->assertSame(
  53. 'sessionCookieName must be a string',
  54. $ex->getMessage()
  55. );
  56. }
  57. try {
  58. $provider = $this->getMockBuilder( ImmutableSessionProviderWithCookie::class )
  59. ->setConstructorArgs( [ [
  60. 'sessionCookieOptions' => 'x',
  61. ] ] )
  62. ->getMockForAbstractClass();
  63. $this->fail( 'Expected exception not thrown' );
  64. } catch ( \InvalidArgumentException $ex ) {
  65. $this->assertSame(
  66. 'sessionCookieOptions must be an array',
  67. $ex->getMessage()
  68. );
  69. }
  70. }
  71. public function testBasics() {
  72. $provider = $this->getProvider( null );
  73. $this->assertFalse( $provider->persistsSessionID() );
  74. $this->assertFalse( $provider->canChangeUser() );
  75. $provider = $this->getProvider( 'Foo' );
  76. $this->assertTrue( $provider->persistsSessionID() );
  77. $this->assertFalse( $provider->canChangeUser() );
  78. $msg = $provider->whyNoSession();
  79. $this->assertInstanceOf( 'Message', $msg );
  80. $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
  81. }
  82. public function testGetVaryCookies() {
  83. $provider = $this->getProvider( null );
  84. $this->assertSame( [], $provider->getVaryCookies() );
  85. $provider = $this->getProvider( 'Foo' );
  86. $this->assertSame( [ 'wgCookiePrefixFoo' ], $provider->getVaryCookies() );
  87. $provider = $this->getProvider( 'Foo', 'Bar' );
  88. $this->assertSame( [ 'BarFoo' ], $provider->getVaryCookies() );
  89. $provider = $this->getProvider( 'Foo', '' );
  90. $this->assertSame( [ 'Foo' ], $provider->getVaryCookies() );
  91. }
  92. public function testGetSessionIdFromCookie() {
  93. $this->setMwGlobals( 'wgCookiePrefix', 'wgCookiePrefix' );
  94. $request = new \FauxRequest();
  95. $request->setCookies( [
  96. '' => 'empty---------------------------',
  97. 'Foo' => 'foo-----------------------------',
  98. 'wgCookiePrefixFoo' => 'wgfoo---------------------------',
  99. 'BarFoo' => 'foobar--------------------------',
  100. 'bad' => 'bad',
  101. ], '' );
  102. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( null ) );
  103. try {
  104. $provider->getSessionIdFromCookie( $request );
  105. $this->fail( 'Expected exception not thrown' );
  106. } catch ( \BadMethodCallException $ex ) {
  107. $this->assertSame(
  108. 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie::getSessionIdFromCookie ' .
  109. 'may not be called when $this->sessionCookieName === null',
  110. $ex->getMessage()
  111. );
  112. }
  113. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo' ) );
  114. $this->assertSame(
  115. 'wgfoo---------------------------',
  116. $provider->getSessionIdFromCookie( $request )
  117. );
  118. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', 'Bar' ) );
  119. $this->assertSame(
  120. 'foobar--------------------------',
  121. $provider->getSessionIdFromCookie( $request )
  122. );
  123. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', '' ) );
  124. $this->assertSame(
  125. 'foo-----------------------------',
  126. $provider->getSessionIdFromCookie( $request )
  127. );
  128. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'bad', '' ) );
  129. $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
  130. $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'none', '' ) );
  131. $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
  132. }
  133. protected function getSentRequest() {
  134. $sentResponse = $this->getMock( 'FauxResponse', [ 'headersSent', 'setCookie', 'header' ] );
  135. $sentResponse->expects( $this->any() )->method( 'headersSent' )
  136. ->will( $this->returnValue( true ) );
  137. $sentResponse->expects( $this->never() )->method( 'setCookie' );
  138. $sentResponse->expects( $this->never() )->method( 'header' );
  139. $sentRequest = $this->getMock( 'FauxRequest', [ 'response' ] );
  140. $sentRequest->expects( $this->any() )->method( 'response' )
  141. ->will( $this->returnValue( $sentResponse ) );
  142. return $sentRequest;
  143. }
  144. /**
  145. * @dataProvider providePersistSession
  146. * @param bool $secure
  147. * @param bool $remember
  148. */
  149. public function testPersistSession( $secure, $remember ) {
  150. $this->setMwGlobals( [
  151. 'wgCookieExpiration' => 100,
  152. 'wgSecureLogin' => false,
  153. ] );
  154. $provider = $this->getProvider( 'session' );
  155. $provider->setLogger( new \Psr\Log\NullLogger() );
  156. $priv = \TestingAccessWrapper::newFromObject( $provider );
  157. $priv->sessionCookieOptions = [
  158. 'prefix' => 'x',
  159. 'path' => 'CookiePath',
  160. 'domain' => 'CookieDomain',
  161. 'secure' => false,
  162. 'httpOnly' => true,
  163. ];
  164. $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
  165. $user = User::newFromName( 'UTSysop' );
  166. $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
  167. $backend = new SessionBackend(
  168. new SessionId( $sessionId ),
  169. new SessionInfo( SessionInfo::MIN_PRIORITY, [
  170. 'provider' => $provider,
  171. 'id' => $sessionId,
  172. 'persisted' => true,
  173. 'userInfo' => UserInfo::newFromUser( $user, true ),
  174. 'idIsSafe' => true,
  175. ] ),
  176. new TestBagOStuff(),
  177. new \Psr\Log\NullLogger(),
  178. 10
  179. );
  180. \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
  181. $backend->setRememberUser( $remember );
  182. $backend->setForceHTTPS( $secure );
  183. // No cookie
  184. $priv->sessionCookieName = null;
  185. $request = new \FauxRequest();
  186. $provider->persistSession( $backend, $request );
  187. $this->assertSame( [], $request->response()->getCookies() );
  188. // Cookie
  189. $priv->sessionCookieName = 'session';
  190. $request = new \FauxRequest();
  191. $time = time();
  192. $provider->persistSession( $backend, $request );
  193. $cookie = $request->response()->getCookieData( 'xsession' );
  194. $this->assertInternalType( 'array', $cookie );
  195. if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
  196. // Round expiry so we don't randomly fail if the seconds ticked during the test.
  197. $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
  198. }
  199. $this->assertEquals( [
  200. 'value' => $sessionId,
  201. 'expire' => null,
  202. 'path' => 'CookiePath',
  203. 'domain' => 'CookieDomain',
  204. 'secure' => $secure,
  205. 'httpOnly' => true,
  206. 'raw' => false,
  207. ], $cookie );
  208. $cookie = $request->response()->getCookieData( 'forceHTTPS' );
  209. if ( $secure ) {
  210. $this->assertInternalType( 'array', $cookie );
  211. if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
  212. // Round expiry so we don't randomly fail if the seconds ticked during the test.
  213. $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
  214. }
  215. $this->assertEquals( [
  216. 'value' => 'true',
  217. 'expire' => null,
  218. 'path' => 'CookiePath',
  219. 'domain' => 'CookieDomain',
  220. 'secure' => false,
  221. 'httpOnly' => true,
  222. 'raw' => false,
  223. ], $cookie );
  224. } else {
  225. $this->assertNull( $cookie );
  226. }
  227. // Headers sent
  228. $request = $this->getSentRequest();
  229. $provider->persistSession( $backend, $request );
  230. $this->assertSame( [], $request->response()->getCookies() );
  231. }
  232. public static function providePersistSession() {
  233. return [
  234. [ false, false ],
  235. [ false, true ],
  236. [ true, false ],
  237. [ true, true ],
  238. ];
  239. }
  240. public function testUnpersistSession() {
  241. $provider = $this->getProvider( 'session', '' );
  242. $provider->setLogger( new \Psr\Log\NullLogger() );
  243. $priv = \TestingAccessWrapper::newFromObject( $provider );
  244. // No cookie
  245. $priv->sessionCookieName = null;
  246. $request = new \FauxRequest();
  247. $provider->unpersistSession( $request );
  248. $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
  249. // Cookie
  250. $priv->sessionCookieName = 'session';
  251. $request = new \FauxRequest();
  252. $provider->unpersistSession( $request );
  253. $this->assertSame( '', $request->response()->getCookie( 'session', '' ) );
  254. // Headers sent
  255. $request = $this->getSentRequest();
  256. $provider->unpersistSession( $request );
  257. $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
  258. }
  259. }