fix(auth): members learn of ended sessions in the background
The full reading's L5: every change or reset of a password told the members inside the request, up to 500 notices one after another with a timeout of 10 seconds each, so a member out of reach held the change up for minutes. Now announceSessionsEnded tells the members of this account's notices alone, in the background on a session of its own; those not confirmed wait for the hourly run as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
5eb74639b6
commit
76291e6ab4
2 changed files with 74 additions and 8 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:clock/clock.dart';
|
||||
|
|
@ -7,6 +8,8 @@ import 'package:halkyon_base_server/src/password/password_endpoint.dart'
|
|||
show changesBegunPerSession;
|
||||
import 'package:halkyon_base_server/src/password/passwords.dart'
|
||||
show writeNewPassword;
|
||||
import 'package:halkyon_base_server/src/register/sessions_ended.dart'
|
||||
show sessionNoticesDelivered;
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
import 'package:serverpod_auth_idp_server/core.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
|
@ -24,6 +27,10 @@ final class _Members implements MemberNotifier {
|
|||
final sessionsTold = <(Uri, UuidValue, DateTime, UuidValue?)>[];
|
||||
bool answering = true;
|
||||
|
||||
/// What a member's answer waits for, as one out of reach keeps it
|
||||
/// waiting; none by default.
|
||||
Future<void>? answerAfter;
|
||||
|
||||
@override
|
||||
Future<bool> accountGone(Uri member, UuidValue account) async => answering;
|
||||
|
||||
|
|
@ -34,6 +41,7 @@ final class _Members implements MemberNotifier {
|
|||
required DateTime endedBefore,
|
||||
UuidValue? keepSession,
|
||||
}) async {
|
||||
await answerAfter;
|
||||
sessionsTold.add((member, account, endedBefore, keepSession));
|
||||
return answering;
|
||||
}
|
||||
|
|
@ -241,6 +249,7 @@ void main() {
|
|||
final sessions = await sessionsOf(anna);
|
||||
expect(sessions, contains(annasPhone));
|
||||
expect(sessions, isNot(contains(annasLaptop)));
|
||||
await sessionNoticesDelivered();
|
||||
final (member, account, endedBefore, kept) =
|
||||
members.sessionsTold.first;
|
||||
expect(member, _todo);
|
||||
|
|
@ -307,6 +316,7 @@ void main() {
|
|||
kept,
|
||||
);
|
||||
expect(await sessionsOf(anna), {annasPhone, annasLaptop});
|
||||
await sessionNoticesDelivered();
|
||||
expect(members.sessionsTold, isEmpty);
|
||||
});
|
||||
|
||||
|
|
@ -566,10 +576,23 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('is done without waiting for the members: one out of reach '
|
||||
"holds no change up (the full reading's L5)", () async {
|
||||
final reached = Completer<void>();
|
||||
members.answerAfter = reached.future;
|
||||
await change();
|
||||
expect(members.sessionsTold, isEmpty);
|
||||
reached.complete();
|
||||
await sessionNoticesDelivered();
|
||||
expect(members.sessionsTold, hasLength(1));
|
||||
expect(await SessionsEndedNotice.db.count(session), 0);
|
||||
});
|
||||
|
||||
test('tells a member that missed it again, hourly, until it '
|
||||
'confirms', () async {
|
||||
members.answering = false;
|
||||
await change();
|
||||
await sessionNoticesDelivered();
|
||||
expect(await SessionsEndedNotice.db.count(session), 1);
|
||||
|
||||
members.answering = true;
|
||||
|
|
@ -1042,6 +1065,7 @@ void main() {
|
|||
expect(sessions, hasLength(1));
|
||||
expect(sessions, isNot(contains(annasPhone)));
|
||||
expect(sessions, isNot(contains(annasLaptop)));
|
||||
await sessionNoticesDelivered();
|
||||
expect(members.sessionsTold.single.$2, anna);
|
||||
expect(members.sessionsTold.single.$4, isNull);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import 'package:halkyon_base_server/src/register/deletion_notices.dart';
|
|||
import 'package:halkyon_base_server/src/register/instance_api.dart';
|
||||
import 'package:halkyon_base_server/src/register/register_protocol.dart';
|
||||
import 'package:halkyon_base_server/src/register/remote_register.dart';
|
||||
import 'package:halkyon_base_server/src/register/sessions_ended.dart'
|
||||
show sessionNoticesDelivered;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/testing.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
|
|
@ -677,6 +679,7 @@ void main() {
|
|||
(account: annaOnTodo, endedBefore: now),
|
||||
);
|
||||
// Every member is told to end the account's sessions too.
|
||||
await sessionNoticesDelivered();
|
||||
expect(members.sessionsTold.single, (_todo, annaOnTodo, now, null));
|
||||
|
||||
final forReset = await remote.beginPasswordSignIn(
|
||||
|
|
@ -809,6 +812,7 @@ void main() {
|
|||
password: device.newPassword(),
|
||||
keepSession: annasPhone,
|
||||
);
|
||||
await sessionNoticesDelivered();
|
||||
expect(members.sessionsTold.single, (
|
||||
_todo,
|
||||
annaOnTodo,
|
||||
|
|
|
|||
Loading…
Reference in a new issue