@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Adapter.d.mts +4 -2
- package/dist/Adapter.mjs +3 -1
- package/dist/internal/authority-snapshot.mjs +53 -0
- package/dist/internal/authority-snapshot.mjs.map +1 -0
- package/dist/internal/configuration.d.mts +1 -1
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/models/password-model.d.mts +3 -53
- package/dist/internal/models/password-model.mjs +0 -2
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +30 -0
- package/dist/internal/native-sql-table.mjs +104 -0
- package/dist/internal/native-sql-table.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +1 -1
- package/dist/internal/passkeys.mjs +1 -1
- package/dist/internal/password-attempts.d.mts +12 -0
- package/dist/internal/password-attempts.mjs +197 -0
- package/dist/internal/password-attempts.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +14 -11
- package/dist/internal/password-kernel.mjs +39 -202
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +2 -0
- package/dist/internal/persistence.mjs +4 -4
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/session-kernel.d.mts +3 -1
- package/dist/internal/session-kernel.mjs +22 -12
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-persistence.mjs +3 -1
- package/dist/internal/sql-persistence.mjs.map +1 -1
- package/dist/internal/storage-mapping.mjs +1 -30
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-tables.d.mts +0 -43
- package/dist/internal/storage-tables.mjs +0 -26
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.d.mts +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +9 -4
- package/src/internal/authority-snapshot.ts +91 -0
- package/src/internal/configuration.ts +1 -6
- package/src/internal/models/password-model.ts +0 -64
- package/src/internal/native-sql-table.ts +164 -0
- package/src/internal/password-attempts.ts +466 -0
- package/src/internal/password-kernel.ts +102 -623
- package/src/internal/persistence.ts +5 -10
- package/src/internal/session-kernel.ts +89 -27
- package/src/internal/sql-persistence.ts +2 -0
- package/src/internal/storage-mapping.ts +0 -29
- package/src/internal/storage-tables.ts +0 -15
|
@@ -16,10 +16,7 @@ import {
|
|
|
16
16
|
snapshotPasswordRequirement,
|
|
17
17
|
snapshotPasswordRevision,
|
|
18
18
|
type PasswordAction,
|
|
19
|
-
type PasswordAttemptDecision,
|
|
20
|
-
type PasswordCredentialSnapshot,
|
|
21
19
|
type PasswordMutationDecision,
|
|
22
|
-
type PasswordAttemptPolicy,
|
|
23
20
|
} from "@yielded/auth/Password";
|
|
24
21
|
import { reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
25
22
|
import {
|
|
@@ -34,16 +31,23 @@ import {
|
|
|
34
31
|
type SecurityRevision,
|
|
35
32
|
} from "@yielded/auth/Sessions";
|
|
36
33
|
import { Cause, Context, DateTime, Effect, Option, Redacted, Schema } from "effect";
|
|
34
|
+
import type { SqlClient } from "effect/sql/SqlClient";
|
|
37
35
|
import type * as SqlError from "effect/sql/SqlError";
|
|
38
36
|
|
|
39
37
|
import { PersistenceMappingError } from "./mapping-error";
|
|
40
38
|
import {
|
|
41
39
|
type AnyPasswordPersistenceMapping,
|
|
42
|
-
type PasswordAttemptAction,
|
|
43
|
-
type PasswordRateScopeKind,
|
|
44
|
-
type PasswordScopeKeys,
|
|
45
40
|
requiredPasswordConstraints,
|
|
46
41
|
} from "./models/password-model";
|
|
42
|
+
import { makeNativeSqlTables, sqlMapping, type NativeSqlTables } from "./native-sql-table";
|
|
43
|
+
import {
|
|
44
|
+
makePasswordAttempts,
|
|
45
|
+
samePasswordCredentialSnapshot as sameCredentialSnapshot,
|
|
46
|
+
} from "./password-attempts";
|
|
47
|
+
import {
|
|
48
|
+
CurrentPasswordPreparedTransaction,
|
|
49
|
+
PasswordPreparedPostconditions,
|
|
50
|
+
} from "./PasswordPreparedPostconditions";
|
|
47
51
|
import {
|
|
48
52
|
CurrentProofSql,
|
|
49
53
|
type ProofSqlConfiguration,
|
|
@@ -53,7 +57,7 @@ import {
|
|
|
53
57
|
/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */
|
|
54
58
|
import type { QueryFailure } from "./query-operations";
|
|
55
59
|
import type { QueryOperations, SqlFragment, SqlColumn } from "./query-operations";
|
|
56
|
-
import
|
|
60
|
+
import { NativeDatabase } from "./transaction-kernel";
|
|
57
61
|
|
|
58
62
|
type AdapterFailure = QueryFailure | PersistenceMappingError | SqlError.SqlError;
|
|
59
63
|
|
|
@@ -104,23 +108,18 @@ export interface PasswordSqlConfiguration {
|
|
|
104
108
|
|
|
105
109
|
type Database = PasswordSqlDatabase;
|
|
106
110
|
|
|
107
|
-
interface ScopeEntry {
|
|
108
|
-
readonly kind: PasswordRateScopeKind;
|
|
109
|
-
readonly key: string;
|
|
110
|
-
readonly limit: number;
|
|
111
|
-
readonly windowMillis: number;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
111
|
export const makePasswordKernel = <
|
|
115
112
|
Fragment extends SqlFragment = SqlFragment,
|
|
116
113
|
Column extends SqlColumn = SqlColumn,
|
|
117
114
|
>(
|
|
118
115
|
operations: QueryOperations<Fragment, Column>,
|
|
119
116
|
proofs: Pick<ReturnType<typeof makeProofKernel>, "completeProofPlanIn">,
|
|
117
|
+
nativeTables: (client: SqlClient, database: object) => NativeSqlTables = (client) =>
|
|
118
|
+
makeNativeSqlTables(client),
|
|
120
119
|
) => {
|
|
121
120
|
type Mapping = AnyPasswordPersistenceMapping<Fragment>;
|
|
122
121
|
|
|
123
|
-
const { and, eq,
|
|
122
|
+
const { and, eq, inArray, lte, column, updateValues } = operations;
|
|
124
123
|
const { completeProofPlanIn } = proofs;
|
|
125
124
|
const unavailable = () => PasswordUnavailable.make({});
|
|
126
125
|
|
|
@@ -200,23 +199,6 @@ export const makePasswordKernel = <
|
|
|
200
199
|
state: column(mapping.attempt.table, mapping.attempt.state),
|
|
201
200
|
});
|
|
202
201
|
|
|
203
|
-
const scopeColumns = (mapping: Mapping) => ({
|
|
204
|
-
moduleId: column(mapping.rateScope.table, mapping.rateScope.moduleId),
|
|
205
|
-
action: column(mapping.rateScope.table, mapping.rateScope.action),
|
|
206
|
-
scopeKind: column(mapping.rateScope.table, mapping.rateScope.scopeKind),
|
|
207
|
-
scopeKey: column(mapping.rateScope.table, mapping.rateScope.scopeKey),
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const chargeColumns = (mapping: Mapping) => ({
|
|
211
|
-
moduleId: column(mapping.charge.table, mapping.charge.moduleId),
|
|
212
|
-
action: column(mapping.charge.table, mapping.charge.action),
|
|
213
|
-
scopeKind: column(mapping.charge.table, mapping.charge.scopeKind),
|
|
214
|
-
scopeKey: column(mapping.charge.table, mapping.charge.scopeKey),
|
|
215
|
-
attemptId: column(mapping.charge.table, mapping.charge.attemptId),
|
|
216
|
-
occurredAt: column(mapping.charge.table, mapping.charge.occurredAt),
|
|
217
|
-
retentionUntil: column(mapping.charge.table, mapping.charge.retentionUntil),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
202
|
const commandColumns = (mapping: Mapping) => ({
|
|
221
203
|
moduleId: column(mapping.command.table, mapping.command.moduleId),
|
|
222
204
|
commandId: column(mapping.command.table, mapping.command.commandId),
|
|
@@ -270,28 +252,6 @@ export const makePasswordKernel = <
|
|
|
270
252
|
const sameIdentifier = (left: LoginIdentifier, right: LoginIdentifier) =>
|
|
271
253
|
left.namespace === right.namespace && left.value === right.value;
|
|
272
254
|
|
|
273
|
-
const sameCredentialSnapshot = (
|
|
274
|
-
left: PasswordCredentialSnapshot,
|
|
275
|
-
right: PasswordCredentialSnapshot,
|
|
276
|
-
) =>
|
|
277
|
-
left.moduleId === right.moduleId &&
|
|
278
|
-
left.revision.subjectId === right.revision.subjectId &&
|
|
279
|
-
left.revision.securityRevision === right.revision.securityRevision &&
|
|
280
|
-
left.credentialId === right.credentialId &&
|
|
281
|
-
left.credentialRevision === right.credentialRevision &&
|
|
282
|
-
left.verifierVersion === right.verifierVersion &&
|
|
283
|
-
left.normalization === right.normalization &&
|
|
284
|
-
left.identifierVerifiedAtMillis === right.identifierVerifiedAtMillis &&
|
|
285
|
-
left.revision.credentials.length === right.revision.credentials.length &&
|
|
286
|
-
left.revision.credentials.every((entry) =>
|
|
287
|
-
right.revision.credentials.some(
|
|
288
|
-
(other) => other.credentialId === entry.credentialId && other.revision === entry.revision,
|
|
289
|
-
),
|
|
290
|
-
) &&
|
|
291
|
-
left.identifierBindingRevision === right.identifierBindingRevision &&
|
|
292
|
-
sameIdentifier(left.identifier, right.identifier) &&
|
|
293
|
-
Redacted.value(left.verifier) === Redacted.value(right.verifier);
|
|
294
|
-
|
|
295
255
|
const evidenceSatisfiedAt = (
|
|
296
256
|
evidence: AuthenticationEvidence,
|
|
297
257
|
requirement: AuthenticationRequirement,
|
|
@@ -482,145 +442,6 @@ export const makePasswordKernel = <
|
|
|
482
442
|
};
|
|
483
443
|
});
|
|
484
444
|
|
|
485
|
-
const scopeEntries = (
|
|
486
|
-
keys: PasswordScopeKeys,
|
|
487
|
-
policy: PasswordAttemptPolicy,
|
|
488
|
-
): ReadonlyArray<ScopeEntry> => {
|
|
489
|
-
const values: ScopeEntry[] = [
|
|
490
|
-
{ kind: "action", key: keys.action, ...policy.action },
|
|
491
|
-
{ kind: "identifier", key: keys.identifier, ...policy.identifier },
|
|
492
|
-
];
|
|
493
|
-
|
|
494
|
-
if (keys.subject !== undefined)
|
|
495
|
-
values.push({ kind: "subject", key: keys.subject, ...policy.subject });
|
|
496
|
-
|
|
497
|
-
return values;
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
const lockScope = Effect.fn("DrizzlePassword.lockScope")(function* (
|
|
501
|
-
mapping: Mapping,
|
|
502
|
-
configuration: PasswordSqlConfiguration,
|
|
503
|
-
moduleId: string,
|
|
504
|
-
action: PasswordAttemptAction,
|
|
505
|
-
entry: ScopeEntry,
|
|
506
|
-
) {
|
|
507
|
-
const database = yield* CurrentPasswordSql;
|
|
508
|
-
|
|
509
|
-
const query = database.insert(mapping.rateScope.table).values(
|
|
510
|
-
mapping.rateScope.encodeInsert({
|
|
511
|
-
moduleId,
|
|
512
|
-
action,
|
|
513
|
-
scopeKind: entry.kind,
|
|
514
|
-
scopeKey: entry.key,
|
|
515
|
-
}),
|
|
516
|
-
);
|
|
517
|
-
|
|
518
|
-
yield* configuration.insertIfAbsent(query, mapping.rateScope.scopeKey, entry.key);
|
|
519
|
-
const c = scopeColumns(mapping);
|
|
520
|
-
|
|
521
|
-
yield* selectRows(
|
|
522
|
-
database
|
|
523
|
-
.select()
|
|
524
|
-
.from(mapping.rateScope.table)
|
|
525
|
-
.where(
|
|
526
|
-
and(
|
|
527
|
-
eq(c.moduleId, moduleId),
|
|
528
|
-
eq(c.action, action),
|
|
529
|
-
eq(c.scopeKind, entry.kind),
|
|
530
|
-
eq(c.scopeKey, entry.key),
|
|
531
|
-
),
|
|
532
|
-
)
|
|
533
|
-
.limit(1),
|
|
534
|
-
configuration.locking,
|
|
535
|
-
);
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
const scopeAdmits = Effect.fn("DrizzlePassword.scopeAdmits")(function* (
|
|
539
|
-
mapping: Mapping,
|
|
540
|
-
moduleId: string,
|
|
541
|
-
action: PasswordAttemptAction,
|
|
542
|
-
entry: ScopeEntry,
|
|
543
|
-
policy: PasswordAttemptPolicy,
|
|
544
|
-
identifier: LoginIdentifier,
|
|
545
|
-
nativeSubjectId: unknown | undefined,
|
|
546
|
-
now: number,
|
|
547
|
-
) {
|
|
548
|
-
const database = yield* CurrentPasswordSql;
|
|
549
|
-
|
|
550
|
-
const c = chargeColumns(mapping);
|
|
551
|
-
|
|
552
|
-
const charges = yield* selectRows(
|
|
553
|
-
database
|
|
554
|
-
.select({ attemptId: c.attemptId })
|
|
555
|
-
.from(mapping.charge.table)
|
|
556
|
-
.where(
|
|
557
|
-
and(
|
|
558
|
-
eq(c.moduleId, moduleId),
|
|
559
|
-
eq(c.action, action),
|
|
560
|
-
eq(c.scopeKind, entry.kind),
|
|
561
|
-
eq(c.scopeKey, entry.key),
|
|
562
|
-
gte(c.occurredAt, mapping.encodeInstant(now - entry.windowMillis)),
|
|
563
|
-
),
|
|
564
|
-
)
|
|
565
|
-
.limit(entry.limit),
|
|
566
|
-
true,
|
|
567
|
-
);
|
|
568
|
-
|
|
569
|
-
if (charges.length >= entry.limit) return false;
|
|
570
|
-
const a = attemptColumns(mapping);
|
|
571
|
-
|
|
572
|
-
// Retention is the pending horizon. Expired rows stay until cleanup, but they
|
|
573
|
-
// no longer consume the admission budget after that horizon.
|
|
574
|
-
const pendingWhere = and(
|
|
575
|
-
entry.kind === "action"
|
|
576
|
-
? and(eq(a.moduleId, moduleId), eq(a.action, action))
|
|
577
|
-
: entry.kind === "identifier"
|
|
578
|
-
? and(
|
|
579
|
-
eq(a.moduleId, moduleId),
|
|
580
|
-
eq(a.action, action),
|
|
581
|
-
eq(a.identifierNamespace, identifier.namespace),
|
|
582
|
-
eq(a.identifierValue, identifier.value),
|
|
583
|
-
)
|
|
584
|
-
: and(eq(a.moduleId, moduleId), eq(a.action, action), eq(a.subjectId, nativeSubjectId)),
|
|
585
|
-
eq(a.state, "pending"),
|
|
586
|
-
gt(a.retentionUntil, mapping.encodeInstant(now)),
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
const pending = yield* selectRows(
|
|
590
|
-
database
|
|
591
|
-
.select({ attemptId: a.attemptId })
|
|
592
|
-
.from(mapping.attempt.table)
|
|
593
|
-
.where(pendingWhere)
|
|
594
|
-
.limit(policy.maximumPending),
|
|
595
|
-
true,
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
return pending.length < policy.maximumPending;
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
const insertCharge = Effect.fn("Drizzle.insertCharge")(function* (
|
|
602
|
-
mapping: Mapping,
|
|
603
|
-
moduleId: string,
|
|
604
|
-
action: PasswordAttemptAction,
|
|
605
|
-
entry: ScopeEntry,
|
|
606
|
-
attemptId: any,
|
|
607
|
-
now: number,
|
|
608
|
-
) {
|
|
609
|
-
const database = yield* CurrentPasswordSql;
|
|
610
|
-
|
|
611
|
-
return yield* database.insert(mapping.charge.table).values(
|
|
612
|
-
mapping.charge.encodeInsert({
|
|
613
|
-
moduleId,
|
|
614
|
-
action,
|
|
615
|
-
scopeKind: entry.kind,
|
|
616
|
-
scopeKey: entry.key,
|
|
617
|
-
attemptId,
|
|
618
|
-
occurredAtMillis: now,
|
|
619
|
-
retentionUntilMillis: now + entry.windowMillis,
|
|
620
|
-
}),
|
|
621
|
-
);
|
|
622
|
-
});
|
|
623
|
-
|
|
624
445
|
const owned = <A, E, R>(
|
|
625
446
|
database: Database,
|
|
626
447
|
mapping: Mapping,
|
|
@@ -650,6 +471,25 @@ export const makePasswordKernel = <
|
|
|
650
471
|
: run;
|
|
651
472
|
};
|
|
652
473
|
|
|
474
|
+
// A coordinator that accepts postconditions re-runs this check against its
|
|
475
|
+
// current transaction after application work, before commit.
|
|
476
|
+
const registerFinal = Effect.fnUntraced(function* <E>(
|
|
477
|
+
check: Effect.Effect<boolean, E, CurrentPasswordSql>,
|
|
478
|
+
) {
|
|
479
|
+
const postconditions = yield* Effect.serviceOption(PasswordPreparedPostconditions);
|
|
480
|
+
|
|
481
|
+
if (Option.isNone(postconditions)) return;
|
|
482
|
+
|
|
483
|
+
const postcondition = Effect.gen(function* () {
|
|
484
|
+
const transaction = yield* CurrentPasswordPreparedTransaction;
|
|
485
|
+
|
|
486
|
+
if (!(yield* check.pipe(Effect.provideService(CurrentPasswordSql, transaction))))
|
|
487
|
+
return yield* unavailable();
|
|
488
|
+
}).pipe(translateFailure);
|
|
489
|
+
|
|
490
|
+
if (!postconditions.value.register(postcondition)) return yield* unavailable();
|
|
491
|
+
});
|
|
492
|
+
|
|
653
493
|
const safeRead = <A, E, R>(
|
|
654
494
|
database: Database,
|
|
655
495
|
configuration: PasswordSqlConfiguration,
|
|
@@ -1067,55 +907,16 @@ export const makePasswordKernel = <
|
|
|
1067
907
|
);
|
|
1068
908
|
yield* insertCommand(mapping, input, "add-password", commandNow ?? authority.now);
|
|
1069
909
|
|
|
1070
|
-
const
|
|
910
|
+
const applied = checkMutationApplied(
|
|
1071
911
|
mapping,
|
|
1072
|
-
input
|
|
912
|
+
input,
|
|
1073
913
|
authority.nativeSubjectId,
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const authorityInserted = (yield* database
|
|
1078
|
-
.select()
|
|
1079
|
-
.from(mapping.authorityCredential.table)
|
|
1080
|
-
.where(
|
|
1081
|
-
and(
|
|
1082
|
-
eq(authorityCredentialColumns(mapping).subjectId, authority.nativeSubjectId),
|
|
1083
|
-
eq(authorityCredentialColumns(mapping).credentialId, credentialId),
|
|
1084
|
-
eq(authorityCredentialColumns(mapping).revision, credentialRevision),
|
|
1085
|
-
),
|
|
1086
|
-
)
|
|
1087
|
-
.limit(1))[0];
|
|
1088
|
-
|
|
1089
|
-
const subjectUpdated = (yield* database
|
|
1090
|
-
.select()
|
|
1091
|
-
.from(mapping.subject.table)
|
|
1092
|
-
.where(
|
|
1093
|
-
and(
|
|
1094
|
-
eq(subjectColumns(mapping).id, authority.nativeSubjectId),
|
|
1095
|
-
eq(subjectColumns(mapping).securityRevision, nextSecurityRevision),
|
|
1096
|
-
),
|
|
1097
|
-
)
|
|
1098
|
-
.limit(1))[0];
|
|
914
|
+
revisions,
|
|
915
|
+
commandNow ?? authority.now,
|
|
916
|
+
);
|
|
1099
917
|
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1102
|
-
inserted[mapping.credential.credentialId] !== credentialId ||
|
|
1103
|
-
inserted[mapping.credential.credentialRevision] !== credentialRevision ||
|
|
1104
|
-
inserted[mapping.credential.verifierVersion] !== verifierVersion ||
|
|
1105
|
-
inserted[mapping.credential.verifier] !== Redacted.value(input.replacement.verifier) ||
|
|
1106
|
-
inserted[mapping.credential.normalization] !== input.replacement.normalization ||
|
|
1107
|
-
authorityInserted === undefined ||
|
|
1108
|
-
subjectUpdated === undefined ||
|
|
1109
|
-
!(yield* mutationPostconditions(
|
|
1110
|
-
mapping,
|
|
1111
|
-
input,
|
|
1112
|
-
authority.nativeSubjectId,
|
|
1113
|
-
subjectUpdated,
|
|
1114
|
-
authorityInserted,
|
|
1115
|
-
commandNow ?? authority.now,
|
|
1116
|
-
))
|
|
1117
|
-
)
|
|
1118
|
-
return yield* unavailable();
|
|
918
|
+
if (!(yield* applied)) return yield* unavailable();
|
|
919
|
+
yield* registerFinal(applied);
|
|
1119
920
|
|
|
1120
921
|
return true;
|
|
1121
922
|
});
|
|
@@ -1139,267 +940,19 @@ export const makePasswordKernel = <
|
|
|
1139
940
|
).pipe(Effect.mapError(unavailable));
|
|
1140
941
|
const hooks = yield* LifecycleHooks;
|
|
1141
942
|
|
|
1142
|
-
return
|
|
1143
|
-
|
|
1144
|
-
Effect.gen(function* () {
|
|
1145
|
-
const attemptId = yield* allocate(
|
|
1146
|
-
configuration.mode,
|
|
1147
|
-
mapping.allocateAttemptId,
|
|
1148
|
-
mapping.allocateAttemptIdSync,
|
|
1149
|
-
);
|
|
1150
|
-
|
|
1151
|
-
return yield* owned(
|
|
1152
|
-
database,
|
|
1153
|
-
mapping,
|
|
1154
|
-
configuration,
|
|
1155
|
-
Effect.gen(function* () {
|
|
1156
|
-
const transaction = yield* CurrentPasswordSql;
|
|
1157
|
-
const journal = yield* CurrentCommitJournal;
|
|
1158
|
-
|
|
1159
|
-
const resolved = yield* resolveCredential(
|
|
1160
|
-
mapping,
|
|
1161
|
-
input.moduleId,
|
|
1162
|
-
input.identifier,
|
|
1163
|
-
input.subjectId,
|
|
1164
|
-
configuration.locking,
|
|
1165
|
-
);
|
|
1166
|
-
|
|
1167
|
-
const keys = mapping.scopeKeys({
|
|
1168
|
-
moduleId: input.moduleId,
|
|
1169
|
-
action: input.action,
|
|
1170
|
-
identifier: input.identifier,
|
|
1171
|
-
...(resolved?.snapshot === undefined
|
|
1172
|
-
? {}
|
|
1173
|
-
: { subjectId: resolved.snapshot.revision.subjectId }),
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
const scopes = scopeEntries(keys, input.policy);
|
|
1177
|
-
const admitted: ScopeEntry[] = [];
|
|
1178
|
-
|
|
1179
|
-
yield* lockScope(mapping, configuration, input.moduleId, input.action, scopes[0]!);
|
|
1180
|
-
let currentNow = yield* nowMillis;
|
|
943
|
+
if (!validConstraints(mapping)) return yield* unavailable();
|
|
944
|
+
const root = yield* NativeDatabase;
|
|
1181
945
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
input.identifier,
|
|
1190
|
-
resolved?.nativeSubjectId,
|
|
1191
|
-
currentNow,
|
|
1192
|
-
)
|
|
1193
|
-
)
|
|
1194
|
-
admitted.push(scopes[0]!);
|
|
1195
|
-
if (admitted.length === 1) {
|
|
1196
|
-
for (const entry of scopes.slice(1)) {
|
|
1197
|
-
yield* lockScope(mapping, configuration, input.moduleId, input.action, entry);
|
|
1198
|
-
currentNow = yield* nowMillis;
|
|
1199
|
-
if (
|
|
1200
|
-
yield* scopeAdmits(
|
|
1201
|
-
mapping,
|
|
1202
|
-
input.moduleId,
|
|
1203
|
-
input.action,
|
|
1204
|
-
entry,
|
|
1205
|
-
input.policy,
|
|
1206
|
-
input.identifier,
|
|
1207
|
-
resolved?.nativeSubjectId,
|
|
1208
|
-
currentNow,
|
|
1209
|
-
)
|
|
1210
|
-
)
|
|
1211
|
-
admitted.push(entry);
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
const now = yield* nowMillis;
|
|
1215
|
-
|
|
1216
|
-
for (const entry of admitted)
|
|
1217
|
-
yield* insertCharge(mapping, input.moduleId, input.action, entry, attemptId, now);
|
|
1218
|
-
if (admitted.length !== scopes.length)
|
|
1219
|
-
return prepare({ _tag: "Denied" as const }, journal);
|
|
1220
|
-
const snapshot = resolved?.snapshot;
|
|
1221
|
-
const deadline = now + input.policy.attemptLifetimeMillis;
|
|
1222
|
-
|
|
1223
|
-
const retentionUntil = Math.max(
|
|
1224
|
-
deadline,
|
|
1225
|
-
...scopes.map((entry) => now + entry.windowMillis),
|
|
1226
|
-
);
|
|
1227
|
-
|
|
1228
|
-
yield* transaction.insert(mapping.attempt.table).values(
|
|
1229
|
-
mapping.attempt.encodeInsert(
|
|
1230
|
-
{
|
|
1231
|
-
moduleId: input.moduleId,
|
|
1232
|
-
action: input.action,
|
|
1233
|
-
attemptId,
|
|
1234
|
-
identifier: input.identifier,
|
|
1235
|
-
...(snapshot === undefined
|
|
1236
|
-
? {}
|
|
1237
|
-
: {
|
|
1238
|
-
subjectId: snapshot.revision.subjectId,
|
|
1239
|
-
credentialId: snapshot.credentialId,
|
|
1240
|
-
securityRevision: snapshot.revision.securityRevision,
|
|
1241
|
-
credentialRevision: snapshot.credentialRevision,
|
|
1242
|
-
verifierVersion: snapshot.verifierVersion,
|
|
1243
|
-
identifierBindingRevision: snapshot.identifierBindingRevision,
|
|
1244
|
-
}),
|
|
1245
|
-
admittedAtMillis: now,
|
|
1246
|
-
deadlineMillis: deadline,
|
|
1247
|
-
retentionUntilMillis: retentionUntil,
|
|
1248
|
-
},
|
|
1249
|
-
{
|
|
1250
|
-
...(resolved?.nativeSubjectId === undefined
|
|
1251
|
-
? {}
|
|
1252
|
-
: { nativeSubjectId: resolved.nativeSubjectId }),
|
|
1253
|
-
state: "pending",
|
|
1254
|
-
},
|
|
1255
|
-
),
|
|
1256
|
-
);
|
|
1257
|
-
|
|
1258
|
-
return prepare(
|
|
1259
|
-
{
|
|
1260
|
-
_tag: "Admitted" as const,
|
|
1261
|
-
attemptId,
|
|
1262
|
-
...(snapshot === undefined ? {} : { credential: snapshot }),
|
|
1263
|
-
},
|
|
1264
|
-
journal,
|
|
1265
|
-
);
|
|
1266
|
-
}),
|
|
1267
|
-
);
|
|
1268
|
-
}).pipe(
|
|
1269
|
-
Effect.provideService(CurrentPasswordSql, database),
|
|
1270
|
-
Effect.provideService(LifecycleHooks, hooks),
|
|
1271
|
-
translateFailure,
|
|
1272
|
-
),
|
|
1273
|
-
settleAttempt: (uncaptured, prepare) =>
|
|
1274
|
-
Effect.gen(function* () {
|
|
1275
|
-
const captured =
|
|
1276
|
-
uncaptured.captured === undefined
|
|
1277
|
-
? undefined
|
|
1278
|
-
: yield* snapshotPasswordCredential(uncaptured.captured);
|
|
1279
|
-
|
|
1280
|
-
const input = { ...uncaptured, ...(captured === undefined ? {} : { captured }) };
|
|
1281
|
-
|
|
1282
|
-
return yield* owned(
|
|
1283
|
-
database,
|
|
1284
|
-
mapping,
|
|
1285
|
-
configuration,
|
|
1286
|
-
Effect.gen(function* () {
|
|
1287
|
-
const transaction = yield* CurrentPasswordSql;
|
|
1288
|
-
const journal = yield* CurrentCommitJournal;
|
|
1289
|
-
|
|
1290
|
-
const c = attemptColumns(mapping);
|
|
1291
|
-
|
|
1292
|
-
const discovered = yield* selectRows(
|
|
1293
|
-
transaction
|
|
1294
|
-
.select()
|
|
1295
|
-
.from(mapping.attempt.table)
|
|
1296
|
-
.where(and(eq(c.moduleId, input.moduleId), eq(c.attemptId, input.attemptId)))
|
|
1297
|
-
.limit(1),
|
|
1298
|
-
false,
|
|
1299
|
-
);
|
|
1300
|
-
|
|
1301
|
-
let row = discovered[0];
|
|
1302
|
-
|
|
1303
|
-
if (row === undefined || row[mapping.attempt.state] !== "pending")
|
|
1304
|
-
return prepare("rejected", journal);
|
|
1305
|
-
let decision: PasswordAttemptDecision = "rejected";
|
|
1306
|
-
let current: any;
|
|
1307
|
-
let attemptMatches = false;
|
|
1308
|
-
|
|
1309
|
-
if (input.outcome === "verified" && input.captured !== undefined) {
|
|
1310
|
-
current = yield* resolveCredential(
|
|
1311
|
-
mapping,
|
|
1312
|
-
input.moduleId,
|
|
1313
|
-
input.captured.identifier,
|
|
1314
|
-
input.captured.revision.subjectId,
|
|
1315
|
-
configuration.locking,
|
|
1316
|
-
);
|
|
1317
|
-
attemptMatches =
|
|
1318
|
-
input.captured.moduleId === input.moduleId &&
|
|
1319
|
-
row[mapping.attempt.identifierNamespace] ===
|
|
1320
|
-
input.captured.identifier.namespace &&
|
|
1321
|
-
row[mapping.attempt.identifierValue] === input.captured.identifier.value &&
|
|
1322
|
-
mapping.subjectId.equals(
|
|
1323
|
-
row[mapping.attempt.subjectId],
|
|
1324
|
-
current?.nativeSubjectId,
|
|
1325
|
-
) &&
|
|
1326
|
-
row[mapping.attempt.credentialId] === input.captured.credentialId &&
|
|
1327
|
-
row[mapping.attempt.securityRevision] ===
|
|
1328
|
-
input.captured.revision.securityRevision &&
|
|
1329
|
-
row[mapping.attempt.credentialRevision] === input.captured.credentialRevision &&
|
|
1330
|
-
row[mapping.attempt.verifierVersion] === input.captured.verifierVersion &&
|
|
1331
|
-
row[mapping.attempt.identifierBindingRevision] ===
|
|
1332
|
-
input.captured.identifierBindingRevision;
|
|
1333
|
-
}
|
|
1334
|
-
row = (yield* selectRows(
|
|
1335
|
-
transaction
|
|
1336
|
-
.select()
|
|
1337
|
-
.from(mapping.attempt.table)
|
|
1338
|
-
.where(and(eq(c.moduleId, input.moduleId), eq(c.attemptId, input.attemptId)))
|
|
1339
|
-
.limit(1),
|
|
1340
|
-
configuration.locking,
|
|
1341
|
-
))[0];
|
|
1342
|
-
if (row === undefined || row[mapping.attempt.state] !== "pending")
|
|
1343
|
-
return prepare("rejected", journal);
|
|
1344
|
-
const now = yield* nowMillis;
|
|
1345
|
-
const deadline = yield* mapping.decodeInstant(row[mapping.attempt.deadline]);
|
|
1346
|
-
|
|
1347
|
-
if (
|
|
1348
|
-
attemptMatches &&
|
|
1349
|
-
current?.snapshot !== undefined &&
|
|
1350
|
-
input.captured !== undefined &&
|
|
1351
|
-
deadline > now &&
|
|
1352
|
-
sameCredentialSnapshot(current.snapshot, input.captured)
|
|
1353
|
-
)
|
|
1354
|
-
decision = "verified";
|
|
1355
|
-
const prepared = prepare(decision, journal);
|
|
1356
|
-
|
|
1357
|
-
yield* transaction
|
|
1358
|
-
.update(mapping.attempt.table)
|
|
1359
|
-
.set(updateValues([[mapping.attempt.state, decision]]))
|
|
1360
|
-
.where(
|
|
1361
|
-
and(
|
|
1362
|
-
eq(c.moduleId, input.moduleId),
|
|
1363
|
-
eq(c.attemptId, input.attemptId),
|
|
1364
|
-
eq(c.state, "pending"),
|
|
1365
|
-
),
|
|
1366
|
-
);
|
|
1367
|
-
if (
|
|
1368
|
-
decision === "verified" &&
|
|
1369
|
-
input.rehash !== undefined &&
|
|
1370
|
-
current?.snapshot !== undefined
|
|
1371
|
-
) {
|
|
1372
|
-
const cc = credentialColumns(mapping);
|
|
1373
|
-
|
|
1374
|
-
const nextVersion = yield* allocate(
|
|
1375
|
-
configuration.mode,
|
|
1376
|
-
mapping.allocateRevision,
|
|
1377
|
-
mapping.allocateRevisionSync,
|
|
1378
|
-
);
|
|
1379
|
-
|
|
1380
|
-
yield* transaction
|
|
1381
|
-
.update(mapping.credential.table)
|
|
1382
|
-
.set(mapping.credential.encodeVerifier(input.rehash.nextVerifier, nextVersion))
|
|
1383
|
-
.where(
|
|
1384
|
-
and(
|
|
1385
|
-
eq(cc.moduleId, input.moduleId),
|
|
1386
|
-
eq(cc.subjectId, current.nativeSubjectId),
|
|
1387
|
-
eq(cc.credentialId, current.snapshot.credentialId),
|
|
1388
|
-
eq(cc.credentialRevision, current.snapshot.credentialRevision),
|
|
1389
|
-
eq(cc.verifierVersion, input.rehash.expectedVersion),
|
|
1390
|
-
eq(cc.verifier, Redacted.value(input.rehash.expectedVerifier)),
|
|
1391
|
-
),
|
|
1392
|
-
);
|
|
1393
|
-
}
|
|
946
|
+
const attempts = yield* makePasswordAttempts(
|
|
947
|
+
yield* sqlMapping(() => nativeTables(root.$client.withoutTransforms(), root)).pipe(
|
|
948
|
+
translateFailure,
|
|
949
|
+
),
|
|
950
|
+
mapping,
|
|
951
|
+
configuration,
|
|
952
|
+
);
|
|
1394
953
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
);
|
|
1398
|
-
}).pipe(
|
|
1399
|
-
Effect.provideService(CurrentPasswordSql, database),
|
|
1400
|
-
Effect.provideService(LifecycleHooks, hooks),
|
|
1401
|
-
translateFailure,
|
|
1402
|
-
),
|
|
954
|
+
return PasswordPersistence.of({
|
|
955
|
+
...attempts,
|
|
1403
956
|
readForSubject: (input) =>
|
|
1404
957
|
safeRead(
|
|
1405
958
|
database,
|
|
@@ -1691,13 +1244,30 @@ export const makePasswordKernel = <
|
|
|
1691
1244
|
configuration.proof!.configuration,
|
|
1692
1245
|
input.completion,
|
|
1693
1246
|
Effect.gen(function* () {
|
|
1694
|
-
|
|
1247
|
+
const revisions = {
|
|
1248
|
+
credentialRevision,
|
|
1249
|
+
verifierVersion,
|
|
1250
|
+
nextSecurityRevision,
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
const applied = yield* writeReplacement(
|
|
1695
1254
|
mapping,
|
|
1696
1255
|
input,
|
|
1697
1256
|
authority.nativeSubjectId,
|
|
1698
|
-
|
|
1257
|
+
revisions,
|
|
1699
1258
|
authority.now,
|
|
1700
1259
|
);
|
|
1260
|
+
|
|
1261
|
+
if (applied)
|
|
1262
|
+
yield* registerReplacement(
|
|
1263
|
+
mapping,
|
|
1264
|
+
input,
|
|
1265
|
+
authority.nativeSubjectId,
|
|
1266
|
+
revisions,
|
|
1267
|
+
authority.now,
|
|
1268
|
+
);
|
|
1269
|
+
|
|
1270
|
+
return applied;
|
|
1701
1271
|
}),
|
|
1702
1272
|
(decision) => {
|
|
1703
1273
|
passwordReceipt = prepare(
|
|
@@ -1729,103 +1299,19 @@ export const makePasswordKernel = <
|
|
|
1729
1299
|
|
|
1730
1300
|
const now = yield* nowMillis;
|
|
1731
1301
|
const nativeNow = mapping.encodeInstant(now);
|
|
1732
|
-
let remaining = input.limit;
|
|
1733
|
-
let hasMore = false;
|
|
1734
1302
|
const a = attemptColumns(mapping);
|
|
1735
|
-
const c = chargeColumns(mapping);
|
|
1736
1303
|
|
|
1737
1304
|
const attempts = yield* selectRows(
|
|
1738
1305
|
transaction
|
|
1739
1306
|
.select({ attemptId: a.attemptId })
|
|
1740
1307
|
.from(mapping.attempt.table)
|
|
1741
1308
|
.where(and(eq(a.moduleId, input.moduleId), lte(a.retentionUntil, nativeNow)))
|
|
1742
|
-
.limit(
|
|
1309
|
+
.limit(input.limit + 1),
|
|
1743
1310
|
false,
|
|
1744
1311
|
);
|
|
1745
1312
|
|
|
1746
|
-
|
|
1747
|
-
const attemptCandidates = attempts.slice(0,
|
|
1748
|
-
|
|
1749
|
-
remaining -= attemptCandidates.length;
|
|
1750
|
-
|
|
1751
|
-
const charges = yield* selectRows(
|
|
1752
|
-
transaction
|
|
1753
|
-
.select({
|
|
1754
|
-
action: c.action,
|
|
1755
|
-
scopeKind: c.scopeKind,
|
|
1756
|
-
scopeKey: c.scopeKey,
|
|
1757
|
-
attemptId: c.attemptId,
|
|
1758
|
-
})
|
|
1759
|
-
.from(mapping.charge.table)
|
|
1760
|
-
.where(and(eq(c.moduleId, input.moduleId), lte(c.retentionUntil, nativeNow)))
|
|
1761
|
-
.limit(remaining + 1),
|
|
1762
|
-
false,
|
|
1763
|
-
);
|
|
1764
|
-
|
|
1765
|
-
if (charges.length > remaining) hasMore = true;
|
|
1766
|
-
const chargeCandidates = charges.slice(0, remaining);
|
|
1767
|
-
|
|
1768
|
-
remaining -= chargeCandidates.length;
|
|
1769
|
-
const sc = scopeColumns(mapping);
|
|
1770
|
-
|
|
1771
|
-
const scopes = yield* selectRows(
|
|
1772
|
-
transaction
|
|
1773
|
-
.select({ action: sc.action, scopeKind: sc.scopeKind, scopeKey: sc.scopeKey })
|
|
1774
|
-
.from(mapping.rateScope.table)
|
|
1775
|
-
.where(
|
|
1776
|
-
and(
|
|
1777
|
-
eq(sc.moduleId, input.moduleId),
|
|
1778
|
-
notExists(
|
|
1779
|
-
transaction
|
|
1780
|
-
.select({ one: sql`1` })
|
|
1781
|
-
.from(mapping.charge.table)
|
|
1782
|
-
.where(
|
|
1783
|
-
and(
|
|
1784
|
-
eq(c.moduleId, sc.moduleId),
|
|
1785
|
-
eq(c.action, sc.action),
|
|
1786
|
-
eq(c.scopeKind, sc.scopeKind),
|
|
1787
|
-
eq(c.scopeKey, sc.scopeKey),
|
|
1788
|
-
),
|
|
1789
|
-
),
|
|
1790
|
-
),
|
|
1791
|
-
),
|
|
1792
|
-
)
|
|
1793
|
-
.limit(remaining + 1),
|
|
1794
|
-
remaining === 0 ? false : configuration.locking,
|
|
1795
|
-
);
|
|
1796
|
-
|
|
1797
|
-
if (scopes.length > remaining) hasMore = true;
|
|
1798
|
-
const selectedScopes = scopes.slice(0, remaining);
|
|
1799
|
-
|
|
1800
|
-
remaining -= selectedScopes.length;
|
|
1801
|
-
const selectedCharges: typeof chargeCandidates = [];
|
|
1802
|
-
|
|
1803
|
-
for (const candidate of chargeCandidates) {
|
|
1804
|
-
const locked = yield* selectRows(
|
|
1805
|
-
transaction
|
|
1806
|
-
.select({
|
|
1807
|
-
action: c.action,
|
|
1808
|
-
scopeKind: c.scopeKind,
|
|
1809
|
-
scopeKey: c.scopeKey,
|
|
1810
|
-
attemptId: c.attemptId,
|
|
1811
|
-
})
|
|
1812
|
-
.from(mapping.charge.table)
|
|
1813
|
-
.where(
|
|
1814
|
-
and(
|
|
1815
|
-
eq(c.moduleId, input.moduleId),
|
|
1816
|
-
eq(c.action, candidate.action),
|
|
1817
|
-
eq(c.scopeKind, candidate.scopeKind),
|
|
1818
|
-
eq(c.scopeKey, candidate.scopeKey),
|
|
1819
|
-
eq(c.attemptId, candidate.attemptId),
|
|
1820
|
-
lte(c.retentionUntil, nativeNow),
|
|
1821
|
-
),
|
|
1822
|
-
)
|
|
1823
|
-
.limit(1),
|
|
1824
|
-
configuration.locking,
|
|
1825
|
-
);
|
|
1826
|
-
|
|
1827
|
-
if (locked[0] !== undefined) selectedCharges.push(locked[0] as any);
|
|
1828
|
-
}
|
|
1313
|
+
const hasMore = attempts.length > input.limit;
|
|
1314
|
+
const attemptCandidates = attempts.slice(0, input.limit);
|
|
1829
1315
|
const selectedAttempts: typeof attemptCandidates = [];
|
|
1830
1316
|
|
|
1831
1317
|
for (const candidate of attemptCandidates) {
|
|
@@ -1849,25 +1335,12 @@ export const makePasswordKernel = <
|
|
|
1849
1335
|
|
|
1850
1336
|
const prepared = prepare(
|
|
1851
1337
|
{
|
|
1852
|
-
removed: selectedAttempts.length
|
|
1338
|
+
removed: selectedAttempts.length,
|
|
1853
1339
|
hasMore,
|
|
1854
1340
|
},
|
|
1855
1341
|
journal,
|
|
1856
1342
|
);
|
|
1857
1343
|
|
|
1858
|
-
for (const row of selectedCharges)
|
|
1859
|
-
yield* transaction
|
|
1860
|
-
.delete(mapping.charge.table)
|
|
1861
|
-
.where(
|
|
1862
|
-
and(
|
|
1863
|
-
eq(c.moduleId, input.moduleId),
|
|
1864
|
-
eq(c.action, row.action),
|
|
1865
|
-
eq(c.scopeKind, row.scopeKind),
|
|
1866
|
-
eq(c.scopeKey, row.scopeKey),
|
|
1867
|
-
eq(c.attemptId, row.attemptId),
|
|
1868
|
-
lte(c.retentionUntil, nativeNow),
|
|
1869
|
-
),
|
|
1870
|
-
);
|
|
1871
1344
|
if (selectedAttempts.length > 0)
|
|
1872
1345
|
yield* transaction.delete(mapping.attempt.table).where(
|
|
1873
1346
|
and(
|
|
@@ -1879,28 +1352,6 @@ export const makePasswordKernel = <
|
|
|
1879
1352
|
lte(a.retentionUntil, nativeNow),
|
|
1880
1353
|
),
|
|
1881
1354
|
);
|
|
1882
|
-
for (const row of selectedScopes)
|
|
1883
|
-
yield* transaction.delete(mapping.rateScope.table).where(
|
|
1884
|
-
and(
|
|
1885
|
-
eq(sc.moduleId, input.moduleId),
|
|
1886
|
-
eq(sc.action, row.action),
|
|
1887
|
-
eq(sc.scopeKind, row.scopeKind),
|
|
1888
|
-
eq(sc.scopeKey, row.scopeKey),
|
|
1889
|
-
notExists(
|
|
1890
|
-
transaction
|
|
1891
|
-
.select({ one: sql`1` })
|
|
1892
|
-
.from(mapping.charge.table)
|
|
1893
|
-
.where(
|
|
1894
|
-
and(
|
|
1895
|
-
eq(c.moduleId, sc.moduleId),
|
|
1896
|
-
eq(c.action, sc.action),
|
|
1897
|
-
eq(c.scopeKind, sc.scopeKind),
|
|
1898
|
-
eq(c.scopeKey, sc.scopeKey),
|
|
1899
|
-
),
|
|
1900
|
-
),
|
|
1901
|
-
),
|
|
1902
|
-
),
|
|
1903
|
-
);
|
|
1904
1355
|
|
|
1905
1356
|
return prepared;
|
|
1906
1357
|
}),
|
|
@@ -1987,8 +1438,14 @@ export const makePasswordKernel = <
|
|
|
1987
1438
|
)
|
|
1988
1439
|
.limit(1))[0];
|
|
1989
1440
|
|
|
1441
|
+
// SQL equality follows column collation; compare the persisted values exactly.
|
|
1990
1442
|
return (
|
|
1991
1443
|
credential !== undefined &&
|
|
1444
|
+
credential[mapping.credential.credentialId] === revisions.credentialId &&
|
|
1445
|
+
credential[mapping.credential.credentialRevision] === revisions.credentialRevision &&
|
|
1446
|
+
credential[mapping.credential.verifierVersion] === revisions.verifierVersion &&
|
|
1447
|
+
credential[mapping.credential.verifier] === Redacted.value(input.replacement.verifier) &&
|
|
1448
|
+
credential[mapping.credential.normalization] === input.replacement.normalization &&
|
|
1992
1449
|
(yield* mutationPostconditions(
|
|
1993
1450
|
mapping,
|
|
1994
1451
|
input,
|
|
@@ -2069,6 +1526,27 @@ export const makePasswordKernel = <
|
|
|
2069
1526
|
);
|
|
2070
1527
|
});
|
|
2071
1528
|
|
|
1529
|
+
const registerReplacement = (
|
|
1530
|
+
mapping: Mapping,
|
|
1531
|
+
input: PasswordMutationInput,
|
|
1532
|
+
nativeSubjectId: unknown,
|
|
1533
|
+
revisions: {
|
|
1534
|
+
readonly credentialRevision: SecurityRevision;
|
|
1535
|
+
readonly verifierVersion: SecurityRevision;
|
|
1536
|
+
readonly nextSecurityRevision: SecurityRevision;
|
|
1537
|
+
},
|
|
1538
|
+
now: number,
|
|
1539
|
+
) =>
|
|
1540
|
+
registerFinal(
|
|
1541
|
+
checkMutationApplied(
|
|
1542
|
+
mapping,
|
|
1543
|
+
input,
|
|
1544
|
+
nativeSubjectId,
|
|
1545
|
+
{ ...revisions, credentialId: input.credential!.credentialId },
|
|
1546
|
+
now,
|
|
1547
|
+
),
|
|
1548
|
+
);
|
|
1549
|
+
|
|
2072
1550
|
const replaceIn = Effect.fn("Drizzle.replaceIn")(function* <A>(
|
|
2073
1551
|
mapping: Mapping,
|
|
2074
1552
|
configuration: PasswordSqlConfiguration,
|
|
@@ -2102,6 +1580,7 @@ export const makePasswordKernel = <
|
|
|
2102
1580
|
))
|
|
2103
1581
|
)
|
|
2104
1582
|
return yield* unavailable();
|
|
1583
|
+
yield* registerReplacement(mapping, input, authority.nativeSubjectId, revisions, authority.now);
|
|
2105
1584
|
|
|
2106
1585
|
return prepared;
|
|
2107
1586
|
});
|