@fuzdev/fuz_app 0.57.2 → 0.59.0
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/actions/CLAUDE.md +8 -3
- package/dist/auth/CLAUDE.md +70 -37
- package/dist/auth/account_action_specs.d.ts +9 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +9 -0
- package/dist/auth/account_schema.d.ts +1 -1
- package/dist/auth/account_schema.js +1 -1
- package/dist/auth/admin_action_specs.d.ts +35 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +35 -0
- package/dist/auth/audit_log_ddl.d.ts +24 -0
- package/dist/auth/audit_log_ddl.d.ts.map +1 -0
- package/dist/auth/audit_log_ddl.js +42 -0
- package/dist/auth/audit_log_schema.d.ts +3 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +3 -34
- package/dist/auth/{ddl.d.ts → auth_ddl.d.ts} +7 -4
- package/dist/auth/auth_ddl.d.ts.map +1 -0
- package/dist/auth/{ddl.js → auth_ddl.js} +6 -3
- package/dist/auth/migrations.js +4 -4
- package/dist/auth/role_grant_offer_action_specs.d.ts +17 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +17 -0
- package/dist/auth/role_grant_offer_ddl.d.ts +43 -0
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_ddl.js +99 -0
- package/dist/auth/role_grant_offer_queries.d.ts +1 -1
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +1 -1
- package/dist/auth/role_grant_offer_schema.d.ts +3 -28
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_schema.js +3 -80
- package/dist/auth/role_grant_queries.d.ts +1 -1
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +1 -1
- package/dist/auth/self_service_role_action_specs.d.ts +8 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +8 -0
- package/package.json +1 -1
- package/dist/auth/ddl.d.ts.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit log
|
|
2
|
+
* Audit log types and client-safe Zod schemas.
|
|
3
3
|
*
|
|
4
4
|
* Records auth mutations (login, logout, grant, revoke, etc.) for
|
|
5
5
|
* security monitoring and operational visibility.
|
|
6
6
|
*
|
|
7
|
+
* Table DDL and indexes live in `auth/audit_log_ddl.ts`.
|
|
8
|
+
*
|
|
7
9
|
* @module
|
|
8
10
|
*/
|
|
9
11
|
import { z } from 'zod';
|
|
@@ -345,36 +347,3 @@ export const RoleGrantHistoryEventJson = AuditLogEventJson.extend({
|
|
|
345
347
|
export const AdminSessionJson = AuthSessionJson.extend({
|
|
346
348
|
username: z.string(),
|
|
347
349
|
});
|
|
348
|
-
// Schema DDL
|
|
349
|
-
//
|
|
350
|
-
// Multi-actor invariants the envelope columns assume:
|
|
351
|
-
// - `actor_id` + `account_id`, when both populated, refer to the same
|
|
352
|
-
// account (derivable via `actor.account_id`). Denormalized for
|
|
353
|
-
// indexed audit queries; do not let them disagree.
|
|
354
|
-
// - `target_actor_id` + `target_account_id`, same rule when both populated.
|
|
355
|
-
// - `target_account_id` is the SSE/WS socket-close key — sessions stay
|
|
356
|
-
// account-grain after multi-actor lands, so this column carries
|
|
357
|
-
// the routing identity even on actor-bound events.
|
|
358
|
-
// - `target_actor_id` is populated iff the event subject is actor-bound
|
|
359
|
-
// (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
|
|
360
|
-
export const AUDIT_LOG_SCHEMA = `
|
|
361
|
-
CREATE TABLE IF NOT EXISTS audit_log (
|
|
362
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
363
|
-
seq SERIAL NOT NULL,
|
|
364
|
-
event_type TEXT NOT NULL,
|
|
365
|
-
outcome TEXT NOT NULL DEFAULT 'success',
|
|
366
|
-
actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
367
|
-
account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
368
|
-
target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
|
|
369
|
-
target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
|
|
370
|
-
ip TEXT,
|
|
371
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
372
|
-
metadata JSONB
|
|
373
|
-
)`;
|
|
374
|
-
export const AUDIT_LOG_INDEXES = [
|
|
375
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_seq ON audit_log(seq DESC)`,
|
|
376
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
|
|
377
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
|
|
378
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
|
|
379
|
-
`CREATE INDEX IF NOT EXISTS idx_audit_log_target_actor ON audit_log(target_actor_id)`,
|
|
380
|
-
];
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
|
|
3
|
+
* core auth tables (account, actor, role_grant, auth_session, api_token,
|
|
4
|
+
* bootstrap_lock, invite, app_settings).
|
|
3
5
|
*
|
|
4
|
-
* Consumed by `auth/migrations.ts`.
|
|
5
|
-
*
|
|
6
|
+
* Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
|
|
7
|
+
* (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
|
|
8
|
+
* in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
|
|
6
9
|
*
|
|
7
10
|
* @module
|
|
8
11
|
*/
|
|
@@ -24,4 +27,4 @@ export declare const INVITE_SCHEMA = "\nCREATE TABLE IF NOT EXISTS invite (\n i
|
|
|
24
27
|
export declare const INVITE_INDEXES: string[];
|
|
25
28
|
export declare const APP_SETTINGS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS app_settings (\n id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),\n open_signup BOOLEAN NOT NULL DEFAULT false,\n updated_at TIMESTAMPTZ,\n updated_by UUID\n)";
|
|
26
29
|
export declare const APP_SETTINGS_SEED = "\nINSERT INTO app_settings (id) VALUES (1) ON CONFLICT DO NOTHING";
|
|
27
|
-
//# sourceMappingURL=
|
|
30
|
+
//# sourceMappingURL=auth_ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/auth_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,cAAc,8WAWzB,CAAC;AAEH,eAAO,MAAM,YAAY,mUAQvB,CAAC;AAEH,eAAO,MAAM,WAAW,wEAC0C,CAAC;AAEnE,eAAO,MAAM,iBAAiB,2ZAU5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,UAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,0RAO9B,CAAC;AAEH,eAAO,MAAM,oBAAoB,UAGhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,iUAU3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,4GACsE,CAAC;AAEvG,eAAO,MAAM,yBAAyB,6FACiD,CAAC;AAExF,eAAO,MAAM,eAAe,gFAC8C,CAAC;AAE3E,eAAO,MAAM,qBAAqB,wJAIhC,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,yHAGP,CAAC;AAE1B,eAAO,MAAM,aAAa,6ZAUxB,CAAC;AAEH,eAAO,MAAM,cAAc,UAI1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,oMAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,sEACkC,CAAC"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
|
|
3
|
+
* core auth tables (account, actor, role_grant, auth_session, api_token,
|
|
4
|
+
* bootstrap_lock, invite, app_settings).
|
|
3
5
|
*
|
|
4
|
-
* Consumed by `auth/migrations.ts`.
|
|
5
|
-
*
|
|
6
|
+
* Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
|
|
7
|
+
* (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
|
|
8
|
+
* in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
|
|
6
9
|
*
|
|
7
10
|
* @module
|
|
8
11
|
*/
|
package/dist/auth/migrations.js
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
*
|
|
37
37
|
* @module
|
|
38
38
|
*/
|
|
39
|
-
import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './
|
|
40
|
-
import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './
|
|
41
|
-
import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './
|
|
39
|
+
import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './auth_ddl.js';
|
|
40
|
+
import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './audit_log_ddl.js';
|
|
41
|
+
import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './role_grant_offer_ddl.js';
|
|
42
42
|
/** Namespace identifier for fuz_app auth migrations. */
|
|
43
43
|
export const AUTH_MIGRATION_NAMESPACE = 'fuz_auth';
|
|
44
44
|
/**
|
|
@@ -105,7 +105,7 @@ export const AUTH_MIGRATIONS = [
|
|
|
105
105
|
},
|
|
106
106
|
// v1: consentful role_grants — role_grant_offer table + scoped role_grants
|
|
107
107
|
{
|
|
108
|
-
name: '
|
|
108
|
+
name: 'role_grant_offer_and_scoped_role_grants',
|
|
109
109
|
up: async (db) => {
|
|
110
110
|
await db.query(ROLE_GRANT_OFFER_SCHEMA);
|
|
111
111
|
await db.query(ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX);
|
|
@@ -209,6 +209,16 @@ export declare const RoleGrantRevokeOutput: z.ZodObject<{
|
|
|
209
209
|
revoked: z.ZodLiteral<true>;
|
|
210
210
|
}, z.core.$strict>;
|
|
211
211
|
export type RoleGrantRevokeOutput = z.infer<typeof RoleGrantRevokeOutput>;
|
|
212
|
+
/**
|
|
213
|
+
* `rate_limit: 'account'` throttles offer-spam at the authenticated
|
|
214
|
+
* grantor and bounds the account-existence oracle on `to_account_id` —
|
|
215
|
+
* the same shape as `invite_create_action_spec` upstream addresses, where
|
|
216
|
+
* a hostile authed caller iterates recipients to probe
|
|
217
|
+
* `ERROR_ACCOUNT_NOT_FOUND` (and the actor-binding via
|
|
218
|
+
* `ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH`) as an enumeration
|
|
219
|
+
* vector. Failure-outcome audit rows preserve the forensic trail; the
|
|
220
|
+
* rate cap closes the budget.
|
|
221
|
+
*/
|
|
212
222
|
export declare const role_grant_offer_create_action_spec: {
|
|
213
223
|
method: string;
|
|
214
224
|
kind: "request_response";
|
|
@@ -250,6 +260,7 @@ export declare const role_grant_offer_create_action_spec: {
|
|
|
250
260
|
async: true;
|
|
251
261
|
description: string;
|
|
252
262
|
error_reasons: ("role_grant_offer_self_target" | "role_grant_offer_role_not_grantable" | "role_grant_offer_not_authorized" | "role_grant_offer_actor_account_mismatch")[];
|
|
263
|
+
rate_limit: "account";
|
|
253
264
|
};
|
|
254
265
|
export declare const role_grant_offer_accept_action_spec: {
|
|
255
266
|
method: string;
|
|
@@ -405,6 +416,12 @@ export declare const role_grant_offer_history_action_spec: {
|
|
|
405
416
|
async: true;
|
|
406
417
|
description: string;
|
|
407
418
|
};
|
|
419
|
+
/**
|
|
420
|
+
* `rate_limit: 'account'` bounds admin-side burn of `role_grant_revoke` —
|
|
421
|
+
* the action is admin-gated and audit-trailed, but the per-account cap
|
|
422
|
+
* keeps a single admin script from churning role_grants in a loop and
|
|
423
|
+
* obscuring audit context for unrelated activity.
|
|
424
|
+
*/
|
|
408
425
|
export declare const role_grant_revoke_action_spec: {
|
|
409
426
|
method: string;
|
|
410
427
|
kind: "request_response";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -157,6 +157,16 @@ export const RoleGrantRevokeOutput = z.strictObject({
|
|
|
157
157
|
revoked: z.literal(true),
|
|
158
158
|
});
|
|
159
159
|
// -- Action specs -----------------------------------------------------------
|
|
160
|
+
/**
|
|
161
|
+
* `rate_limit: 'account'` throttles offer-spam at the authenticated
|
|
162
|
+
* grantor and bounds the account-existence oracle on `to_account_id` —
|
|
163
|
+
* the same shape as `invite_create_action_spec` upstream addresses, where
|
|
164
|
+
* a hostile authed caller iterates recipients to probe
|
|
165
|
+
* `ERROR_ACCOUNT_NOT_FOUND` (and the actor-binding via
|
|
166
|
+
* `ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH`) as an enumeration
|
|
167
|
+
* vector. Failure-outcome audit rows preserve the forensic trail; the
|
|
168
|
+
* rate cap closes the budget.
|
|
169
|
+
*/
|
|
160
170
|
export const role_grant_offer_create_action_spec = {
|
|
161
171
|
method: 'role_grant_offer_create',
|
|
162
172
|
kind: 'request_response',
|
|
@@ -173,6 +183,7 @@ export const role_grant_offer_create_action_spec = {
|
|
|
173
183
|
ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED,
|
|
174
184
|
ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH,
|
|
175
185
|
],
|
|
186
|
+
rate_limit: 'account',
|
|
176
187
|
};
|
|
177
188
|
export const role_grant_offer_accept_action_spec = {
|
|
178
189
|
method: 'role_grant_offer_accept',
|
|
@@ -237,6 +248,12 @@ export const role_grant_offer_history_action_spec = {
|
|
|
237
248
|
async: true,
|
|
238
249
|
description: 'List every offer involving the caller (either direction), including terminal rows, newest first. Admins may pass `account_id` to inspect another account.',
|
|
239
250
|
};
|
|
251
|
+
/**
|
|
252
|
+
* `rate_limit: 'account'` bounds admin-side burn of `role_grant_revoke` —
|
|
253
|
+
* the action is admin-gated and audit-trailed, but the per-account cap
|
|
254
|
+
* keeps a single admin script from churning role_grants in a loop and
|
|
255
|
+
* obscuring audit context for unrelated activity.
|
|
256
|
+
*/
|
|
240
257
|
export const role_grant_revoke_action_spec = {
|
|
241
258
|
method: 'role_grant_revoke',
|
|
242
259
|
kind: 'request_response',
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
|
|
3
|
+
* sentinel constants the queries / migrations interpolate.
|
|
4
|
+
*
|
|
5
|
+
* Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
|
|
6
|
+
* Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
9
|
+
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
10
|
+
* at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
|
|
11
|
+
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
12
|
+
* produced by `query_accept_offer`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
17
|
+
export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
18
|
+
/**
|
|
19
|
+
* Index-side token for the global case in the partial unique index. Uppercase
|
|
20
|
+
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
21
|
+
* are lowercase by regex). Never appears as a column value — column-level
|
|
22
|
+
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
|
|
25
|
+
export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
|
|
26
|
+
/**
|
|
27
|
+
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
28
|
+
*
|
|
29
|
+
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
30
|
+
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
31
|
+
* for the same student and scope. A same-grantor re-offer upserts the
|
|
32
|
+
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
33
|
+
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
34
|
+
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
35
|
+
* always either both null (global) or both non-null (scoped) per the
|
|
36
|
+
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
37
|
+
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
38
|
+
* match this expression literally.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
41
|
+
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
42
|
+
export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
43
|
+
//# sourceMappingURL=role_grant_offer_ddl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role_grant_offer_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,mHAAmH;AACnH,eAAO,MAAM,oCAAoC,yCAAyC,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,WAAW,CAAC;AAEjE,eAAO,MAAM,uBAAuB,qzCAkClC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qCAAqC,2XAYpB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,kOAMX,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
|
|
3
|
+
* sentinel constants the queries / migrations interpolate.
|
|
4
|
+
*
|
|
5
|
+
* Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
|
|
6
|
+
* Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
|
|
7
|
+
*
|
|
8
|
+
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
9
|
+
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
10
|
+
* at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
|
|
11
|
+
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
12
|
+
* produced by `query_accept_offer`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
17
|
+
export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
18
|
+
/**
|
|
19
|
+
* Index-side token for the global case in the partial unique index. Uppercase
|
|
20
|
+
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
21
|
+
* are lowercase by regex). Never appears as a column value — column-level
|
|
22
|
+
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
23
|
+
*/
|
|
24
|
+
export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
|
|
25
|
+
export const ROLE_GRANT_OFFER_SCHEMA = `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS role_grant_offer (
|
|
27
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
28
|
+
from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
29
|
+
to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
|
|
30
|
+
to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
31
|
+
role TEXT NOT NULL,
|
|
32
|
+
scope_kind TEXT NULL,
|
|
33
|
+
scope_id UUID NULL,
|
|
34
|
+
message TEXT NULL,
|
|
35
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
36
|
+
expires_at TIMESTAMPTZ NOT NULL,
|
|
37
|
+
accepted_at TIMESTAMPTZ NULL,
|
|
38
|
+
declined_at TIMESTAMPTZ NULL,
|
|
39
|
+
decline_reason TEXT NULL,
|
|
40
|
+
retracted_at TIMESTAMPTZ NULL,
|
|
41
|
+
superseded_at TIMESTAMPTZ NULL,
|
|
42
|
+
resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
|
|
43
|
+
CONSTRAINT role_grant_offer_single_terminal CHECK (
|
|
44
|
+
(accepted_at IS NOT NULL)::int
|
|
45
|
+
+ (declined_at IS NOT NULL)::int
|
|
46
|
+
+ (retracted_at IS NOT NULL)::int
|
|
47
|
+
+ (superseded_at IS NOT NULL)::int
|
|
48
|
+
<= 1
|
|
49
|
+
),
|
|
50
|
+
CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
|
|
51
|
+
(accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
|
|
52
|
+
),
|
|
53
|
+
CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
|
|
54
|
+
decline_reason IS NULL OR declined_at IS NOT NULL
|
|
55
|
+
),
|
|
56
|
+
CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
|
|
57
|
+
(scope_kind IS NULL) = (scope_id IS NULL)
|
|
58
|
+
)
|
|
59
|
+
)`;
|
|
60
|
+
/**
|
|
61
|
+
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
62
|
+
*
|
|
63
|
+
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
64
|
+
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
65
|
+
* for the same student and scope. A same-grantor re-offer upserts the
|
|
66
|
+
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
67
|
+
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
68
|
+
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
69
|
+
* always either both null (global) or both non-null (scoped) per the
|
|
70
|
+
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
71
|
+
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
72
|
+
* match this expression literally.
|
|
73
|
+
*/
|
|
74
|
+
export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
|
|
75
|
+
CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
|
|
76
|
+
ON role_grant_offer (
|
|
77
|
+
to_account_id,
|
|
78
|
+
role,
|
|
79
|
+
COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
|
|
80
|
+
COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
|
|
81
|
+
from_actor_id
|
|
82
|
+
)
|
|
83
|
+
WHERE accepted_at IS NULL
|
|
84
|
+
AND declined_at IS NULL
|
|
85
|
+
AND retracted_at IS NULL
|
|
86
|
+
AND superseded_at IS NULL`;
|
|
87
|
+
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
88
|
+
export const ROLE_GRANT_OFFER_INBOX_INDEX = `
|
|
89
|
+
CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
|
|
90
|
+
ON role_grant_offer (to_account_id, expires_at)
|
|
91
|
+
WHERE accepted_at IS NULL
|
|
92
|
+
AND declined_at IS NULL
|
|
93
|
+
AND retracted_at IS NULL
|
|
94
|
+
AND superseded_at IS NULL`;
|
|
95
|
+
// **Deferred**: a `role_grant_offer_to_actor` partial index belongs here once
|
|
96
|
+
// an actor-side inbox query (`query_role_grant_offer_list_for_actor`) lands —
|
|
97
|
+
// no current consumer filters on `to_actor_id`, and adding the index
|
|
98
|
+
// before the query is paying write-amp for nothing. Land the index in
|
|
99
|
+
// the same slice as the query.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
15
15
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
16
16
|
import type { RoleGrant } from './account_schema.js';
|
|
17
|
-
import {
|
|
17
|
+
import type { CreateRoleGrantOfferInput, RoleGrantOffer, SupersededOffer } from './role_grant_offer_schema.js';
|
|
18
18
|
import type { AuditLogEvent } from './audit_log_schema.js';
|
|
19
19
|
/**
|
|
20
20
|
* Error thrown by offer-lifecycle queries when the offer is in a non-pending
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @module
|
|
13
13
|
*/
|
|
14
14
|
import { assert_row } from '../db/assert_row.js';
|
|
15
|
-
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './
|
|
15
|
+
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
|
|
16
16
|
import { query_audit_log } from './audit_log_queries.js';
|
|
17
17
|
/**
|
|
18
18
|
* Error thrown by offer-lifecycle queries when the offer is in a non-pending
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Role grant offer
|
|
2
|
+
* Role grant offer types and client-safe schemas.
|
|
3
3
|
*
|
|
4
4
|
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
5
5
|
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
@@ -7,41 +7,16 @@
|
|
|
7
7
|
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
8
8
|
* produced by `query_accept_offer`.
|
|
9
9
|
*
|
|
10
|
+
* Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
|
|
11
|
+
*
|
|
10
12
|
* @module
|
|
11
13
|
*/
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
14
|
-
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
15
|
-
export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
16
16
|
/** Maximum length of the optional message attached to an offer. */
|
|
17
17
|
export declare const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
|
|
18
18
|
/** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
|
|
19
19
|
export declare const ROLE_GRANT_OFFER_DEFAULT_TTL_MS: number;
|
|
20
|
-
export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
|
|
21
|
-
/**
|
|
22
|
-
* Index-side token for the global case in the partial unique index. Uppercase
|
|
23
|
-
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
24
|
-
* are lowercase by regex). Never appears as a column value — column-level
|
|
25
|
-
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
26
|
-
*/
|
|
27
|
-
export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
|
|
28
|
-
/**
|
|
29
|
-
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
30
|
-
*
|
|
31
|
-
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
32
|
-
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
33
|
-
* for the same student and scope. A same-grantor re-offer upserts the
|
|
34
|
-
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
35
|
-
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
36
|
-
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
37
|
-
* always either both null (global) or both non-null (scoped) per the
|
|
38
|
-
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
39
|
-
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
40
|
-
* match this expression literally.
|
|
41
|
-
*/
|
|
42
|
-
export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
43
|
-
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
44
|
-
export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
|
|
45
20
|
/** Role grant offer row as returned by the database. */
|
|
46
21
|
export interface RoleGrantOffer {
|
|
47
22
|
id: Uuid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAI5C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD0D,CAAC;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Role grant offer
|
|
2
|
+
* Role grant offer types and client-safe schemas.
|
|
3
3
|
*
|
|
4
4
|
* An offer is a pending grant awaiting recipient consent. Lifecycle states
|
|
5
5
|
* are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
|
|
@@ -7,94 +7,17 @@
|
|
|
7
7
|
* On accept, the offer's `resulting_role_grant_id` links to the role_grant row
|
|
8
8
|
* produced by `query_accept_offer`.
|
|
9
9
|
*
|
|
10
|
+
* Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
|
|
11
|
+
*
|
|
10
12
|
* @module
|
|
11
13
|
*/
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
14
16
|
import { RoleName } from './role_schema.js';
|
|
15
|
-
/** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
|
|
16
|
-
export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
17
17
|
/** Maximum length of the optional message attached to an offer. */
|
|
18
18
|
export const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
|
|
19
19
|
/** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
|
|
20
20
|
export const ROLE_GRANT_OFFER_DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
21
|
-
export const ROLE_GRANT_OFFER_SCHEMA = `
|
|
22
|
-
CREATE TABLE IF NOT EXISTS role_grant_offer (
|
|
23
|
-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
24
|
-
from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
25
|
-
to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
|
|
26
|
-
to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
|
|
27
|
-
role TEXT NOT NULL,
|
|
28
|
-
scope_kind TEXT NULL,
|
|
29
|
-
scope_id UUID NULL,
|
|
30
|
-
message TEXT NULL,
|
|
31
|
-
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
32
|
-
expires_at TIMESTAMPTZ NOT NULL,
|
|
33
|
-
accepted_at TIMESTAMPTZ NULL,
|
|
34
|
-
declined_at TIMESTAMPTZ NULL,
|
|
35
|
-
decline_reason TEXT NULL,
|
|
36
|
-
retracted_at TIMESTAMPTZ NULL,
|
|
37
|
-
superseded_at TIMESTAMPTZ NULL,
|
|
38
|
-
resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
|
|
39
|
-
CONSTRAINT role_grant_offer_single_terminal CHECK (
|
|
40
|
-
(accepted_at IS NOT NULL)::int
|
|
41
|
-
+ (declined_at IS NOT NULL)::int
|
|
42
|
-
+ (retracted_at IS NOT NULL)::int
|
|
43
|
-
+ (superseded_at IS NOT NULL)::int
|
|
44
|
-
<= 1
|
|
45
|
-
),
|
|
46
|
-
CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
|
|
47
|
-
(accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
|
|
48
|
-
),
|
|
49
|
-
CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
|
|
50
|
-
decline_reason IS NULL OR declined_at IS NOT NULL
|
|
51
|
-
),
|
|
52
|
-
CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
|
|
53
|
-
(scope_kind IS NULL) = (scope_id IS NULL)
|
|
54
|
-
)
|
|
55
|
-
)`;
|
|
56
|
-
/**
|
|
57
|
-
* Index-side token for the global case in the partial unique index. Uppercase
|
|
58
|
-
* so it cannot collide with consumer-declared `ScopeKindName` values (which
|
|
59
|
-
* are lowercase by regex). Never appears as a column value — column-level
|
|
60
|
-
* `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
|
|
61
|
-
*/
|
|
62
|
-
export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
|
|
63
|
-
/**
|
|
64
|
-
* At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
|
|
65
|
-
*
|
|
66
|
-
* Including `from_actor_id` in the tuple lets multiple grantors coexist —
|
|
67
|
-
* teacher A and teacher B can each have a pending `classroom_student` offer
|
|
68
|
-
* for the same student and scope. A same-grantor re-offer upserts the
|
|
69
|
-
* existing pending row. `COALESCE` collapses `NULL` scopes into the
|
|
70
|
-
* sentinel values so Postgres's NULL-in-unique-index quirk does not allow
|
|
71
|
-
* duplicate global pending offers; the `scope_kind` / `scope_id` pair is
|
|
72
|
-
* always either both null (global) or both non-null (scoped) per the
|
|
73
|
-
* `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
|
|
74
|
-
* always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
|
|
75
|
-
* match this expression literally.
|
|
76
|
-
*/
|
|
77
|
-
export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
|
|
78
|
-
CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
|
|
79
|
-
ON role_grant_offer (
|
|
80
|
-
to_account_id,
|
|
81
|
-
role,
|
|
82
|
-
COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
|
|
83
|
-
COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
|
|
84
|
-
from_actor_id
|
|
85
|
-
)
|
|
86
|
-
WHERE accepted_at IS NULL
|
|
87
|
-
AND declined_at IS NULL
|
|
88
|
-
AND retracted_at IS NULL
|
|
89
|
-
AND superseded_at IS NULL`;
|
|
90
|
-
/** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
|
|
91
|
-
export const ROLE_GRANT_OFFER_INBOX_INDEX = `
|
|
92
|
-
CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
|
|
93
|
-
ON role_grant_offer (to_account_id, expires_at)
|
|
94
|
-
WHERE accepted_at IS NULL
|
|
95
|
-
AND declined_at IS NULL
|
|
96
|
-
AND retracted_at IS NULL
|
|
97
|
-
AND superseded_at IS NULL`;
|
|
98
21
|
/** Zod schema for client-safe role_grant offer data. */
|
|
99
22
|
export const RoleGrantOfferJson = z
|
|
100
23
|
.strictObject({
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
10
10
|
import type { QueryDeps } from '../db/query_deps.js';
|
|
11
11
|
import type { RoleGrant, CreateRoleGrantInput } from './account_schema.js';
|
|
12
|
-
import {
|
|
12
|
+
import type { SupersededOffer } from './role_grant_offer_schema.js';
|
|
13
13
|
/**
|
|
14
14
|
* Grant a role_grant to an actor.
|
|
15
15
|
* Idempotent — if an active role_grant already exists for this actor, role, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAC,SAAS,EAAE,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import { assert_row } from '../db/assert_row.js';
|
|
10
|
-
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './
|
|
10
|
+
import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
|
|
11
11
|
/**
|
|
12
12
|
* Grant a role_grant to an actor.
|
|
13
13
|
* Idempotent — if an active role_grant already exists for this actor, role, and
|
|
@@ -29,6 +29,13 @@ export declare const SelfServiceRoleSetOutput: z.ZodObject<{
|
|
|
29
29
|
changed: z.ZodBoolean;
|
|
30
30
|
}, z.core.$strict>;
|
|
31
31
|
export type SelfServiceRoleSetOutput = z.infer<typeof SelfServiceRoleSetOutput>;
|
|
32
|
+
/**
|
|
33
|
+
* `rate_limit: 'account'` bounds audit-row churn. The toggle is idempotent
|
|
34
|
+
* (`changed: false` re-grants/re-revokes), but every call still writes a
|
|
35
|
+
* `role_grant_create` or `role_grant_revoke` audit row with
|
|
36
|
+
* `self_service: true`. Without the cap, a caller could flap the role in
|
|
37
|
+
* a loop to inflate the audit log and obscure other activity.
|
|
38
|
+
*/
|
|
32
39
|
export declare const self_service_role_set_action_spec: {
|
|
33
40
|
method: string;
|
|
34
41
|
kind: "request_response";
|
|
@@ -50,6 +57,7 @@ export declare const self_service_role_set_action_spec: {
|
|
|
50
57
|
}, z.core.$strict>;
|
|
51
58
|
async: true;
|
|
52
59
|
description: string;
|
|
60
|
+
rate_limit: "account";
|
|
53
61
|
};
|
|
54
62
|
/**
|
|
55
63
|
* All self-service role action specs — a codegen-ready registry. Single-element
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF,eAAO,MAAM,iCAAiC
|
|
1
|
+
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;;CAYT,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,aAAa,CAAC,yBAAyB,CAEvF,CAAC"}
|