@@ -267,7 +267,8 @@ public function updateForm(int $formId, array $keyValuePairs): DataResponse {
267
267
'keyValuePairs ' => $ keyValuePairs
268
268
]);
269
269
270
- $ form = $ this ->getFormIfAllowed ($ formId );
270
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
271
+ $ this ->obtainFormLock ($ form );
271
272
272
273
// Don't allow empty array
273
274
if (sizeof ($ keyValuePairs ) === 0 ) {
@@ -277,6 +278,12 @@ public function updateForm(int $formId, array $keyValuePairs): DataResponse {
277
278
278
279
// Process owner transfer
279
280
if (sizeof ($ keyValuePairs ) === 1 && key_exists ('ownerId ' , $ keyValuePairs )) {
281
+ // Only allow owner transfer if current user is the form owner
282
+ if ($ this ->currentUser ->getUID () !== $ form ->getOwnerId ()) {
283
+ $ this ->logger ->debug ('Only the form owner can transfer ownership ' );
284
+ throw new OCSForbiddenException ('Only the form owner can transfer ownership ' );
285
+ }
286
+
280
287
$ this ->logger ->debug ('Updating owner: formId: {formId}, userId: {uid} ' , [
281
288
'formId ' => $ formId ,
282
289
'uid ' => $ keyValuePairs ['ownerId ' ]
@@ -477,7 +484,9 @@ public function getQuestion(int $formId, int $questionId): DataResponse {
477
484
#[BruteForceProtection(action: 'form ' )]
478
485
#[ApiRoute(verb: 'POST ' , url: '/api/v3/forms/{formId}/questions ' )]
479
486
public function newQuestion (int $ formId , ?string $ type = null , string $ text = '' , ?int $ fromId = null ): DataResponse {
480
- $ form = $ this ->getFormIfAllowed ($ formId );
487
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
488
+ $ this ->obtainFormLock ($ form );
489
+
481
490
if ($ this ->formsService ->isFormArchived ($ form )) {
482
491
$ this ->logger ->debug ('This form is archived and can not be modified ' );
483
492
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -608,7 +617,9 @@ public function updateQuestion(int $formId, int $questionId, array $keyValuePair
608
617
609
618
// Make sure we query the form first to check the user has permissions
610
619
// So the user does not get information about "questions" if they do not even have permissions to the form
611
- $ form = $ this ->getFormIfAllowed ($ formId );
620
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
621
+ $ this ->obtainFormLock ($ form );
622
+
612
623
if ($ this ->formsService ->isFormArchived ($ form )) {
613
624
$ this ->logger ->debug ('This form is archived and can not be modified ' );
614
625
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -682,7 +693,9 @@ public function deleteQuestion(int $formId, int $questionId): DataResponse {
682
693
]);
683
694
684
695
685
- $ form = $ this ->getFormIfAllowed ($ formId );
696
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
697
+ $ this ->obtainFormLock ($ form );
698
+
686
699
if ($ this ->formsService ->isFormArchived ($ form )) {
687
700
$ this ->logger ->debug ('This form is archived and can not be modified ' );
688
701
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -748,7 +761,9 @@ public function reorderQuestions(int $formId, array $newOrder): DataResponse {
748
761
'newOrder ' => $ newOrder
749
762
]);
750
763
751
- $ form = $ this ->getFormIfAllowed ($ formId );
764
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
765
+ $ this ->obtainFormLock ($ form );
766
+
752
767
if ($ this ->formsService ->isFormArchived ($ form )) {
753
768
$ this ->logger ->debug ('This form is archived and can not be modified ' );
754
769
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -847,7 +862,9 @@ public function newOption(int $formId, int $questionId, array $optionTexts): Dat
847
862
'text ' => $ optionTexts ,
848
863
]);
849
864
850
- $ form = $ this ->getFormIfAllowed ($ formId );
865
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
866
+ $ this ->obtainFormLock ($ form );
867
+
851
868
if ($ this ->formsService ->isFormArchived ($ form )) {
852
869
$ this ->logger ->debug ('This form is archived and can not be modified ' );
853
870
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -929,7 +946,9 @@ public function updateOption(int $formId, int $questionId, int $optionId, array
929
946
'keyValuePairs ' => $ keyValuePairs
930
947
]);
931
948
932
- $ form = $ this ->getFormIfAllowed ($ formId );
949
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
950
+ $ this ->obtainFormLock ($ form );
951
+
933
952
if ($ this ->formsService ->isFormArchived ($ form )) {
934
953
$ this ->logger ->debug ('This form is archived and can not be modified ' );
935
954
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -996,7 +1015,9 @@ public function deleteOption(int $formId, int $questionId, int $optionId): DataR
996
1015
'optionId ' => $ optionId
997
1016
]);
998
1017
999
- $ form = $ this ->getFormIfAllowed ($ formId );
1018
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
1019
+ $ this ->obtainFormLock ($ form );
1020
+
1000
1021
if ($ this ->formsService ->isFormArchived ($ form )) {
1001
1022
$ this ->logger ->debug ('This form is archived and can not be modified ' );
1002
1023
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -1052,7 +1073,9 @@ public function deleteOption(int $formId, int $questionId, int $optionId): DataR
1052
1073
#[BruteForceProtection(action: 'form ' )]
1053
1074
#[ApiRoute(verb: 'PATCH ' , url: '/api/v3/forms/{formId}/questions/{questionId}/options ' )]
1054
1075
public function reorderOptions (int $ formId , int $ questionId , array $ newOrder ) {
1055
- $ form = $ this ->getFormIfAllowed ($ formId );
1076
+ $ form = $ this ->getFormIfAllowed ($ formId , Constants::PERMISSION_EDIT );
1077
+ $ this ->obtainFormLock ($ form );
1078
+
1056
1079
if ($ this ->formsService ->isFormArchived ($ form )) {
1057
1080
$ this ->logger ->debug ('This form is archived and can not be modified ' );
1058
1081
throw new OCSForbiddenException ('This form is archived and can not be modified ' );
@@ -1790,6 +1813,12 @@ private function getFormIfAllowed(int $formId, string $permissions = 'all'): For
1790
1813
throw new NoSuchFormException ('This form is not owned by the current user and user has no `results_delete` permission ' , Http::STATUS_FORBIDDEN );
1791
1814
}
1792
1815
break ;
1816
+ case Constants::PERMISSION_EDIT :
1817
+ if (!$ this ->formsService ->canEditForm ($ form )) {
1818
+ $ this ->logger ->debug ('This form is not owned by the current user and user has no `edit` permission ' );
1819
+ throw new NoSuchFormException ('This form is not owned by the current user and user has no `edit` permission ' , Http::STATUS_FORBIDDEN );
1820
+ }
1821
+ break ;
1793
1822
default :
1794
1823
// By default we request full permissions
1795
1824
if ($ form ->getOwnerId () !== $ this ->currentUser ->getUID ()) {
@@ -1800,4 +1829,23 @@ private function getFormIfAllowed(int $formId, string $permissions = 'all'): For
1800
1829
}
1801
1830
return $ form ;
1802
1831
}
1832
+
1833
+ /**
1834
+ * Locks the given form for the current user for a duration of 15 minutes.
1835
+ *
1836
+ * @param Form $form The form instance to lock.
1837
+ */
1838
+ private function obtainFormLock (Form $ form ): void {
1839
+ // Only lock if not locked or locked by current user, or lock has expired
1840
+ if (
1841
+ $ form ->getLockedBy () === null ||
1842
+ $ form ->getLockedBy () === $ this ->currentUser ->getUID () ||
1843
+ $ form ->getLockedUntil () < time ()
1844
+ ) {
1845
+ $ form ->setLockedBy ($ this ->currentUser ->getUID ());
1846
+ $ form ->setLockedUntil (time () + 15 * 60 );
1847
+ } else {
1848
+ throw new OCSForbiddenException ('Form is currently locked by another user. ' );
1849
+ }
1850
+ }
1803
1851
}
0 commit comments