Mercurial > hg > isophonics-drupal-site
comparison core/modules/system/src/Tests/Session/SessionTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\system\Tests\Session; | |
4 | |
5 use Drupal\simpletest\WebTestBase; | |
6 | |
7 /** | |
8 * Drupal session handling tests. | |
9 * | |
10 * @group Session | |
11 */ | |
12 class SessionTest extends WebTestBase { | |
13 | |
14 /** | |
15 * Modules to enable. | |
16 * | |
17 * @var array | |
18 */ | |
19 public static $modules = ['session_test']; | |
20 | |
21 protected $dumpHeaders = TRUE; | |
22 | |
23 /** | |
24 * Tests for \Drupal\Core\Session\WriteSafeSessionHandler::setSessionWritable() | |
25 * ::isSessionWritable and \Drupal\Core\Session\SessionManager::regenerate(). | |
26 */ | |
27 public function testSessionSaveRegenerate() { | |
28 $session_handler = $this->container->get('session_handler.write_safe'); | |
29 $this->assertTrue($session_handler->isSessionWritable(), 'session_handler->isSessionWritable() initially returns TRUE.'); | |
30 $session_handler->setSessionWritable(FALSE); | |
31 $this->assertFalse($session_handler->isSessionWritable(), '$session_handler->isSessionWritable() returns FALSE after disabling.'); | |
32 $session_handler->setSessionWritable(TRUE); | |
33 $this->assertTrue($session_handler->isSessionWritable(), '$session_handler->isSessionWritable() returns TRUE after enabling.'); | |
34 | |
35 // Test session hardening code from SA-2008-044. | |
36 $user = $this->drupalCreateUser(); | |
37 | |
38 // Enable sessions. | |
39 $this->sessionReset($user->id()); | |
40 | |
41 // Make sure the session cookie is set as HttpOnly. | |
42 $this->drupalLogin($user); | |
43 $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie', TRUE)), 'Session cookie is set as HttpOnly.'); | |
44 $this->drupalLogout(); | |
45 | |
46 // Verify that the session is regenerated if a module calls exit | |
47 // in hook_user_login(). | |
48 $user->name = 'session_test_user'; | |
49 $user->save(); | |
50 $this->drupalGet('session-test/id'); | |
51 $matches = []; | |
52 preg_match('/\s*session_id:(.*)\n/', $this->getRawContent(), $matches); | |
53 $this->assertTrue(!empty($matches[1]), 'Found session ID before logging in.'); | |
54 $original_session = $matches[1]; | |
55 | |
56 // We cannot use $this->drupalLogin($user); because we exit in | |
57 // session_test_user_login() which breaks a normal assertion. | |
58 $edit = [ | |
59 'name' => $user->getUsername(), | |
60 'pass' => $user->pass_raw | |
61 ]; | |
62 $this->drupalPostForm('user/login', $edit, t('Log in')); | |
63 $this->drupalGet('user'); | |
64 $pass = $this->assertText($user->getUsername(), format_string('Found name: %name', ['%name' => $user->getUsername()]), 'User login'); | |
65 $this->_logged_in = $pass; | |
66 | |
67 $this->drupalGet('session-test/id'); | |
68 $matches = []; | |
69 preg_match('/\s*session_id:(.*)\n/', $this->getRawContent(), $matches); | |
70 $this->assertTrue(!empty($matches[1]), 'Found session ID after logging in.'); | |
71 $this->assertTrue($matches[1] != $original_session, 'Session ID changed after login.'); | |
72 } | |
73 | |
74 /** | |
75 * Test data persistence via the session_test module callbacks. | |
76 */ | |
77 public function testDataPersistence() { | |
78 $user = $this->drupalCreateUser([]); | |
79 // Enable sessions. | |
80 $this->sessionReset($user->id()); | |
81 | |
82 $this->drupalLogin($user); | |
83 | |
84 $value_1 = $this->randomMachineName(); | |
85 $this->drupalGet('session-test/set/' . $value_1); | |
86 $this->assertText($value_1, 'The session value was stored.', 'Session'); | |
87 $this->drupalGet('session-test/get'); | |
88 $this->assertText($value_1, 'Session correctly returned the stored data for an authenticated user.', 'Session'); | |
89 | |
90 // Attempt to write over val_1. If drupal_save_session(FALSE) is working. | |
91 // properly, val_1 will still be set. | |
92 $value_2 = $this->randomMachineName(); | |
93 $this->drupalGet('session-test/no-set/' . $value_2); | |
94 $this->assertText($value_2, 'The session value was correctly passed to session-test/no-set.', 'Session'); | |
95 $this->drupalGet('session-test/get'); | |
96 $this->assertText($value_1, 'Session data is not saved for drupal_save_session(FALSE).', 'Session'); | |
97 | |
98 // Switch browser cookie to anonymous user, then back to user 1. | |
99 $this->sessionReset(); | |
100 $this->sessionReset($user->id()); | |
101 $this->assertText($value_1, 'Session data persists through browser close.', 'Session'); | |
102 | |
103 // Logout the user and make sure the stored value no longer persists. | |
104 $this->drupalLogout(); | |
105 $this->sessionReset(); | |
106 $this->drupalGet('session-test/get'); | |
107 $this->assertNoText($value_1, "After logout, previous user's session data is not available.", 'Session'); | |
108 | |
109 // Now try to store some data as an anonymous user. | |
110 $value_3 = $this->randomMachineName(); | |
111 $this->drupalGet('session-test/set/' . $value_3); | |
112 $this->assertText($value_3, 'Session data stored for anonymous user.', 'Session'); | |
113 $this->drupalGet('session-test/get'); | |
114 $this->assertText($value_3, 'Session correctly returned the stored data for an anonymous user.', 'Session'); | |
115 | |
116 // Try to store data when drupal_save_session(FALSE). | |
117 $value_4 = $this->randomMachineName(); | |
118 $this->drupalGet('session-test/no-set/' . $value_4); | |
119 $this->assertText($value_4, 'The session value was correctly passed to session-test/no-set.', 'Session'); | |
120 $this->drupalGet('session-test/get'); | |
121 $this->assertText($value_3, 'Session data is not saved for drupal_save_session(FALSE).', 'Session'); | |
122 | |
123 // Login, the data should persist. | |
124 $this->drupalLogin($user); | |
125 $this->sessionReset($user->id()); | |
126 $this->drupalGet('session-test/get'); | |
127 $this->assertNoText($value_1, 'Session has persisted for an authenticated user after logging out and then back in.', 'Session'); | |
128 | |
129 // Change session and create another user. | |
130 $user2 = $this->drupalCreateUser([]); | |
131 $this->sessionReset($user2->id()); | |
132 $this->drupalLogin($user2); | |
133 } | |
134 | |
135 /** | |
136 * Tests storing data in Session() object. | |
137 */ | |
138 public function testSessionPersistenceOnLogin() { | |
139 // Store information via hook_user_login(). | |
140 $user = $this->drupalCreateUser(); | |
141 $this->drupalLogin($user); | |
142 // Test property added to session object form hook_user_login(). | |
143 $this->drupalGet('session-test/get-from-session-object'); | |
144 $this->assertText('foobar', 'Session data is saved in Session() object.', 'Session'); | |
145 } | |
146 | |
147 /** | |
148 * Test that empty anonymous sessions are destroyed. | |
149 */ | |
150 public function testEmptyAnonymousSession() { | |
151 // Disable the dynamic_page_cache module; it'd cause session_test's debug | |
152 // output (that is added in | |
153 // SessionTestSubscriber::onKernelResponseSessionTest()) to not be added. | |
154 $this->container->get('module_installer')->uninstall(['dynamic_page_cache']); | |
155 | |
156 // Verify that no session is automatically created for anonymous user when | |
157 // page caching is disabled. | |
158 $this->container->get('module_installer')->uninstall(['page_cache']); | |
159 $this->drupalGet(''); | |
160 $this->assertSessionCookie(FALSE); | |
161 $this->assertSessionEmpty(TRUE); | |
162 | |
163 // The same behavior is expected when caching is enabled. | |
164 $this->container->get('module_installer')->install(['page_cache']); | |
165 $config = $this->config('system.performance'); | |
166 $config->set('cache.page.max_age', 300); | |
167 $config->save(); | |
168 $this->drupalGet(''); | |
169 $this->assertSessionCookie(FALSE); | |
170 // @todo Reinstate when REQUEST and RESPONSE events fire for cached pages. | |
171 // $this->assertSessionEmpty(TRUE); | |
172 $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.'); | |
173 | |
174 // Start a new session by setting a message. | |
175 $this->drupalGet('session-test/set-message'); | |
176 $this->assertSessionCookie(TRUE); | |
177 $this->assertTrue($this->drupalGetHeader('Set-Cookie'), 'New session was started.'); | |
178 | |
179 // Display the message, during the same request the session is destroyed | |
180 // and the session cookie is unset. | |
181 $this->drupalGet(''); | |
182 $this->assertSessionCookie(FALSE); | |
183 $this->assertSessionEmpty(FALSE); | |
184 $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.'); | |
185 $this->assertText(t('This is a dummy message.'), 'Message was displayed.'); | |
186 $this->assertTrue(preg_match('/SESS\w+=deleted/', $this->drupalGetHeader('Set-Cookie')), 'Session cookie was deleted.'); | |
187 | |
188 // Verify that session was destroyed. | |
189 $this->drupalGet(''); | |
190 $this->assertSessionCookie(FALSE); | |
191 // @todo Reinstate when REQUEST and RESPONSE events fire for cached pages. | |
192 // $this->assertSessionEmpty(TRUE); | |
193 $this->assertNoText(t('This is a dummy message.'), 'Message was not cached.'); | |
194 $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.'); | |
195 $this->assertFalse($this->drupalGetHeader('Set-Cookie'), 'New session was not started.'); | |
196 | |
197 // Verify that no session is created if drupal_save_session(FALSE) is called. | |
198 $this->drupalGet('session-test/set-message-but-dont-save'); | |
199 $this->assertSessionCookie(FALSE); | |
200 $this->assertSessionEmpty(TRUE); | |
201 | |
202 // Verify that no message is displayed. | |
203 $this->drupalGet(''); | |
204 $this->assertSessionCookie(FALSE); | |
205 // @todo Reinstate when REQUEST and RESPONSE events fire for cached pages. | |
206 // $this->assertSessionEmpty(TRUE); | |
207 $this->assertNoText(t('This is a dummy message.'), 'The message was not saved.'); | |
208 } | |
209 | |
210 /** | |
211 * Test that sessions are only saved when necessary. | |
212 */ | |
213 public function testSessionWrite() { | |
214 $user = $this->drupalCreateUser([]); | |
215 $this->drupalLogin($user); | |
216 | |
217 $sql = 'SELECT u.access, s.timestamp FROM {users_field_data} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE u.uid = :uid'; | |
218 $times1 = db_query($sql, [':uid' => $user->id()])->fetchObject(); | |
219 | |
220 // Before every request we sleep one second to make sure that if the session | |
221 // is saved, its timestamp will change. | |
222 | |
223 // Modify the session. | |
224 sleep(1); | |
225 $this->drupalGet('session-test/set/foo'); | |
226 $times2 = db_query($sql, [':uid' => $user->id()])->fetchObject(); | |
227 $this->assertEqual($times2->access, $times1->access, 'Users table was not updated.'); | |
228 $this->assertNotEqual($times2->timestamp, $times1->timestamp, 'Sessions table was updated.'); | |
229 | |
230 // Write the same value again, i.e. do not modify the session. | |
231 sleep(1); | |
232 $this->drupalGet('session-test/set/foo'); | |
233 $times3 = db_query($sql, [':uid' => $user->id()])->fetchObject(); | |
234 $this->assertEqual($times3->access, $times1->access, 'Users table was not updated.'); | |
235 $this->assertEqual($times3->timestamp, $times2->timestamp, 'Sessions table was not updated.'); | |
236 | |
237 // Do not change the session. | |
238 sleep(1); | |
239 $this->drupalGet(''); | |
240 $times4 = db_query($sql, [':uid' => $user->id()])->fetchObject(); | |
241 $this->assertEqual($times4->access, $times3->access, 'Users table was not updated.'); | |
242 $this->assertEqual($times4->timestamp, $times3->timestamp, 'Sessions table was not updated.'); | |
243 | |
244 // Force updating of users and sessions table once per second. | |
245 $this->settingsSet('session_write_interval', 0); | |
246 // Write that value also into the test settings.php file. | |
247 $settings['settings']['session_write_interval'] = (object) [ | |
248 'value' => 0, | |
249 'required' => TRUE, | |
250 ]; | |
251 $this->writeSettings($settings); | |
252 $this->drupalGet(''); | |
253 $times5 = db_query($sql, [':uid' => $user->id()])->fetchObject(); | |
254 $this->assertNotEqual($times5->access, $times4->access, 'Users table was updated.'); | |
255 $this->assertNotEqual($times5->timestamp, $times4->timestamp, 'Sessions table was updated.'); | |
256 } | |
257 | |
258 /** | |
259 * Test that empty session IDs are not allowed. | |
260 */ | |
261 public function testEmptySessionID() { | |
262 $user = $this->drupalCreateUser([]); | |
263 $this->drupalLogin($user); | |
264 $this->drupalGet('session-test/is-logged-in'); | |
265 $this->assertResponse(200, 'User is logged in.'); | |
266 | |
267 // Reset the sid in {sessions} to a blank string. This may exist in the | |
268 // wild in some cases, although we normally prevent it from happening. | |
269 db_query("UPDATE {sessions} SET sid = '' WHERE uid = :uid", [':uid' => $user->id()]); | |
270 // Send a blank sid in the session cookie, and the session should no longer | |
271 // be valid. Closing the curl handler will stop the previous session ID | |
272 // from persisting. | |
273 $this->curlClose(); | |
274 $this->additionalCurlOptions[CURLOPT_COOKIE] = rawurlencode($this->getSessionName()) . '=;'; | |
275 $this->drupalGet('session-test/id-from-cookie'); | |
276 $this->assertRaw("session_id:\n", 'Session ID is blank as sent from cookie header.'); | |
277 // Assert that we have an anonymous session now. | |
278 $this->drupalGet('session-test/is-logged-in'); | |
279 $this->assertResponse(403, 'An empty session ID is not allowed.'); | |
280 } | |
281 | |
282 /** | |
283 * Reset the cookie file so that it refers to the specified user. | |
284 * | |
285 * @param $uid | |
286 * User id to set as the active session. | |
287 */ | |
288 public function sessionReset($uid = 0) { | |
289 // Close the internal browser. | |
290 $this->curlClose(); | |
291 $this->loggedInUser = FALSE; | |
292 | |
293 // Change cookie file for user. | |
294 $this->cookieFile = \Drupal::service('stream_wrapper_manager')->getViaScheme('temporary')->getDirectoryPath() . '/cookie.' . $uid . '.txt'; | |
295 $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile; | |
296 $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE; | |
297 $this->drupalGet('session-test/get'); | |
298 $this->assertResponse(200, 'Session test module is correctly enabled.', 'Session'); | |
299 } | |
300 | |
301 /** | |
302 * Assert whether the SimpleTest browser sent a session cookie. | |
303 */ | |
304 public function assertSessionCookie($sent) { | |
305 if ($sent) { | |
306 $this->assertNotNull($this->sessionId, 'Session cookie was sent.'); | |
307 } | |
308 else { | |
309 $this->assertNull($this->sessionId, 'Session cookie was not sent.'); | |
310 } | |
311 } | |
312 | |
313 /** | |
314 * Assert whether $_SESSION is empty at the beginning of the request. | |
315 */ | |
316 public function assertSessionEmpty($empty) { | |
317 if ($empty) { | |
318 $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '1', 'Session was empty.'); | |
319 } | |
320 else { | |
321 $this->assertIdentical($this->drupalGetHeader('X-Session-Empty'), '0', 'Session was not empty.'); | |
322 } | |
323 } | |
324 | |
325 } |