Commit ea4f6bce authored by webchick's avatar webchick

#363580 by rfay, chx, Berdir, Rob Loach, Gábor Hojtsy, Shawn DeArmond: Fixed...

#363580 by rfay, chx, Berdir, Rob Loach, Gábor Hojtsy, Shawn DeArmond: Fixed OpenID login fails when in maintenance mode, and 403 errors on certain user paths for logged-in users.
parent a8812bb7
...@@ -238,6 +238,11 @@ ...@@ -238,6 +238,11 @@
*/ */
define('MENU_SITE_OFFLINE', 4); define('MENU_SITE_OFFLINE', 4);
/**
* Internal menu status code -- Everything is working fine.
*/
define('MENU_SITE_ONLINE', 5);
/** /**
* @} End of "Menu status codes". * @} End of "Menu status codes".
*/ */
...@@ -447,10 +452,17 @@ function menu_get_item($path = NULL, $router_item = NULL) { ...@@ -447,10 +452,17 @@ function menu_get_item($path = NULL, $router_item = NULL) {
* the result to the caller (FALSE). * the result to the caller (FALSE).
*/ */
function menu_execute_active_handler($path = NULL, $deliver = TRUE) { function menu_execute_active_handler($path = NULL, $deliver = TRUE) {
if (_menu_site_is_offline()) { // Check if site is offline.
$page_callback_result = MENU_SITE_OFFLINE; $page_callback_result = _menu_site_is_offline() ? MENU_SITE_OFFLINE : MENU_SITE_ONLINE;
}
else { // Allow other modules to change the site status but not the path because that
// would not change the global variable. hook_url_inbound_alter() can be used
// to change the path. Code later will not use the $read_only_path variable.
$read_only_path = !empty($path) ? $path : $_GET['q'];
drupal_alter('menu_site_status', $page_callback_result, $read_only_path);
// Only continue if the site status is not set.
if ($page_callback_result == MENU_SITE_ONLINE) {
// Rebuild if we know it's needed, or if the menu masks are missing which // Rebuild if we know it's needed, or if the menu masks are missing which
// occurs rarely, likely due to a race condition of multiple rebuilds. // occurs rarely, likely due to a race condition of multiple rebuilds.
if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) {
...@@ -3387,15 +3399,7 @@ function _menu_site_is_offline($check_only = FALSE) { ...@@ -3387,15 +3399,7 @@ function _menu_site_is_offline($check_only = FALSE) {
} }
} }
else { else {
// Anonymous users get a FALSE at the login prompt, TRUE otherwise. return TRUE;
if (user_is_anonymous()) {
return ($_GET['q'] != 'user' && $_GET['q'] != 'user/login');
}
// Logged in users are unprivileged here, so they are logged out.
if (!$check_only) {
require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'user') . '/user.pages.inc';
user_logout();
}
} }
} }
return FALSE; return FALSE;
......
...@@ -38,6 +38,16 @@ function openid_menu() { ...@@ -38,6 +38,16 @@ function openid_menu() {
return $items; return $items;
} }
/**
* Implements hook_menu_site_status_alter().
*/
function openid_menu_site_status_alter(&$menu_site_status, $path) {
// Allow access to openid/authenticate even if site is in offline mode.
if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && $path == 'openid/authenticate') {
$menu_site_status = MENU_SITE_ONLINE;
}
}
/** /**
* Implements hook_help(). * Implements hook_help().
*/ */
......
...@@ -146,6 +146,38 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { ...@@ -146,6 +146,38 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
$this->assertResponse(200); $this->assertResponse(200);
} }
/**
* Test login using OpenID during maintenance mode.
*/
function testLoginMaintenanceMode() {
$this->web_user = $this->drupalCreateUser(array('access site in maintenance mode'));
$this->drupalLogin($this->web_user);
// Use a User-supplied Identity that is the URL of an XRDS document.
$identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
$this->addIdentity($identity);
$this->drupalLogout();
// Enable maintenance mode.
variable_set('maintenance_mode', 1);
// Test logging in via the user/login page while the site is offline.
$edit = array('openid_identifier' => $identity);
$this->drupalPost('user/login', $edit, t('Log in'));
// Check we are on the OpenID redirect form.
$this->assertTitle(t('OpenID redirect'), t('OpenID redirect page was displayed.'));
// Submit form to the OpenID Provider Endpoint.
$this->drupalPost(NULL, array(), t('Send'));
$this->assertLink($this->web_user->name, 0, t('User was logged in.'));
// Verify user was redirected away from user/login to an accessible page.
$this->assertText(t('Operating in maintenance mode.'));
$this->assertResponse(200);
}
/** /**
* Test deleting an OpenID identity from a user's profile. * Test deleting an OpenID identity from a user's profile.
*/ */
......
...@@ -64,6 +64,16 @@ function openid_test_menu() { ...@@ -64,6 +64,16 @@ function openid_test_menu() {
return $items; return $items;
} }
/**
* Implements hook_menu_site_status_alter().
*/
function openid_test_menu_site_status_alter(&$menu_site_status, $path) {
// Allow access to openid endpoint and identity even in offline mode.
if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && in_array($path, array('openid-test/yadis/xrds', 'openid-test/endpoint'))) {
$menu_site_status = MENU_SITE_ONLINE;
}
}
/** /**
* Menu callback; XRDS document that references the OP Endpoint URL. * Menu callback; XRDS document that references the OP Endpoint URL.
*/ */
......
...@@ -90,6 +90,39 @@ class MenuRouterTestCase extends DrupalWebTestCase { ...@@ -90,6 +90,39 @@ class MenuRouterTestCase extends DrupalWebTestCase {
$this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page.")); $this->assertRaw('seven/style.css', t("The administrative theme's CSS appears on the page."));
} }
/**
* Make sure the maintenance mode can be bypassed using hook_menu_site_status_alter().
*
* @see hook_menu_site_status_alter().
*/
function testMaintenanceModeLoginPaths() {
variable_set('maintenance_mode', TRUE);
$offline_message = t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')));
$this->drupalLogout();
$this->drupalGet('node');
$this->assertText($offline_message);
$this->drupalGet('menu_login_callback');
$this->assertText('This is menu_login_callback().', t('Maintenance mode can be bypassed through hook_login_paths().'));
}
/**
* Test that an authenticated user hitting 'user/login' gets redirected to
* 'user' and 'user/register' gets redirected to the user edit page.
*/
function testAuthUserUserLogin() {
$loggedInUser = $this->drupalCreateUser(array());
$this->drupalLogin($loggedInUser);
$this->DrupalGet('user/login');
// Check that we got to 'user'.
$this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login"));
// user/register should redirect to user/UID/edit.
$this->DrupalGet('user/register');
$this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register"));
}
/** /**
* Test the theme callback when it is set to use an optional theme. * Test the theme callback when it is set to use an optional theme.
*/ */
...@@ -491,4 +524,3 @@ class MenuTreeDataTestCase extends DrupalUnitTestCase { ...@@ -491,4 +524,3 @@ class MenuTreeDataTestCase extends DrupalUnitTestCase {
return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : t('First link is identical to second link')); return $this->assert($link1['mlid'] == $link2['mlid'], $message ? $message : t('First link is identical to second link'));
} }
} }
...@@ -189,6 +189,12 @@ function menu_test_menu() { ...@@ -189,6 +189,12 @@ function menu_test_menu() {
'type' => MENU_LOCAL_TASK, 'type' => MENU_LOCAL_TASK,
); );
$items['menu_login_callback'] = array(
'title' => 'Used as a login path',
'page callback' => 'menu_login_callback',
'access callback' => TRUE,
);
return $items; return $items;
} }
...@@ -329,3 +335,20 @@ function menu_test_static_variable($value = NULL) { ...@@ -329,3 +335,20 @@ function menu_test_static_variable($value = NULL) {
} }
return $variable; return $variable;
} }
/**
* Implements hook_menu_site_status_alter().
*/
function menu_test_menu_site_status_alter(&$menu_site_status, $path) {
// Allow access to ?q=menu_login_callback even if in maintenance mode.
if ($menu_site_status == MENU_SITE_OFFLINE && $path == 'menu_login_callback') {
$menu_site_status = MENU_SITE_ONLINE;
}
}
/**
* Menu callback to be used as a login path.
*/
function menu_login_callback() {
return 'This is menu_login_callback().';
}
...@@ -3960,6 +3960,31 @@ function hook_filetransfer_backends() { ...@@ -3960,6 +3960,31 @@ function hook_filetransfer_backends() {
return $backends; return $backends;
} }
/**
* Control site status before menu dispatching.
*
* The hook is called after checking whether the site is offline but before
* the current router item is retrieved and executed by
* menu_execute_active_handler(). If the site is in offline mode,
* $menu_site_status is set to MENU_SITE_OFFLINE.
*
* @param $menu_site_status
* Supported values are MENU_SITE_OFFLINE, MENU_ACCESS_DENIED,
* MENU_NOT_FOUND and MENU_SITE_ONLINE. Any other value than
* MENU_SITE_ONLINE will skip the default menu handling system and be passed
* for delivery to drupal_deliver_page() with a NULL
* $default_delivery_callback.
* @param $path
* Contains the system path that is going to be loaded. This is read only,
* use hook_url_inbound_alter() to change the path.
*/
function hook_menu_site_status_alter(&$menu_site_status, $path) {
// Allow access to my_module/authentication even if site is in offline mode.
if ($menu_site_status == MENU_SITE_OFFLINE && user_is_anonymous() && $path == 'my_module/authentication') {
$menu_site_status = MENU_SITE_ONLINE;
}
}
/** /**
* @} End of "addtogroup hooks". * @} End of "addtogroup hooks".
*/ */
...@@ -770,8 +770,6 @@ class SiteMaintenanceTestCase extends DrupalWebTestCase { ...@@ -770,8 +770,6 @@ class SiteMaintenanceTestCase extends DrupalWebTestCase {
$this->assertText($offline_message); $this->assertText($offline_message);
$this->drupalGet('user/register'); $this->drupalGet('user/register');
$this->assertText($offline_message); $this->assertText($offline_message);
$this->drupalGet('user/password');
$this->assertText($offline_message);
// Verify that user is able to log in. // Verify that user is able to log in.
$this->drupalGet('user'); $this->drupalGet('user');
...@@ -804,6 +802,23 @@ class SiteMaintenanceTestCase extends DrupalWebTestCase { ...@@ -804,6 +802,23 @@ class SiteMaintenanceTestCase extends DrupalWebTestCase {
$this->drupalLogout(); $this->drupalLogout();
$this->drupalGet(''); $this->drupalGet('');
$this->assertRaw($offline_message, t('Found the site offline message.')); $this->assertRaw($offline_message, t('Found the site offline message.'));
// Verify that custom site offline message is not displayed on user/password.
$this->drupalGet('user/password');
$this->assertText(t('Username or e-mail address'), t('Anonymous users can access user/password'));
// Submit password reset form.
$edit = array(
'name' => $this->user->name,
);
$this->drupalPost('user/password', $edit, t('E-mail new password'));
$mails = $this->drupalGetMails();
$start = strpos($mails[0]['body'], 'user/reset/'. $this->user->uid);
$path = substr($mails[0]['body'], $start, 66 + strlen($this->user->uid));
// Log in with temporary login link.
$this->drupalPost($path, array(), t('Log in'));
$this->assertText($user_message);
} }
} }
......
...@@ -1740,6 +1740,48 @@ function user_menu() { ...@@ -1740,6 +1740,48 @@ function user_menu() {
return $items; return $items;
} }
/**
* Implements hook_menu_site_status_alter().
*/
function user_menu_site_status_alter(&$menu_site_status, $path) {
if ($menu_site_status == MENU_SITE_OFFLINE) {
// If the site is offline, log out unprivileged users.
if (user_is_logged_in() && !user_access('access site in maintenance mode')) {
module_load_include('pages.inc', 'user', 'user');
user_logout();
}
if (user_is_anonymous()) {
switch ($path) {
case 'user':
// Forward anonymous user to login page.
drupal_goto('user/login');
case 'user/login':
case 'user/password':
// Disable offline mode.
$menu_site_status = MENU_SITE_ONLINE;
break;
default:
if (strpos($path, 'user/reset/') === 0) {
// Disable offline mode.
$menu_site_status = MENU_SITE_ONLINE;
}
break;
}
}
}
if (user_is_logged_in()) {
if ($path == 'user/login') {
// If user is logged in, redirect to 'user' instead of giving 403.
drupal_goto('user');
}
if ($path == 'user/register') {
// Authenticated user should be redirected to user edit page.
drupal_goto('user/' . $GLOBALS['user']->uid . '/edit');
}
}
}
/** /**
* Implements hook_init(). * Implements hook_init().
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment