| 1 |
<?php
|
| 2 |
// $Id: masquerade.module,v 1.22 2009/03/04 21:03:15 deekayen Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file masquerade.module
|
| 6 |
*
|
| 7 |
* The masquerade module allows administrators to masquerade as other user.
|
| 8 |
*/
|
| 9 |
|
| 10 |
/**
|
| 11 |
* Implementation of hook_help().
|
| 12 |
*/
|
| 13 |
function masquerade_help($path, $arg) {
|
| 14 |
switch ($path) {
|
| 15 |
case 'admin/help#masquerade':
|
| 16 |
return t('<p>The masquerade module adds a link on a user\'s profile page that allows permitted users to masquerade as that user. Upon masquerading, the link to "switch back" to the original user will appear in the menu. While masquerading, the option to masquerade as another user will not appear. All masquerading transactions are logged, and $user->masquerading will be set; this could be displayed via theme.</p><p>In the masquerade settings a list of roles are presented; any checked role is considered an "administrator" and requires the second level "masquerade as admin" permission to masquerade as. User #1 is automatically considered an administrator, regardless of roles.</p>');
|
| 17 |
}
|
| 18 |
}
|
| 19 |
|
| 20 |
/**
|
| 21 |
* Implementation of hook_perm().
|
| 22 |
*
|
| 23 |
* @return array
|
| 24 |
*/
|
| 25 |
function masquerade_perm() {
|
| 26 |
return array(
|
| 27 |
'masquerade as user' => array(
|
| 28 |
'title' => t("Masquerade as user"),
|
| 29 |
'description' => t('Grants permission to masquerade as a different user.')
|
| 30 |
),
|
| 31 |
'masquerade as admin' => array(
|
| 32 |
'title' => t('Masquerade as administrator'),
|
| 33 |
'description' => t('Extra permission beyond regular user masquerading for users in selected roles.')
|
| 34 |
)
|
| 35 |
);
|
| 36 |
}
|
| 37 |
|
| 38 |
/**
|
| 39 |
* Implementation of hook_init().
|
| 40 |
*/
|
| 41 |
function masquerade_init() {
|
| 42 |
global $user;
|
| 43 |
|
| 44 |
// load from table uid + session id
|
| 45 |
$query = db_select('masquerade', 'm');
|
| 46 |
$query->addField('m', 'uid_from');
|
| 47 |
$query->condition(db_and()->condition('sid', session_id(), '=')->condition('uid_as', $user->uid, '='));
|
| 48 |
$query->range(0, 1);
|
| 49 |
$result = $query->execute();
|
| 50 |
$uid = $result->fetchField();
|
| 51 |
// using if so that we get unset rather than false if not masqing
|
| 52 |
if ($uid) {
|
| 53 |
$GLOBALS['masquerading'] = $uid;
|
| 54 |
}
|
| 55 |
else {
|
| 56 |
$GLOBALS['masquerading'] = null;
|
| 57 |
}
|
| 58 |
}
|
| 59 |
|
| 60 |
/**
|
| 61 |
* Implementation of hook_cron()
|
| 62 |
*
|
| 63 |
* Cleanup masquerade records where people didn't use the switch back link
|
| 64 |
* that would have cleanly removed the user switch record.
|
| 65 |
*/
|
| 66 |
function masquerade_cron() {
|
| 67 |
// see http://drupal.org/node/268487 before modifying this query
|
| 68 |
db_delete('masquerade')
|
| 69 |
->condition('sid', db_select('sessions', 'sid'), 'NOT IN')
|
| 70 |
->execute();
|
| 71 |
}
|
| 72 |
|
| 73 |
/**
|
| 74 |
* Implementation of hook_menu().
|
| 75 |
*/
|
| 76 |
function masquerade_menu() {
|
| 77 |
$items = array();
|
| 78 |
|
| 79 |
$default_test_user = _masquerade_test_user();
|
| 80 |
if ($default_test_user->uid) {
|
| 81 |
$items['masquerade/switch/' . $default_test_user->uid] = array(
|
| 82 |
'title' => 'Masquerade as @testuser',
|
| 83 |
'title arguments' => array('@testuser' => $default_test_user->name),
|
| 84 |
'page callback' => 'masquerade_switch_user',
|
| 85 |
'page arguments' => array(2),
|
| 86 |
'access callback' => 'masquerade_access',
|
| 87 |
'access arguments' => array('switch'),
|
| 88 |
'type' => MENU_NORMAL_ITEM,
|
| 89 |
);
|
| 90 |
}
|
| 91 |
|
| 92 |
$items['masquerade/switch/%'] = array(
|
| 93 |
'title' => 'Masquerading',
|
| 94 |
'page callback' => 'masquerade_switch_user',
|
| 95 |
'page arguments' => array(2),
|
| 96 |
'access callback' => 'masquerade_access',
|
| 97 |
'access arguments' => array('switch'),
|
| 98 |
'type' => MENU_NORMAL_ITEM,
|
| 99 |
);
|
| 100 |
$items['masquerade/unswitch'] = array(
|
| 101 |
'title' => 'Switch back',
|
| 102 |
'page callback' => 'masquerade_switch_back',
|
| 103 |
'access callback' => 'masquerade_access',
|
| 104 |
'access arguments' => array('unswitch'),
|
| 105 |
'type' => MENU_NORMAL_ITEM,
|
| 106 |
);
|
| 107 |
$items['masquerade/autocomplete'] = array(
|
| 108 |
'title' => '',
|
| 109 |
'page callback' => 'masquerade_autocomplete',
|
| 110 |
'access callback' => 'masquerade_access',
|
| 111 |
'access arguments' => array('autocomplete'),
|
| 112 |
'type' => MENU_CALLBACK,
|
| 113 |
);
|
| 114 |
$items['masquerade/autocomplete/multiple'] = array(
|
| 115 |
'title' => '',
|
| 116 |
'page callback' => 'masquerade_autocomplete_multiple',
|
| 117 |
'access callback' => 'masquerade_access',
|
| 118 |
'access arguments' => array('autocomplete'),
|
| 119 |
'type' => MENU_CALLBACK,
|
| 120 |
);
|
| 121 |
$items['admin/settings/masquerade'] = array(
|
| 122 |
'title' => 'Masquerade',
|
| 123 |
'description' => 'Masquerade module allows administrators to masquerade as other users.',
|
| 124 |
'page callback' => 'drupal_get_form',
|
| 125 |
'page arguments' => array('masquerade_admin_settings'),
|
| 126 |
'access callback' => 'user_access',
|
| 127 |
'access arguments' => array('administer permissions'),
|
| 128 |
'type' => MENU_NORMAL_ITEM,
|
| 129 |
);
|
| 130 |
|
| 131 |
return $items;
|
| 132 |
}
|
| 133 |
|
| 134 |
function masquerade_access($type) {
|
| 135 |
switch ($type) {
|
| 136 |
case 'unswitch':
|
| 137 |
return $GLOBALS['masquerading'] || arg(2) == 'menu-customize' || arg(2) == 'menu';
|
| 138 |
case 'autocomplete':
|
| 139 |
return $GLOBALS['masquerading'] || (user_access('masquerade as user') || user_access('masquerade as admin'));
|
| 140 |
break;
|
| 141 |
case 'switch':
|
| 142 |
return empty($GLOBALS['masquerading']) && (user_access('masquerade as user') || user_access('masquerade as admin'));
|
| 143 |
break;
|
| 144 |
}
|
| 145 |
}
|
| 146 |
|
| 147 |
function masquerade_admin_settings() {
|
| 148 |
// create a list of roles; all selected roles are considered administrative.
|
| 149 |
$rids = array();
|
| 150 |
$result = db_select('role', 'r')
|
| 151 |
->fields('r', array('rid', 'name'))
|
| 152 |
->orderBy('name', 'ASC')
|
| 153 |
->execute();
|
| 154 |
while ($obj = $result->fetchObject()) {
|
| 155 |
$rids[$obj->rid] = $obj->name;
|
| 156 |
}
|
| 157 |
|
| 158 |
$form['masquerade_admin_roles'] = array(
|
| 159 |
'#type' => 'checkboxes',
|
| 160 |
'#title' => t('Roles that are considered "administrators" for masquerading'),
|
| 161 |
'#options' => $rids,
|
| 162 |
'#default_value' => variable_get('masquerade_admin_roles', array()),
|
| 163 |
);
|
| 164 |
|
| 165 |
$test_name = _masquerade_test_user();
|
| 166 |
|
| 167 |
$form['masquerade_test_user'] = array(
|
| 168 |
'#type' => 'textfield',
|
| 169 |
'#title' => t('Menu <em>Quick Switch</em> user'),
|
| 170 |
'#autocomplete_path' => 'masquerade/autocomplete',
|
| 171 |
'#default_value' => check_plain($test_name->name),
|
| 172 |
'#description' => t('Enter the username of an account you wish to switch easily between via a menu item.')
|
| 173 |
);
|
| 174 |
|
| 175 |
$form['masquerade_quick_switches'] = array(
|
| 176 |
'#type' => 'textfield',
|
| 177 |
'#title' => t('Block <em>Quick Switch</em> users'),
|
| 178 |
'#autocomplete_path' => 'masquerade/autocomplete/multiple',
|
| 179 |
'#default_value' => variable_get('masquerade_quick_switches', ''),
|
| 180 |
'#description' => t('Enter the usernames, separated by commas, of accounts to show as quick switch links in the Masquerade block.')
|
| 181 |
);
|
| 182 |
|
| 183 |
$form['#validate'][] = 'masquerade_admin_settings_validate';
|
| 184 |
$form['#submit'][] = 'masquerade_admin_settings_submit';
|
| 185 |
|
| 186 |
return system_settings_form($form);
|
| 187 |
}
|
| 188 |
|
| 189 |
function masquerade_admin_settings_validate($form, &$form_state) {
|
| 190 |
unset($form);
|
| 191 |
$test_user = user_load(array('name' => $form_state['values']['masquerade_test_user']));
|
| 192 |
if (!$test_user) {
|
| 193 |
form_set_error('masquerade_test_user', t('%user does not exist. Please enter a valid username.', array('%user' => $form_state['values']['masquerade_test_user'])));
|
| 194 |
}
|
| 195 |
|
| 196 |
// A comma-separated list of users.
|
| 197 |
$masquerade_switches = drupal_explode_tags($form_state['values']['masquerade_quick_switches']);
|
| 198 |
foreach ($masquerade_switches as $switch_user) {
|
| 199 |
$test_user = user_load(array('name' => $switch_user));
|
| 200 |
if (!$test_user) {
|
| 201 |
form_set_error('masquerade_quick_switches', t('%user does not exist. Please enter a valid username.', array('%user' => $switch_user)));
|
| 202 |
}
|
| 203 |
}
|
| 204 |
}
|
| 205 |
|
| 206 |
function masquerade_admin_settings_submit($form, &$form_state) {
|
| 207 |
menu_rebuild();
|
| 208 |
}
|
| 209 |
|
| 210 |
function _masquerade_test_user() {
|
| 211 |
$test_user->uid = 0;
|
| 212 |
$test_user->name = '';
|
| 213 |
|
| 214 |
$test_user = user_load(array('name' => variable_get('masquerade_test_user', $test_user->name)));
|
| 215 |
|
| 216 |
return $test_user;
|
| 217 |
}
|
| 218 |
|
| 219 |
/**
|
| 220 |
* Implementation of hook_user_logout().
|
| 221 |
*/
|
| 222 |
function masquerade_user_logout(&$edit, &$edit_user, $category = NULL) {
|
| 223 |
if (!empty($edit_user->masquerading)) {
|
| 224 |
global $user;
|
| 225 |
cache_clear_all($user->uid, 'cache_menu', true);
|
| 226 |
$real_user = user_load(array('uid' => $user->masquerading));
|
| 227 |
watchdog('masquerade', "User %user no longer masquerading as %masq_as.", array('%user' => $real_user->name, '%masq_as' => $user->name), WATCHDOG_INFO);
|
| 228 |
db_delete('masquerade')
|
| 229 |
->condition(db_and()->condition('sid', session_id())->condition('uid_as', $edit_user->uid))
|
| 230 |
-execute();
|
| 231 |
}
|
| 232 |
}
|
| 233 |
|
| 234 |
/**
|
| 235 |
* Implementation of hook_user_view().
|
| 236 |
*/
|
| 237 |
function masquerade_user_view(&$edit, &$edit_user, $category = NULL) {
|
| 238 |
// check if user qualifies as admin
|
| 239 |
$roles = array_keys(array_filter(variable_get('masquerade_admin_roles', array())));
|
| 240 |
$perm = $edit_user->uid == 1 || array_intersect(array_keys((array)$edit_user->roles), $roles) ?
|
| 241 |
'masquerade as admin' :
|
| 242 |
'masquerade as user';
|
| 243 |
|
| 244 |
global $user;
|
| 245 |
if (user_access($perm) && empty($edit_user->masquerading) && $user->uid != $edit_user->uid) {
|
| 246 |
$edit_user->content['Masquerade'] = array(
|
| 247 |
'#value' => l(t('Masquerade as !user', array('!user' => $edit_user->name)), 'masquerade/switch/'. $edit_user->uid, array('destination' => $_GET['q'])),
|
| 248 |
'#weight' => 10
|
| 249 |
);
|
| 250 |
}
|
| 251 |
}
|
| 252 |
|
| 253 |
/**
|
| 254 |
* Implementation of hook_block_list().
|
| 255 |
*/
|
| 256 |
function masquerade_block_list($delta = 0, $edit = array()) {
|
| 257 |
$blocks[0]['info'] = t('Masquerade');
|
| 258 |
return $blocks;
|
| 259 |
}
|
| 260 |
|
| 261 |
/**
|
| 262 |
* Implementation of hook_block_list().
|
| 263 |
*/
|
| 264 |
function masquerade_block_view($delta = 0, $edit = array()) {
|
| 265 |
if (masquerade_access('autocomplete')) {
|
| 266 |
switch ($delta) {
|
| 267 |
case 0:
|
| 268 |
$block['subject'] = t('Masquerade');
|
| 269 |
$block['content'] = drupal_get_form('masquerade_block_1');
|
| 270 |
break;
|
| 271 |
}
|
| 272 |
return $block;
|
| 273 |
}
|
| 274 |
}
|
| 275 |
|
| 276 |
/**
|
| 277 |
* Masquerade block form.
|
| 278 |
*/
|
| 279 |
function masquerade_block_1($record) {
|
| 280 |
$markup_value = '';
|
| 281 |
if ($GLOBALS['masquerading']) {
|
| 282 |
global $user;
|
| 283 |
$quick_switch_link[] = l(t('Switch back'), 'masquerade/unswitch', array());
|
| 284 |
$markup_value = t('You are masquerading as:<br />%masq_as', array('%masq_as' => $user->name)) . theme('item_list', $quick_switch_link);
|
| 285 |
}
|
| 286 |
else {
|
| 287 |
// A comma-separated list of users.
|
| 288 |
$masquerade_switches = drupal_explode_tags(variable_get('masquerade_quick_switches', ''));
|
| 289 |
foreach ($masquerade_switches as $switch_user) {
|
| 290 |
if ($switch_user != $GLOBALS['user']->name) {
|
| 291 |
$user_name = user_load(array('name' => $switch_user));
|
| 292 |
if ($user_name->uid) {
|
| 293 |
$quick_switch_link[] = l($user_name->name, 'masquerade/switch/'. $user_name->uid);
|
| 294 |
}
|
| 295 |
}
|
| 296 |
}
|
| 297 |
|
| 298 |
$markup_value .= t('Enter username to masquerade as.') . '<br /><br />';
|
| 299 |
$form['masquerade_user_field'] = array(
|
| 300 |
'#prefix' => '<div class="container-inline">',
|
| 301 |
'#type' => 'textfield',
|
| 302 |
'#size' => '18',
|
| 303 |
'#default_value' => $GLOBALS['masquerading'] ? t('Switch back to use') : '',
|
| 304 |
'#autocomplete_path' => 'masquerade/autocomplete',
|
| 305 |
);
|
| 306 |
$form['submit'] = array(
|
| 307 |
'#type' => 'submit',
|
| 308 |
'#value' => t('Go'),
|
| 309 |
'#suffix' => '</div>',
|
| 310 |
);
|
| 311 |
|
| 312 |
if (isset($quick_switch_link) && count($quick_switch_link)) {
|
| 313 |
$markup_value .= '<div id="quick_switch_links">'. t('Quick switches:') . theme('item_list', $quick_switch_link) .'</div>';
|
| 314 |
}
|
| 315 |
}
|
| 316 |
$form['masquerade_desc'] = array(
|
| 317 |
'#prefix' => '<div class="form-item"><div class="description">',
|
| 318 |
'#type' => 'markup',
|
| 319 |
'#value' => $markup_value,
|
| 320 |
'#suffix' => '</div></div>',
|
| 321 |
);
|
| 322 |
return $form;
|
| 323 |
}
|
| 324 |
|
| 325 |
/**
|
| 326 |
* Masquerade block form validation.
|
| 327 |
*/
|
| 328 |
function masquerade_block_1_validate($form, &$form_state) {
|
| 329 |
unset($form);
|
| 330 |
if ($form_state['values']['masquerade_user_field'] == '') {
|
| 331 |
form_set_error('masquerade_user_field', t('You cannot masquerade as %anonymous!', array('%anonymous' => variable_get('anonymous', 'Anonymous'))));
|
| 332 |
}
|
| 333 |
if ($GLOBALS['masquerading']) {
|
| 334 |
form_set_error('masquerade_user_field', t('You are already masquerading!'));
|
| 335 |
}
|
| 336 |
global $user;
|
| 337 |
$masq_user = user_load(array('name' => $form_state['values']['masquerade_user_field']));
|
| 338 |
if (!$masq_user) {
|
| 339 |
form_set_error('masquerade_user_field', t('User %masq_as does not exist. Please enter a valid username.', array('%masq_as' => $form_state['values']['masquerade_user_field'])));
|
| 340 |
}
|
| 341 |
if ($masq_user->uid == $user->uid) {
|
| 342 |
form_set_error('masquerade_user_field', t('You cannot masquerade on yourself!'));
|
| 343 |
}
|
| 344 |
if (variable_get('site_offline', 0)) {
|
| 345 |
form_set_error('masquerade_user_field', t('It is not possible to masquerade in off-line mode!'));
|
| 346 |
}
|
| 347 |
}
|
| 348 |
|
| 349 |
/**
|
| 350 |
* Masquerade block form submission.
|
| 351 |
*/
|
| 352 |
function masquerade_block_1_submit($form, &$form_state) {
|
| 353 |
unset($form);
|
| 354 |
$masq_user = user_load(array('name' => $form_state['values']['masquerade_user_field']));
|
| 355 |
masquerade_switch_user($masq_user->uid);
|
| 356 |
}
|
| 357 |
|
| 358 |
/**
|
| 359 |
* Returns JS array for Masquerade autocomplete fields.
|
| 360 |
*/
|
| 361 |
function masquerade_autocomplete($string) {
|
| 362 |
$matches = array();
|
| 363 |
$result = db_query_range("SELECT u.name FROM {users} u WHERE LOWER(u.name) LIKE LOWER(:name)", array(':name' => $string . "%"), 0, 10);
|
| 364 |
while ($user = $result->fetchObject()) {
|
| 365 |
$matches[$user->name] = check_plain($user->name);
|
| 366 |
}
|
| 367 |
exit(drupal_to_js($matches));
|
| 368 |
}
|
| 369 |
|
| 370 |
/**
|
| 371 |
* Returns JS array for Masquerade autocomplete fields. Supports multiple entries separated by a comma.
|
| 372 |
*/
|
| 373 |
function masquerade_autocomplete_multiple($string) {
|
| 374 |
// The user enters a comma-separated list of users. We only autocomplete the last user.
|
| 375 |
$array = drupal_explode_tags($string);
|
| 376 |
|
| 377 |
// Fetch last tag
|
| 378 |
$last_string = trim(array_pop($array));
|
| 379 |
|
| 380 |
$matches = array();
|
| 381 |
$result = db_query_range("SELECT u.name FROM {users} u WHERE LOWER(u.name) LIKE LOWER(':name')", array(':name' => $last_string . '%'), 0, 10);
|
| 382 |
|
| 383 |
$prefix = count($array) ? implode(', ', $array) .', ' : '';
|
| 384 |
|
| 385 |
while ($user = $result->fetchObject()) {
|
| 386 |
$matches[$prefix . $user->name] = check_plain($user->name);
|
| 387 |
}
|
| 388 |
exit(drupal_to_js($matches));
|
| 389 |
}
|
| 390 |
|
| 391 |
/**
|
| 392 |
* Page callback that allows a user with the right permissions to become
|
| 393 |
* the selected user.
|
| 394 |
*/
|
| 395 |
function masquerade_switch_user($uid) {
|
| 396 |
if (!is_numeric($uid)) {
|
| 397 |
drupal_set_message(t('A user id was not correctly passed to the switching function.'));
|
| 398 |
watchdog('masquerade', 'The user id provided to switch users was not numeric.', NULL, WATCHDOG_ERROR);
|
| 399 |
return drupal_goto($_SERVER['HTTP_REFERER']);
|
| 400 |
}
|
| 401 |
|
| 402 |
$new_user = user_load(array('uid' => $uid));
|
| 403 |
|
| 404 |
$roles = array_keys(array_filter(variable_get('masquerade_admin_roles', array())));
|
| 405 |
$perm = $uid == 1 || array_intersect(array_keys($new_user->roles), $roles) ?
|
| 406 |
'masquerade as admin' :
|
| 407 |
'masquerade as user';
|
| 408 |
// check to see if we need admin permission
|
| 409 |
if (!user_access($perm) && !$GLOBALS['masquerading']) {
|
| 410 |
return drupal_access_denied();
|
| 411 |
}
|
| 412 |
|
| 413 |
global $user;
|
| 414 |
|
| 415 |
if ($user->uid == $uid || isset($user->masquerading)) {
|
| 416 |
return drupal_access_denied();
|
| 417 |
}
|
| 418 |
|
| 419 |
if (variable_get('site_offline', 0) && !user_access('administer site configuration', $new_user)) {
|
| 420 |
drupal_set_message(t('The user is not allowed to access site in off-line mode!'), 'error');
|
| 421 |
return drupal_access_denied();
|
| 422 |
}
|
| 423 |
|
| 424 |
db_insert('masquerade')
|
| 425 |
->fields(array(
|
| 426 |
'uid_from' => $user->uid,
|
| 427 |
'uid_as' => $new_user->uid,
|
| 428 |
'sid' => session_id()
|
| 429 |
))
|
| 430 |
->execute();
|
| 431 |
// switch user
|
| 432 |
|
| 433 |
watchdog('masquerade', 'User %user now masquerading as %masq_as.', array('%user' => $user->name, '%masq_as' => $new_user->name), WATCHDOG_INFO);
|
| 434 |
drupal_set_message(t('Now masquerading as %masq_as.', array('%masq_as' => $new_user->name)));
|
| 435 |
$user->masquerading = $new_user->uid;
|
| 436 |
$user = $new_user;
|
| 437 |
drupal_goto(referer_uri());
|
| 438 |
}
|
| 439 |
|
| 440 |
/**
|
| 441 |
* Page callback that allows a user who is currently masquerading to become
|
| 442 |
* a new user.
|
| 443 |
*/
|
| 444 |
function masquerade_switch_back() {
|
| 445 |
// switch user
|
| 446 |
global $user;
|
| 447 |
cache_clear_all($user->uid, 'cache_menu', true);
|
| 448 |
$query = db_select('masquerade', 'm');
|
| 449 |
$query->addField('m', 'uid_from');
|
| 450 |
$query->condition(db_and()->condition('sid', session_id(), '=')->condition('uid_as', $user->uid, '='));
|
| 451 |
$query->range(0, 1);
|
| 452 |
$result = $query->execute();
|
| 453 |
$uid = $result->fetchField();
|
| 454 |
// erase record
|
| 455 |
db_delete('masquerade')
|
| 456 |
->condition(db_and()->condition('sid', session_id())->condition('uid_as', $user->uid))
|
| 457 |
->execute();
|
| 458 |
$oldname = $user->name;
|
| 459 |
$user = user_load(array('uid' => $uid));
|
| 460 |
watchdog('masquerade', 'User %user no longer masquerading as %masq_as.', array('%user' => $user->name, '%masq_as' => $oldname), WATCHDOG_INFO);
|
| 461 |
drupal_set_message(t('No longer masquerading as %masq_as.', array('%masq_as' => $oldname)));
|
| 462 |
drupal_goto($_SERVER['HTTP_REFERER']);
|
| 463 |
}
|