Skip to content

Commit 4fc7b48

Browse files
authored
Allow only one appeal from an email address. (#8163)
1 parent ba053e6 commit 4fc7b48

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

app/lib/admin/backend.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,4 +929,16 @@ class AdminBackend {
929929
_logger.info('Deleting moderated user: ${user.userId}');
930930
}
931931
}
932+
933+
/// Whether the [ModerationCase] has been appealed by [email] already.
934+
Future<bool> isModerationCaseAppealedByEmail({
935+
required String caseId,
936+
required String email,
937+
}) async {
938+
final query = dbService.query<ModerationCase>()
939+
..filter('appealedCaseId =', caseId);
940+
final list = await query.run().toList();
941+
final emails = list.map((mc) => mc.reporterEmail).toSet();
942+
return emails.contains(email);
943+
}
932944
}

app/lib/frontend/handlers/report.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ Future<Message> processReportPageHandler(
206206

207207
final isAppeal = form.caseId != null;
208208

209+
if (isAppeal) {
210+
final appealExists = await adminBackend.isModerationCaseAppealedByEmail(
211+
caseId: form.caseId!,
212+
email: userEmail!,
213+
);
214+
if (appealExists) {
215+
throw InvalidInputException(
216+
'You have previously appealed this incident, we are unable to accept another appeal.');
217+
}
218+
}
219+
209220
bool isSubjectOwner = false;
210221
if (user != null) {
211222
if (subject.isPackage) {

app/test/frontend/handlers/report_test.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,13 @@ void main() {
363363
);
364364
});
365365

366-
testWithProfile('unauthenticated appeal success', fn: () async {
366+
testWithProfile('unauthenticated appeal success, second appeal fails',
367+
fn: () async {
367368
await _prepareApplied(
368369
logSubject: 'package-version:oxygen/1.2.0',
369370
);
370371

372+
// first report: success
371373
await withHttpPubApiClient(
372374
fn: (client) async {
373375
final msg = await client.postReport(ReportForm(
@@ -396,6 +398,22 @@ void main() {
396398
expect(mc.isSubjectOwner, false);
397399
},
398400
);
401+
402+
// second report: rejected
403+
await withHttpPubApiClient(fn: (client) async {
404+
await expectApiException(
405+
client.postReport(ReportForm(
406+
407+
subject: 'package-version:oxygen/1.2.0',
408+
caseId: 'case/1',
409+
message: 'Huston, we have a problem.',
410+
)),
411+
code: 'InvalidInput',
412+
status: 400,
413+
message:
414+
'You have previously appealed this incident, we are unable to accept another appeal.',
415+
);
416+
});
399417
});
400418

401419
testWithProfile('authenticated appeal success', fn: () async {

0 commit comments

Comments
 (0)