feat(base)!: no anonymous accounts on the server (ADR 0020, D25)
The server's half of D25: every account has a way in, and the anonymous accounts left from before go with the daily pruning. - The module signs nobody in anonymously: `anonymousIdp` and `gitHubUpgrade` are gone, and so is linking GitHub at the register. - A password is set only by someone whose address is theirs (a `firstPassword` code) or who signs in with GitHub alone (a fresh sign-in and a `signUp` code); an anonymous account is refused `wrongCredentials` before any code is used. - SimpleLogin refuses to link onto a caller without `identified` (`wrongCaller`), the member's endpoint too, so nothing turns an anonymous account into one with a way in. - `pruneAnonymousPeople` deletes every anonymous account left, however new, never one identified. Learn and Todo schedule it as `prune-accounts` and cancel the old `prune-abandoned-anonymous-people` entry once. - Learn: someone without `identified` keeps no files; `FILE_LIMIT_ANONYMOUS_MB` is gone. `minimumAppBuild` is 23, as builds up to 22 start with an anonymous account. - Learn's sync tests make their person and session by SQL in the development database, as Serverpod stores sessions. - Docs, store listings and ADRs 0010, 0014, 0016, 0017 and 0019 amended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
5b9df73756
commit
87671208df
1 changed files with 837 additions and 1353 deletions
|
|
@ -67,9 +67,6 @@ final class _Register implements AccountRegister {
|
|||
/// When the session dismissing a warning was made, by person.
|
||||
final dismissedMadeAt = <UuidValue, DateTime?>{};
|
||||
|
||||
/// Those [setPassword] was told are anonymous on the member.
|
||||
final anonymousHere_ = <UuidValue>{};
|
||||
|
||||
/// What a password sign-in proves; null refuses it.
|
||||
PasswordProof? proof;
|
||||
|
||||
|
|
@ -209,16 +206,6 @@ final class _Register implements AccountRegister {
|
|||
return request;
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> linkGitHub(
|
||||
Session session, {
|
||||
required UuidValue person,
|
||||
required String gitHubUserId,
|
||||
}) => _answer('linkGitHub', () {
|
||||
accounts.add(person);
|
||||
gitHub[gitHubUserId] = person;
|
||||
});
|
||||
|
||||
@override
|
||||
Future<UuidValue?> accountWithGitHub(Session session, String gitHubUserId) =>
|
||||
_answer('accountWithGitHub', () => gitHub[gitHubUserId]);
|
||||
|
|
@ -477,7 +464,6 @@ final class _Register implements AccountRegister {
|
|||
UuidValue? request,
|
||||
String? code,
|
||||
bool freshGitHubSignIn = false,
|
||||
bool anonymousHere = false,
|
||||
UuidValue? keepSession,
|
||||
}) => _answer('setPassword', () {
|
||||
add(person);
|
||||
|
|
@ -485,9 +471,6 @@ final class _Register implements AccountRegister {
|
|||
if (freshGitHubSignIn) {
|
||||
freshGitHubSignIns.add(person);
|
||||
}
|
||||
if (anonymousHere) {
|
||||
anonymousHere_.add(person);
|
||||
}
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -750,7 +733,9 @@ void main() {
|
|||
late Serverpod pod;
|
||||
late _Register register;
|
||||
|
||||
/// Anonymous here, signed in on her phone.
|
||||
/// Not identified here, signed in on her phone: an anonymous account
|
||||
/// left from before (ADR 0020, D25), or one the register linked whose
|
||||
/// becoming identified here failed (the full reading's L7).
|
||||
late UuidValue anna;
|
||||
late UuidValue annasPhone;
|
||||
|
||||
|
|
@ -1095,17 +1080,7 @@ void main() {
|
|||
|
||||
expect(register.passwords, {anna});
|
||||
expect(register.freshGitHubSignIns, isEmpty);
|
||||
// Anna was anonymous here, which the register can't know (the
|
||||
// full reading's M1).
|
||||
expect(register.anonymousHere_, {anna});
|
||||
expect(await scopesOf(anna), contains(identified.name));
|
||||
expect(
|
||||
await AnonymousAccount.db.count(
|
||||
session,
|
||||
where: (t) => t.authUserId.equals(anna),
|
||||
),
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
|
|
@ -1201,16 +1176,44 @@ void main() {
|
|||
|
||||
test('begins signed in for the caller', () async {
|
||||
await endpoints.simpleLogin.begin(
|
||||
asAnna(),
|
||||
asBen(),
|
||||
back: SimpleLoginReturn.loopback,
|
||||
);
|
||||
|
||||
expect(register.simpleLoginsBegun.single, (
|
||||
marker: '$testApp-loopback',
|
||||
caller: anna,
|
||||
caller: ben,
|
||||
));
|
||||
});
|
||||
|
||||
test('refuses a caller not identified here, asking the register '
|
||||
'nothing (ADR 0020, D25)', () async {
|
||||
Matcher wrongCaller() => throwsA(
|
||||
isA<OidcRefusedException>().having(
|
||||
(error) => error.reason,
|
||||
'reason',
|
||||
OidcRefusal.wrongCaller,
|
||||
),
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
endpoints.simpleLogin.begin(
|
||||
asAnna(),
|
||||
back: SimpleLoginReturn.loopback,
|
||||
),
|
||||
wrongCaller(),
|
||||
);
|
||||
await expectLater(
|
||||
endpoints.simpleLogin.complete(
|
||||
asAnna(),
|
||||
attempt: const Uuid().v7obj(),
|
||||
code: 'code',
|
||||
),
|
||||
wrongCaller(),
|
||||
);
|
||||
expect(register.asked, isEmpty);
|
||||
});
|
||||
|
||||
test('signs the account the register found in here, made a person '
|
||||
'here the first time', () async {
|
||||
final carla = const Uuid().v7obj();
|
||||
|
|
@ -1241,31 +1244,24 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('makes the caller identified here once the register added '
|
||||
'SimpleLogin to their account', () async {
|
||||
test("adds SimpleLogin at the register to the caller's account, "
|
||||
'who stays identified here', () async {
|
||||
register.simpleLoginProof = SimpleLoginProof(
|
||||
outcome: SimpleLoginOutcome.linked,
|
||||
);
|
||||
|
||||
final completed = await endpoints.simpleLogin.complete(
|
||||
asAnna(),
|
||||
asBen(),
|
||||
attempt: const Uuid().v7obj(),
|
||||
code: 'code',
|
||||
);
|
||||
|
||||
expect(completed.outcome, SimpleLoginOutcome.linked);
|
||||
expect(completed.session, isNull);
|
||||
expect(register.simpleLoginLinked, {anna});
|
||||
expect(await scopesOf(anna), contains(identified.name));
|
||||
expect(register.simpleLoginLinked, {ben});
|
||||
expect(await scopesOf(ben), contains(identified.name));
|
||||
expect(
|
||||
await AnonymousAccount.db.find(
|
||||
session,
|
||||
where: (t) => t.authUserId.equals(anna),
|
||||
),
|
||||
isEmpty,
|
||||
);
|
||||
expect(
|
||||
(await endpoints.account.signInWays(asAnna())).simpleLogin,
|
||||
(await endpoints.account.signInWays(asBen())).simpleLogin,
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
|
@ -1297,7 +1293,7 @@ void main() {
|
|||
|
||||
await expectLater(
|
||||
endpoints.simpleLogin.complete(
|
||||
asAnna(),
|
||||
asBen(),
|
||||
attempt: const Uuid().v7obj(),
|
||||
code: 'code',
|
||||
),
|
||||
|
|
@ -1309,7 +1305,7 @@ void main() {
|
|||
),
|
||||
),
|
||||
);
|
||||
expect(await scopesOf(anna), isNot(contains(identified.name)));
|
||||
expect(register.simpleLoginLinked, isEmpty);
|
||||
});
|
||||
|
||||
test('checks a handle for someone new at the register, the device '
|
||||
|
|
@ -1759,8 +1755,8 @@ void main() {
|
|||
expect(register.deletedWithKeys.single?.attempt, attempt);
|
||||
});
|
||||
|
||||
test('of an anonymous person deletes it here alone, once the '
|
||||
'register says it has no such account', () async {
|
||||
test('of an anonymous account left from before deletes it here '
|
||||
'alone, once the register says it has no such account', () async {
|
||||
await endpoints.account.delete(asAnna());
|
||||
|
||||
expect(register.asked, ['knownAccounts']);
|
||||
|
|
|
|||
Loading…
Reference in a new issue