From a3325325413418fc42f1be0ed948adec3b0f824a Mon Sep 17 00:00:00 2001 From: Christian Hartmann Date: Thu, 6 Nov 2025 00:02:41 +0100 Subject: [PATCH] fix: Update permission checks for archiving forms and transfer ownership Signed-off-by: Christian Hartmann --- lib/Controller/ApiController.php | 34 +++++++++++-------- .../SidebarTabs/SettingsSidebarTab.vue | 7 ++-- .../SidebarTabs/TransferOwnership.vue | 7 +++- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 6b1282c69..1f9d95f6f 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -1786,25 +1786,31 @@ private function checkAccessUpdate(array $keyValuePairs): void { * Checks if the current user is allowed to archive/unarchive the form */ private function checkArchivePermission(Form $form, string $currentUserId, array $keyValuePairs): void { + // Only check if the request is trying to change the archived state + if (!array_key_exists('state', $keyValuePairs)) { + return; + } + $isArchived = $this->formsService->isFormArchived($form); - $owner = $currentUserId === $form->getOwnerId(); - $onlyState = sizeof($keyValuePairs) === 1 && key_exists('state', $keyValuePairs); + $isOwner = $currentUserId === $form->getOwnerId(); - // Only check if the request is trying to change the archived state - if ($onlyState && $keyValuePairs['state'] === Constants::FORM_STATE_ARCHIVED) { + // If the request contains 'state' it must be the only key + if (sizeof($keyValuePairs) !== 1) { + $this->logger->debug('State may only be changed on its own'); + throw new OCSForbiddenException('State may only be changed on its own'); + } + + $state = $keyValuePairs['state']; + + if ($state === Constants::FORM_STATE_ARCHIVED && !$isArchived && !$isOwner) { // Trying to archive - if (!$owner || $isArchived) { - $this->logger->debug('Only the form owner can archive the form, and only if it is not already archived'); - throw new OCSForbiddenException('Only the form owner can archive the form, and only if it is not already archived'); - } - } elseif ($onlyState && $keyValuePairs['state'] === Constants::FORM_STATE_CLOSED) { + $this->logger->debug('Only the form owner can archive the form, and only if it is not already archived'); + throw new OCSForbiddenException('Only the form owner can archive the form, and only if it is not already archived'); + } elseif ($state === Constants::FORM_STATE_CLOSED && $isArchived && !$isOwner) { // Trying to unarchive - if (!$owner || !$isArchived) { - $this->logger->debug('Only the form owner can unarchive the form, and only if it is currently archived'); - throw new OCSForbiddenException('Only the form owner can unarchive the form, and only if it is currently archived'); - } + $this->logger->debug('Only the form owner can unarchive the form, and only if it is currently archived'); + throw new OCSForbiddenException('Only the form owner can unarchive the form, and only if it is currently archived'); } - // All other updates are allowed (including updates that do not touch the state) } private function isLockingRequest(array $keyValuePairs): bool { diff --git a/src/components/SidebarTabs/SettingsSidebarTab.vue b/src/components/SidebarTabs/SettingsSidebarTab.vue index 26ea4c88a..0110d5167 100644 --- a/src/components/SidebarTabs/SettingsSidebarTab.vue +++ b/src/components/SidebarTabs/SettingsSidebarTab.vue @@ -103,7 +103,7 @@ {{ t('forms', 'Archive form') }} @@ -168,7 +168,10 @@ - + diff --git a/src/components/SidebarTabs/TransferOwnership.vue b/src/components/SidebarTabs/TransferOwnership.vue index b6d1cbbdb..e39985333 100644 --- a/src/components/SidebarTabs/TransferOwnership.vue +++ b/src/components/SidebarTabs/TransferOwnership.vue @@ -10,7 +10,7 @@ alignment="start" variant="tertiary" wide - :disabled="locked" + :disabled="locked || !isOwner" @click="openModal"> {{ t('forms', 'Transfer ownership') @@ -117,6 +117,11 @@ export default { required: true, }, + isOwner: { + type: Boolean, + required: true, + }, + locked: { type: Boolean, required: true,