|
22 | 22 | use OCA\Forms\Db\SubmissionMapper;
|
23 | 23 | use OCA\Forms\Db\UploadedFile;
|
24 | 24 | use OCA\Forms\Db\UploadedFileMapper;
|
25 |
| -use OCA\Forms\Exception\NoSuchFormException; |
26 | 25 | use OCA\Forms\ResponseDefinitions;
|
27 | 26 | use OCA\Forms\Service\ConfigService;
|
28 | 27 | use OCA\Forms\Service\FormsService;
|
@@ -272,161 +271,42 @@ public function updateForm(int $formId, array $keyValuePairs): DataResponse {
|
272 | 271 | $form = $this->formsService->getFormIfAllowed($formId, Constants::PERMISSION_EDIT);
|
273 | 272 | $currentUserId = $this->currentUser->getUID();
|
274 | 273 |
|
275 |
| - // Don't allow empty array |
276 |
| - if (sizeof($keyValuePairs) === 0) { |
| 274 | + if (empty($keyValuePairs)) { |
277 | 275 | $this->logger->info('Empty keyValuePairs, will not update.');
|
278 | 276 | throw new OCSForbiddenException('Empty keyValuePairs, will not update.');
|
279 | 277 | }
|
280 | 278 |
|
281 | 279 | // Only allow the form owner to set/unset the "archived" state
|
282 |
| - if ( |
283 |
| - ( |
284 |
| - $this->formsService->isFormArchived($form) |
285 |
| - && !( |
286 |
| - $currentUserId === $form->getOwnerId() |
287 |
| - && sizeof($keyValuePairs) === 1 |
288 |
| - && key_exists('state', $keyValuePairs) |
289 |
| - && $keyValuePairs['state'] === Constants::FORM_STATE_CLOSED |
290 |
| - ) |
291 |
| - ) |
292 |
| - || ( |
293 |
| - !$this->formsService->isFormArchived($form) |
294 |
| - && !( |
295 |
| - $currentUserId === $form->getOwnerId() |
296 |
| - && sizeof($keyValuePairs) === 1 |
297 |
| - && key_exists('state', $keyValuePairs) |
298 |
| - && $keyValuePairs['state'] === Constants::FORM_STATE_ARCHIVED |
299 |
| - ) |
300 |
| - ) |
301 |
| - ) { |
302 |
| - $this->logger->debug('Only the form owner can set/unset the \`archived\' state'); |
303 |
| - throw new OCSForbiddenException('Only the form owner can set/unset the \`archived\' state'); |
304 |
| - } |
| 280 | + $this->checkArchivePermission($form, $currentUserId, $keyValuePairs); |
305 | 281 |
|
306 |
| - // Process complete form locking (lockedUntil: 0) |
307 |
| - if ( |
308 |
| - sizeof($keyValuePairs) === 1 |
309 |
| - && array_key_exists('lockedUntil', $keyValuePairs) |
310 |
| - && $keyValuePairs['lockedUntil'] === 0 |
311 |
| - ) { |
312 |
| - // Only allow form locking for form owner |
313 |
| - if ($currentUserId !== $form->getOwnerId() || ($form->getLockedBy() !== null && $currentUserId !== $form->getLockedBy())) { |
314 |
| - $this->logger->debug('Only the form owner can lock the form permanently'); |
315 |
| - throw new OCSForbiddenException('Only the form owner can lock the form permanently'); |
316 |
| - } |
317 |
| - |
318 |
| - // Only allow if the form is not currently locked by another user |
319 |
| - if ( |
320 |
| - $form->getLockedBy() !== null |
321 |
| - && $form->getLockedBy() !== $currentUserId |
322 |
| - && $form->getLockedUntil() >= time() |
323 |
| - ) { |
324 |
| - $this->logger->debug('Form is currently locked by another user.'); |
325 |
| - throw new OCSForbiddenException('Form is currently locked by another user.'); |
326 |
| - } |
327 |
| - |
328 |
| - // Abort if form is already completely locked |
329 |
| - if ($form->getLockedUntil() === 0) { |
330 |
| - $this->logger->debug('Form is already locked completely.'); |
331 |
| - throw new OCSBadRequestException('Form is already locked completely.'); |
332 |
| - } |
333 |
| - |
334 |
| - $form->setLockedBy($form->getOwnerId()); |
335 |
| - $form->setLockedUntil(0); |
336 |
| - |
337 |
| - // Update changed Columns in Db. |
338 |
| - $this->formMapper->update($form); |
339 |
| - |
340 |
| - return new DataResponse($form->getId()); |
| 282 | + // Handle form locking/unlocking |
| 283 | + if ($this->isLockingRequest($keyValuePairs)) { |
| 284 | + return $this->handleFormLocking($form, $currentUserId); |
341 | 285 | }
|
342 |
| - |
343 |
| - // Process form unlocking |
344 |
| - if ( |
345 |
| - sizeof($keyValuePairs) === 1 |
346 |
| - && array_key_exists('lockedUntil', $keyValuePairs) && is_null($keyValuePairs['lockedUntil']) |
347 |
| - ) { |
348 |
| - // Only allow form unlocking if for form owner or lock user |
349 |
| - if ($currentUserId !== $form->getOwnerId() && $currentUserId !== $form->getLockedBy() && $form->getLockedUntil() !== 0) { |
350 |
| - $this->logger->debug('Only the form owner or the user who obtained the lock can unlock the form'); |
351 |
| - throw new OCSForbiddenException('Only the form owner or the user who obtained the lock can unlock the form'); |
352 |
| - } |
353 |
| - |
354 |
| - // remove form lock |
355 |
| - $form->setLockedBy(null); |
356 |
| - $form->setLockedUntil(null); |
357 |
| - |
358 |
| - // Update changed Columns in Db. |
359 |
| - $this->formMapper->update($form); |
360 |
| - |
361 |
| - return new DataResponse($form->getId()); |
| 286 | + if ($this->isUnlockingRequest($keyValuePairs)) { |
| 287 | + return $this->handleFormUnlocking($form, $currentUserId); |
362 | 288 | }
|
363 | 289 |
|
364 | 290 | // Lock form temporary
|
365 | 291 | $this->formsService->obtainFormLock($form);
|
366 |
| - |
367 |
| - // Process owner transfer |
368 |
| - if (sizeof($keyValuePairs) === 1 && key_exists('ownerId', $keyValuePairs)) { |
369 |
| - // Only allow owner transfer if current user is the form owner |
370 |
| - if ($currentUserId !== $form->getOwnerId()) { |
371 |
| - $this->logger->debug('Only the form owner can transfer ownership'); |
372 |
| - throw new OCSForbiddenException('Only the form owner can transfer ownership'); |
373 |
| - } |
374 |
| - |
375 |
| - $this->logger->debug('Updating owner: formId: {formId}, userId: {uid}', [ |
376 |
| - 'formId' => $formId, |
377 |
| - 'uid' => $keyValuePairs['ownerId'] |
378 |
| - ]); |
379 |
| - |
380 |
| - $user = $this->userManager->get($keyValuePairs['ownerId']); |
381 |
| - if ($user == null) { |
382 |
| - $this->logger->debug('Could not find new form owner'); |
383 |
| - throw new OCSBadRequestException('Could not find new form owner'); |
384 |
| - } |
385 | 292 |
|
386 |
| - // update form owner and remove form lock |
387 |
| - $form->setOwnerId($keyValuePairs['ownerId']); |
388 |
| - $form->setLockedBy(null); |
389 |
| - $form->setLockedUntil(null); |
390 |
| - |
391 |
| - // Update changed Columns in Db. |
392 |
| - $this->formMapper->update($form); |
393 |
| - |
394 |
| - return new DataResponse($form->getOwnerId()); |
| 293 | + // Handle owner transfer |
| 294 | + if ($this->isOwnerTransferRequest($keyValuePairs)) { |
| 295 | + return $this->handleOwnerTransfer($form, $formId, $currentUserId, $keyValuePairs); |
395 | 296 | }
|
396 | 297 |
|
397 | 298 | // Don't allow to change the following attributes
|
398 |
| - $forbiddenKeys = [ |
399 |
| - 'id', 'hash', 'ownerId', 'created', 'lastUpdated', 'lockedBy', 'lockedUntil' |
400 |
| - ]; |
401 |
| - |
402 |
| - foreach ($forbiddenKeys as $key) { |
403 |
| - if (array_key_exists($key, $keyValuePairs)) { |
404 |
| - $this->logger->info("Not allowed to update {$key}"); |
405 |
| - throw new OCSForbiddenException("Not allowed to update {$key}"); |
406 |
| - } |
407 |
| - } |
| 299 | + $this->checkForbiddenKeys($keyValuePairs); |
408 | 300 |
|
409 | 301 | // Don't allow to change fileId
|
410 |
| - if (isset($keyValuePairs['fileId'])) { |
411 |
| - $this->logger->info('Not allowed to update fileId'); |
412 |
| - throw new OCSForbiddenException('Not allowed to update fileId'); |
413 |
| - } |
| 302 | + $this->checkFileIdUpdate($keyValuePairs); |
414 | 303 |
|
415 |
| - // Do not allow changing showToAllUsers if disabled |
416 |
| - if (isset($keyValuePairs['access'])) { |
417 |
| - $showAll = $keyValuePairs['access']['showToAllUsers'] ?? false; |
418 |
| - $permitAll = $keyValuePairs['access']['permitAllUsers'] ?? false; |
419 |
| - if (($showAll && !$this->configService->getAllowShowToAll()) |
420 |
| - || ($permitAll && !$this->configService->getAllowPermitAll())) { |
421 |
| - $this->logger->info('Not allowed to update showToAllUsers or permitAllUsers'); |
422 |
| - throw new OCSForbiddenException(); |
423 |
| - } |
424 |
| - } |
| 304 | + // Do not allow changing showToAllUsers or permitAllUsers if disabled |
| 305 | + $this->checkAccessUpdate($keyValuePairs); |
425 | 306 |
|
426 | 307 | // Process file linking
|
427 | 308 | if (isset($keyValuePairs['path']) && isset($keyValuePairs['fileFormat'])) {
|
428 | 309 | $file = $this->submissionService->writeFileToCloud($form, $keyValuePairs['path'], $keyValuePairs['fileFormat']);
|
429 |
| - |
430 | 310 | $form->setFileId($file->getId());
|
431 | 311 | $form->setFileFormat($keyValuePairs['fileFormat']);
|
432 | 312 | }
|
@@ -1429,7 +1309,7 @@ public function newSubmission(int $formId, array $answers, string $shareHash = '
|
1429 | 1309 | 'shareHash' => $shareHash,
|
1430 | 1310 | ]);
|
1431 | 1311 |
|
1432 |
| - $form = $this->loadFormForSubmission($formId, $shareHash); |
| 1312 | + $form = $this->formsService->loadFormForSubmission($formId, $shareHash); |
1433 | 1313 |
|
1434 | 1314 | $questions = $this->formsService->getQuestions($formId);
|
1435 | 1315 | try {
|
@@ -1515,7 +1395,7 @@ public function updateSubmission(int $formId, int $submissionId, array $answers)
|
1515 | 1395 | ]);
|
1516 | 1396 |
|
1517 | 1397 | // submissions can't be updated on public shares, so passing empty shareHash
|
1518 |
| - $form = $this->loadFormForSubmission($formId, ''); |
| 1398 | + $form = $this->formsService->loadFormForSubmission($formId, ''); |
1519 | 1399 |
|
1520 | 1400 | if (!$form->getAllowEditSubmissions()) {
|
1521 | 1401 | throw new OCSBadRequestException('Can only update if allowEditSubmissions is set');
|
@@ -1680,7 +1560,7 @@ public function uploadFiles(int $formId, int $questionId, string $shareHash = ''
|
1680 | 1560 | throw new OCSBadRequestException('No files provided');
|
1681 | 1561 | }
|
1682 | 1562 |
|
1683 |
| - $form = $this->loadFormForSubmission($formId, $shareHash); |
| 1563 | + $form = $this->formsService->loadFormForSubmission($formId, $shareHash); |
1684 | 1564 |
|
1685 | 1565 | if (!$this->formsService->canSubmit($form)) {
|
1686 | 1566 | throw new OCSForbiddenException('Already submitted');
|
@@ -1848,40 +1728,148 @@ private function storeAnswersForQuestion(Form $form, $submissionId, array $quest
|
1848 | 1728 | }
|
1849 | 1729 | }
|
1850 | 1730 |
|
1851 |
| - private function loadFormForSubmission(int $formId, string $shareHash): Form { |
1852 |
| - try { |
1853 |
| - $form = $this->formMapper->findById($formId); |
1854 |
| - } catch (IMapperException $e) { |
1855 |
| - $this->logger->debug('Could not find form'); |
1856 |
| - throw new NoSuchFormException('Could not find form'); |
| 1731 | + /** |
| 1732 | + * Throws if forbidden keys are present in update |
| 1733 | + */ |
| 1734 | + private function checkForbiddenKeys(array $keyValuePairs): void { |
| 1735 | + $forbiddenKeys = [ |
| 1736 | + 'id', 'hash', 'ownerId', 'created', 'lastUpdated', 'lockedBy', 'lockedUntil' |
| 1737 | + ]; |
| 1738 | + foreach ($forbiddenKeys as $key) { |
| 1739 | + if (array_key_exists($key, $keyValuePairs)) { |
| 1740 | + $this->logger->info("Not allowed to update {$key}"); |
| 1741 | + throw new OCSForbiddenException("Not allowed to update {$key}"); |
| 1742 | + } |
1857 | 1743 | }
|
| 1744 | + } |
1858 | 1745 |
|
1859 |
| - // Does the user have access to the form (Either by logged-in user, or by providing public share-hash.) |
1860 |
| - try { |
1861 |
| - $isPublicShare = false; |
1862 |
| - |
1863 |
| - // If hash given, find the corresponding share & check if hash corresponds to given formId. |
1864 |
| - if ($shareHash !== '') { |
1865 |
| - // Public link share |
1866 |
| - $share = $this->shareMapper->findPublicShareByHash($shareHash); |
1867 |
| - if ($share->getFormId() === $formId) { |
1868 |
| - $isPublicShare = true; |
1869 |
| - } |
| 1746 | + /** |
| 1747 | + * Throws if fileId is present in update |
| 1748 | + */ |
| 1749 | + private function checkFileIdUpdate(array $keyValuePairs): void { |
| 1750 | + if (isset($keyValuePairs['fileId'])) { |
| 1751 | + $this->logger->info('Not allowed to update fileId'); |
| 1752 | + throw new OCSForbiddenException('Not allowed to update fileId'); |
| 1753 | + } |
| 1754 | + } |
| 1755 | + |
| 1756 | + /** |
| 1757 | + * Throws if access keys are being updated when not allowed |
| 1758 | + */ |
| 1759 | + private function checkAccessUpdate(array $keyValuePairs): void { |
| 1760 | + if (isset($keyValuePairs['access'])) { |
| 1761 | + $showAll = $keyValuePairs['access']['showToAllUsers'] ?? false; |
| 1762 | + $permitAll = $keyValuePairs['access']['permitAllUsers'] ?? false; |
| 1763 | + if (($showAll && !$this->configService->getAllowShowToAll()) |
| 1764 | + || ($permitAll && !$this->configService->getAllowPermitAll())) { |
| 1765 | + $this->logger->info('Not allowed to update showToAllUsers or permitAllUsers'); |
| 1766 | + throw new OCSForbiddenException(); |
1870 | 1767 | }
|
1871 |
| - } catch (DoesNotExistException $e) { |
1872 |
| - // $isPublicShare already false. |
1873 |
| - } finally { |
1874 |
| - // Now forbid, if no public share and no direct share. |
1875 |
| - if (!$isPublicShare && !$this->formsService->hasUserAccess($form)) { |
1876 |
| - throw new NoSuchFormException('Not allowed to access this form', Http::STATUS_FORBIDDEN); |
| 1768 | + } |
| 1769 | + } |
| 1770 | + |
| 1771 | + /** |
| 1772 | + * Checks if the current user is allowed to archive/unarchive the form |
| 1773 | + */ |
| 1774 | + private function checkArchivePermission(Form $form, string $currentUserId, array $keyValuePairs): void { |
| 1775 | + $isArchived = $this->formsService->isFormArchived($form); |
| 1776 | + $owner = $currentUserId === $form->getOwnerId(); |
| 1777 | + $onlyState = sizeof($keyValuePairs) === 1 && key_exists('state', $keyValuePairs); |
| 1778 | + |
| 1779 | + // Only check if the request is trying to change the archived state |
| 1780 | + if ($onlyState && $keyValuePairs['state'] === Constants::FORM_STATE_ARCHIVED) { |
| 1781 | + // Trying to archive |
| 1782 | + if (!$owner || $isArchived) { |
| 1783 | + $this->logger->debug('Only the form owner can archive the form, and only if it is not already archived'); |
| 1784 | + throw new OCSForbiddenException('Only the form owner can archive the form, and only if it is not already archived'); |
| 1785 | + } |
| 1786 | + } elseif ($onlyState && $keyValuePairs['state'] === Constants::FORM_STATE_CLOSED) { |
| 1787 | + // Trying to unarchive |
| 1788 | + if (!$owner || !$isArchived) { |
| 1789 | + $this->logger->debug('Only the form owner can unarchive the form, and only if it is currently archived'); |
| 1790 | + throw new OCSForbiddenException('Only the form owner can unarchive the form, and only if it is currently archived'); |
1877 | 1791 | }
|
1878 | 1792 | }
|
| 1793 | + // All other updates are allowed (including updates that do not touch the state) |
| 1794 | + } |
1879 | 1795 |
|
1880 |
| - // Not allowed if form has expired. |
1881 |
| - if ($this->formsService->hasFormExpired($form)) { |
1882 |
| - throw new OCSForbiddenException('This form is no longer taking answers'); |
| 1796 | + private function isLockingRequest(array $keyValuePairs): bool { |
| 1797 | + return sizeof($keyValuePairs) === 1 |
| 1798 | + && array_key_exists('lockedUntil', $keyValuePairs) |
| 1799 | + && $keyValuePairs['lockedUntil'] === 0; |
| 1800 | + } |
| 1801 | + |
| 1802 | + private function isUnlockingRequest(array $keyValuePairs): bool { |
| 1803 | + return sizeof($keyValuePairs) === 1 |
| 1804 | + && array_key_exists('lockedUntil', $keyValuePairs) |
| 1805 | + && is_null($keyValuePairs['lockedUntil']); |
| 1806 | + } |
| 1807 | + |
| 1808 | + private function isOwnerTransferRequest(array $keyValuePairs): bool { |
| 1809 | + return sizeof($keyValuePairs) === 1 && key_exists('ownerId', $keyValuePairs); |
| 1810 | + } |
| 1811 | + |
| 1812 | + /** |
| 1813 | + * @return DataResponse<Http::STATUS_OK, int, array{}> |
| 1814 | + */ |
| 1815 | + private function handleFormLocking(Form $form, string $currentUserId): DataResponse { |
| 1816 | + if ($currentUserId !== $form->getOwnerId() || ($form->getLockedBy() !== null && $currentUserId !== $form->getLockedBy())) { |
| 1817 | + $this->logger->debug('Only the form owner can lock the form permanently'); |
| 1818 | + throw new OCSForbiddenException('Only the form owner can lock the form permanently'); |
| 1819 | + } |
| 1820 | + if ( |
| 1821 | + $form->getLockedBy() !== null |
| 1822 | + && $form->getLockedBy() !== $currentUserId |
| 1823 | + && $form->getLockedUntil() >= time() |
| 1824 | + ) { |
| 1825 | + $this->logger->debug('Form is currently locked by another user.'); |
| 1826 | + throw new OCSForbiddenException('Form is currently locked by another user.'); |
| 1827 | + } |
| 1828 | + if ($form->getLockedUntil() === 0) { |
| 1829 | + $this->logger->debug('Form is already locked completely.'); |
| 1830 | + throw new OCSBadRequestException('Form is already locked completely.'); |
| 1831 | + } |
| 1832 | + $form->setLockedBy($form->getOwnerId()); |
| 1833 | + $form->setLockedUntil(0); |
| 1834 | + $this->formMapper->update($form); |
| 1835 | + return new DataResponse($form->getId()); |
| 1836 | + } |
| 1837 | + |
| 1838 | + /** |
| 1839 | + * @return DataResponse<Http::STATUS_OK, int, array{}> |
| 1840 | + */ |
| 1841 | + private function handleFormUnlocking(Form $form, string $currentUserId): DataResponse { |
| 1842 | + if ($currentUserId !== $form->getOwnerId() && $currentUserId !== $form->getLockedBy() && $form->getLockedUntil() !== 0) { |
| 1843 | + $this->logger->debug('Only the form owner or the user who obtained the lock can unlock the form'); |
| 1844 | + throw new OCSForbiddenException('Only the form owner or the user who obtained the lock can unlock the form'); |
1883 | 1845 | }
|
| 1846 | + $form->setLockedBy(null); |
| 1847 | + $form->setLockedUntil(null); |
| 1848 | + $this->formMapper->update($form); |
| 1849 | + return new DataResponse($form->getId()); |
| 1850 | + } |
1884 | 1851 |
|
1885 |
| - return $form; |
| 1852 | + /** |
| 1853 | + * @return DataResponse<Http::STATUS_OK, string, array{}> |
| 1854 | + */ |
| 1855 | + private function handleOwnerTransfer(Form $form, int $formId, string $currentUserId, array $keyValuePairs): DataResponse { |
| 1856 | + if ($currentUserId !== $form->getOwnerId()) { |
| 1857 | + $this->logger->debug('Only the form owner can transfer ownership'); |
| 1858 | + throw new OCSForbiddenException('Only the form owner can transfer ownership'); |
| 1859 | + } |
| 1860 | + $this->logger->debug('Updating owner: formId: {formId}, userId: {uid}', [ |
| 1861 | + 'formId' => $formId, |
| 1862 | + 'uid' => $keyValuePairs['ownerId'] |
| 1863 | + ]); |
| 1864 | + $user = $this->userManager->get($keyValuePairs['ownerId']); |
| 1865 | + if ($user == null) { |
| 1866 | + $this->logger->debug('Could not find new form owner'); |
| 1867 | + throw new OCSBadRequestException('Could not find new form owner'); |
| 1868 | + } |
| 1869 | + $form->setOwnerId($keyValuePairs['ownerId']); |
| 1870 | + $form->setLockedBy(null); |
| 1871 | + $form->setLockedUntil(null); |
| 1872 | + $this->formMapper->update($form); |
| 1873 | + return new DataResponse($form->getOwnerId()); |
1886 | 1874 | }
|
1887 | 1875 | }
|
0 commit comments