@fuzdev/fuz_app 0.109.0 → 0.110.1
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/auth/CLAUDE.md +6 -5
- package/dist/auth/account_action_specs.d.ts +18 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +14 -1
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +6 -2
- package/dist/auth/bootstrap_account.d.ts +7 -2
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +9 -4
- package/dist/auth/bootstrap_routes.d.ts +12 -4
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +15 -6
- package/dist/auth/daemon_token_middleware.d.ts +7 -69
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +8 -104
- package/dist/auth/deps.d.ts +9 -5
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/middleware.js +1 -1
- package/dist/auth/request_context.d.ts +1 -3
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +2 -5
- package/dist/auth/session_cookie.d.ts +18 -36
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +16 -26
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +1 -4
- package/dist/auth/session_queries.d.ts +14 -30
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +15 -45
- package/dist/auth/token_lifetime.d.ts +51 -0
- package/dist/auth/token_lifetime.d.ts.map +1 -0
- package/dist/auth/token_lifetime.js +54 -0
- package/dist/dev/setup.d.ts +5 -9
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +18 -21
- package/dist/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +27 -1
- package/dist/runtime/deps.d.ts +38 -4
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/fs.d.ts +24 -5
- package/dist/runtime/fs.d.ts.map +1 -1
- package/dist/runtime/fs.js +38 -7
- package/dist/runtime/mock.d.ts +7 -0
- package/dist/runtime/mock.d.ts.map +1 -1
- package/dist/runtime/mock.js +41 -1
- package/dist/runtime/node.d.ts.map +1 -1
- package/dist/runtime/node.js +7 -1
- package/dist/runtime/secure_file.d.ts +71 -0
- package/dist/runtime/secure_file.d.ts.map +1 -0
- package/dist/runtime/secure_file.js +125 -0
- package/dist/server/app_backend.d.ts +6 -5
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +2 -3
- package/dist/testing/CLAUDE.md +15 -1
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +57 -20
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +13 -8
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +5 -1
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.js +34 -1
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +2 -1
- package/dist/testing/cross_backend/setup.js +1 -1
- package/dist/testing/cross_backend/token_lifetime.d.ts +15 -0
- package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -0
- package/dist/testing/cross_backend/token_lifetime.js +99 -0
- package/dist/testing/cross_backend/token_scope_surface.js +1 -1
- package/dist/testing/daemon_token_rotation.d.ts +83 -0
- package/dist/testing/daemon_token_rotation.d.ts.map +1 -0
- package/dist/testing/daemon_token_rotation.js +99 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +15 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +4 -4
- package/dist/ui/AccountSessions.svelte +0 -5
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +7 -4
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +0 -5
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hardened secret-file read — the TS twin of the Rust spine's
|
|
3
|
+
* `fuz_sys::secure_file::load_secure_file`.
|
|
4
|
+
*
|
|
5
|
+
* The file this guards is typically the highest-value credential on the host
|
|
6
|
+
* (the bootstrap token mints the keeper account), so the read fails loud
|
|
7
|
+
* rather than degrading: a symlink, a group/other-accessible mode, or an
|
|
8
|
+
* oversized file is refused, never returned. The deploy recipe places the
|
|
9
|
+
* token at `0600`, so the check ratifies the shape zap produces; a
|
|
10
|
+
* hand-placed `0644` file fails at boot instead of being read.
|
|
11
|
+
*
|
|
12
|
+
* The Node implementation lives here (`load_secure_file_node`, wired as
|
|
13
|
+
* `create_node_runtime().read_secure_file`); the Deno runtime implements the
|
|
14
|
+
* same contract over `Deno.open` in `runtime/deno.ts`, and the mock honors
|
|
15
|
+
* the mode/size checks over its in-memory map. All three refuse through the
|
|
16
|
+
* shared `assert_secure_mode` / `assert_secure_size` / `read_secure_bounded`
|
|
17
|
+
* helpers so the checks — and their operator-facing messages — can't drift
|
|
18
|
+
* between runtimes.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import { constants } from 'node:fs';
|
|
23
|
+
import { open } from 'node:fs/promises';
|
|
24
|
+
import process from 'node:process';
|
|
25
|
+
/**
|
|
26
|
+
* Maximum size for secure files. Prevents DoS from unexpectedly large files;
|
|
27
|
+
* 4 KiB is generous for token/key material. Matches the Rust twin's
|
|
28
|
+
* `MAX_SECURE_FILE_SIZE`.
|
|
29
|
+
*/
|
|
30
|
+
export const MAX_SECURE_FILE_SIZE = 4096;
|
|
31
|
+
/**
|
|
32
|
+
* Whether POSIX file modes are meaningful on this platform. On Windows Node
|
|
33
|
+
* synthesizes modes (always group/other-readable), so enforcing the check
|
|
34
|
+
* would refuse every file; the Rust twin is Unix-only and never faces this.
|
|
35
|
+
* The mock runtime checks unconditionally — its modes are simulated.
|
|
36
|
+
*/
|
|
37
|
+
const modes_apply = process.platform !== 'win32';
|
|
38
|
+
/**
|
|
39
|
+
* Refuse any group/other-accessible mode (only `0600`/`0400` pass).
|
|
40
|
+
*
|
|
41
|
+
* Callers own the platform gating: the real runtimes skip the check where
|
|
42
|
+
* modes aren't meaningful (Node on Windows, a `null` Deno mode); the mock
|
|
43
|
+
* checks its simulated modes unconditionally.
|
|
44
|
+
*
|
|
45
|
+
* @throws Error naming the path, the offending mode, and the `chmod` fix
|
|
46
|
+
*/
|
|
47
|
+
export const assert_secure_mode = (path, mode) => {
|
|
48
|
+
if ((mode & 0o077) !== 0) {
|
|
49
|
+
throw new Error(`insecure permissions on ${path}: ${(mode & 0o777).toString(8)} (expected 0600) — fix with: chmod 600 ${path}`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Refuse a byte count over `MAX_SECURE_FILE_SIZE`.
|
|
54
|
+
*
|
|
55
|
+
* @throws Error naming the path, the size, and the cap
|
|
56
|
+
*/
|
|
57
|
+
export const assert_secure_size = (path, size) => {
|
|
58
|
+
if (size > MAX_SECURE_FILE_SIZE) {
|
|
59
|
+
throw new Error(`secure file too large: ${path} (${size} bytes, max ${MAX_SECURE_FILE_SIZE})`);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Drain `read_chunk` into a cap-bounded buffer.
|
|
64
|
+
*
|
|
65
|
+
* Reads up to cap + 1 bytes so a file growing between stat and read is still
|
|
66
|
+
* caught (the stat-time size check alone races). `read_chunk` fills the given
|
|
67
|
+
* target from the file's current position and returns the bytes read
|
|
68
|
+
* (`null`/`0` = EOF) — the seam that lets the Node and Deno handle APIs share
|
|
69
|
+
* one loop.
|
|
70
|
+
*
|
|
71
|
+
* @throws Error when more than `MAX_SECURE_FILE_SIZE` bytes arrive
|
|
72
|
+
*/
|
|
73
|
+
export const read_secure_bounded = async (path, read_chunk) => {
|
|
74
|
+
const buffer = new Uint8Array(MAX_SECURE_FILE_SIZE + 1);
|
|
75
|
+
let total = 0;
|
|
76
|
+
while (total < buffer.length) {
|
|
77
|
+
const n = await read_chunk(buffer.subarray(total));
|
|
78
|
+
if (n === null || n === 0)
|
|
79
|
+
break;
|
|
80
|
+
total += n;
|
|
81
|
+
}
|
|
82
|
+
assert_secure_size(path, total);
|
|
83
|
+
return buffer.subarray(0, total);
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Read a secret file with fail-loud checks (Node implementation).
|
|
87
|
+
*
|
|
88
|
+
* - `O_NOFOLLOW` atomically rejects symlinks during open (no TOCTOU window)
|
|
89
|
+
* - the permission check runs on the open descriptor, not the path, so the
|
|
90
|
+
* file can't be swapped between check and read — any group/other access
|
|
91
|
+
* (not `0600`/`0400`) is refused
|
|
92
|
+
* - a size cap bounds the read; the read itself is limited to cap + 1 so a
|
|
93
|
+
* file growing between stat and read is still caught
|
|
94
|
+
*
|
|
95
|
+
* @param path - path to the secret file
|
|
96
|
+
* @returns the file's bytes
|
|
97
|
+
* @throws Error on a missing file, symlink, permissive mode, oversized file, or I/O failure
|
|
98
|
+
*/
|
|
99
|
+
export const load_secure_file_node = async (path) => {
|
|
100
|
+
let handle;
|
|
101
|
+
try {
|
|
102
|
+
handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// ELOOP means the path was a symlink (O_NOFOLLOW refused it).
|
|
106
|
+
if (err.code === 'ELOOP') {
|
|
107
|
+
throw new Error(`secure file is a symlink: ${path}`);
|
|
108
|
+
}
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const s = await handle.stat();
|
|
113
|
+
if (modes_apply)
|
|
114
|
+
assert_secure_mode(path, s.mode);
|
|
115
|
+
assert_secure_size(path, s.size);
|
|
116
|
+
// `position: null` reads sequentially from the handle's own cursor.
|
|
117
|
+
return await read_secure_bounded(path, async (target) => {
|
|
118
|
+
const { bytesRead } = await handle.read(target, 0, target.length, null);
|
|
119
|
+
return bytesRead;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await handle.close();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
@@ -16,7 +16,6 @@ import { type AuditEmitter } from '../auth/audit_emitter.js';
|
|
|
16
16
|
import type { DbType, Db } from '../db/db.js';
|
|
17
17
|
import type { Keyring } from '../auth/keyring.js';
|
|
18
18
|
import type { PasswordHashDeps } from '../auth/password.js';
|
|
19
|
-
import type { StatResult } from '../runtime/deps.js';
|
|
20
19
|
import { type MigrationNamespace, type MigrationResult } from '../db/migrate.js';
|
|
21
20
|
/**
|
|
22
21
|
* Result of `create_app_backend()` — database metadata + deps bundle.
|
|
@@ -83,10 +82,12 @@ export declare const default_audit_factory: AuditFactory;
|
|
|
83
82
|
* (e.g., tx uses `runtime.exit(1)` on invalid keys).
|
|
84
83
|
*/
|
|
85
84
|
export interface CreateAppBackendOptions {
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Hardened secret-file read for the bootstrap token — pass the runtime's
|
|
87
|
+
* `read_secure_file` (see `FsSecureReadDeps`). Rejects symlinks,
|
|
88
|
+
* group/other-accessible modes, and oversized files.
|
|
89
|
+
*/
|
|
90
|
+
read_secure_file: (path: string) => Promise<Uint8Array>;
|
|
90
91
|
/** Delete a file. */
|
|
91
92
|
delete_file: (path: string) => Promise<void>;
|
|
92
93
|
/** Database connection URL (`postgres://`, `file://`, or `memory://`). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,
|
|
1
|
+
{"version":3,"file":"app_backend.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAwB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIjG;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oIAAoI;IACpI,QAAQ,CAAC,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC3D,iEAAiE;IACjE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,KAAK,YAAY,CAAC;AAE7E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,YACF,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACxD,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,iFAAiF;IACjF,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;CACzD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,uBAAuB,KAAG,OAAO,CAAC,UAAU,CAiD7F,CAAC"}
|
|
@@ -39,7 +39,7 @@ export const default_audit_factory = ({ db, log }) => create_audit_emitter({ db,
|
|
|
39
39
|
* @throws Error if `migration_namespaces` contains a namespace in `reserved_migration_namespaces`
|
|
40
40
|
*/
|
|
41
41
|
export const create_app_backend = async (options) => {
|
|
42
|
-
const { database_url, keyring, password,
|
|
42
|
+
const { database_url, keyring, password, read_secure_file, delete_file, audit_factory } = options;
|
|
43
43
|
const log = options.log ?? new Logger('server');
|
|
44
44
|
const { db, close, db_type, db_name } = await create_db(database_url);
|
|
45
45
|
// Everything after `create_db` can throw — reserved-namespace check,
|
|
@@ -69,8 +69,7 @@ export const create_app_backend = async (options) => {
|
|
|
69
69
|
keyring,
|
|
70
70
|
password,
|
|
71
71
|
db,
|
|
72
|
-
|
|
73
|
-
read_text_file,
|
|
72
|
+
read_secure_file,
|
|
74
73
|
delete_file,
|
|
75
74
|
log,
|
|
76
75
|
audit
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -32,7 +32,7 @@ time (never runtime), where a throwing guard would break `vite build`.
|
|
|
32
32
|
- `stub_handler()` — fresh `Response('stub')`.
|
|
33
33
|
- `stub_mw` — pass-through middleware (`async (_c, next) => next()`).
|
|
34
34
|
- `stub_app_deps` — frozen `AppDeps`, every capability throwing, `audit` a no-op `AuditEmitter` from `create_test_audit_emitter`.
|
|
35
|
-
- `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op
|
|
35
|
+
- `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op keyring/password/`delete_file`, a `read_secure_file` that throws ENOENT (the no-token-file state), a `create_noop_stub` DB, silent `Logger`, no-op `audit`.
|
|
36
36
|
- `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0.
|
|
37
37
|
- `create_stub_audit_sse()` — no-op `AuditLogSse` for surface-test wiring without booting real SSE. `subscribe` returns a no-op cleanup; `on_audit_event` no-op; `registry` is a fresh `SubscriberRegistry` (live `.size` / `.close_*` for registry-state tests, isolated per call). For real SSE plumbing build via `create_audit_log_sse` against `create_test_app`.
|
|
38
38
|
- `create_stub_api_middleware({include_daemon_token?})` — stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack.
|
|
@@ -1418,6 +1418,20 @@ module's own tests (`account_status.test.ts`, `action_rpc.test.ts`,
|
|
|
1418
1418
|
`serve_fact_route.db.test.ts`); fuz_app's own wiring is
|
|
1419
1419
|
`cross_backend/query_shape.cross.test.ts`.
|
|
1420
1420
|
|
|
1421
|
+
### API-token lifetime parity — `cross_backend/token_lifetime.ts`
|
|
1422
|
+
|
|
1423
|
+
`describe_token_lifetime_cross_tests({setup_test, rpc_path?})` — the
|
|
1424
|
+
imperative round-trip on `account_token_create`'s required `lifetime` union:
|
|
1425
|
+
a `{kind:'ttl', days}` mint returns and stores a bounded `expires_at`
|
|
1426
|
+
(create output + `account_token_list` read-back), `{kind:'eternal'}` stores
|
|
1427
|
+
NULL, and an omitted or out-of-range lifetime 400s identically on both
|
|
1428
|
+
spines. Exists because the action-manifest parity gate captures no param
|
|
1429
|
+
schemas while the Rust spine hand-parses params — a spine that silently
|
|
1430
|
+
dropped the field would answer the same mint with a 200 and an eternal
|
|
1431
|
+
token; only the stored-state read-back catches it. Ungated (mint + list are
|
|
1432
|
+
on every spine's standard surface). Runs both legs
|
|
1433
|
+
(`token_lifetime_parity.db.test.ts` + `token_lifetime.cross.test.ts`).
|
|
1434
|
+
|
|
1421
1435
|
### Readiness probe parity — `cross_backend/ready.ts`
|
|
1422
1436
|
|
|
1423
1437
|
`describe_ready_cross_tests({setup_test, capabilities, ready_path?})` — the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW3E,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAmB1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAYvE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAm5BF,CAAC"}
|
|
@@ -36,6 +36,14 @@ import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for
|
|
|
36
36
|
import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec } from '../auth/role_grant_offer_action_specs.js';
|
|
37
37
|
import { admin_account_list_action_spec, ADMIN_ACCOUNT_LIST_LIMIT_MAX, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec } from '../auth/admin_action_specs.js';
|
|
38
38
|
import { account_token_create_action_spec, account_verify_action_spec } from '../auth/account_action_specs.js';
|
|
39
|
+
/**
|
|
40
|
+
* How long a failure-audit read waits for the pool-routed row to land.
|
|
41
|
+
*/
|
|
42
|
+
const AUDIT_FAILURE_ROW_TIMEOUT = 2000;
|
|
43
|
+
/**
|
|
44
|
+
* Gap between failure-audit read attempts.
|
|
45
|
+
*/
|
|
46
|
+
const AUDIT_FAILURE_ROW_POLL_INTERVAL = 20;
|
|
39
47
|
/**
|
|
40
48
|
* Pick a role for admin-grant testing, preferring a non-admin app-defined
|
|
41
49
|
* role whose `RoleSpec.grant_paths` includes `'admin'` (the
|
|
@@ -112,6 +120,43 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
112
120
|
* the cross-suite contract.
|
|
113
121
|
*/
|
|
114
122
|
const offer_and_accept = (args) => role_grant_offer_and_accept({ ...args, rpc_path });
|
|
123
|
+
/**
|
|
124
|
+
* Read `audit_log_list` for `event_type` until a failure-outcome row
|
|
125
|
+
* appears, or the deadline passes.
|
|
126
|
+
*
|
|
127
|
+
* Failure audits are **pool-routed** on both spines — the write is
|
|
128
|
+
* deliberately detached from the request transaction so the forensic row
|
|
129
|
+
* survives the rollback that discards the attempted mutation. A single
|
|
130
|
+
* read taken straight after the refusal therefore races that write. The
|
|
131
|
+
* TS spine wins the race by construction (`create_test_app` sets
|
|
132
|
+
* `await_pending_effects: true`, so the emit is awaited before the
|
|
133
|
+
* response returns) and the first attempt succeeds; the Rust spine emits
|
|
134
|
+
* from a detached task, so under load the row can land after the read —
|
|
135
|
+
* observed as a load-dependent drop of the revoke-all failure-audit
|
|
136
|
+
* cases in a busy cross-process run.
|
|
137
|
+
*
|
|
138
|
+
* The cross-backend conformance suites reach for the deterministic
|
|
139
|
+
* `_testing_drain_effects` barrier instead. That action is test-binary
|
|
140
|
+
* only, and this suite also runs against consumers' production RPC
|
|
141
|
+
* surfaces where it isn't mounted — hence a bounded poll.
|
|
142
|
+
*/
|
|
143
|
+
const read_failure_audit_event = async (fixture, event_type) => {
|
|
144
|
+
const deadline = Date.now() + AUDIT_FAILURE_ROW_TIMEOUT;
|
|
145
|
+
for (;;) {
|
|
146
|
+
const audit_res = await rpc_call_for_spec({
|
|
147
|
+
app: { request: fixture.transport },
|
|
148
|
+
path: rpc_path,
|
|
149
|
+
spec: audit_log_list_action_spec,
|
|
150
|
+
params: { event_type },
|
|
151
|
+
headers: fixture.create_session_headers()
|
|
152
|
+
});
|
|
153
|
+
assert.ok(audit_res.ok, 'audit_log_list should succeed');
|
|
154
|
+
const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
|
|
155
|
+
if (failure || Date.now() >= deadline)
|
|
156
|
+
return failure;
|
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, AUDIT_FAILURE_ROW_POLL_INTERVAL));
|
|
158
|
+
}
|
|
159
|
+
};
|
|
115
160
|
// --- 1. Admin account listing (RPC) ---
|
|
116
161
|
describe('admin account listing', () => {
|
|
117
162
|
test('admin can list all accounts', async () => {
|
|
@@ -476,15 +521,7 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
476
521
|
// Failure audit row should be visible on the audit-log feed.
|
|
477
522
|
// `target_account_id` is null (FK prevents referencing a missing id)
|
|
478
523
|
// — the probed id is preserved under `metadata.attempted_account_id`.
|
|
479
|
-
const
|
|
480
|
-
app: { request: fixture.transport },
|
|
481
|
-
path: rpc_path,
|
|
482
|
-
spec: audit_log_list_action_spec,
|
|
483
|
-
params: { event_type: 'session_revoke_all' },
|
|
484
|
-
headers: fixture.create_session_headers()
|
|
485
|
-
});
|
|
486
|
-
assert.ok(audit_res.ok, 'audit_log_list should succeed');
|
|
487
|
-
const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
|
|
524
|
+
const failure = await read_failure_audit_event(fixture, 'session_revoke_all');
|
|
488
525
|
assert.ok(failure, 'Expected a failure-outcome session_revoke_all audit event');
|
|
489
526
|
assert.strictEqual(failure.target_account_id, null);
|
|
490
527
|
const failure_meta = failure.metadata;
|
|
@@ -504,15 +541,7 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
504
541
|
assert.ok(!res.ok, 'Expected 404 for missing account');
|
|
505
542
|
assert.strictEqual(res.status, 404);
|
|
506
543
|
assert.strictEqual(res.error.data.reason, 'account_not_found');
|
|
507
|
-
const
|
|
508
|
-
app: { request: fixture.transport },
|
|
509
|
-
path: rpc_path,
|
|
510
|
-
spec: audit_log_list_action_spec,
|
|
511
|
-
params: { event_type: 'token_revoke_all' },
|
|
512
|
-
headers: fixture.create_session_headers()
|
|
513
|
-
});
|
|
514
|
-
assert.ok(audit_res.ok, 'audit_log_list should succeed');
|
|
515
|
-
const failure = audit_res.result.events.find((e) => e.outcome === 'failure');
|
|
544
|
+
const failure = await read_failure_audit_event(fixture, 'token_revoke_all');
|
|
516
545
|
assert.ok(failure, 'Expected a failure-outcome token_revoke_all audit event');
|
|
517
546
|
assert.strictEqual(failure.target_account_id, null);
|
|
518
547
|
const failure_meta = failure.metadata;
|
|
@@ -594,7 +623,11 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
594
623
|
app: { request: fixture.transport },
|
|
595
624
|
path: rpc_path,
|
|
596
625
|
spec: account_token_create_action_spec,
|
|
597
|
-
params: {
|
|
626
|
+
params: {
|
|
627
|
+
name: 'audit-test-token',
|
|
628
|
+
scope: { kind: 'full' },
|
|
629
|
+
lifetime: { kind: 'eternal' }
|
|
630
|
+
},
|
|
598
631
|
headers: fixture.create_session_headers()
|
|
599
632
|
});
|
|
600
633
|
assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
|
|
@@ -721,7 +754,11 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
721
754
|
app: { request: fixture.transport },
|
|
722
755
|
path: rpc_path,
|
|
723
756
|
spec: account_token_create_action_spec,
|
|
724
|
-
params: {
|
|
757
|
+
params: {
|
|
758
|
+
name: 'admin-b-token',
|
|
759
|
+
scope: { kind: 'full' },
|
|
760
|
+
lifetime: { kind: 'eternal' }
|
|
761
|
+
},
|
|
725
762
|
headers: create_headers(admin_b.session_cookie)
|
|
726
763
|
});
|
|
727
764
|
assert.ok(token_res.ok, `account_token_create failed: ${token_res.ok ? '' : JSON.stringify(token_res.error)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -157,8 +157,9 @@ export const bootstrap_test_keeper = async (options) => {
|
|
|
157
157
|
/** Silent logger for tests — suppresses all output. */
|
|
158
158
|
const test_log = new Logger('test', { level: 'off' });
|
|
159
159
|
const default_test_fs_stubs = {
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
read_secure_file: async (path) => {
|
|
161
|
+
throw new Error(`ENOENT: no such file or directory: ${path}`);
|
|
162
|
+
},
|
|
162
163
|
delete_file: async () => { }
|
|
163
164
|
};
|
|
164
165
|
/**
|
|
@@ -375,14 +376,18 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
375
376
|
if (options.db) {
|
|
376
377
|
await options.db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
|
|
377
378
|
}
|
|
378
|
-
// Token-aware fs stubs: the bootstrap route's
|
|
379
|
-
//
|
|
379
|
+
// Token-aware fs stubs: the bootstrap route's secure read resolves against
|
|
380
|
+
// the configured token_path (both the boot-time availability probe and the
|
|
381
|
+
// request-time read go through it); a deleted or unknown path throws like
|
|
382
|
+
// the real runtimes' ENOENT.
|
|
380
383
|
let token_file_deleted = false;
|
|
381
384
|
const fs_stubs = {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
read_secure_file: async (path) => {
|
|
386
|
+
if (path === bootstrap.token_path && !token_file_deleted) {
|
|
387
|
+
return new TextEncoder().encode(bootstrap_token);
|
|
388
|
+
}
|
|
389
|
+
throw new Error(`ENOENT: no such file or directory: ${path}`);
|
|
390
|
+
},
|
|
386
391
|
delete_file: async (path) => {
|
|
387
392
|
if (path === bootstrap.token_path)
|
|
388
393
|
token_file_deleted = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAinBzF,CAAC"}
|
|
@@ -169,7 +169,11 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
169
169
|
app: { request: fixture.transport },
|
|
170
170
|
path: rpc_path,
|
|
171
171
|
spec: account_token_create_action_spec,
|
|
172
|
-
params: {
|
|
172
|
+
params: {
|
|
173
|
+
name: 'audit-test',
|
|
174
|
+
scope: { kind: 'full' },
|
|
175
|
+
lifetime: { kind: 'eternal' }
|
|
176
|
+
},
|
|
173
177
|
headers: fixture.create_session_headers()
|
|
174
178
|
});
|
|
175
179
|
assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_grant_role.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_grant_role.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAyB,CAAC;AAEvD,oFAAoF;AACpF,eAAO,MAAM,yBAAyB,qBAAqB,CAAC;AAK5D,eAAO,MAAM,oCAAoC,GAAI,SAAS,wBAAwB,KAAG,IAsOxF,CAAC"}
|
|
@@ -34,7 +34,7 @@ import '../assert_dev_env.js';
|
|
|
34
34
|
* @module
|
|
35
35
|
*/
|
|
36
36
|
import { describe, assert } from 'vitest';
|
|
37
|
-
import { CellCreateOutput, CellGetOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
|
|
37
|
+
import { CellCreateOutput, CellGetOutput, CellListOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
|
|
38
38
|
import { CellGrantCreateOutput, ERROR_CELL_GRANT_UNKNOWN_ROLE } from '../../auth/cell_grant_action_specs.js';
|
|
39
39
|
import { test_if } from './capabilities.js';
|
|
40
40
|
import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
|
|
@@ -71,6 +71,39 @@ export const describe_cell_grant_role_cross_tests = (options) => {
|
|
|
71
71
|
assert.ok(!stranger_view.ok, 'non-holder must not see the cell');
|
|
72
72
|
assert.strictEqual(error_reason(stranger_view), 'cell_not_found');
|
|
73
73
|
});
|
|
74
|
+
test_if(capabilities.cell_relations, "cell_list shared_with:'me' admits through the role branch of the grant predicate", async () => {
|
|
75
|
+
// The role-admit arm of the shared-with predicate (the
|
|
76
|
+
// `caller_role_grants` branch of the CTE in the Rust
|
|
77
|
+
// `query_cell_list` / the TS list query) — pinned nowhere else:
|
|
78
|
+
// the actor-shaped `shared_with:'me'` case in `cell_relations.ts`
|
|
79
|
+
// runs the CTE but its role branch never matches, and the
|
|
80
|
+
// storage-half copy in `fuz_cell/tests` is a textually separate
|
|
81
|
+
// EXISTS the CTE can silently drift from. An ungranted control
|
|
82
|
+
// cell keeps the semi-join non-vacuous (dropping it must change
|
|
83
|
+
// the result), and a non-holder proves the branch keys on the
|
|
84
|
+
// role, not on authentication.
|
|
85
|
+
const fixture = await setup_test();
|
|
86
|
+
const t = fixture.transport;
|
|
87
|
+
const owner = await fixture.create_account({ username: 'cell_role_list_owner' });
|
|
88
|
+
const owner_h = owner.create_session_headers();
|
|
89
|
+
const holder = fixture.extra_accounts[CELL_ROLE_HOLDER_USERNAME];
|
|
90
|
+
assert.ok(holder, `fixture must seed the ${CELL_ROLE_HOLDER_USERNAME} extra account`);
|
|
91
|
+
const stranger = await fixture.create_account({ username: 'cell_role_list_stranger' });
|
|
92
|
+
const granted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'role-shared' } }, owner_h), CellCreateOutput);
|
|
93
|
+
// control: same owner, same visibility, no grant — must stay out
|
|
94
|
+
const ungranted = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: { label: 'unshared control' } }, owner_h), CellCreateOutput);
|
|
95
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_grant_create', {
|
|
96
|
+
cell_id: granted.cell.id,
|
|
97
|
+
level: 'viewer',
|
|
98
|
+
principal: { kind: 'role', role: CELL_EDITOR_ROLE }
|
|
99
|
+
}, owner_h), CellGrantCreateOutput);
|
|
100
|
+
const holder_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, holder.create_session_headers()), CellListOutput);
|
|
101
|
+
const holder_ids = holder_list.cells.map((c) => c.id);
|
|
102
|
+
assert.ok(holder_ids.includes(granted.cell.id), 'the role-granted cell must reach a holder of the role via shared_with:me');
|
|
103
|
+
assert.ok(!holder_ids.includes(ungranted.cell.id), 'the ungranted control cell must not — the semi-join is load-bearing');
|
|
104
|
+
const stranger_list = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', { shared_with: 'me' }, stranger.create_session_headers()), CellListOutput);
|
|
105
|
+
assert.ok(!stranger_list.cells.some((c) => c.id === granted.cell.id), 'a non-holder must not be admitted through the role branch');
|
|
106
|
+
});
|
|
74
107
|
test_if(capabilities.cell_relations, 'role-shaped grant for an unregistered role is rejected at create', async () => {
|
|
75
108
|
const fixture = await setup_test();
|
|
76
109
|
const t = fixture.transport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAqPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
|
|
@@ -71,7 +71,8 @@ const mint_scoped_token = async (fixture, resolved_rpc_endpoints) => {
|
|
|
71
71
|
spec: account_token_create_action_spec,
|
|
72
72
|
params: {
|
|
73
73
|
name: 'conformance scoped token',
|
|
74
|
-
scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] }
|
|
74
|
+
scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] },
|
|
75
|
+
lifetime: { kind: 'eternal' }
|
|
75
76
|
},
|
|
76
77
|
headers: fixture.create_session_headers()
|
|
77
78
|
});
|
|
@@ -375,7 +375,7 @@ const mint_account = async (handle, options) => {
|
|
|
375
375
|
// point of the default-deny design. A minted-account fixture stands in for an
|
|
376
376
|
// ordinary account credential, so it takes full account authority; the
|
|
377
377
|
// narrowed credential is the conformance table's `scoped_token` principal.
|
|
378
|
-
const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' } }, handle.config.name);
|
|
378
|
+
const token_result = await rpc_via_transport(transport, handle.config.rpc_path, 'account_token_create', { scope: { kind: 'full' }, lifetime: { kind: 'eternal' } }, handle.config.name);
|
|
379
379
|
const token_parsed = TokenCreateResponseShape.safeParse(token_result);
|
|
380
380
|
if (!token_parsed.success) {
|
|
381
381
|
throw new Error(`account_token_create(${handle.config.name}) returned unexpected result: ${JSON.stringify(token_result)}`);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { SetupTest } from './setup.js';
|
|
3
|
+
/**
|
|
4
|
+
* Options for the token-lifetime parity suite. Ungated — the mint + list pair
|
|
5
|
+
* is on every spine's standard surface, so there is no capability flag (the
|
|
6
|
+
* sibling of `identity_parity.ts` rather than the cell suites).
|
|
7
|
+
*/
|
|
8
|
+
export interface TokenLifetimeCrossTestOptions {
|
|
9
|
+
/** Per-test fixture-producing function (fresh keeper + db per call). */
|
|
10
|
+
readonly setup_test: SetupTest;
|
|
11
|
+
/** RPC endpoint path the methods are mounted on. Default `/api/rpc`. */
|
|
12
|
+
readonly rpc_path?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const describe_token_lifetime_cross_tests: (options: TokenLifetimeCrossTestOptions) => void;
|
|
15
|
+
//# sourceMappingURL=token_lifetime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token_lifetime.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_lifetime.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+C9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAaD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IAiHF,CAAC"}
|