Newer
Older
* @file
* Allows privileged users to masquerade as another user.
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\user\UserInterface;
* Implements hook_help().
function masquerade_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.masquerade':
Daniel Kudwien
committed
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Masquerade module allows users to temporarily switch to another user account. It records the original user account, so users can easily switch back.') . '</p>';
Daniel Kudwien
committed
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Granting masquerade access') . '</dt>';
Sascha Grossenbacher
committed
$output .= '<dd>' . t('Users may only masquerade as another user if they have the <a href=":permission_link">Masquerade as any user</a> permission or if they have all the <a href=":permission_link">Masquerade as ROLE</a> permissions for all the target account\'s roles.', [
':permission_link' => Url::fromRoute('user.admin_permissions', [], ['fragment' => 'module-masquerade'])->toString(),
]) . '</dd>';
Daniel Kudwien
committed
$output .= '<dt>' . t('Masquerading as another user') . '</dt>';
$output .= '<dd>' . t('There are multiple ways to masquerade as another user:');
$output .= '<ul>';
$output .= '<li>' . t('On the <a href=":admin-people-url">administrative user listing</a>, choose the <em>Masquerade</em> operation of a certain user account.', [
':admin-people-url' => Url::fromRoute('entity.user.collection')->toString(),
]) . '</li>';
$output .= '<li>' . t('Masquerade can be used directly from menus provided by the %toolbar module.', [
'%toolbar' => t('Toolbar'),
]) . '</li>';
Daniel Kudwien
committed
$output .= '</ul>';
$output .= '</dd>';
$output .= '<dt>' . t('Switching back') . '</dt>';
$output .= '<dd>' . t('To stop masquerading as another user, click the <em>Unmasquerade</em> link in the user account links menu.') . '</dd>';
Daniel Kudwien
committed
$output .= '</dl>';
return $output;
/**
* Implements hook_toolbar().
*
* @todo Nest this with the "View profile", "Edit profile", and "Log out"
* links under the username tab.
*/
function masquerade_toolbar() {
Arpad Rozsa
committed
$items = [
'masquerade_switch_back' => [
'#cache' => [
'contexts' => ['session.is_masquerading'],
],
],
];
if (\Drupal::service('masquerade')->isMasquerading()) {
Arpad Rozsa
committed
$items['masquerade_switch_back'] += [
'#type' => 'toolbar_item',
'tab' => [
David Norman
committed
'#title' => t('Unmasquerade'),
'#url' => Url::fromRoute('masquerade.unmasquerade'),
],
// Hopefully shows immediately after the username tab.
'#weight' => 101,
];
}
return $items;
}
* Returns whether the current user is allowed to masquerade as a target user.
Daniel Kudwien
committed
*
Daniel Kudwien
committed
* The user account object to masquerade as.
*
* @return bool
* TRUE if allowed, FALSE otherwise.
*
* @see hook_masquerade_access()
David Norman
committed
function masquerade_target_user_access(UserInterface $target_account) {
// Deny access if the current user is masquerading already or tries to
// masquerade as himself.
if (\Drupal::service('masquerade')->isMasquerading() || $user->id() == $target_account->id()) {
return FALSE;
}
// Invoke hook_masquerade_access() implementations.
Samit Khulve
committed
$results = \Drupal::moduleHandler()->invokeAll('masquerade_access', [
$user,
$target_account,
]);
Sascha Grossenbacher
committed
// If an implementation explicitly denies access, then there is nothing else
// to check.
if (in_array(FALSE, $results, TRUE)) {
return FALSE;
}
Sascha Grossenbacher
committed
// If access is not explicitly denied, allow access if at least one hook
// implementation allowed access.
return in_array(TRUE, $results, TRUE);
}
/**
* Implements hook_masquerade_access().
Daniel Kudwien
committed
*
* This default implementation only returns TRUE and never FALSE, since
* alternative access implementations could not work otherwise.
function masquerade_masquerade_access($user, UserInterface $target_account) {
Daniel Kudwien
committed
// Uid 1 may masquerade as anyone.
Daniel Kudwien
committed
return TRUE;
}
// Uid 1 gets special treatment with its own permission.
Daniel Kudwien
committed
if ($target_account->id() == 1) {
if ($user->hasPermission('masquerade as super user')) {
return TRUE;
}
else {
Sascha Grossenbacher
committed
return NULL;
Daniel Kudwien
committed
}
// The current user must be allowed to masquerade.
David Norman
committed
if ($user->hasPermission('masquerade as any user')) {
Daniel Kudwien
committed
return TRUE;
}
David Norman
committed
// Permissions may be granted on a per-role basis.
$target_account_roles = $target_account->getRoles();
foreach ($target_account_roles as $role_id) {
Sascha Grossenbacher
committed
if (!$user->hasPermission("masquerade as $role_id")) {
return NULL;
David Norman
committed
}
Sascha Grossenbacher
committed
// Only allow masquerade if a user has access to all the target account roles.
return TRUE;
David Norman
committed
/**
* Implements hook_entity_extra_field_info().
*/
function masquerade_entity_extra_field_info() {
$fields['user']['user']['display']['masquerade'] = [
David Norman
committed
'label' => t('Masquerade'),
'description' => t('Masquerade as user link.'),
'weight' => 50,
];
David Norman
committed
return $fields;
}
Andrey Postnikov
committed
* Implements hook_ENTITY_TYPE_view().
Andrey Postnikov
committed
function masquerade_user_view(array &$build, UserInterface $account, EntityViewDisplayInterface $display, $view_mode) {
if ($display->getComponent('masquerade')) {
// Use post render to allow caching.
$build['masquerade'] = [
'#lazy_builder' => [
Andrey Postnikov
committed
'masquerade.callbacks:renderCacheLink', [
$account->id(),
'#create_placeholder' => TRUE,
/**
* Implements hook_entity_type_alter().
*
* Adds useful link template to user entity.
*/
function masquerade_entity_type_alter(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
$entity_types['user']->setLinkTemplate('masquerade', '/user/{user}/masquerade');
}
/**
* Implements hook_entity_operation().
*/
function masquerade_entity_operation(EntityInterface $entity) {
$operations = [];
if ($entity->getEntityTypeId() === 'user') {
if (masquerade_target_user_access($entity)) {
$operations['masquerade'] = [
'title' => t('Masquerade as'),
'weight' => 100,
];
}
}
return $operations;
}
* 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\UserInterface $target_account
Daniel Kudwien
committed
* The user account object to masquerade as.
* @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_validate(UserInterface $target_account) {
Andrey Postnikov
committed
if (\Drupal::service('masquerade')->isMasquerading()) {
return t('You are masquerading already. Please <a href="@unmasquerade-url">switch back</a> to your account to masquerade as another user.', [
'@unmasquerade-url' => Url::fromRoute('masquerade.unmasquerade')->toString(),
]);
return t('You cannot masquerade as yourself. Please choose a different user to masquerade as.');
if (\Drupal::config('system.maintenance')->get('enabled') && !$target_account->hasPermission('access site in maintenance mode')) {
return t('@user is not permitted to %permission. <a href=":maintenance-url">Disable maintenance mode</a> to masquerade as @user.', [
Andrey Postnikov
committed
'@user' => $target_account->getDisplayName(),
'%permission' => t('Use the site in maintenance mode'),
':maintenance-url' => Url::fromRoute('system.site_maintenance_mode')->toString(),
]);
Daniel Kudwien
committed
}
David Norman
committed
if (!masquerade_target_user_access($target_account)) {
return t('You are not allowed to masquerade as %name.', [
'%name' => $target_account->getDisplayName(),
]);
Daniel Kudwien
committed
}
/**
* Implements hook_migration_plugins_alter().
*
* Names of masquerade permissions changed between Drupal 7 and 8|9. Map the
* permission names respectively.
*/
function masquerade_migration_plugins_alter(array &$migrations) {
if (
!empty($migrations['d7_user_role']['process']['permissions'][0]) &&
$migrations['d7_user_role']['process']['permissions'][0]['plugin'] === 'static_map'
) {
$migrations['d7_user_role']['process']['permissions'][0]['map']['masquerade as admin'] = 'masquerade as super user';
}
}