Skip to content

Commit

Permalink
Merge branch 'feature/impersonateUser-setting' into 3.2
Browse files Browse the repository at this point in the history
Resolves #3501
  • Loading branch information
brandonkelly committed Jun 17, 2019
2 parents b82b3e4 + a01397e commit f186071
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
## Unreleased (3.2)

### Added
- Added the “Impersonate users” permission. ([#3501](https://github.com/craftcms/cms/issues/3501))
- Added `craft\base\ElementInterface::getIsDraft()`.
- Added `craft\base\ElementInterface::getIsRevision()`.
- Added `craft\services\Users::canImpersonate()`.

### Fixed
- Fixed a bug where entry drafts weren’t getting updated slug values once their initial slug had been saved, if their entry type had a custom title format. ([#4373](https://github.com/craftcms/cms/issues/4373))
Expand Down
11 changes: 9 additions & 2 deletions src/controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,11 @@ public function actionLogin()
* Logs a user in for impersonation. Requires you to be an administrator.
*
* @return Response|null
* @throws ForbiddenHttpException
*/
public function actionImpersonate()
{
$this->requireLogin();
$this->requireAdmin(false);
$this->requirePostRequest();

$userSession = Craft::$app->getUser();
Expand All @@ -189,6 +189,13 @@ public function actionImpersonate()

$userId = $request->getBodyParam('userId');

// Make sure they're allowed to impersonate this user
$usersService = Craft::$app->getUsers();
$impersonatee = $usersService->getUserById($userId);
if (!$usersService->canImpersonate($userSession->getIdentity(), $impersonatee)) {
throw new ForbiddenHttpException('You do not have sufficient permissions to impersonate this user');
}

// Save the original user ID to the session now so User::findIdentity()
// knows not to worry if the user isn't active yet
$session->set(User::IMPERSONATE_KEY, $userSession->getId());
Expand Down Expand Up @@ -697,7 +704,7 @@ public function actionEditUser($userId = null, User $user = null, array $errors
}

if (!$isCurrentUser) {
if ($userSession->getIsAdmin()) {
if (Craft::$app->getUsers()->canImpersonate($currentUser, $user)) {
$sessionActions[] = [
'action' => 'users/impersonate',
'label' => Craft::t('app', 'Login as {user}', ['user' => $user->getName()])
Expand Down
3 changes: 3 additions & 0 deletions src/services/UserPermissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ public function getAllPermissions(): array
'info' => Craft::t('app', 'Includes activating user accounts, resetting passwords, and changing email addresses.'),
'warning' => Craft::t('app', 'Accounts with this permission could use it to escalate their own permissions.'),
],
'impersonateUsers' => [
'label' => Craft::t('app', 'Impersonate users'),
],
],
],
'deleteUsers' => [
Expand Down
40 changes: 39 additions & 1 deletion src/services/Users.php
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,44 @@ public function saveLayout(FieldLayout $layout)
return true;
}

/**
* Returns whether a user is allowed to impersonate another user.
*
* @param User $impersonator
* @param User $impersonatee
* @return bool
* @since 3.2
*/
public function canImpersonate(User $impersonator, User $impersonatee): bool
{
// Admins can do whatever they want
if ($impersonator->admin) {
return true;
}

// Only admins are allowed to impersonate another admin
if ($impersonatee->admin) {
return false;
}

// impersonateUsers permission is obviously required
if (!$impersonator->can('impersonateUsers')) {
return false;
}

// Make sure the impersonator has at least all the same permissions as the impersonatee
$permissionsService = Craft::$app->getUserPermissions();
$impersonatorPermissions = array_flip($permissionsService->getPermissionsByUserId($impersonator->id));
$impersonateePermissions = $permissionsService->getPermissionsByUserId($impersonatee->id);

foreach ($impersonateePermissions as $permission) {
if (!isset($impersonatorPermissions[$permission])) {
return false;
}
}

return true;
}

/**
* Prune a deleted field from user group layout.
Expand Down Expand Up @@ -1208,4 +1246,4 @@ private function _getUserUrl(User $user, string $action): string

return UrlHelper::siteUrl($path, $params, $scheme, $siteId);
}
}
}
1 change: 0 additions & 1 deletion src/web/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ public function startElevatedSession(string $password): bool
$user = UserElement::find()
->addSelect(['users.password'])
->id($previousUserId)
->admin(true)
->one();
} else {
// Get the current user
Expand Down

0 comments on commit f186071

Please sign in to comment.