feat(base): handles hidden until their people go public, every answer about a new handle counted (ADR 0020, D5)
A handle's page and profiles.byHandle answer as for a handle nobody has until its person goes public: a handle fixed by a publish, or a display name, a bio, a link or an avatar (isPublic). A person's own profile, and the host's own lookups (personWithHandle), are unchanged. So every answer about a new handle now tells that an account has it, and countHandleCheck counts them together on the register, 30 an hour per person (or attempt) and 60 per network, refused beyond with ProfileRefusal.tooManyHandleChecks: profile.checkHandle and saving a profile with a handle new for the person, simpleLogin.checkHandle and signUp, keys.set with a handle, and Halkyon Learn's inbox.block and unblock, which look people up by handle. A person's own handle and what is no handle aren't counted. The SimpleLogin limiters of commit 3 give way to it, so a network's 60 are shared between people signed in and people new. The register's checkHandle, saveProfile, signUpWithSimpleLogin and setFirstKeys carry the visitor's network from the member that asked. Halkyon Learn's handle dialog words the new refusal (English, German). Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
e5b9578916
commit
7eb1961b9f
37 changed files with 674 additions and 134 deletions
File diff suppressed because one or more lines are too long
|
|
@ -719,7 +719,9 @@ class EndpointInbox extends _isc.EndpointRef {
|
|||
|
||||
/// Keeps the person with [handle] from writing on the caller's published
|
||||
/// studysets: what they wrote there, and write, shows to them alone.
|
||||
/// Throws [NotFoundException] for a handle nobody has.
|
||||
/// Throws [NotFoundException] for a handle nobody has; at most 30 an hour
|
||||
/// with the caller's other answers about handles, as checking one
|
||||
/// (`ProfileRefusal.tooManyHandleChecks`).
|
||||
_ida.Future<void> block(String handle) => caller.callServerEndpoint<void>(
|
||||
'inbox',
|
||||
'block',
|
||||
|
|
@ -727,7 +729,7 @@ class EndpointInbox extends _isc.EndpointRef {
|
|||
);
|
||||
|
||||
/// Lets the person with [handle] write on the caller's published
|
||||
/// studysets again.
|
||||
/// studysets again; counted as [block] is.
|
||||
_ida.Future<void> unblock(String handle) => caller.callServerEndpoint<void>(
|
||||
'inbox',
|
||||
'unblock',
|
||||
|
|
|
|||
|
|
@ -541,6 +541,7 @@
|
|||
"profileProblemLinkInvalid": "Links beginnen mit https:// oder http://.",
|
||||
"profileProblemAvatarInvalid": "Dieses Bild lässt sich nicht verwenden: Wähle ein PNG-, JPEG-, WebP- oder GIF-Bild.",
|
||||
"profileProblemTooManyLinkChecks": "Die Links wurden zu oft geprüft. Versuche es in einer Stunde noch einmal.",
|
||||
"profileProblemTooManyHandleChecks": "Es wurden zu viele Handles ausprobiert. Versuche es in einer Stunde noch einmal.",
|
||||
"publicProfilePicture": "Bild",
|
||||
"publicProfileChoosePicture": "Bild wählen",
|
||||
"publicProfileRemovePicture": "Bild entfernen",
|
||||
|
|
|
|||
|
|
@ -612,6 +612,7 @@
|
|||
"profileProblemLinkInvalid": "Links start with https:// or http://.",
|
||||
"profileProblemAvatarInvalid": "This picture can't be used: choose a PNG, JPEG, WebP or GIF image.",
|
||||
"profileProblemTooManyLinkChecks": "The links were checked too often. Try again in an hour.",
|
||||
"profileProblemTooManyHandleChecks": "Too many handles were tried. Try again in an hour.",
|
||||
"publicProfilePicture": "Picture",
|
||||
"publicProfileChoosePicture": "Choose picture",
|
||||
"publicProfileRemovePicture": "Remove picture",
|
||||
|
|
|
|||
|
|
@ -3244,6 +3244,12 @@ abstract class AppLocalizations {
|
|||
/// **'The links were checked too often. Try again in an hour.'**
|
||||
String get profileProblemTooManyLinkChecks;
|
||||
|
||||
/// No description provided for @profileProblemTooManyHandleChecks.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Too many handles were tried. Try again in an hour.'**
|
||||
String get profileProblemTooManyHandleChecks;
|
||||
|
||||
/// No description provided for @publicProfilePicture.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -1963,6 +1963,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
String get profileProblemTooManyLinkChecks =>
|
||||
'Die Links wurden zu oft geprüft. Versuche es in einer Stunde noch einmal.';
|
||||
|
||||
@override
|
||||
String get profileProblemTooManyHandleChecks =>
|
||||
'Es wurden zu viele Handles ausprobiert. Versuche es in einer Stunde noch einmal.';
|
||||
|
||||
@override
|
||||
String get publicProfilePicture => 'Bild';
|
||||
|
||||
|
|
|
|||
|
|
@ -1944,6 +1944,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
String get profileProblemTooManyLinkChecks =>
|
||||
'The links were checked too often. Try again in an hour.';
|
||||
|
||||
@override
|
||||
String get profileProblemTooManyHandleChecks =>
|
||||
'Too many handles were tried. Try again in an hour.';
|
||||
|
||||
@override
|
||||
String get publicProfilePicture => 'Picture';
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,10 @@ enum ProfileProblem() {
|
|||
avatarInvalid,
|
||||
|
||||
/// The links were checked too often in the last hour.
|
||||
tooManyLinkChecks;
|
||||
tooManyLinkChecks,
|
||||
|
||||
/// Too many handles were asked about in the last hour.
|
||||
tooManyHandleChecks;
|
||||
|
||||
/// The problems with a handle.
|
||||
static const handleProblems = {
|
||||
|
|
@ -118,6 +121,7 @@ enum ProfileProblem() {
|
|||
handleReserved,
|
||||
handleTaken,
|
||||
published,
|
||||
tooManyHandleChecks,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -679,20 +679,22 @@ class const _Picture({
|
|||
}
|
||||
}
|
||||
|
||||
String _problemText(AppLocalizations l10n, ProfileProblem problem) =>
|
||||
switch (problem) {
|
||||
ProfileProblem.handleInvalid => l10n.profileProblemHandleInvalid,
|
||||
ProfileProblem.handleReserved => l10n.profileProblemHandleReserved,
|
||||
ProfileProblem.handleTaken => l10n.profileProblemHandleTaken,
|
||||
ProfileProblem.published => l10n.profileProblemPublished,
|
||||
ProfileProblem.displayNameTooLong =>
|
||||
l10n.profileProblemDisplayNameTooLong,
|
||||
ProfileProblem.bioTooLong => l10n.profileProblemBioTooLong,
|
||||
ProfileProblem.tooManyLinks => l10n.profileProblemTooManyLinks,
|
||||
ProfileProblem.linkInvalid => l10n.profileProblemLinkInvalid,
|
||||
ProfileProblem.avatarInvalid => l10n.profileProblemAvatarInvalid,
|
||||
ProfileProblem.tooManyLinkChecks => l10n.profileProblemTooManyLinkChecks,
|
||||
};
|
||||
String _problemText(
|
||||
AppLocalizations l10n,
|
||||
ProfileProblem problem,
|
||||
) => switch (problem) {
|
||||
ProfileProblem.handleInvalid => l10n.profileProblemHandleInvalid,
|
||||
ProfileProblem.handleReserved => l10n.profileProblemHandleReserved,
|
||||
ProfileProblem.handleTaken => l10n.profileProblemHandleTaken,
|
||||
ProfileProblem.published => l10n.profileProblemPublished,
|
||||
ProfileProblem.displayNameTooLong => l10n.profileProblemDisplayNameTooLong,
|
||||
ProfileProblem.bioTooLong => l10n.profileProblemBioTooLong,
|
||||
ProfileProblem.tooManyLinks => l10n.profileProblemTooManyLinks,
|
||||
ProfileProblem.linkInvalid => l10n.profileProblemLinkInvalid,
|
||||
ProfileProblem.avatarInvalid => l10n.profileProblemAvatarInvalid,
|
||||
ProfileProblem.tooManyLinkChecks => l10n.profileProblemTooManyLinkChecks,
|
||||
ProfileProblem.tooManyHandleChecks => l10n.profileProblemTooManyHandleChecks,
|
||||
};
|
||||
|
||||
/// The profile's page on the web, to link back to, with a way to copy its
|
||||
/// address, and what linking back to it does (ADR 0011).
|
||||
|
|
|
|||
|
|
@ -103,6 +103,27 @@ void main() {
|
|||
expect(find.text('Saved'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('the dialog says when the instance answers no more about '
|
||||
'handles this hour, and nothing can be chosen', (tester) async {
|
||||
final profiles = FakeProfileGateway()..checksSpent = true;
|
||||
await showProfile(tester, profiles);
|
||||
|
||||
await tester.tap(find.text('Choose a handle'));
|
||||
await tester.pumpAndSettle();
|
||||
await typeHandle(tester, 'raisondetredev');
|
||||
|
||||
expect(
|
||||
find.text('Too many handles were tried. Try again in an hour.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
tester
|
||||
.widget<FilledButton>(find.widgetWithText(FilledButton, 'Choose'))
|
||||
.onPressed,
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('a profile saves a field when it is left or Enter is pressed, '
|
||||
'only what changed, without losing what is being written', (
|
||||
tester,
|
||||
|
|
|
|||
|
|
@ -39,13 +39,18 @@ class FakeProfileGateway({
|
|||
/// The handles checked, in order.
|
||||
final checked = <String>[];
|
||||
|
||||
/// Whether the instance answers no more about handles this hour.
|
||||
bool checksSpent = false;
|
||||
|
||||
@override
|
||||
Future<ProfileProblem?> checkHandle(String handle) async {
|
||||
if (fail case final error?) {
|
||||
throw error;
|
||||
}
|
||||
checked.add(handle);
|
||||
return _problemOf(handle);
|
||||
return checksSpent
|
||||
? ProfileProblem.tooManyHandleChecks
|
||||
: _problemOf(handle);
|
||||
}
|
||||
|
||||
ProfileProblem? _problemOf(String handle) {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ class InboxEndpoint extends Endpoint {
|
|||
|
||||
/// Keeps the person with [handle] from writing on the caller's published
|
||||
/// studysets: what they wrote there, and write, shows to them alone.
|
||||
/// Throws [NotFoundException] for a handle nobody has.
|
||||
/// Throws [NotFoundException] for a handle nobody has; at most 30 an hour
|
||||
/// with the caller's other answers about handles, as checking one
|
||||
/// (`ProfileRefusal.tooManyHandleChecks`).
|
||||
Future<void> block(Session session, String handle) async {
|
||||
final author = session.authenticated!.authUserId;
|
||||
final blocked = await _personOf(session, handle);
|
||||
|
|
@ -119,7 +121,7 @@ class InboxEndpoint extends Endpoint {
|
|||
}
|
||||
|
||||
/// Lets the person with [handle] write on the caller's published
|
||||
/// studysets again.
|
||||
/// studysets again; counted as [block] is.
|
||||
Future<void> unblock(Session session, String handle) async {
|
||||
final author = session.authenticated!.authUserId;
|
||||
final blocked = await _personOf(session, handle);
|
||||
|
|
@ -195,10 +197,19 @@ class InboxEndpoint extends Endpoint {
|
|||
);
|
||||
}
|
||||
|
||||
/// The person with [handle]; throws [NotFoundException] for none.
|
||||
Future<UuidValue> _personOf(Session session, String handle) async =>
|
||||
await personWithHandle(session, handle.trim()) ??
|
||||
(throw NotFoundException(what: 'person'));
|
||||
/// The person with [handle]; throws [NotFoundException] for none. Counted
|
||||
/// with the caller's other answers about handles (`countHandleCheck`),
|
||||
/// since whose a handle is tells that an account has it, while handles
|
||||
/// stay hidden until their people go public (ADR 0020, D5).
|
||||
Future<UuidValue> _personOf(Session session, String handle) async {
|
||||
await countHandleCheck(
|
||||
session,
|
||||
asker: session.authenticated!.authUserId.uuid,
|
||||
network: networkOf(session),
|
||||
);
|
||||
return await personWithHandle(session, handle.trim()) ??
|
||||
(throw NotFoundException(what: 'person'));
|
||||
}
|
||||
|
||||
/// Whether the thread's [payload] names the file [sha256] among those of
|
||||
/// its proposed content (`files`).
|
||||
|
|
|
|||
|
|
@ -261,6 +261,33 @@ void main() {
|
|||
},
|
||||
);
|
||||
|
||||
test('blocking and unblocking count among the answers about handles '
|
||||
'(30 an hour), as whose a handle is tells that an account has '
|
||||
'it', () async {
|
||||
// Her handle, new as her profile was made, counted one.
|
||||
for (var i = 1; i < handleChecksPerAsker; i++) {
|
||||
await countHandleCheck(session, asker: alice.uuid, network: null);
|
||||
}
|
||||
|
||||
for (final call in [
|
||||
() => endpoints.inbox.block(signedIn(alice), 'bob'),
|
||||
() => endpoints.inbox.unblock(signedIn(alice), 'bob'),
|
||||
]) {
|
||||
await expectLater(
|
||||
call(),
|
||||
throwsA(
|
||||
isA<ProfileRefusedException>().having(
|
||||
(error) => error.reason,
|
||||
'reason',
|
||||
ProfileRefusal.tooManyHandleChecks,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
expect(await endpoints.inbox.blockedPeople(signedIn(alice)), isEmpty);
|
||||
await endpoints.inbox.block(signedIn(bob), 'alice');
|
||||
});
|
||||
|
||||
test("a suggestion's files go to the author alone", () async {
|
||||
final publication = await japanese();
|
||||
final image = utf8.encode('an image');
|
||||
|
|
|
|||
|
|
@ -52,6 +52,17 @@
|
|||
decoy; mending ends every session of the account, as whoever came in through the old
|
||||
link may still be in; and SimpleLogin needs `SIGN_IN_DECOY_KEY` alone of ADR 0017's
|
||||
three keys, a server with a SimpleLogin app and without it stopping as it starts.
|
||||
Then handles (9, D5): `/@handle` and `profiles.byHandle` answer as for a handle nobody
|
||||
has until its person goes public (a handle fixed by a publish, or a display name, a
|
||||
bio, a link or an avatar), the host's own code still finding whose a handle is.
|
||||
Otherwise than proposed: not only asking whether a handle is free is limited, but
|
||||
every answer about a new handle, counted together (`countHandleCheck`, 30 an hour per
|
||||
person or attempt, 60 per network): saving a profile with one, signing up and the
|
||||
first keys with one, and Halkyon Learn's blocks by handle, as each tells the same, so
|
||||
a limit on checking alone would limit nothing; a person's own handle and what is no
|
||||
handle aren't counted. The handle suggested at sign-up is not counted: it tells at
|
||||
most that the name SimpleLogin shared is someone's handle already, once a sign-in.
|
||||
Step 3's deviations accepted by the developer (23 Oct, night).
|
||||
- **Builds on:** ADR 0010 (email sign-in), ADR 0011 (profile pages), ADR 0016 (the
|
||||
register), ADR 0017 (a password, TOTP and the keys).
|
||||
- **Changes:** ADR 0017's way in on an instance that offers SimpleLogin (its keys, their
|
||||
|
|
|
|||
|
|
@ -287,8 +287,8 @@ class EndpointKeys extends _isc.EndpointRef {
|
|||
/// ten minutes on this device, under [attempt]. Refused for an account
|
||||
/// with keys (`PasswordRefusal.passwordExists`), a setup out of range
|
||||
/// (`PasswordRefusal.invalidSetup`), a handle refused or missing
|
||||
/// (`ProfileRefusedException`), and without that sign-in
|
||||
/// (`OidcRefusal.unknownAttempt`).
|
||||
/// (`ProfileRefusedException`; a handle counted as `profile.checkHandle`
|
||||
/// counts it), and without that sign-in (`OidcRefusal.unknownAttempt`).
|
||||
_ida.Future<void> set({
|
||||
required _isc.UuidValue attempt,
|
||||
required _iec2fqr0.KeysSetup setup,
|
||||
|
|
@ -790,7 +790,8 @@ class EndpointProfile extends _isc.EndpointRef {
|
|||
/// Saves your public profile, making it if you have none. Throws
|
||||
/// [ProfileRefusedException] when it can't be saved (see
|
||||
/// [ProfileRefusal]): a handle someone else has, whatever its case, or a
|
||||
/// new handle once yours is fixed.
|
||||
/// new handle once yours is fixed. A new handle counts as [checkHandle]
|
||||
/// counts it.
|
||||
_ida.Future<_i8qdmv6n.Profile> save(
|
||||
String handle,
|
||||
String displayName,
|
||||
|
|
@ -823,9 +824,13 @@ class EndpointProfile extends _isc.EndpointRef {
|
|||
/// Why [handle] couldn't be yours, as saving it would refuse it: not a
|
||||
/// handle, reserved for the instance, someone else's whatever its case,
|
||||
/// or a new one once yours is fixed; null when it can, as your own can. For
|
||||
/// the app to say so while it is typed; saving checks again. Whose a handle
|
||||
/// is, anyone can find out already ([ProfilesEndpoint.byHandle]), so this
|
||||
/// tells nothing new.
|
||||
/// the app to say so while it is typed; saving checks again. Handles stay
|
||||
/// hidden until their people go public (Halkyon Learn's ADR 0020, D5), so
|
||||
/// this tells that an account exists: at most 30 an hour for you and 60
|
||||
/// from your network, with every other answer about a new handle (saving
|
||||
/// one, signing up with one), refused beyond
|
||||
/// ([ProfileRefusal.tooManyHandleChecks], thrown); your own handle and
|
||||
/// what is no handle aren't counted.
|
||||
_ida.Future<_ih722tf9.ProfileRefusal?> checkHandle(String handle) =>
|
||||
caller.callServerEndpoint<_ih722tf9.ProfileRefusal?>(
|
||||
'halkyon_base.profile',
|
||||
|
|
@ -873,7 +878,10 @@ class EndpointProfiles extends _isc.EndpointRef {
|
|||
String get name => 'halkyon_base.profiles';
|
||||
|
||||
/// The public profile with [handle], whatever its case. Throws
|
||||
/// [ProfileNotFoundException] when there is none.
|
||||
/// [ProfileNotFoundException] when there is none, and as well while its
|
||||
/// person hasn't gone public (Halkyon Learn's ADR 0020, D5): published
|
||||
/// nothing and left the rest of the profile empty (display name, bio,
|
||||
/// links, avatar).
|
||||
_ida.Future<_i8qdmv6n.Profile> byHandle(String handle) =>
|
||||
caller.callServerEndpoint<_i8qdmv6n.Profile>(
|
||||
'halkyon_base.profiles',
|
||||
|
|
@ -931,9 +939,10 @@ class EndpointSimpleLogin extends _isc.EndpointRef {
|
|||
/// said `newcomer`), could take [handle]: null where they could, else why
|
||||
/// not, as saving a profile would say. For the app to say so while it is
|
||||
/// typed; [signUp] checks again. At most 30 for an attempt and 60 from a
|
||||
/// network in an hour (`ProfileRefusal.tooManyHandleChecks`, thrown):
|
||||
/// each answer tells which handles exist. Refused
|
||||
/// (`OidcRefusal.unknownAttempt`) where no identity waits.
|
||||
/// network in an hour, with every other answer about a handle
|
||||
/// (`ProfileRefusal.tooManyHandleChecks`, thrown): each tells which
|
||||
/// handles exist. Refused (`OidcRefusal.unknownAttempt`) where no identity
|
||||
/// waits.
|
||||
_ida.Future<_ih722tf9.ProfileRefusal?> checkHandle({
|
||||
required _isc.UuidValue attempt,
|
||||
required String handle,
|
||||
|
|
@ -952,9 +961,10 @@ class EndpointSimpleLogin extends _isc.EndpointRef {
|
|||
/// keys [setup] describes, all made on the device beforehand; a session
|
||||
/// for it here. Refused, the identity still waiting: for a setup out of
|
||||
/// range (`PasswordRefusal.invalidSetup`) and a handle refused
|
||||
/// (`ProfileRefusedException`, as saving a profile refuses it); for an
|
||||
/// attempt unknown, taken or past its time (`OidcRefusal.unknownAttempt`),
|
||||
/// and called signed in (`OidcRefusal.wrongCaller`).
|
||||
/// (`ProfileRefusedException`, as saving a profile refuses it, the handle
|
||||
/// counted as [checkHandle] counts it); for an attempt unknown, taken or
|
||||
/// past its time (`OidcRefusal.unknownAttempt`), and called signed in
|
||||
/// (`OidcRefusal.wrongCaller`).
|
||||
_ida.Future<_iacc.AuthSuccess> signUp({
|
||||
required _isc.UuidValue attempt,
|
||||
required String handle,
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ enum ProfileRefusal implements _isc.SerializableModel {
|
|||
/// The links were checked too often in the last hour.
|
||||
tooManyLinkChecks,
|
||||
|
||||
/// The handles were checked too often in the last hour: their answers
|
||||
/// tell which handles exist.
|
||||
/// Too many answers about new handles in the last hour (checking them,
|
||||
/// saving, signing up or blocking with them): each tells which handles
|
||||
/// exist.
|
||||
tooManyHandleChecks;
|
||||
|
||||
static ProfileRefusal fromJson(String name) {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
| `emailCode` | `sendCode`: a code by mail, which proves the address and signs nobody in | anyone |
|
||||
| `account` | `delete` (with the password's proof where the account has one, `keysProof` for one that signs in with SimpleLogin), `signInWays` | signed in |
|
||||
| `instance` | `basics`: the app it serves, GitHub's client ID if any and when GitHub sign-in ends, whether codes by mail, password sign-in and SimpleLogin are on, the source link, the oldest app build, where data and backups are | anyone |
|
||||
| `profile` | `mine`, `save`, `checkHandle`, `checkLinks` (at most 10 an hour), `setAvatar`, `removeAvatar`, `remove`: the caller's own public profile | `identified` |
|
||||
| `profiles` | `byHandle`: anyone's public profile, whatever the handle's case | signed in |
|
||||
| `profile` | `mine`, `save`, `checkHandle` (with every answer about a new handle, at most 30 an hour per person and 60 per network), `checkLinks` (at most 10 an hour), `setAvatar`, `removeAvatar`, `remove`: the caller's own public profile | `identified` |
|
||||
| `profiles` | `byHandle`: anyone's public profile once they went public, whatever the handle's case | signed in |
|
||||
|
||||
- GitHub keeps only the numeric GitHub ID, in Serverpod's own table; email keeps only
|
||||
HMAC-SHA256 of the address, keyed with `emailSignInPepper` (`email_identity`), and its
|
||||
|
|
@ -44,7 +44,16 @@ Endpoints, under the nickname `base` in a host's client (`client.modules.base.em
|
|||
in the public storage at `avatars/<SHA-256>.png`. The page is at
|
||||
`https://<account domain>/@handle` (`ACCOUNT_DOMAIN`, else the server's public host),
|
||||
served by `addProfilePageRoute`, in English or German, its foot what the host's
|
||||
`profilePageNote` says.
|
||||
`profilePageNote` says. A handle stays its person's alone until they go public
|
||||
(Halkyon Learn's ADR 0020, D5: a handle fixed, or a display name, bio, link or
|
||||
avatar): until then its page and `profiles.byHandle` answer as for a handle nobody
|
||||
has, while the host's own code still finds whose it is (`personWithHandle`). Every
|
||||
answer about a new handle tells whether an account has it, so they are counted
|
||||
together (`countHandleCheck`, on the register: checking one, saving one, signing up
|
||||
or setting first keys with one, and the host's own lookups, such as Halkyon Learn's
|
||||
blocks), at most 30 an hour per person (or attempt) and 60 per network
|
||||
(`tooManyHandleChecks`); a person's own handle and what is no handle aren't
|
||||
counted.
|
||||
- SimpleLogin (Halkyon Learn's ADR 0020) is halkyon_oidc's sign-in (ADR 0023) with its
|
||||
SimpleLogin preset, on the register alone, which holds the instance's SimpleLogin app
|
||||
and exchanges every code; a member asks it to begin and to complete, and issues its own
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export 'src/password/passwords.dart'
|
|||
signInChallengeLifetime,
|
||||
signsInWithPassword,
|
||||
wrappedKeysLength;
|
||||
export 'src/profile/handle_checks.dart';
|
||||
export 'src/profile/link_verification.dart';
|
||||
export 'src/profile/profile_page.dart' show addProfilePageRoute, profilePage;
|
||||
export 'src/profile/profile_rules.dart'
|
||||
|
|
|
|||
|
|
@ -45,8 +45,9 @@ enum ProfileRefusal implements _is.SerializableModel {
|
|||
/// The links were checked too often in the last hour.
|
||||
tooManyLinkChecks,
|
||||
|
||||
/// The handles were checked too often in the last hour: their answers
|
||||
/// tell which handles exist.
|
||||
/// Too many answers about new handles in the last hour (checking them,
|
||||
/// saving, signing up or blocking with them): each tells which handles
|
||||
/// exist.
|
||||
tooManyHandleChecks;
|
||||
|
||||
static ProfileRefusal fromJson(String name) {
|
||||
|
|
|
|||
|
|
@ -53,14 +53,16 @@ Future<DeviceKeys> unlockKeys(
|
|||
/// ([PasswordRefusal.invalidSetup]) and an account with keys already, under
|
||||
/// either kind of password ([PasswordRefusal.passwordExists]);
|
||||
/// ([ProfileRefusedException]) for a handle refused as saving a profile
|
||||
/// refuses it, and one missing where the account has none or given where it
|
||||
/// has one ([ProfileRefusal.handleInvalid]); the sign-in's refusals as
|
||||
/// refuses it (a handle counted as checking it is, from [network]), and one
|
||||
/// missing where the account has none or given where it has one
|
||||
/// ([ProfileRefusal.handleInvalid]); the sign-in's refusals as
|
||||
/// [takeSimpleLoginConfirmation]'s. Nothing changes then.
|
||||
Future<void> setFirstKeys(
|
||||
Session session, {
|
||||
required UuidValue person,
|
||||
required UuidValue attempt,
|
||||
required KeysSetup setup,
|
||||
required String network,
|
||||
String? handle,
|
||||
}) async {
|
||||
if (!isKeysSetup(setup)) {
|
||||
|
|
@ -87,7 +89,13 @@ Future<void> setFirstKeys(
|
|||
try {
|
||||
await session.db.transaction((transaction) async {
|
||||
if (handle != null) {
|
||||
if (await refusalOfNewHandle(session, handle, transaction: transaction)
|
||||
if (await refusalOfNewHandle(
|
||||
session,
|
||||
handle,
|
||||
asker: person.uuid,
|
||||
network: network,
|
||||
transaction: transaction,
|
||||
)
|
||||
case final reason?) {
|
||||
throw ProfileRefusedException(reason: reason);
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue