You are here

masquerade.module in Masquerade 8.2

Same filename and directory in other branches
  1. 5 masquerade.module
  2. 6 masquerade.module
  3. 7 masquerade.module

Allows privileged users to masquerade as another user.

File

masquerade.module
View source
<?php

/**
 * @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':
      $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>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Granting masquerade access') . '</dt>';
      $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>';
      $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>';
      $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>';
      $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() {
  $items = [
    'masquerade_switch_back' => [
      '#cache' => [
        'contexts' => [
          'session.is_masquerading',
        ],
      ],
    ],
  ];
  if (\Drupal::service('masquerade')
    ->isMasquerading()) {
    $items['masquerade_switch_back'] += [
      '#type' => 'toolbar_item',
      'tab' => [
        '#type' => 'link',
        '#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.
 *
 * @param \Drupal\user\UserInterface $target_account
 *   The user account object to masquerade as.
 *
 * @return bool
 *   TRUE if allowed, FALSE otherwise.
 *
 * @see hook_masquerade_access()
 */
function masquerade_target_user_access(UserInterface $target_account) {
  $user = \Drupal::currentUser();

  // 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.
  $access = NULL;
  foreach (\Drupal::moduleHandler()
    ->getImplementations('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().
 *
 * 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) {

  // Uid 1 may masquerade as anyone.
  if ($user
    ->id() == 1) {
    return TRUE;
  }

  // Uid 1 gets special treatment with its own permission.
  if ($target_account
    ->id() == 1) {
    if ($user
      ->hasPermission('masquerade as super user')) {
      return TRUE;
    }
    else {
      return NULL;
    }
  }

  // The current user must be allowed to masquerade.
  if ($user
    ->hasPermission('masquerade as any user')) {
    return TRUE;
  }

  // Permissions may be granted on a per-role basis.
  $target_account_roles = $target_account
    ->getRoles();
  foreach ($target_account_roles as $role_id) {
    if (!$user
      ->hasPermission("masquerade as {$role_id}")) {
      return NULL;
    }
  }

  // Only allow masquerade if a user has access to all the target account roles.
  return TRUE;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function masquerade_entity_extra_field_info() {
  $fields['user']['user']['display']['masquerade'] = [
    'label' => t('Masquerade'),
    'description' => t('Masquerade as user link.'),
    'weight' => 50,
  ];
  return $fields;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
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' => [
        '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,
        'url' => $entity
          ->toUrl('masquerade'),
      ];
    }
  }
  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
 *   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) {
  $user = \Drupal::currentUser();
  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(),
    ]);
  }
  if ($target_account
    ->id() == $user
    ->id()) {
    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.', [
      '@user' => $target_account
        ->getDisplayName(),
      '%permission' => t('Use the site in maintenance mode'),
      ':maintenance-url' => Url::fromRoute('system.site_maintenance_mode')
        ->toString(),
    ]);
  }
  if (!masquerade_target_user_access($target_account)) {
    return t('You are not allowed to masquerade as %name.', [
      '%name' => $target_account
        ->getDisplayName(),
    ]);
  }
}

Functions

Namesort descending Description
masquerade_entity_extra_field_info Implements hook_entity_extra_field_info().
masquerade_entity_operation Implements hook_entity_operation().
masquerade_entity_type_alter Implements hook_entity_type_alter().
masquerade_help Implements hook_help().
masquerade_masquerade_access Implements hook_masquerade_access().
masquerade_switch_user_validate Validates whether the current user can masquerade as a given target user.
masquerade_target_user_access Returns whether the current user is allowed to masquerade as a target user.
masquerade_toolbar Implements hook_toolbar().
masquerade_user_view Implements hook_ENTITY_TYPE_view().