diff --git a/masquerade.module b/masquerade.module index 5c7da88..cf6ade5 100644 --- a/masquerade.module +++ b/masquerade.module @@ -23,7 +23,7 @@ function masquerade_help($path, $arg) { * @return array */ function masquerade_permission() { - return array( + $perms = array( 'masquerade as user' => array( 'title' => t('Masquerade as user'), 'description' => t('Masquerade as another user.'), @@ -44,6 +44,17 @@ function masquerade_permission() { 'restrict access' => TRUE, ), ); + + // Allow fine grain role masquerading + $roles = user_roles(); + foreach ($roles as $rid => $role) { + if($role == 'anonymous user') continue; + $perm_string = _masquerade_build_perm_string($role); + $perm_title = ucfirst($perm_string); + $perms[$perm_string] = array('title' => $perm_title, 'description' => $perm_title); + } + + return $perms; } /** @@ -248,6 +259,11 @@ function masquerade_menu_access($type, $uid = NULL) { ':uid_from' => $user->uid, ':uid_to' => $account->uid ))->fetchField(); + + // Unset switch to account if no access to at least one role + if(!_masquerade_user_access($account)) { + $switch_to_account = null; + } } } return !isset($_SESSION['masquerading']) && (user_access('masquerade as user') || user_access('masquerade as admin') || $switch_to_account); @@ -604,6 +620,8 @@ function masquerade_block_1() { foreach ($masquerade_switches as $switch_user) { if (!isset($_SESSION['user']->uid) || $switch_user != $_SESSION['user']->uid) { $account = user_load($switch_user); + // Don't show quick switches if no access + if(!_masquerade_user_access($account)) continue; if (isset($account->uid)) { $switch_link = 'masquerade/switch/' . $account->uid; if ($account->uid) { @@ -723,10 +741,12 @@ function masquerade_autocomplete($string) { $matches[$anonymous] = $anonymous; } // Other suggestions. - $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER(:string)", 0, 10, array( + $result = db_query_range("SELECT uid, name FROM {users} WHERE LOWER(name) LIKE LOWER(:string)", 0, 10, array( ':string' => $string . '%', )); foreach ($result as $user) { + $account = user_load($user->uid); + if(!_masquerade_user_access($account)) continue; $matches[$user->name] = check_plain($user->name); } if (module_exists('devel')) { @@ -761,10 +781,12 @@ function masquerade_autocomplete_multiple($string, $add_anonymous = TRUE) { } } // Other suggestions. - $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE :string", 0, 10, array( + $result = db_query_range("SELECT uid, name FROM {users} WHERE LOWER(name) LIKE :string", 0, 10, array( ':string' => $last_string . '%', )); foreach ($result as $user) { + $account = user_load($user->uid); + if(!_masquerade_user_access($account)) continue; $matches[$prefix . $user->name] = check_plain($user->name); } @@ -773,10 +795,12 @@ function masquerade_autocomplete_multiple($string, $add_anonymous = TRUE) { // @todo Check compatibility for D7. if (module_exists('alt_login')) { - $result = db_query_range("SELECT u.alt_login AS alt_login FROM {alt_login} u WHERE LOWER(u.alt_login) LIKE LOWER(:string)", 0, 10, array( + $result = db_query_range("SELECT u.uid, u.alt_login AS alt_login FROM {alt_login} u WHERE LOWER(u.alt_login) LIKE LOWER(:string)", 0, 10, array( ':string' => $last_string . '%', )); foreach ($result as $user) { + $account = user_load($user->uid); + if(!_masquerade_user_access($account)) continue; $matches[$user->alt_login] = check_plain($user->alt_login); } } @@ -828,7 +852,7 @@ function masquerade_switch_user($uid) { ':uid_from' => $user->uid, ':uid_to' => $new_user->uid, )); - if (!user_access($perm) && !isset($_SESSION['masquerading']) && !$results->fetchField()) { + if ((!user_access($perm) || !_masquerade_user_access($new_user)) && !isset($_SESSION['masquerading']) && !$results->fetchField()) { watchdog('masquerade', 'This user requires administrative permissions to switch to the user %user.', array('%user' => $new_user->name), WATCHDOG_ERROR); return FALSE; } @@ -915,3 +939,51 @@ function masquerade_switch_back() { watchdog('masquerade', 'User %user no longer masquerading as %masq_as.', array('%user' => $user->name, '%masq_as' => $oldname), WATCHDOG_INFO); } + +/** + * Generates a permission string for a given a role name. + */ +function _masquerade_build_perm_string($role_name) { + $perm = "masquerade as role {$role_name}"; + return $perm; +} + +/** + * Check that the current user has access to masquerade as the account roles + * + * @global type $user + * @param type $account + */ +function _masquerade_user_access($account) { + global $user; + + // Return true right away if admin access + if(user_access('masquerade as admin')) return true; + + /* + * @TODO + * Right now this assumes no hierarchy in roles and only allows masquerading access + * to $user if they have masquerade permissions to ALL the roles of $account. + * + * A better way to do this would be to create a roles hierarchy module to define + * a hierarchy of roles and limit access to the highest role permission the user has. + */ + if(empty($account->roles)) { + $account = user_load($account->uid); + } + + // Empty roles?! weird. no access for you! + if(empty($account->roles)) { + return false; + } + + // return false on the first unmatched role + foreach($account->roles as $rid => $role) { + $permission = _masquerade_build_perm_string($role); + if(!(user_access($permission, $user))) { + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/masquerade.test b/masquerade.test index a019dc9..857119f 100644 --- a/masquerade.test +++ b/masquerade.test @@ -30,6 +30,7 @@ class MasqueradeTestCase extends DrupalWebTestCase { 'access user profiles', 'masquerade as user', 'masquerade as any user', + 'masquerade as authenticated user', ); $admin = $this->drupalCreateUser($admin_perms); $user = $this->drupalCreateUser();