From f1e7594b7903ffef95148a47e80d9569656a9a7c Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Mon, 20 Oct 2025 21:17:40 +0200 Subject: [PATCH] Remove deleted users and groups in operators and access control Fixes #857 --- CHANGES | 3 + dashboard/src/components/AccessControl.vue | 19 +------ .../src/components/OperatorAccessControl.vue | 20 +------ dashboard/src/components/app/Access.vue | 56 ++++++++++++++----- 4 files changed, 50 insertions(+), 48 deletions(-) diff --git a/CHANGES b/CHANGES index 6f5dc729a..e1c8ed921 100644 --- a/CHANGES +++ b/CHANGES @@ -3016,3 +3016,6 @@ * display backup duration * add hetznercloud DNS provider +[9.0.5] +* access control/operators: remove deleted users and groups + diff --git a/dashboard/src/components/AccessControl.vue b/dashboard/src/components/AccessControl.vue index 27179fe8e..fb02fd30a 100644 --- a/dashboard/src/components/AccessControl.vue +++ b/dashboard/src/components/AccessControl.vue @@ -1,33 +1,18 @@ diff --git a/dashboard/src/components/OperatorAccessControl.vue b/dashboard/src/components/OperatorAccessControl.vue index 9d158728f..ae91405c4 100644 --- a/dashboard/src/components/OperatorAccessControl.vue +++ b/dashboard/src/components/OperatorAccessControl.vue @@ -1,29 +1,13 @@ diff --git a/dashboard/src/components/app/Access.vue b/dashboard/src/components/app/Access.vue index 3eab900fe..538276bab 100644 --- a/dashboard/src/components/app/Access.vue +++ b/dashboard/src/components/app/Access.vue @@ -6,39 +6,64 @@ import AccessControl from '../AccessControl.vue'; import OperatorAccessControl from '../OperatorAccessControl.vue'; import AppsModel from '../../models/AppsModel.js'; import { ACL_OPTIONS } from '../../constants.js'; +import UsersModel from '../../models/UsersModel.js'; +import GroupsModel from '../../models/GroupsModel.js'; const props = defineProps([ 'app' ]); const appsModel = AppsModel.create(); +const usersModel = UsersModel.create(); +const groupsModel = GroupsModel.create(); -const busy = ref(false); +const users = ref([]); +const groups = ref([]); + +const loading = ref(false); +const submitBusy = ref(false); const errorMessage = ref(''); const accessRestrictionOption = ref(ACL_OPTIONS.ANY); const accessRestrictionAcl = ref({ users: [], groups: [] }); const operatorAcl = ref({ users: [], groups: [] }); async function onSubmit() { - busy.value = true; + submitBusy.value = true; errorMessage.value = ''; let [error] = await appsModel.configure(props.app.id, 'access_restriction', { accessRestriction: accessRestrictionOption.value === ACL_OPTIONS.ANY ? null : (accessRestrictionOption.value === ACL_OPTIONS.NOSSO ? false : accessRestrictionAcl.value) }); if (error) { errorMessage.value = error.body ? error.body.message : 'Internal error'; - busy.value = false; + submitBusy.value = false; return console.error(error); } [error] = await appsModel.configure(props.app.id, 'operators', { operators: (operatorAcl.value.users.length || operatorAcl.value.groups.length) ? operatorAcl.value : null}); if (error) { errorMessage.value = error.body ? error.body.message : 'Internal error'; - busy.value = false; + submitBusy.value = false; return console.error(error); } - busy.value = false; + submitBusy.value = false; } -onMounted(() => { +onMounted(async () => { + loading.value = true; + + let [error, result] = await usersModel.list(); + if (error) return console.error(error); + const userIds = new Set(); + for (const u of result) { + u.username = u.username || u.email; // ensure username + userIds.add(u.id); + } + users.value = result; + + [error, result] = await groupsModel.list(); + if (error) return console.error(error); + groups.value = result; + const groupIds = new Set(); + for (const g of result) groupIds.add(g.id); + if (props.app.accessRestriction === null) { accessRestrictionOption.value = ACL_OPTIONS.ANY; accessRestrictionAcl.value = { users: [], groups: [] }; @@ -47,26 +72,31 @@ onMounted(() => { accessRestrictionAcl.value = { users: [], groups: [] }; } else { accessRestrictionOption.value = ACL_OPTIONS.RESTRICTED; - accessRestrictionAcl.value = props.app.accessRestriction; + accessRestrictionAcl.value = JSON.parse(JSON.stringify(props.app.accessRestriction)); // make a copy + accessRestrictionAcl.value.users = accessRestrictionAcl.value.users.filter(uid => userIds.has(uid)); // remove deleted users + accessRestrictionAcl.value.groups = accessRestrictionAcl.value.groups.filter(gid => groupIds.has(gid)); // remove deleted groups } operatorAcl.value = { users: [], groups: [] }; if (props.app.operators) { - operatorAcl.value.users = props.app.operators.users; - operatorAcl.value.groups = props.app.operators.groups; + operatorAcl.value = JSON.parse(JSON.stringify(props.app.operators)); // make a copy + operatorAcl.value.users = operatorAcl.value.users.filter(uid => userIds.has(uid)); // remove deleted users + operatorAcl.value.groups = operatorAcl.value.groups.filter(gid => groupIds.has(gid)); // remove deleted groups } + + loading.value = false; });