@happyvertical/smrt-users 0.52.0 → 0.53.5
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/AGENTS.md +43 -2
- package/README.md +227 -2
- package/agents/permissions.md +12 -1
- package/agents/retention.md +14 -7
- package/dist/chunks/{TerminalAuthService-Br64vHtd.js → TerminalAuthService-BdB_0vDA.js} +3492 -2172
- package/dist/chunks/TerminalAuthService-BdB_0vDA.js.map +1 -0
- package/dist/collections/CliAuthApproveLimitCollection.d.ts +4 -0
- package/dist/collections/CliAuthApproveLimitCollection.d.ts.map +1 -1
- package/dist/collections/CliAuthRequestCollection.d.ts.map +1 -1
- package/dist/collections/LoginAttemptCollection.d.ts +105 -0
- package/dist/collections/LoginAttemptCollection.d.ts.map +1 -0
- package/dist/collections/LoginAuditEventCollection.d.ts +19 -0
- package/dist/collections/LoginAuditEventCollection.d.ts.map +1 -0
- package/dist/collections/PinCredentialCollection.d.ts +26 -0
- package/dist/collections/PinCredentialCollection.d.ts.map +1 -0
- package/dist/collections/RoleCollection.d.ts.map +1 -1
- package/dist/collections/RolePermissionCollection.d.ts +4 -0
- package/dist/collections/RolePermissionCollection.d.ts.map +1 -1
- package/dist/collections/SessionCollection.d.ts +33 -1
- package/dist/collections/SessionCollection.d.ts.map +1 -1
- package/dist/collections/index.d.ts +3 -0
- package/dist/collections/index.d.ts.map +1 -1
- package/dist/collections/seeding-transaction.d.ts +43 -0
- package/dist/collections/seeding-transaction.d.ts.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +180 -32
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +3281 -2018
- package/dist/migrations/deduplicateRolePermissions.d.ts +23 -0
- package/dist/migrations/deduplicateRolePermissions.d.ts.map +1 -0
- package/dist/models/CliAuthApproveLimit.d.ts +8 -1
- package/dist/models/CliAuthApproveLimit.d.ts.map +1 -1
- package/dist/models/LoginAttempt.d.ts +59 -0
- package/dist/models/LoginAttempt.d.ts.map +1 -0
- package/dist/models/LoginAuditEvent.d.ts +49 -0
- package/dist/models/LoginAuditEvent.d.ts.map +1 -0
- package/dist/models/PinCredential.d.ts +31 -0
- package/dist/models/PinCredential.d.ts.map +1 -0
- package/dist/models/RolePermission.d.ts.map +1 -1
- package/dist/models/Session.d.ts +69 -1
- package/dist/models/Session.d.ts.map +1 -1
- package/dist/models/index.d.ts +4 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/retention.d.ts +7 -1
- package/dist/retention.d.ts.map +1 -1
- package/dist/services/DeviceCredentialService.d.ts +348 -0
- package/dist/services/DeviceCredentialService.d.ts.map +1 -0
- package/dist/services/LoginAttemptLimiter.d.ts +227 -0
- package/dist/services/LoginAttemptLimiter.d.ts.map +1 -0
- package/dist/services/MobileAuthService.d.ts.map +1 -1
- package/dist/services/OperationPermissionService.d.ts.map +1 -1
- package/dist/services/PermissionCatalogService.d.ts.map +1 -1
- package/dist/services/SessionPermissionContext.d.ts.map +1 -1
- package/dist/services/SessionService.d.ts +56 -1
- package/dist/services/SessionService.d.ts.map +1 -1
- package/dist/services/TerminalAuthService.d.ts +18 -3
- package/dist/services/TerminalAuthService.d.ts.map +1 -1
- package/dist/services/index.d.ts +4 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +685 -11
- package/dist/sveltekit/device-credential-handlers.d.ts +60 -0
- package/dist/sveltekit/device-credential-handlers.d.ts.map +1 -0
- package/dist/sveltekit/index.d.ts +10 -0
- package/dist/sveltekit/index.d.ts.map +1 -1
- package/dist/sveltekit/types.d.ts +6 -0
- package/dist/sveltekit/types.d.ts.map +1 -1
- package/dist/sveltekit.js +184 -4
- package/dist/sveltekit.js.map +1 -1
- package/package.json +13 -13
- package/dist/__tests__/helpers/oidc-test-server.d.ts +0 -45
- package/dist/__tests__/helpers/oidc-test-server.d.ts.map +0 -1
- package/dist/__tests__/helpers/postgres-rls-reset.d.ts +0 -15
- package/dist/__tests__/helpers/postgres-rls-reset.d.ts.map +0 -1
- package/dist/chunks/TerminalAuthService-Br64vHtd.js.map +0 -1
- package/dist/svelte/components/__tests__/InviteUserModal.test.js +0 -65
- package/dist/svelte/components/__tests__/UserAvatar.test.js +0 -31
- package/dist/svelte/components/__tests__/UserCard.test.js +0 -39
- package/dist/svelte/components/__tests__/UserForm.test.js +0 -50
- package/dist/svelte/components/__tests__/UserList.test.js +0 -48
- package/dist/svelte/components/__tests__/UserMenu.test.js +0 -83
- package/dist/svelte/components/__tests__/UserStatus.test.js +0 -36
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as normalizeAncestorReadPolicy, $t as UsersCliAuthRequest, A as DeviceCredentialForbiddenError, At as MembershipOverrideCollection, B as DEFAULT_LOGIN_LOCKOUT_BASE_SECONDS, Bt as User, C as encodeOidcTransaction, Ct as RolePermission, D as DEFAULT_DEVICE_PERSON_IDLE_SECONDS, Dt as UsersPinCredential, Et as UsersPinCredentialCollection, F as PinVerifier, Ft as GroupRole, G as InvalidCredentialsError, Gt as UsersLoginAttemptCollection, H as DEFAULT_LOGIN_LOCKOUT_MAX_SECONDS, Ht as normalizeEmail, I as ensurePinPermissionsRegistered, It as GroupMemberCollection, J as withoutListBounds, Jt as SessionCollection, K as LoginAttemptLimiter, Kt as UsersLoginAttempt, L as SessionService, Lt as GroupMember, M as DeviceCredentialService, Mt as MembershipCollection, N as PIN_LOGIN_KIND, Nt as Membership, O as DEFAULT_PIN_MANAGE_PERMISSION, Ot as PermissionCollection, P as PIN_PERMISSION_DEFINITIONS, Pt as GroupRoleCollection, Q as isAncestorReadableSlug, Qt as generateSessionId, R as PermissionResolver, Rt as GroupCollection, S as decodeOidcTransaction, St as syncPermissionCatalog, T as resolveOidcProviderConfig, Tt as Role, U as DEFAULT_LOGIN_MAX_ATTEMPTS, Ut as UsersLoginAuditEventCollection, V as DEFAULT_LOGIN_LOCKOUT_FACTOR, Vt as isValidEmail, W as DurableLoginAuditSink, Wt as UsersLoginAuditEvent, X as DEFAULT_ANCESTOR_READ_MAX_DEPTH, Xt as SESSION_DATA_KEYS, Y as ANCESTOR_READ_ACTION, Yt as DEFAULT_SESSION_TTL, Z as getConfiguredAncestorReadPolicy, Zt as Session, _ as MobileAuthService, _t as PermissionCatalogService, a as TerminalAuthError, at as TenantPermissionOverrideCollection, b as OidcLoginError, bt as normalizeOperationPermissionAction, c as OperationPermissionError, ct as Tenant, d as hasOperationPermission, dt as applyTenantHierarchyUpdates, et as OidcProvisioningError, f as getCurrentSessionPermissionContext, ft as assertTenantTableName, g as MobileAuthError, gt as RolePermissionCollection, h as withSessionPermissionContext, ht as DEFAULT_ROLE_PERMISSION_PATTERNS, i as TERMINAL_APPROVE_LOGIN_KIND, it as backfillUserEmailKeys, j as DeviceCredentialPolicyError, jt as MembershipOverride, k as DeviceCredentialError, kt as Permission, l as assertOperationPermission, lt as MAX_TENANT_HIERARCHY_DEPTH, m as withPrincipalPermissionContext, mt as recordTenantHierarchyChanges, n as DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, o as TerminalAuthRateLimitError, ot as TenantPermissionOverride, p as getRequestScopedDatabase, pt as planTenantHierarchy, q as LoginRateLimitError, qt as UsersCliAuthRequestCollection, r as DEFAULT_CLI_SESSION_TTL_SECONDS, rt as UserEmailKeyBackfillError, s as TerminalAuthService, st as TenantCollection, t as DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, tt as UserCollection, u as checkOperationPermission, ut as TenantHierarchyError, v as readMobileBearerToken, vt as deriveOperationPermissionCollectionName, w as getUsersOidcConfig, wt as RoleCollection, x as OidcLoginService, xt as registerPermissionDefinitions, y as validateMobileRedirectUri, yt as deriveOperationPermissionSlug, z as DEFAULT_LOGIN_ATTEMPT_WINDOW_SECONDS, zt as Group } from "./chunks/TerminalAuthService-BdB_0vDA.js";
|
|
2
2
|
import { a as MembershipStatus, c as TenantPermissionEffect, i as DEFAULT_TENANT_POLICY, l as TenantStatus, n as DEFAULT_ROLES, o as OverrideEffect, r as DEFAULT_ROLE_SLUGS, s as SessionStatus, t as AccessRequestStatus, u as UserStatus } from "./chunks/types-DFqeph0R.js";
|
|
3
|
-
import { ObjectRegistry, SmrtCollection, SmrtObject, field, findManifestEntryByQualifiedName, foreignKey, isUniqueViolationError, registerRetentionTask, smrt, unregisterRetentionTask } from "@happyvertical/smrt-core";
|
|
3
|
+
import { ObjectRegistry, SmrtCollection, SmrtObject, bumpChangeFeed, ensureChangeFeedTable, field, findManifestEntryByQualifiedName, foreignKey, isEmbeddedDatabase, isUniqueViolationError, registerRetentionTask, smrt, unregisterRetentionTask, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
4
5
|
import { PROFILE_EMAIL_KEY_BACKFILL_NAME, Person, ProfileCollection, ProfileTypeCollection, normalizeIdentityEmail } from "@happyvertical/smrt-profiles";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
6
|
import { createLogger } from "@happyvertical/logger";
|
|
7
7
|
import { TenantScoped, tenantId, withSystemContext } from "@happyvertical/smrt-tenancy";
|
|
8
8
|
import { BackfillTracker } from "@happyvertical/smrt-core/migrations";
|
|
9
9
|
//#region src/models/MagicLinkToken.ts
|
|
10
|
-
var __defProp$
|
|
11
|
-
var __getOwnPropDesc$
|
|
12
|
-
var __decorateClass$
|
|
13
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
10
|
+
var __defProp$4 = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
13
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
|
|
14
14
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
15
|
-
if (kind && result) __defProp$
|
|
15
|
+
if (kind && result) __defProp$4(target, key, result);
|
|
16
16
|
return result;
|
|
17
17
|
};
|
|
18
18
|
var DEFAULT_TOKEN_EXPIRY_SECONDS = 600;
|
|
@@ -39,15 +39,15 @@ var UsersMagicLinkToken = class extends SmrtObject {
|
|
|
39
39
|
return !this.used && !this.isExpired();
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
|
-
__decorateClass$
|
|
42
|
+
__decorateClass$4([field({
|
|
43
43
|
required: true,
|
|
44
44
|
unique: true
|
|
45
45
|
})], UsersMagicLinkToken.prototype, "nonce", 2);
|
|
46
|
-
__decorateClass$
|
|
46
|
+
__decorateClass$4([field({
|
|
47
47
|
type: "datetime",
|
|
48
48
|
indexed: true
|
|
49
49
|
})], UsersMagicLinkToken.prototype, "expiresAt", 2);
|
|
50
|
-
UsersMagicLinkToken = __decorateClass$
|
|
50
|
+
UsersMagicLinkToken = __decorateClass$4([smrt({
|
|
51
51
|
tableName: "users_magic_link_tokens",
|
|
52
52
|
sensitive: true,
|
|
53
53
|
api: { include: [] },
|
|
@@ -103,10 +103,15 @@ var UsersMagicLinkTokenCollection = class extends SmrtCollection {
|
|
|
103
103
|
var SESSIONS_RETENTION_TASK = "users-sessions";
|
|
104
104
|
var MAGIC_LINK_RETENTION_TASK = "users-magic-link-tokens";
|
|
105
105
|
var CLI_AUTH_RETENTION_TASK = "users-cli-auth-requests";
|
|
106
|
+
var LOGIN_ATTEMPTS_RETENTION_TASK = "users-login-attempts";
|
|
107
|
+
var LOGIN_AUDIT_RETENTION_TASK = "users-login-audit-events";
|
|
108
|
+
var DEFAULT_LOGIN_AUDIT_RETENTION_DAYS = 90;
|
|
106
109
|
var USER_RETENTION_TASKS = [
|
|
107
110
|
SESSIONS_RETENTION_TASK,
|
|
108
111
|
MAGIC_LINK_RETENTION_TASK,
|
|
109
|
-
CLI_AUTH_RETENTION_TASK
|
|
112
|
+
CLI_AUTH_RETENTION_TASK,
|
|
113
|
+
LOGIN_ATTEMPTS_RETENTION_TASK,
|
|
114
|
+
LOGIN_AUDIT_RETENTION_TASK
|
|
110
115
|
];
|
|
111
116
|
function registerUserRetentionTasks() {
|
|
112
117
|
registerRetentionTask({
|
|
@@ -130,18 +135,32 @@ function registerUserRetentionTasks() {
|
|
|
130
135
|
return (await UsersCliAuthRequestCollection.create({ db })).deleteExpired({ dryRun: context.dryRun });
|
|
131
136
|
}
|
|
132
137
|
});
|
|
138
|
+
registerRetentionTask({
|
|
139
|
+
name: LOGIN_ATTEMPTS_RETENTION_TASK,
|
|
140
|
+
description: "Delete idle login-attempt limiter rows",
|
|
141
|
+
run: async (db, context) => {
|
|
142
|
+
return (await UsersLoginAttemptCollection.create({ db })).deleteIdle({ dryRun: context.dryRun });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
registerRetentionTask({
|
|
146
|
+
name: LOGIN_AUDIT_RETENTION_TASK,
|
|
147
|
+
description: `Delete login audit events older than 90 days`,
|
|
148
|
+
run: async (db, context) => {
|
|
149
|
+
return (await UsersLoginAuditEventCollection.create({ db })).deleteOlderThan(2160 * 60 * 60 * 1e3, { dryRun: context.dryRun });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
133
152
|
}
|
|
134
153
|
function unregisterUserRetentionTasks() {
|
|
135
154
|
for (const name of USER_RETENTION_TASKS) unregisterRetentionTask(name);
|
|
136
155
|
}
|
|
137
156
|
//#endregion
|
|
138
157
|
//#region src/models/AccessRequest.ts
|
|
139
|
-
var __defProp$
|
|
140
|
-
var __getOwnPropDesc$
|
|
141
|
-
var __decorateClass$
|
|
142
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$
|
|
158
|
+
var __defProp$3 = Object.defineProperty;
|
|
159
|
+
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
|
160
|
+
var __decorateClass$3 = (decorators, target, key, kind) => {
|
|
161
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
|
143
162
|
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
144
|
-
if (kind && result) __defProp$
|
|
163
|
+
if (kind && result) __defProp$3(target, key, result);
|
|
145
164
|
return result;
|
|
146
165
|
};
|
|
147
166
|
var AccessRequest = class extends SmrtObject {
|
|
@@ -226,24 +245,24 @@ var AccessRequest = class extends SmrtObject {
|
|
|
226
245
|
return this.status === AccessRequestStatus.DECLINED || this.status === AccessRequestStatus.GRADUATED || this.status === AccessRequestStatus.CANCELED;
|
|
227
246
|
}
|
|
228
247
|
};
|
|
229
|
-
__decorateClass$
|
|
248
|
+
__decorateClass$3([field({
|
|
230
249
|
required: true,
|
|
231
250
|
indexed: true
|
|
232
251
|
})], AccessRequest.prototype, "email", 2);
|
|
233
|
-
__decorateClass$
|
|
234
|
-
__decorateClass$
|
|
252
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "name", 2);
|
|
253
|
+
__decorateClass$3([field({
|
|
235
254
|
type: "text",
|
|
236
255
|
indexed: true
|
|
237
256
|
})], AccessRequest.prototype, "status", 2);
|
|
238
|
-
__decorateClass$
|
|
239
|
-
__decorateClass$
|
|
240
|
-
__decorateClass$
|
|
241
|
-
__decorateClass$
|
|
242
|
-
__decorateClass$
|
|
243
|
-
__decorateClass$
|
|
244
|
-
__decorateClass$
|
|
245
|
-
__decorateClass$
|
|
246
|
-
AccessRequest = __decorateClass$
|
|
257
|
+
__decorateClass$3([field({ indexed: true })], AccessRequest.prototype, "source", 2);
|
|
258
|
+
__decorateClass$3([field()], AccessRequest.prototype, "requestContext", 2);
|
|
259
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "note", 2);
|
|
260
|
+
__decorateClass$3([field()], AccessRequest.prototype, "requestedAt", 2);
|
|
261
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "decidedAt", 2);
|
|
262
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "decidedBy", 2);
|
|
263
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "resultingUserId", 2);
|
|
264
|
+
__decorateClass$3([field({ nullable: true })], AccessRequest.prototype, "tenantHint", 2);
|
|
265
|
+
AccessRequest = __decorateClass$3([smrt({
|
|
247
266
|
tableName: "access_requests",
|
|
248
267
|
conflictColumns: ["id"],
|
|
249
268
|
api: { include: [] },
|
|
@@ -296,6 +315,100 @@ var AccessRequestCollection = class extends SmrtCollection {
|
|
|
296
315
|
}
|
|
297
316
|
};
|
|
298
317
|
//#endregion
|
|
318
|
+
//#region src/models/CliAuthApproveLimit.ts
|
|
319
|
+
var __defProp$2 = Object.defineProperty;
|
|
320
|
+
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
321
|
+
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
322
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
323
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
324
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
325
|
+
return result;
|
|
326
|
+
};
|
|
327
|
+
var UsersCliAuthApproveLimit = class extends SmrtObject {
|
|
328
|
+
userId = "";
|
|
329
|
+
attemptCount = 0;
|
|
330
|
+
windowStartedAt = /* @__PURE__ */ new Date();
|
|
331
|
+
};
|
|
332
|
+
__decorateClass$2([foreignKey("User", {
|
|
333
|
+
required: true,
|
|
334
|
+
unique: true
|
|
335
|
+
})], UsersCliAuthApproveLimit.prototype, "userId", 2);
|
|
336
|
+
__decorateClass$2([field({
|
|
337
|
+
type: "integer",
|
|
338
|
+
required: true,
|
|
339
|
+
default: 0
|
|
340
|
+
})], UsersCliAuthApproveLimit.prototype, "attemptCount", 2);
|
|
341
|
+
__decorateClass$2([field({
|
|
342
|
+
type: "datetime",
|
|
343
|
+
required: true
|
|
344
|
+
})], UsersCliAuthApproveLimit.prototype, "windowStartedAt", 2);
|
|
345
|
+
UsersCliAuthApproveLimit = __decorateClass$2([smrt({
|
|
346
|
+
tableName: "users_cli_auth_approve_limits",
|
|
347
|
+
api: false,
|
|
348
|
+
cli: false,
|
|
349
|
+
mcp: false
|
|
350
|
+
})], UsersCliAuthApproveLimit);
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/collections/CliAuthApproveLimitCollection.ts
|
|
353
|
+
var UsersCliAuthApproveLimitCollection = class extends SmrtCollection {
|
|
354
|
+
static _itemClass = UsersCliAuthApproveLimit;
|
|
355
|
+
/**
|
|
356
|
+
* Atomically reserve one attempt across every process using this database.
|
|
357
|
+
* Failed attempts retain their reservation; successful attempts release it.
|
|
358
|
+
*/
|
|
359
|
+
async reserveAttempt(input) {
|
|
360
|
+
const now = /* @__PURE__ */ new Date();
|
|
361
|
+
const nowIso = now.toISOString();
|
|
362
|
+
const windowFloorIso = new Date(now.getTime() - input.windowMs).toISOString();
|
|
363
|
+
const id = randomUUID();
|
|
364
|
+
const reserved = await this.db.query(`INSERT INTO ${this.tableName} (
|
|
365
|
+
id, slug, context, user_id, attempt_count, window_started_at,
|
|
366
|
+
created_at, updated_at
|
|
367
|
+
) VALUES (?, ?, '', ?, 1, ?, ?, ?)
|
|
368
|
+
ON CONFLICT (user_id) DO UPDATE SET
|
|
369
|
+
attempt_count = CASE
|
|
370
|
+
WHEN ${this.tableName}.window_started_at <= ? THEN 1
|
|
371
|
+
ELSE ${this.tableName}.attempt_count + 1
|
|
372
|
+
END,
|
|
373
|
+
window_started_at = CASE
|
|
374
|
+
WHEN ${this.tableName}.window_started_at <= ?
|
|
375
|
+
THEN excluded.window_started_at
|
|
376
|
+
ELSE ${this.tableName}.window_started_at
|
|
377
|
+
END,
|
|
378
|
+
updated_at = excluded.updated_at
|
|
379
|
+
WHERE ${this.tableName}.window_started_at <= ?
|
|
380
|
+
OR ${this.tableName}.attempt_count < ?
|
|
381
|
+
RETURNING attempt_count, window_started_at`, id, `terminal-auth-${id}`, input.userId, nowIso, nowIso, nowIso, windowFloorIso, windowFloorIso, windowFloorIso, input.maxAttempts);
|
|
382
|
+
if (reserved.rows.length === 1) {
|
|
383
|
+
const reservedWindow = reserved.rows[0]?.window_started_at;
|
|
384
|
+
return {
|
|
385
|
+
allowed: true,
|
|
386
|
+
windowStartedAt: reservedWindow instanceof Date ? reservedWindow.toISOString() : new Date(String(reservedWindow)).toISOString()
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const windowStartedAt = (await this.db.query(`SELECT window_started_at FROM ${this.tableName}
|
|
390
|
+
WHERE user_id = ? LIMIT 1`, input.userId)).rows[0]?.window_started_at;
|
|
391
|
+
const startedMs = windowStartedAt instanceof Date ? windowStartedAt.getTime() : new Date(String(windowStartedAt)).getTime();
|
|
392
|
+
const remainingMs = Number.isFinite(startedMs) ? startedMs + input.windowMs - now.getTime() : input.windowMs;
|
|
393
|
+
return {
|
|
394
|
+
allowed: false,
|
|
395
|
+
retryAfterSeconds: Math.max(1, Math.ceil(remainingMs / 1e3))
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/** Release the reservation for an attempt that did not fail authentication. */
|
|
399
|
+
async releaseAttempt(userId, windowStartedAt) {
|
|
400
|
+
await this.db.query(`UPDATE ${this.tableName}
|
|
401
|
+
SET attempt_count = CASE
|
|
402
|
+
WHEN attempt_count > 0 THEN attempt_count - 1
|
|
403
|
+
ELSE 0
|
|
404
|
+
END,
|
|
405
|
+
updated_at = ?
|
|
406
|
+
WHERE user_id = ? AND window_started_at = ?`, (/* @__PURE__ */ new Date()).toISOString(), userId, windowStartedAt);
|
|
407
|
+
await this.db.query(`DELETE FROM ${this.tableName}
|
|
408
|
+
WHERE user_id = ? AND window_started_at = ? AND attempt_count = 0`, userId, windowStartedAt);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
//#endregion
|
|
299
412
|
//#region src/models/ResourceGrant.ts
|
|
300
413
|
var __defProp$1 = Object.defineProperty;
|
|
301
414
|
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
@@ -658,6 +771,41 @@ function readRequiredString(value, label) {
|
|
|
658
771
|
return stringValue;
|
|
659
772
|
}
|
|
660
773
|
//#endregion
|
|
774
|
+
//#region src/migrations/deduplicateRolePermissions.ts
|
|
775
|
+
var duplicateIds = `SELECT id FROM (
|
|
776
|
+
SELECT id, ROW_NUMBER() OVER (
|
|
777
|
+
PARTITION BY role_id, permission_id
|
|
778
|
+
ORDER BY created_at ASC NULLS LAST, id ASC
|
|
779
|
+
) AS duplicate_rank FROM role_permissions
|
|
780
|
+
) AS ranked WHERE duplicate_rank > 1`;
|
|
781
|
+
async function deduplicateRolePermissions(db, options = {}) {
|
|
782
|
+
const count = async (handle) => {
|
|
783
|
+
const result = await handle.query(`SELECT COUNT(*) AS count FROM (${duplicateIds}) AS duplicates`);
|
|
784
|
+
return Number(result.rows[0].count);
|
|
785
|
+
};
|
|
786
|
+
if (options.dryRun) return {
|
|
787
|
+
duplicates: await count(db),
|
|
788
|
+
removed: 0
|
|
789
|
+
};
|
|
790
|
+
if (!options.maintenanceConfirmed) throw new Error("Stop all writers and seeders, then set maintenanceConfirmed: true.");
|
|
791
|
+
if (!db.transaction) throw new Error("Grant deduplication requires a root transaction handle.");
|
|
792
|
+
await ensureChangeFeedTable(db);
|
|
793
|
+
return withEmbeddedWriteTransaction(db, isEmbeddedDatabase(db), async (tx) => {
|
|
794
|
+
if (!isEmbeddedDatabase(db)) await tx.query("LOCK TABLE role_permissions IN EXCLUSIVE MODE");
|
|
795
|
+
const duplicates = await count(tx);
|
|
796
|
+
if (duplicates > 0) await tx.query(`DELETE FROM role_permissions WHERE id IN (${duplicateIds})`);
|
|
797
|
+
await tx.query("CREATE UNIQUE INDEX IF NOT EXISTS role_permissions_role_id_permission_id_idx ON role_permissions (role_id, permission_id)");
|
|
798
|
+
if (duplicates > 0) await bumpChangeFeed(tx, {
|
|
799
|
+
table: "role_permissions",
|
|
800
|
+
operation: "delete"
|
|
801
|
+
});
|
|
802
|
+
return {
|
|
803
|
+
duplicates,
|
|
804
|
+
removed: duplicates
|
|
805
|
+
};
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
//#endregion
|
|
661
809
|
//#region src/migrations/materializeTenantHierarchy.ts
|
|
662
810
|
var TenantHierarchyMaterializationError = class extends Error {
|
|
663
811
|
constructor(problems) {
|
|
@@ -1194,7 +1342,7 @@ var MagicLinkService = class MagicLinkService {
|
|
|
1194
1342
|
* The caller is responsible for emailing the token to the user.
|
|
1195
1343
|
*/
|
|
1196
1344
|
async generate(email) {
|
|
1197
|
-
const { SignJWT } = await import("./chunks/TerminalAuthService-
|
|
1345
|
+
const { SignJWT } = await import("./chunks/TerminalAuthService-BdB_0vDA.js").then((n) => n.E);
|
|
1198
1346
|
const key = await this.getSigningKey();
|
|
1199
1347
|
const nonce = crypto.randomUUID();
|
|
1200
1348
|
const normalizedEmail = normalizeEmail(email);
|
|
@@ -1223,7 +1371,7 @@ var MagicLinkService = class MagicLinkService {
|
|
|
1223
1371
|
* @throws {MagicLinkError} If the token is invalid, expired, or already used
|
|
1224
1372
|
*/
|
|
1225
1373
|
async verify(token) {
|
|
1226
|
-
const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-
|
|
1374
|
+
const { jwtVerify, errors } = await import("./chunks/TerminalAuthService-BdB_0vDA.js").then((n) => n.E);
|
|
1227
1375
|
const key = await this.getSigningKey();
|
|
1228
1376
|
let payload;
|
|
1229
1377
|
try {
|
|
@@ -1821,6 +1969,6 @@ var TenantService = class TenantService {
|
|
|
1821
1969
|
//#region src/index.ts
|
|
1822
1970
|
registerUserRetentionTasks();
|
|
1823
1971
|
//#endregion
|
|
1824
|
-
export { ACCESS_REQUEST_CAPABILITIES, ANCESTOR_READ_ACTION, AccessRequest, AccessRequestCollection, AccessRequestError, AccessRequestService, AccessRequestStatus, CLI_AUTH_RETENTION_TASK, UsersCliAuthRequest as CliAuthRequest, UsersCliAuthRequest, UsersCliAuthRequestCollection as CliAuthRequestCollection, UsersCliAuthRequestCollection, DEFAULT_ANCESTOR_READ_MAX_DEPTH, DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, DEFAULT_CLI_SESSION_TTL_SECONDS, DEFAULT_ROLES, DEFAULT_ROLE_PERMISSION_PATTERNS, DEFAULT_ROLE_SLUGS, DEFAULT_SESSION_TTL, DEFAULT_TENANT_POLICY, DEFAULT_TOKEN_EXPIRY_SECONDS, Group, GroupCollection, GroupMember, GroupMemberCollection, GroupRole, GroupRoleCollection, LEGACY_USER_PROFILE_BACKFILL_NAME, LegacyUserProfileBackfillError, MAGIC_LINK_RETENTION_TASK, MAX_RESOURCE_GRANT_DELEGATION_DEPTH, MAX_TENANT_HIERARCHY_DEPTH, MagicLinkError, MagicLinkService, UsersMagicLinkToken as MagicLinkToken, UsersMagicLinkToken, UsersMagicLinkTokenCollection as MagicLinkTokenCollection, UsersMagicLinkTokenCollection, Membership, MembershipCollection, MembershipOverride, MembershipOverrideCollection, MembershipStatus, MobileAuthError, MobileAuthService, OidcLoginError, OidcLoginService, OidcProvisioningError, OperationPermissionError, OverrideEffect, Permission, PermissionCatalogService, PermissionCollection, PermissionResolver, ResourceGrant, ResourceGrantCollection, ResourceGrantService, Role, RoleCollection, RolePermission, RolePermissionCollection, SESSIONS_RETENTION_TASK, Session, SessionCollection, SessionService, SessionStatus, Tenant, TenantCollection, TenantHierarchyError, TenantHierarchyMaterializationError, TenantIntegration, TenantIntegrationCollection, TenantPermissionEffect, TenantPermissionOverride, TenantPermissionOverrideCollection, TenantService, TenantStatus, TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, USER_RETENTION_TASKS, User, UserCollection, UserEmailKeyBackfillError, UserStatus, UsersCliAuthApproveLimit, UsersCliAuthApproveLimitCollection, applyPostgresPermissionPolicies, assertOperationPermission, backfillLegacyUserProfiles, backfillUserEmailKeys, checkOperationPermission, checkResourceOperationPermission, decodeOidcTransaction, deriveOperationPermissionCollectionName, deriveOperationPermissionSlug, encodeOidcTransaction, generatePostgresPermissionSql, generateSessionId, getConfiguredAncestorReadPolicy, getCurrentSessionPermissionContext, getRequestScopedDatabase, getUsersOidcConfig, hasOperationPermission, isAncestorReadableSlug, materializeTenantHierarchy, normalizeAncestorReadPolicy, normalizeOperationPermissionAction, planTenantHierarchy, readMobileBearerToken, registerPermissionDefinitions, registerUserRetentionTasks, resolveOidcProviderConfig, syncPermissionCatalog, unregisterUserRetentionTasks, validateMobileRedirectUri, withPrincipalPermissionContext, withSessionPermissionContext };
|
|
1972
|
+
export { ACCESS_REQUEST_CAPABILITIES, ANCESTOR_READ_ACTION, AccessRequest, AccessRequestCollection, AccessRequestError, AccessRequestService, AccessRequestStatus, CLI_AUTH_RETENTION_TASK, UsersCliAuthRequest as CliAuthRequest, UsersCliAuthRequest, UsersCliAuthRequestCollection as CliAuthRequestCollection, UsersCliAuthRequestCollection, DEFAULT_ANCESTOR_READ_MAX_DEPTH, DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS, DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS, DEFAULT_CLI_SESSION_TTL_SECONDS, DEFAULT_DEVICE_PERSON_IDLE_SECONDS, DEFAULT_LOGIN_ATTEMPT_WINDOW_SECONDS, DEFAULT_LOGIN_AUDIT_RETENTION_DAYS, DEFAULT_LOGIN_LOCKOUT_BASE_SECONDS, DEFAULT_LOGIN_LOCKOUT_FACTOR, DEFAULT_LOGIN_LOCKOUT_MAX_SECONDS, DEFAULT_LOGIN_MAX_ATTEMPTS, DEFAULT_PIN_MANAGE_PERMISSION, DEFAULT_ROLES, DEFAULT_ROLE_PERMISSION_PATTERNS, DEFAULT_ROLE_SLUGS, DEFAULT_SESSION_TTL, DEFAULT_TENANT_POLICY, DEFAULT_TOKEN_EXPIRY_SECONDS, DeviceCredentialError, DeviceCredentialForbiddenError, DeviceCredentialPolicyError, DeviceCredentialService, DurableLoginAuditSink, Group, GroupCollection, GroupMember, GroupMemberCollection, GroupRole, GroupRoleCollection, InvalidCredentialsError, LEGACY_USER_PROFILE_BACKFILL_NAME, LOGIN_ATTEMPTS_RETENTION_TASK, LOGIN_AUDIT_RETENTION_TASK, LegacyUserProfileBackfillError, LoginAttemptLimiter, LoginRateLimitError, MAGIC_LINK_RETENTION_TASK, MAX_RESOURCE_GRANT_DELEGATION_DEPTH, MAX_TENANT_HIERARCHY_DEPTH, MagicLinkError, MagicLinkService, UsersMagicLinkToken as MagicLinkToken, UsersMagicLinkToken, UsersMagicLinkTokenCollection as MagicLinkTokenCollection, UsersMagicLinkTokenCollection, Membership, MembershipCollection, MembershipOverride, MembershipOverrideCollection, MembershipStatus, MobileAuthError, MobileAuthService, OidcLoginError, OidcLoginService, OidcProvisioningError, OperationPermissionError, OverrideEffect, PIN_LOGIN_KIND, PIN_PERMISSION_DEFINITIONS, Permission, PermissionCatalogService, PermissionCollection, PermissionResolver, PinVerifier, ResourceGrant, ResourceGrantCollection, ResourceGrantService, Role, RoleCollection, RolePermission, RolePermissionCollection, SESSIONS_RETENTION_TASK, SESSION_DATA_KEYS, Session, SessionCollection, SessionService, SessionStatus, TERMINAL_APPROVE_LOGIN_KIND, Tenant, TenantCollection, TenantHierarchyError, TenantHierarchyMaterializationError, TenantIntegration, TenantIntegrationCollection, TenantPermissionEffect, TenantPermissionOverride, TenantPermissionOverrideCollection, TenantService, TenantStatus, TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, USER_RETENTION_TASKS, User, UserCollection, UserEmailKeyBackfillError, UserStatus, UsersCliAuthApproveLimit, UsersCliAuthApproveLimitCollection, UsersLoginAttempt, UsersLoginAttemptCollection, UsersLoginAuditEvent, UsersLoginAuditEventCollection, UsersPinCredential, UsersPinCredentialCollection, applyPostgresPermissionPolicies, assertOperationPermission, backfillLegacyUserProfiles, backfillUserEmailKeys, checkOperationPermission, checkResourceOperationPermission, decodeOidcTransaction, deduplicateRolePermissions, deriveOperationPermissionCollectionName, deriveOperationPermissionSlug, encodeOidcTransaction, ensurePinPermissionsRegistered, generatePostgresPermissionSql, generateSessionId, getConfiguredAncestorReadPolicy, getCurrentSessionPermissionContext, getRequestScopedDatabase, getUsersOidcConfig, hasOperationPermission, isAncestorReadableSlug, materializeTenantHierarchy, normalizeAncestorReadPolicy, normalizeOperationPermissionAction, planTenantHierarchy, readMobileBearerToken, registerPermissionDefinitions, registerUserRetentionTasks, resolveOidcProviderConfig, syncPermissionCatalog, unregisterUserRetentionTasks, validateMobileRedirectUri, withPrincipalPermissionContext, withSessionPermissionContext };
|
|
1825
1973
|
|
|
1826
1974
|
//# sourceMappingURL=index.js.map
|