Merge pull request #46418 from nextcloud/artonge/feat/user_admin_delegation

feat(users): Add users and group management to admin delegation
pull/46689/head
Louis 2024-07-24 11:15:54 +07:00 committed by GitHub
commit 7266a9ef33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 297 additions and 135 deletions

@ -99,7 +99,9 @@ abstract class AUserData extends OCSController {
}
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if ($isAdmin
|| $isDelegatedAdmin
|| $this->groupManager->getSubAdmin()->isUserAccessible($currentLoggedInUser, $targetUserObject)) {
$data['enabled'] = $this->config->getUserValue($targetUserObject->getUID(), 'core', 'enabled', 'true') === 'true';
} else {
@ -117,7 +119,7 @@ abstract class AUserData extends OCSController {
$gids[] = $group->getGID();
}
if ($isAdmin) {
if ($isAdmin || $isDelegatedAdmin) {
try {
# might be thrown by LDAP due to handling of users disappears
# from the external source (reasons unknown to us)

@ -9,8 +9,10 @@ declare(strict_types=1);
namespace OCA\Provisioning_API\Controller;
use OCA\Provisioning_API\ResponseDefinitions;
use OCA\Settings\Settings\Admin\Users;
use OCP\Accounts\IAccountManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
@ -154,8 +156,9 @@ class GroupsController extends AUserData {
}
// Check subadmin has access to this group
if ($this->groupManager->isAdmin($user->getUID())
|| $isSubadminOfGroup) {
$isAdmin = $this->groupManager->isAdmin($user->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
if ($isAdmin || $isDelegatedAdmin || $isSubadminOfGroup) {
$users = $this->groupManager->get($groupId)->getUsers();
$users = array_map(function ($user) {
/** @var IUser $user */
@ -197,7 +200,9 @@ class GroupsController extends AUserData {
}
// Check subadmin has access to this group
if ($this->groupManager->isAdmin($currentUser->getUID()) || $isSubadminOfGroup) {
$isAdmin = $this->groupManager->isAdmin($currentUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentUser->getUID());
if ($isAdmin || $isDelegatedAdmin || $isSubadminOfGroup) {
$users = $group->searchUsers($search, $limit, $offset);
// Extract required number
@ -237,6 +242,7 @@ class GroupsController extends AUserData {
*
* 200: Group created successfully
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function addGroup(string $groupid, string $displayname = ''): DataResponse {
// Validate name
if (empty($groupid)) {
@ -270,6 +276,7 @@ class GroupsController extends AUserData {
*
* 200: Group updated successfully
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function updateGroup(string $groupId, string $key, string $value): DataResponse {
$groupId = urldecode($groupId);
@ -299,6 +306,7 @@ class GroupsController extends AUserData {
*
* 200: Group deleted successfully
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function deleteGroup(string $groupId): DataResponse {
$groupId = urldecode($groupId);
@ -322,6 +330,7 @@ class GroupsController extends AUserData {
*
* 200: Sub admins returned
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function getSubAdminsOfGroup(string $groupId): DataResponse {
// Check group exists
$targetGroup = $this->groupManager->get($groupId);

@ -16,10 +16,12 @@ use OC\KnownUser\KnownUserService;
use OC\User\Backend;
use OCA\Provisioning_API\ResponseDefinitions;
use OCA\Settings\Mailer\NewUserMailHelper;
use OCA\Settings\Settings\Admin\Users;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\IAccountProperty;
use OCP\Accounts\PropertyDoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
@ -101,7 +103,9 @@ class UsersController extends AUserData {
// Admin? Or SubAdmin?
$uid = $user->getUID();
$subAdminManager = $this->groupManager->getSubAdmin();
if ($this->groupManager->isAdmin($uid)) {
$isAdmin = $this->groupManager->isAdmin($uid);
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
if ($isAdmin || $isDelegatedAdmin) {
$users = $this->userManager->search($search, $limit, $offset);
} elseif ($subAdminManager->isSubAdmin($user)) {
$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
@ -142,7 +146,9 @@ class UsersController extends AUserData {
// Admin? Or SubAdmin?
$uid = $currentUser->getUID();
$subAdminManager = $this->groupManager->getSubAdmin();
if ($this->groupManager->isAdmin($uid)) {
$isAdmin = $this->groupManager->isAdmin($uid);
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
if ($isAdmin || $isDelegatedAdmin) {
$users = $this->userManager->search($search, $limit, $offset);
$users = array_keys($users);
} elseif ($subAdminManager->isSubAdmin($currentUser)) {
@ -213,7 +219,9 @@ class UsersController extends AUserData {
// Admin? Or SubAdmin?
$uid = $currentUser->getUID();
$subAdminManager = $this->groupManager->getSubAdmin();
if ($this->groupManager->isAdmin($uid)) {
$isAdmin = $this->groupManager->isAdmin($uid);
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
if ($isAdmin || $isDelegatedAdmin) {
$users = $this->userManager->getDisabledUsers($limit, $offset, $search);
$users = array_map(fn (IUser $user): string => $user->getUID(), $users);
} elseif ($subAdminManager->isSubAdmin($currentUser)) {
@ -275,6 +283,7 @@ class UsersController extends AUserData {
*
* 200: Users details returned based on last logged in information
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function getLastLoggedInUsers(string $search = '',
?int $limit = null,
int $offset = 0,
@ -447,6 +456,7 @@ class UsersController extends AUserData {
): DataResponse {
$user = $this->userSession->getUser();
$isAdmin = $this->groupManager->isAdmin($user->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
$subAdminManager = $this->groupManager->getSubAdmin();
if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
@ -463,12 +473,12 @@ class UsersController extends AUserData {
if (!$this->groupManager->groupExists($group)) {
throw new OCSException($this->l10n->t('Group %1$s does not exist', [$group]), 104);
}
if (!$isAdmin && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
if (!$isAdmin && !($isDelegatedAdmin && $group !== 'admin') && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
throw new OCSException($this->l10n->t('Insufficient privileges for group %1$s', [$group]), 105);
}
}
} else {
if (!$isAdmin) {
if (!$isAdmin && !$isDelegatedAdmin) {
throw new OCSException($this->l10n->t('No group specified (required for sub-admins)'), 106);
}
}
@ -486,7 +496,7 @@ class UsersController extends AUserData {
throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
}
// Check if has permission to promote subadmins
if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin) {
if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin && !$isDelegatedAdmin) {
throw new OCSForbiddenException($this->l10n->t('No permissions to promote sub-admins'));
}
$subadminGroups[] = $group;
@ -718,8 +728,10 @@ class UsersController extends AUserData {
}
$subAdminManager = $this->groupManager->getSubAdmin();
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if (
!$this->groupManager->isAdmin($currentLoggedInUser->getUID())
!($isAdmin || $isDelegatedAdmin)
&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
@ -788,6 +800,7 @@ class UsersController extends AUserData {
}
$subAdminManager = $this->groupManager->getSubAdmin();
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
$isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
@ -798,7 +811,7 @@ class UsersController extends AUserData {
$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
} else {
// Check if admin / subadmin
if ($isAdminOrSubadmin) {
if ($isAdminOrSubadmin || $isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) {
// They have permissions over the user
$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
} else {
@ -903,14 +916,16 @@ class UsersController extends AUserData {
$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
if (
$this->config->getSystemValue('force_language', false) === false ||
$this->groupManager->isAdmin($currentLoggedInUser->getUID())
$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
) {
$permittedFields[] = self::USER_FIELD_LANGUAGE;
}
if (
$this->config->getSystemValue('force_locale', false) === false ||
$this->groupManager->isAdmin($currentLoggedInUser->getUID())
$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
) {
$permittedFields[] = self::USER_FIELD_LOCALE;
$permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
@ -942,7 +957,9 @@ class UsersController extends AUserData {
$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
// If admin they can edit their own quota and manager
if ($this->groupManager->isAdmin($currentLoggedInUser->getUID())) {
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if ($isAdmin || $isDelegatedAdmin) {
$permittedFields[] = self::USER_FIELD_QUOTA;
$permittedFields[] = self::USER_FIELD_MANAGER;
}
@ -950,7 +967,8 @@ class UsersController extends AUserData {
// Check if admin / subadmin
$subAdminManager = $this->groupManager->getSubAdmin();
if (
$this->groupManager->isAdmin($currentLoggedInUser->getUID())
$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID()) && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')
|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
) {
// They have permissions over the user
@ -1217,7 +1235,9 @@ class UsersController extends AUserData {
// If not permitted
$subAdminManager = $this->groupManager->getSubAdmin();
if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
@ -1253,7 +1273,9 @@ class UsersController extends AUserData {
// If not permitted
$subAdminManager = $this->groupManager->getSubAdmin();
if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
@ -1313,7 +1335,9 @@ class UsersController extends AUserData {
// If not permitted
$subAdminManager = $this->groupManager->getSubAdmin();
if (!$this->groupManager->isAdmin($currentLoggedInUser->getUID()) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
@ -1342,7 +1366,9 @@ class UsersController extends AUserData {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
if ($targetUser->getUID() === $loggedInUser->getUID() || $this->groupManager->isAdmin($loggedInUser->getUID())) {
$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
// Self lookup or admin lookup
return new DataResponse([
'groups' => $this->groupManager->getUserGroupIds($targetUser)
@ -1401,7 +1427,9 @@ class UsersController extends AUserData {
// If they're not an admin, check they are a subadmin of the group in question
$loggedInUser = $this->userSession->getUser();
$subAdminManager = $this->groupManager->getSubAdmin();
if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
throw new OCSException('', 104);
}
@ -1442,13 +1470,15 @@ class UsersController extends AUserData {
// If they're not an admin, check they are a subadmin of the group in question
$subAdminManager = $this->groupManager->getSubAdmin();
if (!$this->groupManager->isAdmin($loggedInUser->getUID()) && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
throw new OCSException('', 104);
}
// Check they aren't removing themselves from 'admin' or their 'subadmin; group
if ($targetUser->getUID() === $loggedInUser->getUID()) {
if ($this->groupManager->isAdmin($loggedInUser->getUID())) {
if ($isAdmin || $isDelegatedAdmin) {
if ($group->getGID() === 'admin') {
throw new OCSException($this->l10n->t('Cannot remove yourself from the admin group'), 105);
}
@ -1456,7 +1486,7 @@ class UsersController extends AUserData {
// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
throw new OCSException($this->l10n->t('Cannot remove yourself from this group as you are a sub-admin'), 105);
}
} elseif (!$this->groupManager->isAdmin($loggedInUser->getUID())) {
} elseif (!($isAdmin || $isDelegatedAdmin)) {
/** @var IGroup[] $subAdminGroups */
$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
@ -1488,6 +1518,7 @@ class UsersController extends AUserData {
*
* 200: User added as group subadmin successfully
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function addSubAdmin(string $userId, string $groupid): DataResponse {
$group = $this->groupManager->get($groupid);
$user = $this->userManager->get($userId);
@ -1528,6 +1559,7 @@ class UsersController extends AUserData {
*
* 200: User removed as group subadmin successfully
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function removeSubAdmin(string $userId, string $groupid): DataResponse {
$group = $this->groupManager->get($groupid);
$user = $this->userManager->get($userId);
@ -1560,6 +1592,7 @@ class UsersController extends AUserData {
*
* 200: User subadmin groups returned
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function getUserSubAdminGroups(string $userId): DataResponse {
$groups = $this->getUserSubAdminGroupsData($userId);
return new DataResponse($groups);
@ -1587,9 +1620,11 @@ class UsersController extends AUserData {
// Check if admin / subadmin
$subAdminManager = $this->groupManager->getSubAdmin();
$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
if (
!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
&& !$this->groupManager->isAdmin($currentLoggedInUser->getUID())
&& !($isAdmin || $isDelegatedAdmin)
) {
// No rights
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);

@ -45,13 +45,17 @@ class AppsControllerTest extends \OCA\Provisioning_API\Tests\TestCase {
);
}
protected function tearDown(): void {
$this->userSession->setUser(null);
}
public function testGetAppInfo() {
$result = $this->api->getAppInfo('provisioning_api');
$expected = $this->appManager->getAppInfo('provisioning_api');
$this->assertEquals($expected, $result->getData());
}
public function testGetAppInfoOnBadAppID() {
$this->expectException(\OCP\AppFramework\OCS\OCSException::class);
$this->expectExceptionCode(998);

@ -232,7 +232,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -263,7 +263,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -299,7 +299,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -344,7 +344,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -456,7 +456,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -502,7 +502,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -552,7 +552,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -595,7 +595,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -624,7 +624,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -706,7 +706,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('adminUser');
$this->userSession
@ -732,7 +732,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('regularUser');
$this->userSession
@ -765,7 +765,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('regularUser');
$this->userSession
@ -814,7 +814,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('subAdminUser');
$this->userSession
@ -931,7 +931,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('admin');
$targetUser = $this->getMockBuilder(IUser::class)
@ -1077,7 +1077,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)
@ -1223,7 +1223,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->exactly(3))
->expects($this->exactly(4))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)
@ -1263,7 +1263,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('UID');
$targetUser = $this->getMockBuilder(IUser::class)
@ -2662,7 +2662,7 @@ class UsersControllerTest extends TestCase {
public function testGetUsersGroupsSelfTargetted() {
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(3))
->method('getUID')
->willReturn('UserToLookup');
$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
@ -2691,7 +2691,7 @@ class UsersControllerTest extends TestCase {
public function testGetUsersGroupsForAdminUser() {
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('admin');
$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
@ -2725,7 +2725,7 @@ class UsersControllerTest extends TestCase {
public function testGetUsersGroupsForSubAdminUserAndUserIsAccessible() {
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
@ -2789,7 +2789,7 @@ class UsersControllerTest extends TestCase {
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
@ -2873,7 +2873,7 @@ class UsersControllerTest extends TestCase {
$targetUser = $this->createMock(IUser::class);
$loggedInUser = $this->createMock(IUser::class);
$loggedInUser->expects($this->once())
$loggedInUser->expects($this->exactly(2))
->method('getUID')
->willReturn('subadmin');
@ -2917,7 +2917,7 @@ class UsersControllerTest extends TestCase {
public function testAddToGroupSuccessAsSubadmin() {
$targetUser = $this->createMock(IUser::class);
$loggedInUser = $this->createMock(IUser::class);
$loggedInUser->expects($this->once())
$loggedInUser->expects($this->exactly(2))
->method('getUID')
->willReturn('subadmin');
@ -2961,7 +2961,7 @@ class UsersControllerTest extends TestCase {
public function testAddToGroupSuccessAsAdmin() {
$targetUser = $this->createMock(IUser::class);
$loggedInUser = $this->createMock(IUser::class);
$loggedInUser->expects($this->once())
$loggedInUser->expects($this->exactly(2))
->method('getUID')
->willReturn('admin');
@ -3079,7 +3079,7 @@ class UsersControllerTest extends TestCase {
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->once())
->expects($this->exactly(2))
->method('getUID')
->willReturn('unauthorizedUser');
$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
@ -3600,7 +3600,7 @@ class UsersControllerTest extends TestCase {
->willReturn($targetUser);
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('admin');
$this->userSession
@ -3627,7 +3627,7 @@ class UsersControllerTest extends TestCase {
->willReturn($targetUser);
$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
$loggedInUser
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('getUID')
->willReturn('admin');
$this->userSession
@ -3814,7 +3814,7 @@ class UsersControllerTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->exactly(1))
->expects($this->exactly(2))
->method('getUID')
->willReturn('subadmin');
$targetUser = $this->getMockBuilder(IUser::class)
@ -3883,6 +3883,10 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getSubAdmin')
->willReturn($subAdminManager);
$loggedInUser
->expects($this->exactly(2))
->method('getUID')
->willReturn('logged-user-id');
$targetUser
->expects($this->once())
->method('getEmailAddress')
@ -3924,6 +3928,10 @@ class UsersControllerTest extends TestCase {
->expects($this->once())
->method('getSubAdmin')
->willReturn($subAdminManager);
$loggedInUser
->expects($this->exactly(2))
->method('getUID')
->willReturn('logged-user-id');
$targetUser
->expects($this->once())
->method('getEmailAddress')
@ -3939,6 +3947,9 @@ class UsersControllerTest extends TestCase {
$targetUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
->getMock();
$loggedInUser
->method('getUID')
->willReturn('logged-user-id');
$targetUser
->method('getUID')
->willReturn('user-id');
@ -3987,6 +3998,9 @@ class UsersControllerTest extends TestCase {
$targetUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
->getMock();
$loggedInUser
->method('getUID')
->willReturn('logged-user-id');
$targetUser
->method('getUID')
->willReturn('user-id');
@ -4040,6 +4054,10 @@ class UsersControllerTest extends TestCase {
$targetUser = $this->getMockBuilder(IUser::class)
->disableOriginalConstructor()
->getMock();
$loggedInUser
->expects($this->exactly(2))
->method('getUID')
->willReturn('logged-user-id');
$targetUser
->method('getUID')
->willReturn('user-id');

@ -34,6 +34,7 @@
<admin>OCA\Settings\Settings\Admin\Sharing</admin>
<admin>OCA\Settings\Settings\Admin\Security</admin>
<admin>OCA\Settings\Settings\Admin\Delegation</admin>
<admin>OCA\Settings\Settings\Admin\Users</admin>
<admin-section>OCA\Settings\Sections\Admin\Additional</admin-section>
<admin-section>OCA\Settings\Sections\Admin\Delegation</admin-section>
<admin-section>OCA\Settings\Sections\Admin\Groupware</admin-section>

@ -71,6 +71,7 @@ return array(
'OCA\\Settings\\Settings\\Admin\\Security' => $baseDir . '/../lib/Settings/Admin/Security.php',
'OCA\\Settings\\Settings\\Admin\\Server' => $baseDir . '/../lib/Settings/Admin/Server.php',
'OCA\\Settings\\Settings\\Admin\\Sharing' => $baseDir . '/../lib/Settings/Admin/Sharing.php',
'OCA\\Settings\\Settings\\Admin\\Users' => $baseDir . '/../lib/Settings/Admin/Users.php',
'OCA\\Settings\\Settings\\Personal\\Additional' => $baseDir . '/../lib/Settings/Personal/Additional.php',
'OCA\\Settings\\Settings\\Personal\\PersonalInfo' => $baseDir . '/../lib/Settings/Personal/PersonalInfo.php',
'OCA\\Settings\\Settings\\Personal\\Security\\Authtokens' => $baseDir . '/../lib/Settings/Personal/Security/Authtokens.php',

@ -86,6 +86,7 @@ class ComposerStaticInitSettings
'OCA\\Settings\\Settings\\Admin\\Security' => __DIR__ . '/..' . '/../lib/Settings/Admin/Security.php',
'OCA\\Settings\\Settings\\Admin\\Server' => __DIR__ . '/..' . '/../lib/Settings/Admin/Server.php',
'OCA\\Settings\\Settings\\Admin\\Sharing' => __DIR__ . '/..' . '/../lib/Settings/Admin/Sharing.php',
'OCA\\Settings\\Settings\\Admin\\Users' => __DIR__ . '/..' . '/../lib/Settings/Admin/Users.php',
'OCA\\Settings\\Settings\\Personal\\Additional' => __DIR__ . '/..' . '/../lib/Settings/Personal/Additional.php',
'OCA\\Settings\\Settings\\Personal\\PersonalInfo' => __DIR__ . '/..' . '/../lib/Settings/Personal/PersonalInfo.php',
'OCA\\Settings\\Settings\\Personal\\Security\\Authtokens' => __DIR__ . '/..' . '/../lib/Settings/Personal/Security/Authtokens.php',

@ -19,12 +19,14 @@ use OC\Security\IdentityProof\Manager;
use OC\User\Manager as UserManager;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCA\Settings\Events\BeforeTemplateRenderedEvent;
use OCA\Settings\Settings\Admin\Users;
use OCA\User_LDAP\User_Proxy;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
use OCP\Accounts\PropertyDoesNotExistException;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
@ -93,6 +95,7 @@ class UsersController extends Controller {
$user = $this->userSession->getUser();
$uid = $user->getUID();
$isAdmin = $this->groupManager->isAdmin($uid);
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
\OC::$server->getNavigationManager()->setActiveEntry('core_users');
@ -118,6 +121,7 @@ class UsersController extends Controller {
$groupsInfo = new \OC\Group\MetaData(
$uid,
$isAdmin,
$isDelegatedAdmin,
$this->groupManager,
$this->userSession
);
@ -135,7 +139,7 @@ class UsersController extends Controller {
$userCount = 0;
if (!$isLDAPUsed) {
if ($isAdmin) {
if ($isAdmin || $isDelegatedAdmin) {
$disabledUsers = $this->userManager->countDisabledUsers();
$userCount = array_reduce($this->userManager->countUsers(), function ($v, $w) {
return $v + (int)$w;
@ -201,6 +205,7 @@ class UsersController extends Controller {
$serverData['groups'] = array_merge_recursive($adminGroup, [$recentUsersGroup, $disabledUsersGroup], $groups);
// Various data
$serverData['isAdmin'] = $isAdmin;
$serverData['isDelegatedAdmin'] = $isDelegatedAdmin;
$serverData['sortGroups'] = $forceSortGroupByName
? \OC\Group\MetaData::SORT_GROUPNAME
: (int)$this->config->getAppValue('core', 'group.sortBy', (string)\OC\Group\MetaData::SORT_USERCOUNT);
@ -232,6 +237,7 @@ class UsersController extends Controller {
*
* @return JSONResponse
*/
#[AuthorizedAdminSetting(settings:Users::class)]
public function setPreference(string $key, string $value): JSONResponse {
$allowed = ['newUser.sendEmail', 'group.sortBy'];
if (!in_array($key, $allowed, true)) {

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Settings\Settings\Admin;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\Settings\IDelegatedSettings;
/**
* Empty settings class, used only for admin delegation.
*/
class Users implements IDelegatedSettings {
public function __construct(
protected string $appName,
private IL10N $l10n,
) {
}
/**
* Empty template response
*/
public function getForm(): TemplateResponse {
return new /** @template-extends TemplateResponse<\OCP\AppFramework\Http::STATUS_OK, array{}> */ class($this->appName, '') extends TemplateResponse {
public function render(): string {
return '';
}
};
}
public function getSection(): ?string {
return 'admindelegation';
}
/**
* @return int whether the form should be rather on the top or bottom of
* the admin section. The forms are arranged in ascending order of the
* priority values. It is required to return a value between 0 and 100.
*
* E.g.: 70
*/
public function getPriority(): int {
return 0;
}
public function getName(): string {
return $this->l10n->t('Users');
}
public function getAuthorizedAppConfig(): array {
return [];
}
}

@ -45,7 +45,7 @@
</NcCounterBubble>
</template>
<template #actions>
<NcActionInput v-if="id !== 'admin' && id !== 'disabled' && settings.isAdmin"
<NcActionInput v-if="id !== 'admin' && id !== 'disabled' && (settings.isAdmin || settings.isDelegatedAdmin)"
ref="displayNameInput"
:trailing-button-label="t('settings', 'Submit')"
type="text"
@ -56,7 +56,7 @@
<Pencil :size="20" />
</template>
</NcActionInput>
<NcActionButton v-if="id !== 'admin' && id !== 'disabled' && settings.isAdmin"
<NcActionButton v-if="id !== 'admin' && id !== 'disabled' && (settings.isAdmin || settings.isDelegatedAdmin)"
@click="showRemoveGroupModal = true">
<template #icon>
<Delete :size="20" />

@ -169,10 +169,6 @@ export default {
if (this.selectedGroup === 'disabled') {
return this.users.filter(user => user.enabled === false)
}
if (!this.settings.isAdmin) {
// we don't want subadmins to edit themselves
return this.users.filter(user => user.enabled !== false)
}
return this.users.filter(user => user.enabled !== false)
},

@ -61,7 +61,7 @@
:required="newUser.password === '' || settings.newUserRequireEmail" />
<div class="dialog__item">
<NcSelect class="dialog__select"
:input-label="!settings.isAdmin ? t('settings', 'Member of the following groups (required)') : t('settings', 'Member of the following groups')"
:input-label="!settings.isAdmin && !settings.isDelegatedAdmin ? t('settings', 'Member of the following groups (required)') : t('settings', 'Member of the following groups')"
:placeholder="t('settings', 'Set account groups')"
:disabled="loading.groups || loading.all"
:options="canAddGroups"
@ -70,7 +70,7 @@
:close-on-select="false"
:multiple="true"
:taggable="true"
:required="!settings.isAdmin"
:required="!settings.isAdmin && !settings.isDelegatedAdmin"
@input="handleGroupInput"
@option:created="createGroup" />
<!-- If user is not admin, he is a subadmin.

@ -42,7 +42,7 @@
scope="col">
<span>{{ t('settings', 'Groups') }}</span>
</th>
<th v-if="subAdminsGroups.length > 0 && settings.isAdmin"
<th v-if="subAdminsGroups.length > 0 && (settings.isAdmin || settings.isDelegatedAdmin)"
class="header__cell header__cell--large"
data-cy-user-list-header-subadmins
scope="col">

@ -112,7 +112,7 @@
:append-to-body="false"
:options="availableGroups"
:placeholder="t('settings', 'Add account to group')"
:taggable="settings.isAdmin"
:taggable="settings.isAdmin || settings.isDelegatedAdmin"
:value="userGroups"
label="name"
:no-wrap="true"
@ -127,10 +127,10 @@
</span>
</td>
<td v-if="subAdminsGroups.length > 0 && settings.isAdmin"
<td v-if="subAdminsGroups.length > 0 && (settings.isAdmin || settings.isDelegatedAdmin)"
data-cy-user-list-cell-subadmins
class="row__cell row__cell--large row__cell--multiline">
<template v-if="editing && settings.isAdmin && subAdminsGroups.length > 0">
<template v-if="editing && (settings.isAdmin || settings.isDelegatedAdmin) && subAdminsGroups.length > 0">
<label class="hidden-visually"
:for="'subadmins' + uniqueId">
{{ t('settings', 'Set account as admin for') }}
@ -424,7 +424,7 @@ export default {
},
canEdit() {
return getCurrentUser().uid !== this.user.id || this.settings.isAdmin
return getCurrentUser().uid !== this.user.id || this.settings.isAdmin || this.settings.isDelegatedAdmin
},
userQuota() {
@ -624,18 +624,21 @@ export default {
*
* @param {string} displayName The display name
*/
updateDisplayName() {
async updateDisplayName() {
this.loading.displayName = true
this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'displayname',
value: this.editedDisplayName,
}).then(() => {
this.loading.displayName = false
try {
await this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'displayname',
value: this.editedDisplayName,
})
if (this.editedDisplayName === this.user.displayname) {
showSuccess(t('setting', 'Display name was successfully changed'))
}
})
} finally {
this.loading.displayName = false
}
},
/**
@ -643,21 +646,23 @@ export default {
*
* @param {string} password The email address
*/
updatePassword() {
async updatePassword() {
this.loading.password = true
if (this.editedPassword.length === 0) {
showError(t('setting', "Password can't be empty"))
this.loading.password = false
} else {
this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'password',
value: this.editedPassword,
}).then(() => {
this.loading.password = false
try {
await this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'password',
value: this.editedPassword,
})
this.editedPassword = ''
showSuccess(t('setting', 'Password was successfully changed'))
})
} finally {
this.loading.password = false
}
}
},
@ -666,23 +671,26 @@ export default {
*
* @param {string} mailAddress The email address
*/
updateEmail() {
async updateEmail() {
this.loading.mailAddress = true
if (this.editedMail === '') {
showError(t('setting', "Email can't be empty"))
this.loading.mailAddress = false
this.editedMail = this.user.email
} else {
this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'email',
value: this.editedMail,
}).then(() => {
this.loading.mailAddress = false
try {
await this.$store.dispatch('setUserData', {
userid: this.user.id,
key: 'email',
value: this.editedMail,
})
if (this.editedMail === this.user.email) {
showSuccess(t('setting', 'Email was successfully changed'))
}
})
} finally {
this.loading.mailAddress = false
}
}
},

@ -641,11 +641,14 @@ const actions = {
* @param {string} userid User id
* @return {Promise}
*/
wipeUserDevices(context, userid) {
return api.requireAdmin().then((response) => {
return api.post(generateOcsUrl('cloud/users/{userid}/wipe', { userid }))
.catch((error) => { throw error })
}).catch((error) => context.commit('API_FAILURE', { userid, error }))
async wipeUserDevices(context, userid) {
try {
await api.requireAdmin()
return await api.post(generateOcsUrl('cloud/users/{userid}/wipe', { userid }))
} catch (error) {
context.commit('API_FAILURE', { userid, error })
return Promise.reject(new Error('Failed to wipe user devices'))
}
},
/**
@ -735,7 +738,7 @@ const actions = {
* @param {string} options.value Value of the change
* @return {Promise}
*/
setUserData(context, { userid, key, value }) {
async setUserData(context, { userid, key, value }) {
const allowedEmpty = ['email', 'displayname', 'manager']
if (['email', 'language', 'quota', 'displayname', 'password', 'manager'].indexOf(key) !== -1) {
// We allow empty email or displayname
@ -745,11 +748,13 @@ const actions = {
|| allowedEmpty.indexOf(key) !== -1
)
) {
return api.requireAdmin().then((response) => {
return api.put(generateOcsUrl('cloud/users/{userid}', { userid }), { key, value })
.then((response) => context.commit('setUserData', { userid, key, value }))
.catch((error) => { throw error })
}).catch((error) => context.commit('API_FAILURE', { userid, error }))
try {
await api.requireAdmin()
await api.put(generateOcsUrl('cloud/users/{userid}', { userid }), { key, value })
return context.commit('setUserData', { userid, key, value })
} catch (error) {
context.commit('API_FAILURE', { userid, error })
}
}
}
return Promise.reject(new Error('Invalid request data'))

@ -81,6 +81,8 @@ class AdminSettingsControllerTest extends TestCase {
protected function tearDown(): void {
\OC::$server->getUserManager()->get($this->adminUid)->delete();
\OC_User::setUserId(null);
\OC::$server->getUserSession()->setUser(null);
parent::tearDown();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@
namespace OC\Group;
use OC\Hooks\PublicEmitter;
use OC\Settings\AuthorizedGroupMapper;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Backend\IBatchMethodsBackend;
use OCP\Group\Backend\ICreateNamedGroupBackend;
@ -333,6 +334,18 @@ class Manager extends PublicEmitter implements IGroupManager {
return $this->isInGroup($userId, 'admin');
}
public function isDelegatedAdmin(string $userId): bool {
if (!$this->remoteAddress->allowsAdminActions()) {
return false;
}
// Check if the user as admin delegation for users listing
$authorizedGroupMapper = \OCP\Server::get(AuthorizedGroupMapper::class);
$user = $this->userManager->get($userId);
$authorizedClasses = $authorizedGroupMapper->findAllClassesForUser($user);
return in_array(\OCA\Settings\Settings\Admin\Users::class, $authorizedClasses, true);
}
/**
* Checks if a userId is in a group
*

@ -17,33 +17,22 @@ class MetaData {
public const SORT_USERCOUNT = 1; // May have performance issues on LDAP backends
public const SORT_GROUPNAME = 2;
/** @var string */
protected $user;
/** @var bool */
protected $isAdmin;
/** @var array */
protected $metaData = [];
/** @var GroupManager */
protected $groupManager;
/** @var int */
protected $sorting = self::SORT_NONE;
/** @var IUserSession */
protected $userSession;
/**
* @param string $user the uid of the current user
* @param bool $isAdmin whether the current users is an admin
*/
public function __construct(
string $user,
bool $isAdmin,
IGroupManager $groupManager,
IUserSession $userSession
private string $user,
private bool $isAdmin,
private bool $isDelegatedAdmin,
private IGroupManager $groupManager,
private IUserSession $userSession
) {
$this->user = $user;
$this->isAdmin = $isAdmin;
$this->groupManager = $groupManager;
$this->userSession = $userSession;
}
/**
@ -162,11 +151,11 @@ class MetaData {
* @return IGroup[]
*/
public function getGroups(string $search = ''): array {
if ($this->isAdmin) {
if ($this->isAdmin || $this->isDelegatedAdmin) {
return $this->groupManager->search($search);
} else {
$userObject = $this->userSession->getUser();
if ($userObject !== null) {
if ($userObject !== null && $this->groupManager instanceof GroupManager) {
$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($userObject);
} else {
$groups = [];

@ -233,6 +233,11 @@ class SubAdmin extends PublicEmitter implements ISubAdmin {
return true;
}
// Check if the user is already an admin
if ($this->groupManager->isDelegatedAdmin($user->getUID())) {
return true;
}
$qb = $this->dbConn->getQueryBuilder();
$result = $qb->select('gid')

@ -114,6 +114,14 @@ interface IGroupManager {
*/
public function isAdmin($userId);
/**
* Checks if a userId is eligible to users administration delegation
* @param string $userId
* @return bool if delegated admin
* @since 30.0.0
*/
public function isDelegatedAdmin(string $userId): bool;
/**
* Checks if a userId is in a group
* @param string $userId

@ -10,14 +10,11 @@ namespace Test\Group;
use OCP\IUserSession;
class MetaDataTest extends \Test\TestCase {
/** @var \OC\Group\Manager */
private $groupManager;
/** @var \OCP\IUserSession */
private $userSession;
/** @var \OC\Group\MetaData */
private $groupMetadata;
/** @var bool */
private $isAdmin = true;
private \OC\Group\Manager $groupManager;
private IUserSession $userSession;
private \OC\Group\MetaData $groupMetadata;
private bool $isAdmin = true;
private bool $isDelegatedAdmin = true;
protected function setUp(): void {
parent::setUp();
@ -28,6 +25,7 @@ class MetaDataTest extends \Test\TestCase {
$this->groupMetadata = new \OC\Group\MetaData(
'foo',
$this->isAdmin,
$this->isDelegatedAdmin,
$this->groupManager,
$this->userSession
);