fix(privacy): the instance's side of the reading of private studysets (ADR 0019)

An upload said to be encrypted must start as a sealed file does (HLKF,
version 1), so a readable file can't skip the PDF check by saying so. A
person's room counts the files a live asset row names and those claimed
within the week of grace, so a private studyset's rewrite near the limit
no longer fails for a week while pruning keeps the replaced files. Copy
reports are limited to 60 an hour per person. Tests: what goes with the
person, `counted` refused to anonymous people.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-22 08:45:01 +02:00
commit d9b5e06618
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -2,6 +2,7 @@ import 'dart:typed_data';
import 'package:halkyon_base_server/base.dart';
import 'package:halkyon_learn_server/src/explore/learner_count.dart';
import 'package:halkyon_learn_server/src/explore/learning_count_endpoint.dart';
import 'package:halkyon_learn_server/src/generated/protocol.dart';
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_idp_server/core.dart';
@ -222,8 +223,51 @@ void main() {
await learner();
expect(await counted(), 1);
final gone = people.last;
await deletePerson(session, people.removeLast());
expect(await counted(), 0);
// Nothing of theirs stays behind, whatever the count says.
expect(
await CountedLearner.db.count(
session,
where: (t) => t.authUserId.equals(gone),
),
0,
);
expect(
await LearningCopy.db.count(
session,
where: (t) => t.authUserId.equals(gone),
),
0,
);
});
test('a person reports at most an hour\'s worth of times', () async {
final copy = await learner();
final person = people.last;
final report = [
CopyReport(
publicationId: japanese.publicId.uuid,
studysetIds: [copy],
),
];
// The learner reported once already.
for (var i = 1; i < copyReportsPerHour; i++) {
expect(
await endpoints.learningCount.reportCopies(
signedIn(person),
report,
),
isTrue,
);
}
expect(
await endpoints.learningCount.reportCopies(signedIn(person), []),
isFalse,
);
expect(await counted(), 1);
});
test('reports are kept only while the person chose to be counted, '
@ -274,6 +318,10 @@ void main() {
),
throwsA(isA<ServerpodInsufficientAccessException>()),
);
await expectLater(
endpoints.learningCount.counted(signedIn(person, anonymous: true)),
throwsA(isA<ServerpodInsufficientAccessException>()),
);
expect(await CountedLearner.db.count(session), 0);
});
},