From 309b1db6668a4c7692ed71bc8fbd1bbd755a99c5 Mon Sep 17 00:00:00 2001
From: sun <sun@unleashedmind.com>
Date: Sun, 24 Feb 2013 19:53:24 +0100
Subject: [PATCH] - #1836516 by sun: Revamped masquerade access and UI
 validation.

---
 masquerade.module | 168 +++++++++++++++++++++++++++++-------------------------
 1 file changed, 91 insertions(+), 77 deletions(-)

diff --git a/masquerade.module b/masquerade.module
index 60bd9e7..89d6dc0 100644
--- a/masquerade.module
+++ b/masquerade.module
@@ -115,7 +115,44 @@ function masquerade_menu() {
  */
 function masquerade_user_access(User $target_account) {
   global $user;
-  return !isset($_SESSION['masquerading']) && $user->uid != $target_account->id() && user_access('masquerade');
+
+  // Deny access if the current user is masquerading already or tries to
+  // masquerade as himself.
+  if (isset($_SESSION['masquerading']) || $user->uid == $target_account->id()) {
+    return FALSE;
+  }
+
+  // Invoke hook_masquerade_access() implementations.
+  $access = NULL;
+  foreach (module_implements('masquerade_access') as $module) {
+    $function = $module . '_masquerade_access';
+    $result = $function($user, $target_account);
+    // If an implementation explicitly denies access, then there is nothing else
+    // to check.
+    if ($result === FALSE) {
+      $access = FALSE;
+      break;
+    }
+    elseif ($result === TRUE) {
+      $access = TRUE;
+    }
+  }
+  // If no module granted access, then access is denied.
+  if (!isset($access) || !$access) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Implements hook_masquerade_access().
+ */
+function masquerade_masquerade_access($user, $target_user) {
+  // Only return TRUE, since alternative access implementations could not work
+  // otherwise.
+  if (user_access('masquerade')) {
+    return TRUE;
+  }
 }
 
 /**
@@ -220,36 +257,18 @@ function masquerade_block_form() {
  * Masquerade block form validation.
  */
 function masquerade_block_form_validate($form, &$form_state) {
-  global $user;
-
-  if (isset($_SESSION['masquerading'])) {
-    form_set_error('masquerade_as', t('You are masquerading already. Please <a href="@unmasquerade-url">switch back</a> to your account to masquerade as another user.', array(
-      '@unmasquerade-url' => url('unmasquerade', array(
-        'query' => array('token' => drupal_get_token('unmasquerade')),
-      )),
-    )));
-    return;
-  }
-
   $name = $form_state['values']['masquerade_as'];
-  $target_user = user_load_by_name($name);
-  if (!$target_user) {
-    form_set_error('masquerade_as', t('The username %taret_user does not exist. Please enter a valid username.', array(
+  if (!$target_account = user_load_by_name($name)) {
+    form_set_error('masquerade_as', t('The username %target_user does not exist. Please enter a valid username.', array(
       '%target_user' => $name,
     )));
+    return;
   }
-  elseif ($target_user->uid == $user->uid) {
-    form_set_error('masquerade_as', t('You cannot masquerade as yourself. Please choose a different user to masquerade as.'));
-  }
-  elseif (variable_get('maintenance_mode', 0) && !user_access('access site in maintenance mode', $target_user)) {
-    form_set_error('masquerade_as', t('!user is not permitted to %permission. <a href="@maintenance-url">Disable maintenance mode</a> to masquerade as !user.', array(
-      '!user' => theme('username', array('account' => $target_user)),
-      '%permission' => t('Use the site in maintenance mode'),
-      '@maintenance-url' => url('admin/config/development/maintenance'),
-    )));
+  elseif ($error = masquerade_switch_user_validate($target_account)) {
+    form_set_error('masquerade_as', $error);
   }
   else {
-    $form_state['masquerade_target_user'] = $target_user;
+    $form_state['masquerade_target_account'] = $target_account;
   }
 }
 
@@ -257,12 +276,7 @@ function masquerade_block_form_validate($form, &$form_state) {
  * Masquerade block form submission.
  */
 function masquerade_block_form_submit($form, &$form_state) {
-  if (!masquerade_switch_user($form_state['masquerade_target_user']->uid)) {
-    throw new AccessDeniedHttpException();
-  }
-  else {
-    drupal_goto(drupal_container()->get('request')->server->get('HTTP_REFERER'));
-  }
+  masquerade_switch_user($form_state['masquerade_target_account']);
 }
 
 /**
@@ -270,7 +284,14 @@ function masquerade_block_form_submit($form, &$form_state) {
  */
 function masquerade_switch_user_page(User $target_account) {
   $token = drupal_container()->get('request')->query->get('token');
-  if (isset($token) && drupal_valid_token($token, 'user/' . $target_account->id() . '/masquerade') && masquerade_switch_user($target_account)) {
+  if (isset($token) && drupal_valid_token($token, 'user/' . $target_account->id() . '/masquerade')) {
+    $error = masquerade_switch_user_validate($target_account);
+    if (!$error) {
+      masquerade_switch_user($target_account);
+    }
+    else {
+      drupal_set_message($error, 'error');
+    }
     drupal_goto(drupal_container()->get('request')->server->get('HTTP_REFERER'));
   }
   else {
@@ -279,53 +300,57 @@ function masquerade_switch_user_page(User $target_account) {
 }
 
 /**
- * Masquerades the current user as a given user.
+ * Validates whether the current user can masquerade as a given target user.
+ *
+ * Use this function to generate user-friendly error messages to show in the
+ * user interface.
  *
  * @param \Drupal\user\Plugin\Core\Entity\User $target_account
  *   The user account object to masquerade as.
  *
- * @return bool
- *   TRUE if the user was sucessfully switched, or FALSE if there was an error.
+ * @return string|null
+ *   A string containing a validation error message, or NULL if the current user
+ *   can masquerade as $target_account.
  */
-function masquerade_switch_user(User $target_account) {
+function masquerade_switch_user_validate(User $target_account) {
   global $user;
 
   if (isset($_SESSION['masquerading'])) {
-    drupal_set_message(t('You are masquerading already. Unmasquerade first.'));
-    return FALSE;
+    return t('You are masquerading already. Please <a href="@unmasquerade-url">switch back</a> to your account to masquerade as another user.', array(
+      '@unmasquerade-url' => url('unmasquerade', array(
+        'query' => array('token' => drupal_get_token('unmasquerade')),
+      )),
+    ));
   }
-
-  if ($user->uid == $target_account->uid || isset($user->masquerading)) {
-    watchdog('masquerade', 'This user is already %user.', array('%user' => $target_account->name), WATCHDOG_ERROR);
-    return FALSE;
+  if ($target_account->uid == $user->uid) {
+    return t('You cannot masquerade as yourself. Please choose a different user to masquerade as.');
   }
-
-  // Determine access.
-  $access = NULL;
-  foreach (module_implements('masquerade_access') as $module) {
-    $function = $module . '_masquerade_access';
-    $result = $function($user, $target_account);
-    if ($result === FALSE) {
-      $access = FALSE;
-      break;
-    }
-    elseif ($result === TRUE) {
-      $access = TRUE;
-    }
+  if (variable_get('maintenance_mode', 0) && !user_access('access site in maintenance mode', $target_account)) {
+    return t('!user is not permitted to %permission. <a href="@maintenance-url">Disable maintenance mode</a> to masquerade as !user.', array(
+      '!user' => theme('username', array('account' => $target_account)),
+      '%permission' => t('Use the site in maintenance mode'),
+      '@maintenance-url' => url('admin/config/development/maintenance'),
+    ));
   }
-  // If no module granted access, then access is denied.
-  if (!isset($access) || !$access) {
-    return FALSE;
+  if (!masquerade_user_access($target_account)) {
+    return t('You are not allowed to masquerade as %name.', array('%name' => $name));
   }
+}
 
-  if (variable_get('maintenance_mode', 0) && !user_access('access site in maintenance mode', $target_account)) {
-    drupal_set_message(t('It is not possible to masquerade in off-line mode as %user does not have the %config-perm permission. Please <a href="@site-maintenance">set the site status</a> to "online" to masquerade as %user.', array(
-      '%user' => $target_account->name,
-      '%config-perm' => 'use the site in maintenance mode',
-      '@site-maintenance' => url('admin/settings/site-maintenance'),
-    )));
-    return FALSE;
-  }
+/**
+ * Masquerades the current user as a given user.
+ *
+ * Access to masquerade as the target user account has to checked by all callers
+ * via masquerade_user_access() already.
+ *
+ * @param \Drupal\user\Plugin\Core\Entity\User $target_account
+ *   The user account object to masquerade as.
+ *
+ * @return bool
+ *   TRUE if the user was sucessfully switched, or FALSE if there was an error.
+ */
+function masquerade_switch_user(User $target_account) {
+  global $user;
 
   // Call logout hooks when switching from original user.
   module_invoke_all('user_logout', $user);
@@ -351,17 +376,6 @@ function masquerade_switch_user(User $target_account) {
 }
 
 /**
- * Implements hook_masquerade_access().
- */
-function masquerade_masquerade_access($user, $target_user) {
-  // Only return TRUE, since alternative access implementations could not work
-  // otherwise.
-  if (user_access('masquerade')) {
-    return TRUE;
-  }
-}
-
-/**
  * Allows a user who is currently masquerading to become a new user.
  */
 function masquerade_switch_back_page() {
-- 
1.7.11.msysgit.1

