@fuzdev/fuz_app 0.148.0 → 0.150.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 +11 -9
- package/dist/actions/action_bridge.d.ts +7 -0
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +10 -0
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +14 -1
- package/dist/actions/action_event_types.d.ts +1 -1
- package/dist/actions/action_event_types.d.ts.map +1 -1
- package/dist/actions/perform_action.d.ts +17 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +30 -7
- package/dist/actions/register_action_ws.d.ts +8 -2
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +24 -4
- package/dist/actions/transports_ws_auth_guard.d.ts +6 -4
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +9 -6
- package/dist/actions/ws_endpoint_spec.d.ts +1 -1
- package/dist/auth/CLAUDE.md +72 -33
- package/dist/auth/account_action_specs.d.ts +12 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +16 -2
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +7 -4
- package/dist/auth/actor_lookup_queries.d.ts +3 -2
- package/dist/auth/actor_lookup_queries.d.ts.map +1 -1
- package/dist/auth/actor_lookup_queries.js +6 -3
- package/dist/auth/actor_search_queries.d.ts +5 -2
- package/dist/auth/actor_search_queries.d.ts.map +1 -1
- package/dist/auth/actor_search_queries.js +11 -4
- package/dist/auth/admin_action_specs.d.ts +4 -4
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +33 -12
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +35 -6
- package/dist/auth/all_cell_actions.d.ts +11 -2
- package/dist/auth/all_cell_actions.d.ts.map +1 -1
- package/dist/auth/all_cell_actions.js +2 -1
- package/dist/auth/audit_emitter.d.ts +23 -3
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +14 -4
- package/dist/auth/audit_log_schema.d.ts +1 -0
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +9 -5
- package/dist/auth/bootstrap_account.d.ts +25 -1
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +37 -4
- package/dist/auth/bootstrap_routes.d.ts +3 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +5 -4
- package/dist/auth/cell_action_specs.d.ts +215 -15
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +103 -16
- package/dist/auth/cell_actions.d.ts +40 -18
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +126 -19
- package/dist/auth/cell_audit_events.d.ts +1 -1
- package/dist/auth/cell_audit_events.d.ts.map +1 -1
- package/dist/auth/cell_audit_events.js +3 -2
- package/dist/auth/cell_audit_metadata.d.ts +11 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -1
- package/dist/auth/cell_audit_metadata.js +10 -0
- package/dist/auth/cell_grant_action_specs.d.ts +7 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +7 -0
- package/dist/auth/cell_grant_actions.d.ts +36 -4
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +20 -3
- package/dist/auth/credential_type_schema.d.ts +3 -2
- package/dist/auth/credential_type_schema.d.ts.map +1 -1
- package/dist/auth/credential_type_schema.js +3 -2
- package/dist/auth/role_grant_offer_action_specs.d.ts +18 -2
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +30 -6
- package/dist/auth/role_grant_offer_actions.d.ts +9 -4
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +58 -7
- package/dist/auth/role_grant_offer_notifications.d.ts +3 -2
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_notifications.js +3 -2
- package/dist/auth/role_grant_offer_queries.d.ts +20 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +35 -0
- package/dist/auth/role_grant_queries.d.ts +27 -9
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +40 -13
- package/dist/auth/role_schema.d.ts +14 -8
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +6 -3
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +20 -0
- package/dist/db/CLAUDE.md +9 -2
- package/dist/db/cell_ddl.d.ts +9 -1
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +11 -1
- package/dist/db/cell_queries.d.ts +53 -7
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +61 -7
- package/dist/http/CLAUDE.md +14 -2
- package/dist/http/body_limit.d.ts +3 -1
- package/dist/http/body_limit.d.ts.map +1 -1
- package/dist/http/body_limit.js +6 -1
- package/dist/http/error_schemas.d.ts +8 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +12 -0
- package/dist/http/jsonrpc.d.ts +9 -0
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +17 -0
- package/dist/http/jsonrpc_errors.d.ts +40 -15
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +99 -35
- package/dist/http/proxy.d.ts +4 -2
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +7 -2
- package/dist/http/surface.d.ts +3 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +7 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +3 -2
- package/dist/schema_meta.d.ts +92 -1
- package/dist/schema_meta.d.ts.map +1 -1
- package/dist/schema_meta.js +117 -0
- package/dist/server/app_server.d.ts +13 -5
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +32 -8
- package/dist/testing/CLAUDE.md +196 -82
- package/dist/testing/adversarial_input_cases.d.ts +67 -7
- package/dist/testing/adversarial_input_cases.d.ts.map +1 -1
- package/dist/testing/adversarial_input_cases.js +210 -165
- package/dist/testing/app_server.d.ts +3 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.d.ts +3 -2
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +5 -3
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/actor_lookup.js +27 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts +6 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -1
- package/dist/testing/cross_backend/audit_cross_helpers.js +11 -0
- package/dist/testing/cross_backend/capabilities.d.ts +29 -5
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +10 -6
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +117 -2
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +109 -13
- package/dist/testing/cross_backend/cell_moderate.d.ts +7 -1
- package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_moderate.js +68 -4
- package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_ref_admission.js +18 -9
- package/dist/testing/cross_backend/conformance_table.d.ts +5 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +12 -10
- package/dist/testing/cross_backend/cross_rpc.d.ts +3 -1
- package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -1
- package/dist/testing/cross_backend/cross_rpc.js +2 -2
- package/dist/testing/cross_backend/default_backend_configs.d.ts +9 -7
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +3 -3
- package/dist/testing/cross_backend/differential.d.ts +7 -0
- package/dist/testing/cross_backend/differential.d.ts.map +1 -1
- package/dist/testing/cross_backend/differential.js +17 -0
- package/dist/testing/cross_backend/expected_schema.json +1 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +5 -4
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +74 -2
- package/dist/testing/cross_backend/rpc_adversarial.d.ts +9 -0
- package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -1
- package/dist/testing/cross_backend/rpc_adversarial.js +16 -6
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -3
- package/dist/testing/cross_backend/setup.d.ts +2 -2
- package/dist/testing/cross_backend/spine_suites.d.ts +85 -5
- package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_suites.js +291 -20
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +15 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +24 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +18 -19
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +66 -42
- package/dist/testing/cross_backend/testing_backdoor.d.ts +27 -4
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +136 -41
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -15
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +52 -35
- package/dist/testing/cross_backend/transport_composition.js +3 -3
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +6 -3
- package/dist/testing/rpc_attack_surface.d.ts +10 -0
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +15 -3
- package/dist/testing/rpc_envelope_cases.d.ts +5 -2
- package/dist/testing/rpc_envelope_cases.d.ts.map +1 -1
- package/dist/testing/rpc_envelope_cases.js +4 -1
- package/dist/testing/rpc_round_trip.js +3 -3
- package/dist/testing/schema_generators.d.ts +99 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +244 -76
- package/dist/testing/stubs.d.ts +4 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +6 -4
- package/dist/zod.d.ts +9 -0
- package/dist/zod.d.ts.map +1 -1
- package/dist/zod.js +20 -0
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic cell RPC action handlers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The generic `request_response` actions bound to the specs in
|
|
5
5
|
* `auth/cell_action_specs.ts`:
|
|
6
6
|
*
|
|
7
|
-
* - Mutations: `cell_create`, `cell_update`, `cell_delete`, `cell_clone
|
|
7
|
+
* - Mutations: `cell_create`, `cell_update`, `cell_delete`, `cell_clone`,
|
|
8
|
+
* `cell_moderate`, `cell_policy_set`.
|
|
8
9
|
* - Reads: `cell_get`, `cell_list`.
|
|
9
10
|
*
|
|
10
11
|
* Authorization model:
|
|
@@ -20,6 +21,11 @@
|
|
|
20
21
|
* with per-row `can_edit_cell` enforcement. `path` writes on update
|
|
21
22
|
* are admin-only; `visibility` writes require the manage tier
|
|
22
23
|
* (`can_manage_cell`).
|
|
24
|
+
* - `cell_policy_set` is the one writer of a root's contribution `policy` past
|
|
25
|
+
* its create — manage-tier, root-only, and validated by the mounted
|
|
26
|
+
* `authorize_create` (the authorizer that interprets the policy is what
|
|
27
|
+
* accepts one; none mounted refuses every non-null policy). `cell_update`
|
|
28
|
+
* never touches it.
|
|
23
29
|
* - `cell_list` is `optional` auth at the spec level. The SQL-side
|
|
24
30
|
* visibility predicate in `query_cell_list` admits null auth to
|
|
25
31
|
* public-only rows and authed callers to owned + public + grant-admitted
|
|
@@ -49,14 +55,14 @@ import { type RpcAction } from '../actions/action_rpc.js';
|
|
|
49
55
|
import { type RequestActorContext } from './request_context.js';
|
|
50
56
|
import type { ActionFactoryDeps } from './deps.js';
|
|
51
57
|
import type { Uuid } from '../primitive_schemas.js';
|
|
52
|
-
import { type CellJson } from './cell_action_specs.js';
|
|
58
|
+
import { type CellPolicy, type CellJson } from './cell_action_specs.js';
|
|
53
59
|
import { type CellRow } from '../db/cell_queries.js';
|
|
54
60
|
import type { CellData } from './cell_data_schema.js';
|
|
55
61
|
/**
|
|
56
|
-
* Input to
|
|
62
|
+
* Input to `CellCreateAuthorize.authorize` — the TS twin of the Rust
|
|
57
63
|
* `CellCreateAuthorizeInput`. **Parent-aware**: it carries the directory
|
|
58
|
-
* context (`parent_id` / the handler-resolved `root_id`)
|
|
59
|
-
*
|
|
64
|
+
* context (`parent_id` / the handler-resolved `root_id`) and the governing
|
|
65
|
+
* root's `policy`, so the authorizer resolves the rule that applies.
|
|
60
66
|
*/
|
|
61
67
|
export interface CellCreateAuthorizeInput {
|
|
62
68
|
/** The cell's `kind` (the top-level `cell.kind` value); `null` for a typeless cell. */
|
|
@@ -67,8 +73,8 @@ export interface CellCreateAuthorizeInput {
|
|
|
67
73
|
parent_id: Uuid | null;
|
|
68
74
|
/** The governing root of the directory subtree, resolved by the handler from the parent (`parent.root_id ?? parent.id`). `null` for a root creation. */
|
|
69
75
|
root_id: Uuid | null;
|
|
70
|
-
/** The governing root's `
|
|
71
|
-
|
|
76
|
+
/** The governing root's contribution `policy` column — the handler reads the root in-tx and hands its policy over, so a directory-aware authorizer resolves `root_policy[kind]` **without a DB read of its own** (pure predicate; reading in-tx avoids the single-connection deadlock a separate handle would hit on the in-memory drivers). `null` for a root creation and for a root carrying no policy (the authorizer's floor governs); a missing or soft-deleted root answers `cell_not_found` before the authorizer runs. Never the root's `data`: an editor grant can rewrite `data`, so a policy read from it would be the editor's to loosen. */
|
|
77
|
+
root_policy: CellPolicy | null;
|
|
72
78
|
/** Target scope — designed-in for M2 space-scoping; **always `null` in v1** (cells carry no scope column). */
|
|
73
79
|
scope_id: Uuid | null;
|
|
74
80
|
}
|
|
@@ -88,15 +94,30 @@ export type CellCreateVerdict = {
|
|
|
88
94
|
};
|
|
89
95
|
/**
|
|
90
96
|
* Opt-in, parent-aware creation authorizer — the TS twin of the Rust
|
|
91
|
-
* `CellCreateAuthorize` trait
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
97
|
+
* `CellCreateAuthorize` trait, its two members the trait's two methods.
|
|
98
|
+
* Omitted = open create, and every non-null policy write refused
|
|
99
|
+
* `ERROR_CELL_POLICY_UNSUPPORTED`.
|
|
100
|
+
*
|
|
101
|
+
* - `authorize` gates both roots (`parent_id = null`) and contributions;
|
|
102
|
+
* answers "may *this actor* create *this kind* here?" and returns a
|
|
103
|
+
* `CellCreateVerdict`. Runs in `cell_create` after `validate_data` and after
|
|
104
|
+
* the handler resolves `root_id` and `root_policy` from the parent (an
|
|
105
|
+
* unviewable parent or a missing or soft-deleted root already 404-masks
|
|
106
|
+
* before this runs). Async-capable — a DB-backed impl closes over its own
|
|
107
|
+
* `db` (the create handler stays unaware).
|
|
108
|
+
* - `validate_policy` accepts or refuses a root `policy` before it is written
|
|
109
|
+
* (a root's `cell_create`, `cell_policy_set`) — the shape `authorize` reads.
|
|
110
|
+
* It runs after the manage and root-only gates, so it decides validity, never
|
|
111
|
+
* authority: throw `jsonrpc_errors.invalid_params` with
|
|
112
|
+
* `ERROR_CELL_POLICY_INVALID` (or a reason of the consumer's), or a
|
|
113
|
+
* `ZodError`, which the handler turns into `invalid_params`. **Omitted
|
|
114
|
+
* refuses every policy** (`ERROR_CELL_POLICY_UNSUPPORTED`, the Rust trait's
|
|
115
|
+
* default): an authorizer that reads no policy stores none.
|
|
98
116
|
*/
|
|
99
|
-
export
|
|
117
|
+
export interface CellCreateAuthorize {
|
|
118
|
+
authorize: (auth: RequestActorContext, input: CellCreateAuthorizeInput) => CellCreateVerdict | Promise<CellCreateVerdict>;
|
|
119
|
+
validate_policy?: (policy: CellPolicy) => void;
|
|
120
|
+
}
|
|
100
121
|
/**
|
|
101
122
|
* Dependencies for `create_cell_actions`.
|
|
102
123
|
*
|
|
@@ -109,13 +130,14 @@ export type CellCreateAuthorize = (auth: RequestActorContext, input: CellCreateA
|
|
|
109
130
|
* omitted, payloads pass through as-is.
|
|
110
131
|
*
|
|
111
132
|
* `authorize_create` is the optional creation-gate hook (see
|
|
112
|
-
* `CellCreateAuthorize`). When omitted, create is open
|
|
133
|
+
* `CellCreateAuthorize`). When omitted, create is open, and every non-null
|
|
134
|
+
* root policy write is refused.
|
|
113
135
|
*/
|
|
114
136
|
export type CellActionDeps = ActionFactoryDeps & {
|
|
115
137
|
validate_data?: (data: CellData) => CellData;
|
|
116
138
|
authorize_create?: CellCreateAuthorize;
|
|
117
139
|
};
|
|
118
140
|
export declare const to_cell_json: (row: CellRow) => CellJson;
|
|
119
|
-
/** Create the
|
|
141
|
+
/** Create the generic cell RPC actions. */
|
|
120
142
|
export declare const create_cell_actions: (deps: CellActionDeps) => Array<RpcAction>;
|
|
121
143
|
//# sourceMappingURL=cell_actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAIH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAEN,KAAK,mBAAmB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EA6BN,KAAK,UAAU,EAef,KAAK,QAAQ,EAEb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAUN,KAAK,OAAO,EACZ,MAAM,uBAAuB,CAAC;AAoC/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC,uFAAuF;IACvF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6HAA6H;IAC7H,IAAI,EAAE,QAAQ,CAAC;IACf,wHAAwH;IACxH,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,wJAAwJ;IACxJ,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,0nBAA0nB;IAC1nB,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,8GAA8G;IAC9G,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,mBAAmB;IACnC,SAAS,EAAE,CACV,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,wBAAwB,KAC3B,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACpD,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/C;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG;IAChD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAAC;IAC7C,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;CACvC,CAAC;AA+CF,eAAO,MAAM,YAAY,GAAI,KAAK,OAAO,KAAG,QAiB1C,CAAC;AAkDH,2CAA2C;AAC3C,eAAO,MAAM,mBAAmB,GAAI,MAAM,cAAc,KAAG,KAAK,CAAC,SAAS,CA4wBzE,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic cell RPC action handlers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The generic `request_response` actions bound to the specs in
|
|
5
5
|
* `auth/cell_action_specs.ts`:
|
|
6
6
|
*
|
|
7
|
-
* - Mutations: `cell_create`, `cell_update`, `cell_delete`, `cell_clone
|
|
7
|
+
* - Mutations: `cell_create`, `cell_update`, `cell_delete`, `cell_clone`,
|
|
8
|
+
* `cell_moderate`, `cell_policy_set`.
|
|
8
9
|
* - Reads: `cell_get`, `cell_list`.
|
|
9
10
|
*
|
|
10
11
|
* Authorization model:
|
|
@@ -20,6 +21,11 @@
|
|
|
20
21
|
* with per-row `can_edit_cell` enforcement. `path` writes on update
|
|
21
22
|
* are admin-only; `visibility` writes require the manage tier
|
|
22
23
|
* (`can_manage_cell`).
|
|
24
|
+
* - `cell_policy_set` is the one writer of a root's contribution `policy` past
|
|
25
|
+
* its create — manage-tier, root-only, and validated by the mounted
|
|
26
|
+
* `authorize_create` (the authorizer that interprets the policy is what
|
|
27
|
+
* accepts one; none mounted refuses every non-null policy). `cell_update`
|
|
28
|
+
* never touches it.
|
|
23
29
|
* - `cell_list` is `optional` auth at the spec level. The SQL-side
|
|
24
30
|
* visibility predicate in `query_cell_list` admits null auth to
|
|
25
31
|
* public-only rows and authed callers to owned + public + grant-admitted
|
|
@@ -51,8 +57,8 @@ import { jsonrpc_errors, dev_only } from '../http/jsonrpc_errors.js';
|
|
|
51
57
|
import { is_pg_unique_violation } from '../db/pg_error.js';
|
|
52
58
|
import { has_scoped_role } from './request_context.js';
|
|
53
59
|
import { ROLE_ADMIN } from './role_schema.js';
|
|
54
|
-
import { cell_create_action_spec, cell_get_action_spec, cell_update_action_spec, cell_delete_action_spec, cell_list_action_spec, cell_clone_action_spec, cell_moderate_action_spec, ERROR_CELL_NOT_FOUND, ERROR_CELL_PATH_ADMIN_ONLY, ERROR_CELL_PATH_TAKEN, ERROR_CELL_VISIBILITY_MANAGE_ONLY, ERROR_CELL_GET_REQUIRES_ID_OR_PATH, ERROR_CELL_KIND_IN_DATA, ERROR_CELL_KIND_EMPTY, ERROR_CELL_CREATE_FORBIDDEN, ERROR_CELL_MODERATE_FORBIDDEN, ERROR_CELL_NOT_A_CONTRIBUTION, ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH, ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH, CELL_LIST_LIMIT_DEFAULT, CELL_RELATIONS_BUNDLE_LIMIT } from './cell_action_specs.js';
|
|
55
|
-
import { query_cell_create, query_cell_get, query_cell_get_by_path, query_cell_update, query_cell_delete, query_cell_set_moderation, query_cell_list, query_cell_load_many } from '../db/cell_queries.js';
|
|
60
|
+
import { cell_create_action_spec, cell_get_action_spec, cell_update_action_spec, cell_delete_action_spec, cell_list_action_spec, cell_clone_action_spec, cell_moderate_action_spec, cell_policy_set_action_spec, ERROR_CELL_NOT_FOUND, ERROR_CELL_PATH_ADMIN_ONLY, ERROR_CELL_PATH_TAKEN, ERROR_CELL_VISIBILITY_MANAGE_ONLY, ERROR_CELL_GET_REQUIRES_ID_OR_PATH, ERROR_CELL_KIND_IN_DATA, ERROR_CELL_KIND_EMPTY, ERROR_CELL_CREATE_FORBIDDEN, ERROR_CELL_MODERATE_FORBIDDEN, ERROR_CELL_NOT_A_CONTRIBUTION, ERROR_CELL_NOT_PENDING, ERROR_CELL_POLICY_MANAGE_ONLY, ERROR_CELL_POLICY_ROOT_ONLY, ERROR_CELL_POLICY_UNSUPPORTED, ERROR_CELL_LIST_CREATED_BY_REQUIRES_AUTH, ERROR_CELL_LIST_SHARED_WITH_REQUIRES_AUTH, CELL_LIST_LIMIT_DEFAULT, CELL_RELATIONS_BUNDLE_LIMIT } from './cell_action_specs.js';
|
|
61
|
+
import { query_cell_create, query_cell_get, query_cell_get_by_path, query_cell_update, query_cell_delete, query_cell_set_moderation, query_cell_set_policy, query_cell_list, query_cell_load_many } from '../db/cell_queries.js';
|
|
56
62
|
import { query_cell_grant_list_for_cell, query_cell_grants_for_caller_in_cells } from '../db/cell_grant_queries.js';
|
|
57
63
|
import { query_cell_field_list_for_source, query_cell_field_set } from '../db/cell_field_queries.js';
|
|
58
64
|
import { query_cell_item_insert, query_cell_item_list_for_parent } from '../db/cell_item_queries.js';
|
|
@@ -71,6 +77,18 @@ import { to_item_json } from './cell_item_actions.js';
|
|
|
71
77
|
* write is unambiguously a path collision.
|
|
72
78
|
*/
|
|
73
79
|
const path_taken_error = () => jsonrpc_errors.conflict('cell.path is already taken', { reason: ERROR_CELL_PATH_TAKEN });
|
|
80
|
+
/** The refusal of a policy write nothing can interpret. */
|
|
81
|
+
const cell_policy_unsupported_error = () => jsonrpc_errors.invalid_params('cell.policy needs a create authorizer that accepts it', {
|
|
82
|
+
reason: ERROR_CELL_POLICY_UNSUPPORTED
|
|
83
|
+
});
|
|
84
|
+
/** The refusal of a policy write on a cell with a parent. */
|
|
85
|
+
const cell_policy_root_only_error = () => jsonrpc_errors.invalid_params('cell.policy is set only on a root (a cell without a parent)', {
|
|
86
|
+
reason: ERROR_CELL_POLICY_ROOT_ONLY
|
|
87
|
+
});
|
|
88
|
+
/** The `cell_moderate` refusal of a transition the contribution's state doesn't allow. */
|
|
89
|
+
const cell_not_pending_error = () => jsonrpc_errors.invalid_params('cell is not pending moderation', {
|
|
90
|
+
reason: ERROR_CELL_NOT_PENDING
|
|
91
|
+
});
|
|
74
92
|
/**
|
|
75
93
|
* Reject a `kind` key supplied inside `data`. `kind` is the top-level
|
|
76
94
|
* `cell.kind` column (write-once capability axis), so a copy inside the
|
|
@@ -95,6 +113,7 @@ export const to_cell_json = (row) => ({
|
|
|
95
113
|
parent_id: row.parent_id,
|
|
96
114
|
root_id: row.root_id,
|
|
97
115
|
moderation: row.moderation,
|
|
116
|
+
policy: row.policy,
|
|
98
117
|
created_by: row.created_by,
|
|
99
118
|
updated_by: row.updated_by,
|
|
100
119
|
created_at: row.created_at,
|
|
@@ -139,7 +158,7 @@ const admit_viewable_or_stewarded_cell = async (deps, auth, row, grants) => {
|
|
|
139
158
|
return;
|
|
140
159
|
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
141
160
|
};
|
|
142
|
-
/** Create the
|
|
161
|
+
/** Create the generic cell RPC actions. */
|
|
143
162
|
export const create_cell_actions = (deps) => {
|
|
144
163
|
const { validate_data, authorize_create } = deps;
|
|
145
164
|
/**
|
|
@@ -162,6 +181,25 @@ export const create_cell_actions = (deps) => {
|
|
|
162
181
|
throw err;
|
|
163
182
|
}
|
|
164
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Validate a non-null policy write through the mounted authorizer's
|
|
186
|
+
* `validate_policy` — `ERROR_CELL_POLICY_UNSUPPORTED` when there is none
|
|
187
|
+
* (no authorizer, or one without the member). A thrown `ZodError` becomes
|
|
188
|
+
* `invalid_params`, as `validate_data_or_throw` does.
|
|
189
|
+
*/
|
|
190
|
+
const validate_policy_write = (policy) => {
|
|
191
|
+
if (authorize_create?.validate_policy === undefined)
|
|
192
|
+
throw cell_policy_unsupported_error();
|
|
193
|
+
try {
|
|
194
|
+
authorize_create.validate_policy(policy);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
if (err instanceof z.ZodError) {
|
|
198
|
+
throw jsonrpc_errors.invalid_params('cell.policy validation failed', dev_only({ issues: err.issues }));
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
165
203
|
const create_handler = async (input, ctx) => {
|
|
166
204
|
const auth = ctx.auth;
|
|
167
205
|
// Path writes are admin-only. Reject before the insert so the audit
|
|
@@ -185,8 +223,18 @@ export const create_cell_actions = (deps) => {
|
|
|
185
223
|
// known kinds with malformed payloads surface as invalid_params.
|
|
186
224
|
const validated_data = validate_data_or_throw(input.data);
|
|
187
225
|
const kind = input.kind ?? null;
|
|
226
|
+
const parent_id = input.parent_id ?? null;
|
|
227
|
+
const policy = input.policy ?? null;
|
|
228
|
+
// A contribution policy is a root's alone, and the mounted authorizer —
|
|
229
|
+
// what interprets it — must accept it (none mounted refuses it). The
|
|
230
|
+
// authorizer does not gate it: a root's creator manages the root anyway.
|
|
231
|
+
if (policy !== null) {
|
|
232
|
+
if (parent_id !== null)
|
|
233
|
+
throw cell_policy_root_only_error();
|
|
234
|
+
validate_policy_write(policy);
|
|
235
|
+
}
|
|
188
236
|
// Directory tree: resolve the governing `root_id` from the parent (and
|
|
189
|
-
// the root's `
|
|
237
|
+
// the root's `policy` for the authorizer). Runs whether or
|
|
190
238
|
// not an authorizer is mounted — `root_id` is written regardless, and an
|
|
191
239
|
// unviewable parent 404-masks the attempt (the same IDOR mask the read
|
|
192
240
|
// path uses; never reveals a hidden container). A root creation
|
|
@@ -198,10 +246,9 @@ export const create_cell_actions = (deps) => {
|
|
|
198
246
|
// answers `cell_not_found` like a missing parent (soft delete doesn't
|
|
199
247
|
// cascade, so a live parent can sit under a tombstoned root): nothing is
|
|
200
248
|
// written pointing at the tombstone, and an authorizer never sees a root
|
|
201
|
-
// id
|
|
202
|
-
const parent_id = input.parent_id ?? null;
|
|
249
|
+
// id it can't read.
|
|
203
250
|
let root_id = null;
|
|
204
|
-
let
|
|
251
|
+
let root_policy = null;
|
|
205
252
|
if (parent_id !== null) {
|
|
206
253
|
const parent = await query_cell_get(ctx, parent_id);
|
|
207
254
|
if (!parent)
|
|
@@ -212,13 +259,13 @@ export const create_cell_actions = (deps) => {
|
|
|
212
259
|
}
|
|
213
260
|
root_id = parent.root_id ?? parent.id;
|
|
214
261
|
if (root_id === parent.id) {
|
|
215
|
-
|
|
262
|
+
root_policy = parent.policy;
|
|
216
263
|
}
|
|
217
264
|
else {
|
|
218
265
|
const root = await query_cell_get(ctx, root_id);
|
|
219
266
|
if (!root)
|
|
220
267
|
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
221
|
-
|
|
268
|
+
root_policy = root.policy;
|
|
222
269
|
}
|
|
223
270
|
}
|
|
224
271
|
// Parent-aware creation authorizer (opt-in). Runs after shape validation
|
|
@@ -233,12 +280,12 @@ export const create_cell_actions = (deps) => {
|
|
|
233
280
|
let moderation = null;
|
|
234
281
|
let visibility = input.visibility;
|
|
235
282
|
if (authorize_create) {
|
|
236
|
-
const verdict = await authorize_create(auth, {
|
|
283
|
+
const verdict = await authorize_create.authorize(auth, {
|
|
237
284
|
kind,
|
|
238
285
|
data: input.data,
|
|
239
286
|
parent_id,
|
|
240
287
|
root_id,
|
|
241
|
-
|
|
288
|
+
root_policy,
|
|
242
289
|
scope_id: null
|
|
243
290
|
});
|
|
244
291
|
if (!verdict.allow) {
|
|
@@ -282,6 +329,7 @@ export const create_cell_actions = (deps) => {
|
|
|
282
329
|
parent_id,
|
|
283
330
|
root_id,
|
|
284
331
|
moderation,
|
|
332
|
+
policy,
|
|
285
333
|
created_by: auth.actor.id
|
|
286
334
|
});
|
|
287
335
|
}
|
|
@@ -473,14 +521,21 @@ export const create_cell_actions = (deps) => {
|
|
|
473
521
|
if (!can_manage_cell(auth, root)) {
|
|
474
522
|
throw jsonrpc_errors.forbidden('cell_moderate requires moderation authority over the governing root', { reason: ERROR_CELL_MODERATE_FORBIDDEN });
|
|
475
523
|
}
|
|
524
|
+
// The UPDATE carries the allowed from-states: an approval moves only a
|
|
525
|
+
// pending contribution, a rejection a pending or approved one (the
|
|
526
|
+
// takedown); a rejected or unmoderated one never moves.
|
|
476
527
|
const approved = input.moderation === 'approved';
|
|
477
528
|
const updated = await query_cell_set_moderation(ctx, input.cell_id, input.moderation, {
|
|
478
529
|
set_visibility_public: approved,
|
|
479
530
|
updated_by: auth.actor.id
|
|
480
531
|
});
|
|
481
|
-
|
|
482
|
-
|
|
532
|
+
if (!updated) {
|
|
533
|
+
// No row in an allowed from-state: the transition is refused (or
|
|
534
|
+
// another moderator moved it first) — or a deleter won the race (404).
|
|
535
|
+
if (await query_cell_get(ctx, input.cell_id))
|
|
536
|
+
throw cell_not_pending_error();
|
|
483
537
|
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
538
|
+
}
|
|
484
539
|
deps.audit.emit(ctx, {
|
|
485
540
|
event_type: 'cell_moderate',
|
|
486
541
|
actor_id: auth.actor.id,
|
|
@@ -494,6 +549,55 @@ export const create_cell_actions = (deps) => {
|
|
|
494
549
|
});
|
|
495
550
|
return { cell: to_cell_json(updated) };
|
|
496
551
|
};
|
|
552
|
+
/**
|
|
553
|
+
* `cell_policy_set` — replace (or clear) a root's contribution policy,
|
|
554
|
+
* manage-tier on the root. Order: the 404 mask (a caller who can't view the
|
|
555
|
+
* cell), the manage check (403 `cell_policy_manage_only` — an editor grant
|
|
556
|
+
* edits `data` but never the policy that decides who may contribute),
|
|
557
|
+
* root-only (400 `cell_policy_root_only`), then a non-null policy's
|
|
558
|
+
* validation through the mounted authorizer. Audited with the policy written
|
|
559
|
+
* and the one it replaced.
|
|
560
|
+
*/
|
|
561
|
+
const policy_set_handler = async (input, ctx) => {
|
|
562
|
+
const auth = ctx.auth;
|
|
563
|
+
const existing = await query_cell_get(ctx, input.cell_id);
|
|
564
|
+
if (!existing)
|
|
565
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
566
|
+
const grants = await query_cell_grant_list_for_cell(ctx, existing.id);
|
|
567
|
+
if (!can_view_cell(auth, existing, grants)) {
|
|
568
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
569
|
+
}
|
|
570
|
+
if (!can_manage_cell(auth, existing)) {
|
|
571
|
+
throw jsonrpc_errors.forbidden('cell.policy is manage-tier only', {
|
|
572
|
+
reason: ERROR_CELL_POLICY_MANAGE_ONLY
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
if (existing.parent_id !== null)
|
|
576
|
+
throw cell_policy_root_only_error();
|
|
577
|
+
if (input.policy !== null)
|
|
578
|
+
validate_policy_write(input.policy);
|
|
579
|
+
const result = await query_cell_set_policy(ctx, input.cell_id, input.policy, {
|
|
580
|
+
updated_by: auth.actor.id
|
|
581
|
+
});
|
|
582
|
+
// Raced with a deleter between the gates and the UPDATE.
|
|
583
|
+
if (!result)
|
|
584
|
+
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
585
|
+
const updated = result.cell;
|
|
586
|
+
deps.audit.emit(ctx, {
|
|
587
|
+
event_type: 'cell_policy_set',
|
|
588
|
+
actor_id: auth.actor.id,
|
|
589
|
+
account_id: auth.account.id,
|
|
590
|
+
ip: ctx.client_ip,
|
|
591
|
+
metadata: {
|
|
592
|
+
cell_id: updated.id,
|
|
593
|
+
policy: updated.policy,
|
|
594
|
+
// read under the UPDATE's row lock, not from `existing`, which a set
|
|
595
|
+
// racing past the gates may already have overwritten
|
|
596
|
+
previous_policy: result.previous_policy
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
return { cell: to_cell_json(updated) };
|
|
600
|
+
};
|
|
497
601
|
/**
|
|
498
602
|
* Insert one cloned cell row owned by the caller.
|
|
499
603
|
*
|
|
@@ -532,16 +636,16 @@ export const create_cell_actions = (deps) => {
|
|
|
532
636
|
// could violate the kind shape (e.g., remove a required field).
|
|
533
637
|
const validated_data = validate_data_or_throw(merged_data);
|
|
534
638
|
// Creation-capability gate, after the shape check (the `cell_create`
|
|
535
|
-
// order). The clone is parentless, so no `root_id` / `
|
|
639
|
+
// order). The clone is parentless, so no `root_id` / `root_policy`; the
|
|
536
640
|
// authorizer sees the merged (pre-normalization) data, as `cell_create`
|
|
537
641
|
// hands it the raw input.
|
|
538
642
|
if (authorize_create) {
|
|
539
|
-
const verdict = await authorize_create(auth, {
|
|
643
|
+
const verdict = await authorize_create.authorize(auth, {
|
|
540
644
|
kind: source.kind,
|
|
541
645
|
data: merged_data,
|
|
542
646
|
parent_id: null,
|
|
543
647
|
root_id: null,
|
|
544
|
-
|
|
648
|
+
root_policy: null,
|
|
545
649
|
scope_id: null
|
|
546
650
|
});
|
|
547
651
|
if (!verdict.allow) {
|
|
@@ -564,6 +668,8 @@ export const create_cell_actions = (deps) => {
|
|
|
564
668
|
kind: source.kind,
|
|
565
669
|
visibility: source.visibility,
|
|
566
670
|
path: null, // admin-only paths cannot auto-clone
|
|
671
|
+
// no `policy`: a clone is born without one, so cloning a root never
|
|
672
|
+
// copies its governance onto a cell the cloner owns
|
|
567
673
|
created_by: auth.actor.id
|
|
568
674
|
});
|
|
569
675
|
};
|
|
@@ -767,6 +873,7 @@ export const create_cell_actions = (deps) => {
|
|
|
767
873
|
rpc_action(cell_delete_action_spec, delete_handler),
|
|
768
874
|
rpc_action(cell_list_action_spec, list_handler),
|
|
769
875
|
rpc_action(cell_clone_action_spec, clone_handler),
|
|
770
|
-
rpc_action(cell_moderate_action_spec, moderate_handler)
|
|
876
|
+
rpc_action(cell_moderate_action_spec, moderate_handler),
|
|
877
|
+
rpc_action(cell_policy_set_action_spec, policy_set_handler)
|
|
771
878
|
];
|
|
772
879
|
};
|
|
@@ -20,7 +20,7 @@ import type { z } from 'zod';
|
|
|
20
20
|
/**
|
|
21
21
|
* Cell-layer `event_type → metadata schema` map for `extra_events`.
|
|
22
22
|
*
|
|
23
|
-
* Covers the
|
|
23
|
+
* Covers the generic cell verbs' mutation events plus the grant /
|
|
24
24
|
* field / item relation events. Read-only verbs (`cell_get`, `cell_list`,
|
|
25
25
|
* `cell_*_list`, `cell_audit_list`) emit nothing and are absent here.
|
|
26
26
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_audit_events.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_audit_events.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsB7B;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAcjE,CAAC"}
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import { CellAuditMetadata, CellCloneAuditMetadata, CellModerateAuditMetadata } from './cell_audit_metadata.js';
|
|
19
|
+
import { CellAuditMetadata, CellCloneAuditMetadata, CellModerateAuditMetadata, CellPolicySetAuditMetadata } from './cell_audit_metadata.js';
|
|
20
20
|
import { CellGrantCreateAuditMetadata, CellGrantRevokeAuditMetadata } from './cell_grant_audit_metadata.js';
|
|
21
21
|
import { CellFieldSetAuditMetadata, CellFieldDeleteAuditMetadata } from './cell_field_audit_metadata.js';
|
|
22
22
|
import { CellItemInsertAuditMetadata, CellItemMoveAuditMetadata, CellItemDeleteAuditMetadata } from './cell_item_audit_metadata.js';
|
|
23
23
|
/**
|
|
24
24
|
* Cell-layer `event_type → metadata schema` map for `extra_events`.
|
|
25
25
|
*
|
|
26
|
-
* Covers the
|
|
26
|
+
* Covers the generic cell verbs' mutation events plus the grant /
|
|
27
27
|
* field / item relation events. Read-only verbs (`cell_get`, `cell_list`,
|
|
28
28
|
* `cell_*_list`, `cell_audit_list`) emit nothing and are absent here.
|
|
29
29
|
*/
|
|
@@ -33,6 +33,7 @@ export const cell_audit_events = {
|
|
|
33
33
|
cell_delete: CellAuditMetadata,
|
|
34
34
|
cell_clone: CellCloneAuditMetadata,
|
|
35
35
|
cell_moderate: CellModerateAuditMetadata,
|
|
36
|
+
cell_policy_set: CellPolicySetAuditMetadata,
|
|
36
37
|
cell_grant_create: CellGrantCreateAuditMetadata,
|
|
37
38
|
cell_grant_revoke: CellGrantRevokeAuditMetadata,
|
|
38
39
|
cell_field_set: CellFieldSetAuditMetadata,
|
|
@@ -60,4 +60,15 @@ export declare const CellModerateAuditMetadata: z.ZodObject<{
|
|
|
60
60
|
}>;
|
|
61
61
|
}, z.core.$loose>;
|
|
62
62
|
export type CellModerateAuditMetadata = z.infer<typeof CellModerateAuditMetadata>;
|
|
63
|
+
/**
|
|
64
|
+
* Metadata envelope for `cell_policy_set`. `cell_id` is the root; `policy` is
|
|
65
|
+
* the policy written (`null` when cleared) and `previous_policy` the one it
|
|
66
|
+
* replaced, so the audit trail carries every policy a root has had.
|
|
67
|
+
*/
|
|
68
|
+
export declare const CellPolicySetAuditMetadata: z.ZodObject<{
|
|
69
|
+
cell_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
70
|
+
policy: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
71
|
+
previous_policy: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
72
|
+
}, z.core.$loose>;
|
|
73
|
+
export type CellPolicySetAuditMetadata = z.infer<typeof CellPolicySetAuditMetadata>;
|
|
63
74
|
//# sourceMappingURL=cell_audit_metadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_audit_metadata.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB;;;;;;iBAMjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;;;;;iBAIpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"cell_audit_metadata.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_audit_metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB;;;;;;iBAMjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB;;;;;;;iBAIpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;iBAIrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
|
|
@@ -55,3 +55,13 @@ export const CellModerateAuditMetadata = z.looseObject({
|
|
|
55
55
|
root_id: Uuid,
|
|
56
56
|
moderation: z.enum(['approved', 'rejected'])
|
|
57
57
|
});
|
|
58
|
+
/**
|
|
59
|
+
* Metadata envelope for `cell_policy_set`. `cell_id` is the root; `policy` is
|
|
60
|
+
* the policy written (`null` when cleared) and `previous_policy` the one it
|
|
61
|
+
* replaced, so the audit trail carries every policy a root has had.
|
|
62
|
+
*/
|
|
63
|
+
export const CellPolicySetAuditMetadata = z.looseObject({
|
|
64
|
+
cell_id: Uuid,
|
|
65
|
+
policy: z.record(z.string(), z.unknown()).nullable(),
|
|
66
|
+
previous_policy: z.record(z.string(), z.unknown()).nullable()
|
|
67
|
+
});
|
|
@@ -35,6 +35,13 @@ export declare const ERROR_CELL_GRANT_UNKNOWN_ROLE: "cell_grant_unknown_role";
|
|
|
35
35
|
* extend an audience the cell doesn't have yet.
|
|
36
36
|
*/
|
|
37
37
|
export declare const ERROR_CELL_GRANT_UNAPPROVED: "cell_grant_unapproved";
|
|
38
|
+
/**
|
|
39
|
+
* Error reason — the mounted `CellGrantCreateAuthorize` refused a manager's
|
|
40
|
+
* `cell_grant_create`. A **403** `forbidden`, answered only to a caller who
|
|
41
|
+
* manages the cell (everyone else gets the 404 mask before the hook runs).
|
|
42
|
+
* Twin of the Rust `ERROR_CELL_GRANT_NOT_AUTHORIZED`.
|
|
43
|
+
*/
|
|
44
|
+
export declare const ERROR_CELL_GRANT_NOT_AUTHORIZED: "cell_grant_not_authorized";
|
|
38
45
|
/** Grant level — view-only or view-plus-edit. */
|
|
39
46
|
export declare const CellGrantLevel: z.ZodEnum<{
|
|
40
47
|
viewer: "viewer";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_grant_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_grant_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,mCAAmC,EAAG,+BAAwC,CAAC;AAE5F;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAG,uBAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_grant_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_grant_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,mCAAmC,EAAG,+BAAwC,CAAC;AAE5F;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAG,uBAAgC,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,EAAG,2BAAoC,CAAC;AAIpF,iDAAiD;AACjD,eAAO,MAAM,cAAc;;;EAA+B,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;4BAYlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;kBASpB,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAIlD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;kBAO/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;kBAAuC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAI1E,eAAO,MAAM,oBAAoB;;;kBAG/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;kBAGhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAI1E,eAAO,MAAM,kBAAkB;;;kBAG7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;kBAE9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAItE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWL,CAAC;AAEtC,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;CAWL,CAAC;AAEtC,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWH,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAI9B,CAAC"}
|
|
@@ -38,6 +38,13 @@ export const ERROR_CELL_GRANT_UNKNOWN_ROLE = 'cell_grant_unknown_role';
|
|
|
38
38
|
* extend an audience the cell doesn't have yet.
|
|
39
39
|
*/
|
|
40
40
|
export const ERROR_CELL_GRANT_UNAPPROVED = 'cell_grant_unapproved';
|
|
41
|
+
/**
|
|
42
|
+
* Error reason — the mounted `CellGrantCreateAuthorize` refused a manager's
|
|
43
|
+
* `cell_grant_create`. A **403** `forbidden`, answered only to a caller who
|
|
44
|
+
* manages the cell (everyone else gets the 404 mask before the hook runs).
|
|
45
|
+
* Twin of the Rust `ERROR_CELL_GRANT_NOT_AUTHORIZED`.
|
|
46
|
+
*/
|
|
47
|
+
export const ERROR_CELL_GRANT_NOT_AUTHORIZED = 'cell_grant_not_authorized';
|
|
41
48
|
// -- Shared schemas ---------------------------------------------------------
|
|
42
49
|
/** Grant level — view-only or view-plus-edit. */
|
|
43
50
|
export const CellGrantLevel = z.enum(['viewer', 'editor']);
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
* principals against the role schema; rejects owner-as-principal.
|
|
16
16
|
* Actor-shaped principals carry a pre-resolved `actor_id` (callers pick
|
|
17
17
|
* via `actor_search`). Idempotent — re-granting the same principal
|
|
18
|
-
* updates `level` via UPSERT.
|
|
18
|
+
* updates `level` via UPSERT. A mounted `authorize_grant_create` is asked
|
|
19
|
+
* after the spine's own gates, before the owner check and the write (403
|
|
20
|
+
* `cell_grant_not_authorized` on refusal).
|
|
19
21
|
* - `cell_grant_revoke` — admin / owner, plus self for actor-shaped grants
|
|
20
22
|
* ("leave shared cell"). Returns `still_admitted` computed by re-running
|
|
21
23
|
* `can_view_cell` against the remaining grants.
|
|
@@ -34,17 +36,47 @@
|
|
|
34
36
|
import { type RpcAction } from '../actions/action_rpc.js';
|
|
35
37
|
import type { RoleSchemaResult } from './role_schema.js';
|
|
36
38
|
import type { ActionFactoryDeps } from './deps.js';
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
+
import type { RequestActorContext } from './request_context.js';
|
|
40
|
+
import type { Uuid } from '../primitive_schemas.js';
|
|
41
|
+
import { type CellGrantCreateInput, type GrantJson } from './cell_grant_action_specs.js';
|
|
42
|
+
import { type CellGrantRow, type CellGrantPrincipalQueryInput } from '../db/cell_grant_queries.js';
|
|
43
|
+
/**
|
|
44
|
+
* Input to a `CellGrantCreateAuthorize` callback — the twin of the Rust
|
|
45
|
+
* `CellGrantCreateAuthorizeInput`.
|
|
46
|
+
*/
|
|
47
|
+
export interface CellGrantCreateAuthorizeInput {
|
|
48
|
+
/** The cell the grant names. */
|
|
49
|
+
cell_id: Uuid;
|
|
50
|
+
/** The cell's owner (`null` = a system cell). */
|
|
51
|
+
cell_created_by: Uuid | null;
|
|
52
|
+
/** The level the grant confers — on an upsert, the new level. */
|
|
53
|
+
level: CellGrantCreateInput['level'];
|
|
54
|
+
/** Who the grant admits, as the wire named them. */
|
|
55
|
+
principal: CellGrantPrincipalQueryInput;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Opt-in gate on `cell_grant_create` — the TS twin of the Rust
|
|
59
|
+
* `CellGrantCreateAuthorize` trait. Asked after the spine's own gates — the
|
|
60
|
+
* manage tier (a non-manager gets the 404 mask and never reaches it), the
|
|
61
|
+
* moderation admission, and the role registry — and before the owner check
|
|
62
|
+
* and the write, for a new grant and a level change alike. `false` refuses
|
|
63
|
+
* with 403 `ERROR_CELL_GRANT_NOT_AUTHORIZED`, writing neither the grant nor an
|
|
64
|
+
* audit row. A consumer narrows sharing with it (say, role-shaped grants only
|
|
65
|
+
* to roles of the cell's own space); it can't widen it, as every caller it
|
|
66
|
+
* sees already manages the cell. Omitted = no gate past the spine's.
|
|
67
|
+
*/
|
|
68
|
+
export type CellGrantCreateAuthorize = (auth: RequestActorContext, input: CellGrantCreateAuthorizeInput) => boolean | Promise<boolean>;
|
|
39
69
|
/**
|
|
40
70
|
* Dependencies for `create_cell_grant_actions`.
|
|
41
71
|
*
|
|
42
72
|
* `roles` is the role schema — read for the role-validity gate on
|
|
43
|
-
* `cell_grant_create
|
|
73
|
+
* `cell_grant_create`; `authorize_grant_create` is the optional consumer gate
|
|
74
|
+
* (`CellGrantCreateAuthorize`). The other slots match `CellActionDeps` so
|
|
44
75
|
* audit-log emit goes through the same fire-and-forget plumbing.
|
|
45
76
|
*/
|
|
46
77
|
export type CellGrantActionDeps = ActionFactoryDeps & {
|
|
47
78
|
roles: RoleSchemaResult;
|
|
79
|
+
authorize_grant_create?: CellGrantCreateAuthorize;
|
|
48
80
|
};
|
|
49
81
|
export declare const to_grant_json: (row: CellGrantRow) => GrantJson;
|
|
50
82
|
/** Create the three `cell_grant_*` RPC actions. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_grant_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_grant_actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_grant_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_grant_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EASN,KAAK,oBAAoB,EAMzB,KAAK,SAAS,EACd,MAAM,8BAA8B,CAAC;AAKtC,OAAO,EAKN,KAAK,YAAY,EACjB,KAAK,4BAA4B,EACjC,MAAM,6BAA6B,CAAC;AAOrC;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC7C,gCAAgC;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,iDAAiD;IACjD,eAAe,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,iEAAiE;IACjE,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,oDAAoD;IACpD,SAAS,EAAE,4BAA4B,CAAC;CACxC;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,wBAAwB,GAAG,CACtC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,6BAA6B,KAChC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,GAAG;IACrD,KAAK,EAAE,gBAAgB,CAAC;IACxB,sBAAsB,CAAC,EAAE,wBAAwB,CAAC;CAClD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,KAAK,YAAY,KAAG,SAShD,CAAC;AAgDH,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,GAAI,MAAM,mBAAmB,KAAG,KAAK,CAAC,SAAS,CA8JpF,CAAC"}
|