Skip to content

Commit 8e8125c

Browse files
committed
fix(settings): group admins only can add users to their groups
Signed-off-by: Ferdinand Thiessen <[email protected]>
1 parent 2fab6e7 commit 8e8125c

File tree

6 files changed

+242
-30
lines changed

6 files changed

+242
-30
lines changed

apps/settings/lib/Controller/UsersController.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@
3939
use OCP\BackgroundJob\IJobList;
4040
use OCP\Encryption\IManager;
4141
use OCP\EventDispatcher\IEventDispatcher;
42+
use OCP\Group\ISubAdmin;
4243
use OCP\IConfig;
4344
use OCP\IGroup;
4445
use OCP\IGroupManager;
4546
use OCP\IL10N;
47+
use OCP\INavigationManager;
4648
use OCP\IRequest;
4749
use OCP\IUser;
4850
use OCP\IUserSession;
@@ -85,8 +87,8 @@ public function __construct(
8587
*/
8688
#[NoAdminRequired]
8789
#[NoCSRFRequired]
88-
public function usersListByGroup(): TemplateResponse {
89-
return $this->usersList();
90+
public function usersListByGroup(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
91+
return $this->usersList($navigationManager, $subAdmin);
9092
}
9193

9294
/**
@@ -96,13 +98,13 @@ public function usersListByGroup(): TemplateResponse {
9698
*/
9799
#[NoAdminRequired]
98100
#[NoCSRFRequired]
99-
public function usersList(): TemplateResponse {
101+
public function usersList(INavigationManager $navigationManager, ISubAdmin $subAdmin): TemplateResponse {
100102
$user = $this->userSession->getUser();
101103
$uid = $user->getUID();
102104
$isAdmin = $this->groupManager->isAdmin($uid);
103105
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
104106

105-
\OC::$server->getNavigationManager()->setActiveEntry('core_users');
107+
$navigationManager->setActiveEntry('core_users');
106108

107109
/* SORT OPTION: SORT_USERCOUNT or SORT_GROUPNAME */
108110
$sortGroupsBy = \OC\Group\MetaData::SORT_USERCOUNT;
@@ -178,6 +180,14 @@ public function usersList(): TemplateResponse {
178180
'usercount' => $disabledUsers
179181
];
180182

183+
if (!$isAdmin && !$isDelegatedAdmin) {
184+
$subAdminGroups = array_map(
185+
fn (IGroup $group) => ['id' => $group->getGID(), 'name' => $group->getDisplayName()],
186+
$subAdmin->getSubAdminsGroups($user),
187+
);
188+
$subAdminGroups = array_values($subAdminGroups);
189+
}
190+
181191
/* QUOTAS PRESETS */
182192
$quotaPreset = $this->parseQuotaPreset($this->config->getAppValue('files', 'quota_preset', '1 GB, 5 GB, 10 GB'));
183193
$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
@@ -201,12 +211,7 @@ public function usersList(): TemplateResponse {
201211
$serverData = [];
202212
// groups
203213
$serverData['systemGroups'] = [$adminGroupData, $recentUsersGroup, $disabledUsersGroup];
204-
$serverData['userGroups'] = array_values(
205-
array_map(
206-
fn (IGroup $group) => ['id' => $group->getGID(), 'name' => $group->getDisplayName()],
207-
$this->groupManager->getUserGroups($user),
208-
),
209-
);
214+
$serverData['subAdminGroups'] = $subAdminGroups ?? [];
210215
// Various data
211216
$serverData['isAdmin'] = $isAdmin;
212217
$serverData['isDelegatedAdmin'] = $isDelegatedAdmin;

apps/settings/src/components/AppNavigationGroupList.vue

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,16 @@
5757
</template>
5858

5959
<script setup lang="ts">
60+
import type CancelablePromise from 'cancelable-promise'
61+
import type { IGroup } from '../views/user-types.d.ts'
62+
63+
import { mdiAccountGroup, mdiPlus } from '@mdi/js'
64+
import { showError } from '@nextcloud/dialogs'
65+
import { t } from '@nextcloud/l10n'
66+
import { useElementVisibility } from '@vueuse/core'
6067
import { computed, ref, watch, onBeforeMount } from 'vue'
6168
import { Fragment } from 'vue-frag'
6269
import { useRoute, useRouter } from 'vue-router/composables'
63-
import { useElementVisibility } from '@vueuse/core'
64-
import { showError } from '@nextcloud/dialogs'
65-
import { mdiAccountGroup, mdiPlus } from '@mdi/js'
6670
6771
import NcActionInput from '@nextcloud/vue/components/NcActionInput'
6872
import NcActionText from '@nextcloud/vue/components/NcActionText'
@@ -137,12 +141,16 @@ watch(groupsSearchQuery, async () => {
137141
})
138142
139143
/** Cancelable promise for search groups request */
140-
const promise = ref(null)
144+
const promise = ref<CancelablePromise<IGroup[]>>()
141145
142146
/**
143147
* Load groups
144148
*/
145149
async function loadGroups() {
150+
if (!isAdminOrDelegatedAdmin.value) {
151+
return
152+
}
153+
146154
if (promise.value) {
147155
promise.value.cancel()
148156
}
@@ -163,7 +171,7 @@ async function loadGroups() {
163171
} catch (error) {
164172
logger.error(t('settings', 'Failed to load groups'), { error })
165173
}
166-
promise.value = null
174+
promise.value = undefined
167175
loadingGroups.value = false
168176
}
169177

apps/settings/src/components/UserList.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,13 @@ export default {
350350
setNewUserDefaultGroup(value) {
351351
// Is no value set, but user is a line manager we set their group as this is a requirement for line manager
352352
if (!value && !this.settings.isAdmin && !this.settings.isDelegatedAdmin) {
353+
const groups = this.$store.getters.getSubAdminGroups
353354
// if there are multiple groups we do not know which to add,
354355
// so we cannot make the managers life easier by preselecting it.
355-
if (this.groups.length === 1) {
356-
value = this.groups[0].id
356+
if (groups.length === 1) {
357+
this.newUser.groups = [...groups]
357358
}
359+
return
358360
}
359361
360362
if (value) {

apps/settings/src/components/Users/NewUserDialog.vue

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
:required="newUser.password === '' || settings.newUserRequireEmail" />
6262
<div class="dialog__item">
6363
<NcSelect class="dialog__select"
64+
data-test="groups"
6465
:input-label="!settings.isAdmin && !settings.isDelegatedAdmin ? t('settings', 'Member of the following groups (required)') : t('settings', 'Member of the following groups')"
6566
:placeholder="t('settings', 'Set account groups')"
6667
:disabled="loading.groups || loading.all"
@@ -69,7 +70,7 @@
6970
label="name"
7071
:close-on-select="false"
7172
:multiple="true"
72-
:taggable="true"
73+
:taggable="settings.isAdmin || settings.isDelegatedAdmin"
7374
:required="!settings.isAdmin && !settings.isDelegatedAdmin"
7475
:create-option="(value) => ({ id: value, name: value, isCreating: true })"
7576
@search="searchGroups"
@@ -177,7 +178,7 @@ export default {
177178
178179
data() {
179180
return {
180-
availableGroups: this.$store.getters.getSortedGroups.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled'),
181+
availableGroups: [],
181182
possibleManagers: [],
182183
// TRANSLATORS This string describes a manager in the context of an organization
183184
managerInputLabel: t('settings', 'Manager'),
@@ -234,6 +235,13 @@ export default {
234235
},
235236
236237
mounted() {
238+
// admins also can assign the system groups
239+
if (this.isAdmin || this.isDelegatedAdmin) {
240+
this.availableGroups = this.$store.getters.getSortedGroups.filter(group => group.id !== '__nc_internal_recent' && group.id !== 'disabled')
241+
} else {
242+
this.availableGroups = [...this.$store.getters.getSubAdminGroups]
243+
}
244+
237245
this.$refs.username?.focus?.()
238246
},
239247
@@ -272,6 +280,11 @@ export default {
272280
},
273281
274282
async searchGroups(query, toggleLoading) {
283+
if (!this.isAdmin && !this.isDelegatedAdmin) {
284+
// managers cannot search for groups
285+
return
286+
}
287+
275288
if (this.promise) {
276289
this.promise.cancel()
277290
}

apps/settings/src/store/users.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const defaults = {
3737
const state = {
3838
users: [],
3939
groups: [
40-
...(usersSettings.userGroups ?? []),
40+
...(usersSettings.getSubAdminGroups ?? []),
4141
...(usersSettings.systemGroups ?? []),
4242
],
4343
orderBy: usersSettings.sortGroups ?? GroupSorting.UserCount,
@@ -234,12 +234,10 @@ const mutations = {
234234
* @param {object} state the store state
235235
*/
236236
resetGroups(state) {
237-
const systemGroups = state.groups.filter(group => [
238-
'admin',
239-
'__nc_internal_recent',
240-
'disabled',
241-
].includes(group.id))
242-
state.groups = [...systemGroups]
237+
state.groups = [
238+
...(usersSettings.getSubAdminGroups ?? []),
239+
...(usersSettings.systemGroups ?? []),
240+
]
243241
},
244242

245243
setShowConfig(state, { key, value }) {
@@ -266,16 +264,16 @@ const mutations = {
266264
}
267265

268266
const getters = {
269-
getUserGroups() {
270-
return usersSettings.userGroups ?? []
271-
},
272-
273267
getUsers(state) {
274268
return state.users
275269
},
276270
getGroups(state) {
277271
return state.groups
278272
},
273+
getSubAdminGroups() {
274+
return usersSettings.subAdminGroups ?? []
275+
},
276+
279277
getSortedGroups(state) {
280278
const groups = [...state.groups]
281279
if (state.orderBy === GroupSorting.UserCount) {

0 commit comments

Comments
 (0)