Index: masquerade.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/masquerade/masquerade.module,v
retrieving revision 1.16.2.14
diff -u -r1.16.2.14 masquerade.module
--- masquerade.module	20 Jun 2009 04:56:14 -0000	1.16.2.14
+++ masquerade.module	16 Jul 2009 17:39:43 -0000
@@ -80,7 +80,7 @@
     'page callback' => 'masquerade_switch_user',
     'page arguments' => array(2),
     'access callback' => 'masquerade_access',
-    'access arguments' => array('switch'),
+    'access arguments' => array('switch', 2),
     'type' => MENU_NORMAL_ITEM,
   );
   $items['masquerade/unswitch'] = array(
@@ -104,6 +104,12 @@
     'access arguments' => array('autocomplete'),
     'type' => MENU_CALLBACK,
   );
+  $items['masquerade/autocomplete-user'] = array(
+    'title' => 'Masquerade autocomplete',
+    'page callback' => 'masquerade_autocomplete_user',
+    'access arguments' => array('access user profiles'),
+    'type' => MENU_CALLBACK,
+  );
   $items['admin/settings/masquerade'] = array(
     'title' => 'Masquerade',
     'description' => 'Masquerade module allows administrators to masquerade as other users.',
@@ -117,15 +123,37 @@
   return $items;
 }
 
-function masquerade_access($type) {
+/**
+ * Determine if the current user has permission to switch users.
+ *
+ * @param string $type
+ *   Either 'switch', 'unswitch', 'user', or 'autocomplete'.
+ *
+ * @param object $uid
+ *   An optional parameter indicating a specific uid to switch to.
+ *   Otherwise, return if the user can switch to any user account.
+ *
+ * @return
+ *   TRUE, if the user can perform the requested action, FALSE otherwise. 
+ */
+function masquerade_access($type, $uid = NULL) {
   switch ($type) {
     case 'unswitch':
       return !empty($GLOBALS['masquerading']) || arg(2) == 'menu-customize' || arg(2) == 'menu';
     case 'autocomplete':
       return !empty($GLOBALS['masquerading']) || (user_access('masquerade as user') || user_access('masquerade as admin'));
       break;
+    case 'user':
+      global $user;
+      return db_result(db_query("SELECT TRUE FROM {masquerade_users} WHERE uid_from = %d", $user->uid));
+      break;
     case 'switch':
-      return empty($GLOBALS['masquerading']) && ((user_access('masquerade as user') || user_access('masquerade as admin')));
+      global $user;
+      if ($uid) {
+        $account = user_load(array('uid' => $uid));
+        $switch_to_account = db_result(db_query("SELECT TRUE FROM {masquerade_users} WHERE uid_from = %d AND uid_to = %d", $user->uid, $account->uid));
+      }
+      return empty($GLOBALS['masquerading']) && (user_access('masquerade as user') || user_access('masquerade as admin') || $switch_to_account);
       break;
   }
 }
@@ -230,6 +258,53 @@
         );
       }
       break;
+
+    case 'form':
+      $form = array();
+      $form['masquerade'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Masquerade settings'),
+        '#access' => user_access('administer permissions'),
+      );
+      // if this list isn't limited, it could choke a site with lots of users - 1000 is arbitrary
+      $user_list = db_query_range('SELECT name, uid FROM {users} WHERE uid > 0 ORDER BY name ASC', 0, 1000);
+      while ($user_row = db_fetch_array($user_list)) {
+        $user_options[$user_row['uid']] = $user_row['name'];
+      }
+      $result = db_query("SELECT uid_to FROM {masquerade_users} WHERE uid_from = %d", $edit_user->uid);
+      $masquerade_users = array();
+      while ($uid_to = db_result($result)) {
+        $u = user_load($uid_to);
+        $masquerade_users[] = $u->name;
+      }
+      $form['masquerade']['masquerade_users'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Enter the users this user is able to masquerade as'),
+        '#description' => t('Enter a comma seperated list of user names that this user can masquerade as.'),
+        '#autocomplete_path' => 'masquerade/autocomplete-user',
+        '#default_value' => implode(", ", $masquerade_users),
+      );
+      return $form;
+      break;
+
+    case 'validate':
+      $users = drupal_explode_tags($edit['masquerade_users']);
+      foreach ($users as $user) {
+        if (!user_load(array('name' => $user))) {
+          form_set_error('masquerade_users', t('%user is not a valid user name.', array('%user' => $user)));
+        }
+      }
+      break;
+
+    case 'update':
+    	$users = drupal_explode_tags($edit['masquerade_users']);
+      db_query("DELETE FROM {masquerade_users} WHERE uid_from = %d", $edit_user->uid);
+      foreach ($users as $user) {
+        $u = user_load(array('name' => $user));
+        db_query("INSERT INTO {masquerade_users} VALUES (%d, %d)", $edit_user->uid, $u->uid);
+      }
+      $edit['masquerade_users'] = NULL;
+      break;
   }
 }
 
@@ -242,14 +317,14 @@
       $blocks[0]['info'] =  t('Masquerade');
       return $blocks;
     case 'view':
-      if (masquerade_access('autocomplete')) {
-        switch ($delta) {
-          case 0:
+      switch ($delta) {
+        case 0:
+          if (masquerade_access('autocomplete') || masquerade_access('user')) {
             $block['subject'] = t('Masquerade');
             $block['content'] = drupal_get_form('masquerade_block_1');
-            break;
-        }
-        return $block;
+            return $block;
+          }
+          break;
       }
       break;
   }
@@ -259,15 +334,20 @@
  * Masquerade block form.
  */
 function masquerade_block_1($record) {
+  global $user;
   $markup_value = '';
   if ($GLOBALS['masquerading']) {
-    global $user;
     $quick_switch_link[] = l(t('Switch back'), 'masquerade/unswitch', array());
     $markup_value = t('You are masquerading as:<br />%masq_as', array('%masq_as' => $user->name)) . theme('item_list', $quick_switch_link);
   }
   else {
-	// A comma-separated list of users.
+	  // A comma-separated list of users.
     $masquerade_switches = drupal_explode_tags(variable_get('masquerade_quick_switches', ''));
+    // Add in user-specific switches.
+    $result = db_query("SELECT uid_to FROM {masquerade_users} WHERE uid_from = %d", $user->uid);
+    while ($uid_to = db_result($result)) {
+      $masquerade_switches[] = $uid_to;
+    }
     foreach ($masquerade_switches as $switch_user) {
       if ($switch_user != $GLOBALS['user']->name) {
         $user_name = user_load(array('name' => $switch_user));
@@ -277,19 +357,21 @@
       }
     }
 
-	$markup_value .= t('Enter username to masquerade as.') . '<br /><br />';
-    $form['masquerade_user_field'] = array(
-      '#prefix' => '<div class="container-inline">',
-      '#type' => 'textfield',
-      '#size' => '18',
-      '#default_value' => $GLOBALS['masquerading'] ? t('Switch back to use') : '',
-      '#autocomplete_path' => 'masquerade/autocomplete',
-    );
-    $form['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Go'),
-      '#suffix' => '</div>',
-    );
+    if (masquerade_access('autocomplete')) {
+      $markup_value .= t('Enter username to masquerade as.') . '<br /><br />';
+      $form['masquerade_user_field'] = array(
+        '#prefix' => '<div class="container-inline">',
+        '#type' => 'textfield',
+        '#size' => '18',
+        '#default_value' => $GLOBALS['masquerading'] ? t('Switch back to use') : '',
+        '#autocomplete_path' => 'masquerade/autocomplete',
+      );
+      $form['submit'] = array(
+        '#type' => 'submit',
+        '#value' => t('Go'),
+        '#suffix' => '</div>',
+      );
+    }
 
     if (isset($quick_switch_link) && count($quick_switch_link)) {
       $markup_value .= '<div id="quick_switch_links">'. t('Quick switches:') . theme('item_list', $quick_switch_link) .'</div>';
@@ -371,10 +453,31 @@
 }
 
 /**
+ * Replacement function for user_autocomplete which allows the use of a comma
+ * separated list of user names. 
+ */
+function masquerade_autocomplete_user($string) {
+  $array = drupal_explode_tags($string);
+  $search = trim(array_pop($array));
+  $matches = array();
+  if ($search) {
+    $prefix = count($array) ? implode(', ', $array) .', ' : '';
+    $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $search, 0, 10);
+    while ($user = db_fetch_object($result)) {
+      $matches[$prefix . $user->name] = check_plain($user->name);
+    }
+  }
+
+  drupal_json($matches);
+}
+
+/**
  * Page callback that allows a user with the right permissions to become
  * the selected user.
  */
 function masquerade_switch_user($uid) {
+  global $user;
+
   if (!is_numeric($uid)) {
     drupal_set_message(t('A user id was not correctly passed to the switching function.'));
     watchdog('masquerade', 'The user id provided to switch users was not numeric.', NULL, WATCHDOG_ERROR);
@@ -388,12 +491,10 @@
     'masquerade as admin' :
     'masquerade as user';
   // check to see if we need admin permission
-  if (!user_access($perm) && !$GLOBALS['masquerading']) {
+  if (!user_access($perm) && !$GLOBALS['masquerading'] && !db_result(db_query("SELECT TRUE FROM {masquerade_users} WHERE uid_from = %d AND uid_to = %d", $user->uid, $new_user->uid))) {
     return drupal_access_denied();
   }
 
-  global $user;
-
   if ($user->uid == $uid || isset($user->masquerading)) {
     return drupal_access_denied();
   }
Index: masquerade.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/masquerade/masquerade.install,v
retrieving revision 1.4.2.2
diff -u -r1.4.2.2 masquerade.install
--- masquerade.install	6 Mar 2009 19:58:16 -0000	1.4.2.2
+++ masquerade.install	16 Jul 2009 17:39:43 -0000
@@ -36,7 +36,24 @@
         'sid' => array('sid', 'uid_from'),
         'sid_2' => array('sid', 'uid_as')
       )
-    )
+    ),
+    'masquerade_users' => array(
+      'fields' => array(
+        'uid_from' => array(
+          'type' => 'int',
+          'not null' => true,
+          'default' => 0,
+          'disp-width' => 10,
+        ),
+        'uid_to' => array(
+          'type' => 'int',
+          'not null' => true,
+          'default' => 0,
+          'disp-width' => 10,
+        ),
+      ),
+      'primary key' => array('uid_from', 'uid_to'),
+    ),
   );
 }
 
@@ -97,4 +114,32 @@
   db_add_index($ret, 'masquerade', 'sid', array('sid', 'uid_from'));
   db_add_index($ret, 'masquerade', 'sid_2', array('sid', 'uid_as'));
   return $ret;
-}
\ No newline at end of file
+}
+
+/**
+ * Add a table storing specific user pairings a user can masquerade as.
+ */
+function masquerade_update_6003() {
+  $ret = array();
+  $schema = array(
+    'masquerade_users' => array(
+      'fields' => array(
+        'uid_from' => array(
+          'type' => 'int',
+          'not null' => true,
+          'default' => 0,
+          'disp-width' => 10,
+        ),
+        'uid_to' => array(
+          'type' => 'int',
+          'not null' => true,
+          'default' => 0,
+          'disp-width' => 10,
+        ),
+      ),
+      'primary key' => array('uid_from', 'uid_to'),
+    )
+  );
+  db_create_table($ret, 'masquerade_users', $schema['masquerade_users']);
+  return $ret;
+}
