diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 13b4c7d..5de62b6 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -830,6 +830,20 @@ function menu_tail_load($arg, &$map, $index) { return $arg; } +/* + * Loader function: check that a token is valid. + */ +function drupal_token_load($token) { + return drupal_valid_token($token, 'user/logout') ? $token : FALSE; +} + +/** + * Menu to arg function: convert %token into a user specific token. + */ +function drupal_token_to_arg() { + return drupal_get_token('user/logout'); +} + /** * This function is similar to _menu_translate() but does link-specific * preparation such as always calling to_arg functions diff --git a/core/includes/path.inc b/core/includes/path.inc index 1fac235..5bf0cfb 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -554,9 +554,9 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) { elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) { // Path is dynamic (ie 'user/%'), so check directly against menu_router table. if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) { - $item['link_path'] = $form_item['link_path']; - $item['link_title'] = $form_item['link_title']; - $item['external'] = FALSE; + $item['link_path'] = $path; + $item['link_title'] = ''; + $item['external'] = FALSE; $item['options'] = ''; _menu_link_translate($item); } diff --git a/core/modules/menu/menu.test b/core/modules/menu/menu.test index 08bb7e8..bfe5fc5 100644 --- a/core/modules/menu/menu.test +++ b/core/modules/menu/menu.test @@ -518,7 +518,7 @@ class MenuTestCase extends DrupalWebTestCase { */ private function getStandardMenuLink() { // Retrieve menu link id of the Log out menu link, which will always be on the front page. - $mlid = db_query("SELECT mlid FROM {menu_links} WHERE module = 'system' AND router_path = 'user/logout'")->fetchField(); + $mlid = db_query("SELECT mlid FROM {menu_links} WHERE module = 'system' AND router_path = 'user/logout/%'")->fetchField(); $this->assertTrue($mlid > 0, 'Standard menu link id was found'); // Load menu link. // Use api function so that link is translated for rendering. diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php index 91fd2c7..ce7dbc0 100644 --- a/core/modules/simpletest/drupal_web_test_case.php +++ b/core/modules/simpletest/drupal_web_test_case.php @@ -1233,7 +1233,7 @@ class DrupalWebTestCase extends DrupalTestCase { // Make a request to the logout page, and redirect to the user page, the // idea being if you were properly logged out you should be seeing a login // screen. - $this->drupalGet('user/logout'); + $this->drupalGet('user/logout/' . $this->drupalGetToken()); $this->drupalGet('user'); $pass = $this->assertField('name', t('Username field found.'), t('Logout')); $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout')); @@ -1617,8 +1617,7 @@ class DrupalWebTestCase extends DrupalTestCase { curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); if (!$redirect) { - // Reset headers, the session ID and the redirect counter. - $this->session_id = NULL; + // Reset headers and the redirect counter. $this->headers = array(); $this->redirect_count = 0; } @@ -1701,6 +1700,12 @@ class DrupalWebTestCase extends DrupalTestCase { if (isset($this->curlHandle)) { curl_close($this->curlHandle); unset($this->curlHandle); + + // Reset the state of the internal browser. + $this->loggedInUser = FALSE; + $this->cookies = array(); + $this->session_id = NULL; + $this->cookieFile = NULL; } } diff --git a/core/modules/simpletest/tests/session.test b/core/modules/simpletest/tests/session.test index 846f6d3..5c3cc1e 100644 --- a/core/modules/simpletest/tests/session.test +++ b/core/modules/simpletest/tests/session.test @@ -251,14 +251,35 @@ class SessionTestCase extends DrupalWebTestCase { * @param $uid User id to set as the active session. */ function sessionReset($uid = 0) { + // Save the current state of the browser. + if (isset($this->cookieFile)) { + $state = array( + 'loggedInUser' => $this->loggedInUser, + 'cookies' => $this->cookies, + 'session_id' => $this->session_id, + ); + file_put_contents($this->cookieFile . '.state', serialize($state)); + } + // Close the internal browser. $this->curlClose(); - $this->loggedInUser = FALSE; // Change cookie file for user. $this->cookieFile = file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath() . '/cookie.' . $uid . '.txt'; $this->additionalCurlOptions[CURLOPT_COOKIEFILE] = $this->cookieFile; $this->additionalCurlOptions[CURLOPT_COOKIESESSION] = TRUE; + + // Reopen the internal browser. + $this->curlInitialize(); + + // Load the old state, if it exists. + if (file_exists($this->cookieFile . '.state')) { + $state = unserialize(file_get_contents($this->cookieFile . '.state')); + foreach ($state as $k => $v) { + $this->$k = $v; + } + } + $this->drupalGet('session-test/get'); $this->assertResponse(200, t('Session test module is correctly enabled.'), t('Session')); } diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index d25ca2d..36d9c46 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -218,7 +218,7 @@ function toolbar_view() { ), 'logout' => array( 'title' => t('Log out'), - 'href' => 'user/logout', + 'href' => 'user/logout/' . drupal_get_token(), ), ); } diff --git a/core/modules/user/user.module b/core/modules/user/user.module index eb2f521..352415d 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1670,7 +1670,7 @@ function user_menu() { 'file' => 'user.pages.inc', ); - $items['user/logout'] = array( + $items['user/logout/%drupal_token'] = array( 'title' => 'Log out', 'access callback' => 'user_is_logged_in', 'page callback' => 'user_logout', diff --git a/core/modules/user/user.test b/core/modules/user/user.test index 66c4903..5e47e49 100644 --- a/core/modules/user/user.test +++ b/core/modules/user/user.test @@ -1090,6 +1090,39 @@ class UserPictureTestCase extends DrupalWebTestCase { } } +/** + * Test basic user login and logout. Verify that the logout link is protected. + */ +class UserLogoutTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => t('User login / Logout'), + 'description' => t('Test user login and logout.'), + 'group' => t('User') + ); + } + + function setUp() { + parent::setUp(); + + $this->user = $this->drupalCreateUser(); + } + + /** + * Change user permissions and check user_access(). + */ + function testUserLoginLogout() { + // No assertion needed here: assertions are already verified in drupalLogin(). + $this->drupalLogin($this->user); + + $this->drupalGet('user/logout/invalidtoken'); + $this->assertResponse(404, t('Visiting user/logout/[invalid token] results in 404 page.')); + + // No assertion needed here: assertions are already verified in drupalLogout(). + $this->drupalLogout(); + } + +} class UserPermissionsTestCase extends DrupalWebTestCase { protected $admin_user;