Skip to content

Commit a74641f

Browse files
authored
[stable5.2] fix: require explicit share permission for bulk submission endpoints (#3292)
fix: require explicit share permission for bulk submission endpoints (#3290) * fix: require explicit share permission for bulk submission actions Add permission guard to deleteAllSubmissions() to verify the user holds the required share-level permission, matching the pattern already used by the single-item endpoint. -e * PR feedback -e --------- Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
1 parent e79ef5a commit a74641f

2 files changed

Lines changed: 126 additions & 0 deletions

File tree

lib/Controller/ApiController.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,14 @@ public function getSubmission(int $formId, int $submissionId): DataResponse|Data
13161316
public function deleteAllSubmissions(int $formId): DataResponse {
13171317
$form = $this->formsService->getFormIfAllowed($formId, Constants::PERMISSION_RESULTS_DELETE);
13181318

1319+
// Require explicit results_delete permission for bulk deletion.
1320+
// canDeleteResults() also returns true for submitters with allowEditSubmissions,
1321+
// but those users should only be able to delete their own submissions individually.
1322+
if (!in_array(Constants::PERMISSION_RESULTS_DELETE, $this->formsService->getPermissions($form))) {
1323+
$this->logger->debug('User lacks results_delete permission for bulk deletion');
1324+
throw new OCSForbiddenException('No permission to delete all submissions');
1325+
}
1326+
13191327
// Delete all submissions (incl. Answers)
13201328
$this->submissionMapper->deleteByForm($formId);
13211329
$this->formMapper->update($form);

tests/Unit/Controller/ApiControllerTest.php

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,124 @@ public function dataTestDeletePermission() {
10301030
];
10311031
}
10321032

1033+
/**
1034+
* Test that a submitter with allowEditSubmissions but without
1035+
* PERMISSION_RESULTS_DELETE cannot bulk-delete all submissions.
1036+
*/
1037+
public function testDeleteAllSubmissionsNoPermission(): void {
1038+
$form = Form::fromParams([
1039+
'id' => 1,
1040+
'title' => 'Test Form',
1041+
'hash' => 'hash',
1042+
'access' => [
1043+
'permitAllUsers' => false,
1044+
'showToAllUsers' => false,
1045+
],
1046+
'ownerId' => 'otherUser',
1047+
'description' => '',
1048+
'expires' => 0,
1049+
'isAnonymous' => false,
1050+
'submitMultiple' => false,
1051+
'showExpiration' => false,
1052+
]);
1053+
1054+
// getFormIfAllowed passes (canDeleteResults returns true due to allowEditSubmissions)
1055+
$this->formsService
1056+
->method('getFormIfAllowed')
1057+
->with(1, Constants::PERMISSION_RESULTS_DELETE)
1058+
->willReturn($form);
1059+
1060+
// But user only has submit permission, not results_delete
1061+
$this->formsService
1062+
->method('getPermissions')
1063+
->with($form)
1064+
->willReturn([Constants::PERMISSION_SUBMIT]);
1065+
1066+
// Bulk delete must NOT be called
1067+
$this->submissionMapper
1068+
->expects($this->never())
1069+
->method('deleteByForm');
1070+
1071+
$this->expectException(OCSForbiddenException::class);
1072+
$this->apiController->deleteAllSubmissions(1);
1073+
}
1074+
1075+
/**
1076+
* Test that the form owner can bulk-delete all submissions.
1077+
*/
1078+
public function testDeleteAllSubmissionsAsOwner(): void {
1079+
$form = Form::fromParams([
1080+
'id' => 1,
1081+
'title' => 'Test Form',
1082+
'hash' => 'hash',
1083+
'access' => [
1084+
'permitAllUsers' => false,
1085+
'showToAllUsers' => false,
1086+
],
1087+
'ownerId' => 'currentUser',
1088+
'description' => '',
1089+
'expires' => 0,
1090+
'isAnonymous' => false,
1091+
'submitMultiple' => false,
1092+
'showExpiration' => false,
1093+
]);
1094+
1095+
$this->formsService
1096+
->method('getFormIfAllowed')
1097+
->with(1, Constants::PERMISSION_RESULTS_DELETE)
1098+
->willReturn($form);
1099+
1100+
$this->formsService
1101+
->method('getPermissions')
1102+
->with($form)
1103+
->willReturn(Constants::PERMISSION_ALL);
1104+
1105+
$this->submissionMapper
1106+
->expects($this->once())
1107+
->method('deleteByForm')
1108+
->with(1);
1109+
1110+
$this->assertEquals(new DataResponse(1), $this->apiController->deleteAllSubmissions(1));
1111+
}
1112+
1113+
/**
1114+
* Test that a collaborator with PERMISSION_RESULTS_DELETE can bulk-delete.
1115+
*/
1116+
public function testDeleteAllSubmissionsWithResultsDeletePermission(): void {
1117+
$form = Form::fromParams([
1118+
'id' => 1,
1119+
'title' => 'Test Form',
1120+
'hash' => 'hash',
1121+
'access' => [
1122+
'permitAllUsers' => false,
1123+
'showToAllUsers' => false,
1124+
],
1125+
'ownerId' => 'otherUser',
1126+
'description' => '',
1127+
'expires' => 0,
1128+
'isAnonymous' => false,
1129+
'submitMultiple' => false,
1130+
'showExpiration' => false,
1131+
]);
1132+
1133+
$this->formsService
1134+
->method('getFormIfAllowed')
1135+
->with(1, Constants::PERMISSION_RESULTS_DELETE)
1136+
->willReturn($form);
1137+
1138+
$this->formsService
1139+
->method('getPermissions')
1140+
->with($form)
1141+
->willReturn([Constants::PERMISSION_RESULTS_DELETE, Constants::PERMISSION_SUBMIT]);
1142+
1143+
$this->submissionMapper
1144+
->expects($this->once())
1145+
->method('deleteByForm')
1146+
->with(1);
1147+
1148+
$this->assertEquals(new DataResponse(1), $this->apiController->deleteAllSubmissions(1));
1149+
}
1150+
10331151
public function testTransferOwnerNotOwner() {
10341152
$form = new Form();
10351153
$form->setId(1);

0 commit comments

Comments
 (0)