‪TYPO3CMS  10.4
RedisSessionBackendTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
25 
31 class ‪RedisSessionBackendTest extends FunctionalTestCase
32 {
36  protected ‪$subject;
37 
41  protected ‪$redis;
42 
46  protected ‪$testSessionRecord = [
47  // RedisSessionBackend::hash('randomSessionId') with encryption key 12345
48  'ses_id' => '21c0e911565a67315cdc384889c470fd291feafbfa62e31ecf7409430640bc7a',
49  'ses_userid' => 1,
50  // serialize(['foo' => 'bar', 'boo' => 'far'])
51  'ses_data' => 'a:2:{s:3:"foo";s:3:"bar";s:3:"boo";s:3:"far";}',
52  ];
53 
57  protected function ‪setUp(): void
58  {
59  parent::setUp();
60  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] = '12345';
61 
62  if (!getenv('typo3TestingRedisHost')) {
63  self::markTestSkipped('environment variable "typo3TestingRedisHost" must be set to run this test');
64  }
65  // Note we assume that if that typo3TestingRedisHost env is set, we can use that for testing,
66  // there is no test to see if the daemon is actually up and running. Tests will fail if env
67  // is set but daemon is down.
68 
69  // We know this env is set, otherwise setUp() would skip the tests
70  $redisHost = getenv('typo3TestingRedisHost');
71  // If typo3TestingRedisPort env is set, use it, otherwise fall back to standard port
72  $env = getenv('typo3TestingRedisPort');
73  $redisPort = is_string($env) ? (int)$env : 6379;
74 
75  $this->redis = new \Redis();
76  $this->redis->connect($redisHost, $redisPort);
77  $this->redis->select(0);
78  // Clear db to ensure no sessions exist currently
79  $this->redis->flushDB();
80 
81  $this->subject = new ‪RedisSessionBackend();
82  $this->subject->initialize(
83  'default',
84  [
85  'database' => 0,
86  'port' => $redisPort,
87  'hostname' => $redisHost
88  ]
89  );
90  }
91 
95  public function ‪cannotUpdateNonExistingRecord()
96  {
97  $this->expectException(SessionNotUpdatedException::class);
98  $this->expectExceptionCode(1484389971);
99  $this->subject->update('iSoNotExist', []);
100  }
101 
105  public function ‪canValidateSessionBackend()
106  {
107  $this->subject->validateConfiguration();
108  }
109 
114  public function ‪sessionDataIsStoredProperly()
115  {
116  $record = $this->subject->set('randomSessionId', $this->testSessionRecord);
117 
118  $expected = array_merge($this->testSessionRecord, ['ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]);
119 
120  self::assertEquals($record, $expected);
121  $result = $this->subject->get('randomSessionId');
122  self::assertSame($expected, $result);
123  }
124 
129  {
130  $record = $this->subject->set('randomSessionId', array_merge($this->testSessionRecord, ['ses_anonymous' => 1]));
131 
132  $expected = array_merge($this->testSessionRecord, ['ses_anonymous' => 1, 'ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]);
133 
134  self::assertEquals($record, $expected);
135  self::assertSame($expected, $this->subject->get('randomSessionId'));
136  }
137 
143  {
144  $this->expectException(SessionNotFoundException::class);
145  $this->expectExceptionCode(1481885583);
146  $this->subject->get('IDoNotExist');
147  }
148 
153  public function ‪mergeSessionDataWithNewData()
154  {
155  $this->subject->set('randomSessionId', $this->testSessionRecord);
156 
157  $updateData = [
158  'ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire']),
159  'ses_tstamp' => ‪$GLOBALS['EXEC_TIME']
160  ];
161  $expectedMergedData = array_merge($this->testSessionRecord, $updateData);
162  $this->subject->update('randomSessionId', $updateData);
163  $fetchedRecord = $this->subject->get('randomSessionId');
164  self::assertSame($expectedMergedData, $fetchedRecord);
165  }
166 
171  public function ‪nonHashedSessionIdsAreUpdated()
172  {
174  ‪$testSessionRecord['ses_tstamp'] = 1;
175  // simulate old session record by directly inserting it into redis
176  $this->redis->set(
177  'typo3_ses_default_' . sha1(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) . '_randomSessionId',
178  json_encode(‪$testSessionRecord),
179  ['nx']
180  );
181 
182  $updateData = [
183  'ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire']),
184  'ses_tstamp' => ‪$GLOBALS['EXEC_TIME']
185  ];
186  $expectedMergedData = array_merge(‪$testSessionRecord, $updateData);
187  $this->subject->update('randomSessionId', $updateData);
188  $fetchedRecord = $this->subject->get('randomSessionId');
189  self::assertSame($expectedMergedData, $fetchedRecord);
190  }
191 
196  public function ‪existingSessionMustNotBeOverridden()
197  {
198  $this->expectException(SessionNotCreatedException::class);
199  $this->expectExceptionCode(1481895647);
200 
201  $this->subject->set('randomSessionId', $this->testSessionRecord);
202 
203  $newData = array_merge($this->testSessionRecord, ['ses_data' => serialize(['foo' => 'baz', 'idontwantto' => 'set the world on fire'])]);
204  $this->subject->set('randomSessionId', $newData);
205  }
206 
211  public function ‪cannotChangeSessionId()
212  {
213  $this->subject->set('randomSessionId', $this->testSessionRecord);
214 
215  $newSessionId = 'newRandomSessionId';
216  $newData = array_merge($this->testSessionRecord, ['ses_id' => $newSessionId]);
217 
218  // old session id has to exist, no exception must be thrown at this point
219  $this->subject->get('randomSessionId');
220 
221  // Change session id
222  $this->subject->update('randomSessionId', $newData);
223 
224  // no session with key newRandomSessionId should exist
225  $this->expectException(SessionNotFoundException::class);
226  $this->expectExceptionCode(1481885583);
227  $this->subject->get('newRandomSessionId');
228  }
229 
234  public function ‪sessionGetsDestroyed()
235  {
236  $this->subject->set('randomSessionId', $this->testSessionRecord);
237 
238  // Remove session
239  self::assertTrue($this->subject->remove('randomSessionId'));
240 
241  // Check if session was really removed
242  $this->expectException(SessionNotFoundException::class);
243  $this->expectExceptionCode(1481885583);
244  $this->subject->get('randomSessionId');
245  }
246 
251  public function ‪canLoadAllSessions()
252  {
253  $this->subject->set('randomSessionId', $this->testSessionRecord);
254  $this->subject->set('randomSessionId2', $this->testSessionRecord);
255 
256  // Check if session was really removed
257  self::assertEquals(2, count($this->subject->getAll()));
258  }
259 
263  public function ‪canCollectGarbage()
264  {
265  ‪$GLOBALS['EXEC_TIME'] = 150;
266  $authenticatedSession = array_merge($this->testSessionRecord, ['ses_id' => 'authenticatedSession']);
267  $anonymousSession = array_merge($this->testSessionRecord, ['ses_id' => 'anonymousSession', 'ses_anonymous' => 1]);
268 
269  $this->subject->set('authenticatedSession', $authenticatedSession);
270  $this->subject->set('anonymousSession', $anonymousSession);
271 
272  // Assert that we set authenticated session correctly
273  self::assertSame(
274  $authenticatedSession['ses_data'],
275  $this->subject->get('authenticatedSession')['ses_data']
276  );
277  self::assertSame(
278  $authenticatedSession['ses_userid'],
279  $this->subject->get('authenticatedSession')['ses_userid']
280  );
281 
282  // assert that we set anonymous session correctly
283  self::assertSame(
284  $anonymousSession['ses_data'],
285  $this->subject->get('anonymousSession')['ses_data']
286  );
287 
288  // Run the garbage collection
289  ‪$GLOBALS['EXEC_TIME'] = 200;
290  // 150 + 10 < 200 but 150 + 60 >= 200
291  $this->subject->collectGarbage(60, 10);
292 
293  // Authenticated session should still be there
294  self::assertSame(
295  $authenticatedSession['ses_data'],
296  $this->subject->get('authenticatedSession')['ses_data']
297  );
298  self::assertSame(
299  $authenticatedSession['ses_userid'],
300  $this->subject->get('authenticatedSession')['ses_userid']
301  );
302 
303  // Non-authenticated session should be removed
304  $this->expectException(SessionNotFoundException::class);
305  $this->expectExceptionCode(1481885583);
306  $this->subject->get('anonymousSession');
307  }
308 
312  public function ‪canPartiallyUpdateAfterGet()
313  {
314  $updatedRecord = array_merge(
315  $this->testSessionRecord,
316  ['ses_tstamp' => ‪$GLOBALS['EXEC_TIME']]
317  );
318  $sessionId = 'randomSessionId';
319  $this->subject->set($sessionId, $this->testSessionRecord);
320  $this->subject->update($sessionId, []);
321  self::assertSame($updatedRecord, $this->subject->get($sessionId));
322  }
323 }
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\setUp
‪setUp()
Definition: RedisSessionBackendTest.php:54
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotCreatedException
Definition: SessionNotCreatedException.php:24
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\mergeSessionDataWithNewData
‪mergeSessionDataWithNewData()
Definition: RedisSessionBackendTest.php:150
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canPartiallyUpdateAfterGet
‪canPartiallyUpdateAfterGet()
Definition: RedisSessionBackendTest.php:309
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\anonymousSessionDataIsStoredProperly
‪anonymousSessionDataIsStoredProperly()
Definition: RedisSessionBackendTest.php:125
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\existingSessionMustNotBeOverridden
‪existingSessionMustNotBeOverridden()
Definition: RedisSessionBackendTest.php:193
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$testSessionRecord
‪array $testSessionRecord
Definition: RedisSessionBackendTest.php:43
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$subject
‪RedisSessionBackend $subject
Definition: RedisSessionBackendTest.php:35
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canValidateSessionBackend
‪canValidateSessionBackend()
Definition: RedisSessionBackendTest.php:102
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\sessionDataIsStoredProperly
‪sessionDataIsStoredProperly()
Definition: RedisSessionBackendTest.php:111
‪TYPO3\CMS\Core\Session\Backend\RedisSessionBackend
Definition: RedisSessionBackend.php:33
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canCollectGarbage
‪canCollectGarbage()
Definition: RedisSessionBackendTest.php:260
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotUpdatedException
Definition: SessionNotUpdatedException.php:24
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\nonHashedSessionIdsAreUpdated
‪nonHashedSessionIdsAreUpdated()
Definition: RedisSessionBackendTest.php:168
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\canLoadAllSessions
‪canLoadAllSessions()
Definition: RedisSessionBackendTest.php:248
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\cannotChangeSessionId
‪cannotChangeSessionId()
Definition: RedisSessionBackendTest.php:208
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend
Definition: DatabaseSessionBackendTest.php:18
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\$redis
‪Redis $redis
Definition: RedisSessionBackendTest.php:39
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\sessionGetsDestroyed
‪sessionGetsDestroyed()
Definition: RedisSessionBackendTest.php:231
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\cannotUpdateNonExistingRecord
‪cannotUpdateNonExistingRecord()
Definition: RedisSessionBackendTest.php:92
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest\throwExceptionOnNonExistingSessionId
‪throwExceptionOnNonExistingSessionId()
Definition: RedisSessionBackendTest.php:139
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException
Definition: SessionNotFoundException.php:24
‪TYPO3\CMS\Core\Tests\Functional\Session\Backend\RedisSessionBackendTest
Definition: RedisSessionBackendTest.php:32