@fuzdev/fuz_app 0.140.0 → 0.142.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 +72 -22
- package/dist/actions/action_rpc.d.ts +5 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +9 -1
- package/dist/actions/connection_closer.d.ts +44 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +33 -3
- package/dist/actions/perform_action.d.ts +10 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +23 -9
- package/dist/actions/register_action_ws.d.ts +42 -6
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +87 -19
- package/dist/actions/register_ws_endpoint.d.ts +15 -6
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +10 -4
- package/dist/actions/socket.svelte.d.ts +19 -4
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +31 -4
- package/dist/actions/transports.d.ts +30 -1
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -10
- package/dist/actions/transports_ws_backend.d.ts +51 -5
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +62 -9
- package/dist/actions/ws_endpoint_spec.d.ts +11 -10
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +44 -22
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +12 -1
- package/dist/auth/account_route_schema.d.ts +1 -0
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +12 -0
- package/dist/auth/account_routes.d.ts +7 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +240 -187
- package/dist/auth/admin_action_specs.d.ts +6 -1
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +6 -1
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +14 -2
- package/dist/auth/api_token.d.ts +20 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +21 -1
- package/dist/auth/api_token_queries.d.ts +22 -4
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -6
- package/dist/auth/audit_log_routes.d.ts +10 -0
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +20 -0
- package/dist/auth/audit_log_schema.d.ts +30 -5
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +30 -0
- package/dist/auth/bearer_auth.d.ts +4 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +19 -8
- package/dist/auth/bootstrap_route_schema.d.ts +1 -0
- package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
- package/dist/auth/bootstrap_route_schema.js +1 -0
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +85 -76
- package/dist/auth/cell_action_specs.d.ts +7 -3
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +9 -5
- package/dist/auth/cell_actions.d.ts +3 -3
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +67 -38
- package/dist/auth/cell_authorize.d.ts +69 -8
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +89 -9
- package/dist/auth/cell_field_action_specs.d.ts +5 -4
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +7 -7
- package/dist/auth/cell_field_actions.d.ts +7 -5
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +24 -22
- package/dist/auth/cell_grant_action_specs.d.ts +6 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +6 -0
- package/dist/auth/cell_grant_actions.d.ts +3 -1
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +16 -2
- package/dist/auth/cell_item_action_specs.d.ts +5 -4
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +7 -7
- package/dist/auth/cell_item_actions.d.ts +6 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +23 -22
- package/dist/auth/cell_relation_list_limit.d.ts +20 -0
- package/dist/auth/cell_relation_list_limit.d.ts.map +1 -0
- package/dist/auth/cell_relation_list_limit.js +27 -0
- package/dist/auth/cell_relation_visibility.d.ts +4 -3
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -1
- package/dist/auth/cell_relation_visibility.js +6 -5
- package/dist/auth/cell_steward.d.ts +45 -0
- package/dist/auth/cell_steward.d.ts.map +1 -0
- package/dist/auth/cell_steward.js +53 -0
- package/dist/auth/cleanup.d.ts +9 -9
- package/dist/auth/cleanup.js +9 -9
- package/dist/auth/credential_deadline.d.ts +95 -0
- package/dist/auth/credential_deadline.d.ts.map +1 -0
- package/dist/auth/credential_deadline.js +139 -0
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +4 -1
- package/dist/auth/middleware.d.ts +90 -12
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +115 -16
- package/dist/auth/request_context.d.ts +8 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +20 -4
- package/dist/auth/resolved_auth.d.ts +13 -2
- package/dist/auth/resolved_auth.d.ts.map +1 -1
- package/dist/auth/resolved_auth.js +5 -4
- package/dist/auth/role_grant_offer_action_specs.d.ts +5 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +6 -1
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +23 -16
- package/dist/auth/role_grant_offer_queries.d.ts +8 -6
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +16 -8
- package/dist/auth/session_cookie.d.ts +4 -0
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +3 -2
- 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 +4 -1
- package/dist/auth/session_queries.d.ts +19 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +8 -2
- package/dist/auth/signup_route_schema.d.ts.map +1 -1
- package/dist/auth/signup_route_schema.js +1 -0
- package/dist/auth/signup_routes.d.ts +4 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +157 -134
- package/dist/auth/token_scope.d.ts +7 -1
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +8 -2
- package/dist/db/CLAUDE.md +32 -19
- package/dist/db/cell_ddl.d.ts +1 -2
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +1 -2
- package/dist/db/cell_field_queries.d.ts +20 -18
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +28 -22
- package/dist/db/cell_item_queries.d.ts +20 -14
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +28 -18
- package/dist/db/cell_moderation.d.ts +67 -0
- package/dist/db/cell_moderation.d.ts.map +1 -0
- package/dist/db/cell_moderation.js +66 -0
- package/dist/db/cell_queries.d.ts +44 -14
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +68 -45
- package/dist/db/cell_view_sql.d.ts +102 -0
- package/dist/db/cell_view_sql.d.ts.map +1 -0
- package/dist/db/cell_view_sql.js +112 -0
- package/dist/db/db_pg.d.ts +1 -1
- package/dist/db/db_pg.js +1 -1
- package/dist/hono_context.d.ts +23 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +19 -0
- package/dist/http/CLAUDE.md +41 -15
- package/dist/http/auth_shape.d.ts +5 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +5 -2
- package/dist/http/ip_canonical.d.ts +30 -3
- package/dist/http/ip_canonical.d.ts.map +1 -1
- package/dist/http/ip_canonical.js +50 -3
- package/dist/http/middleware_spec.d.ts +16 -0
- package/dist/http/middleware_spec.d.ts.map +1 -1
- package/dist/http/origin.d.ts +113 -18
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +247 -127
- package/dist/http/route_spec.d.ts +21 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/schema_helpers.d.ts +22 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +24 -0
- package/dist/http/surface.d.ts +6 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +12 -5
- package/dist/primitive_schemas.d.ts +10 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +11 -2
- package/dist/rate_limiter.d.ts +179 -12
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +352 -42
- package/dist/realtime/sse_auth_guard.d.ts +22 -6
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +31 -12
- package/dist/realtime/subscriber_registry.d.ts +10 -1
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +10 -1
- package/dist/server/app_server.d.ts +20 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +8 -3
- package/dist/server/env.d.ts +2 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +7 -17
- package/dist/server/serve_fact_route.d.ts +8 -4
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +16 -6
- package/dist/testing/CLAUDE.md +164 -25
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -6
- package/dist/testing/connection_closer_helpers.d.ts +6 -1
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +4 -0
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +37 -2
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +14 -65
- package/dist/testing/cross_backend/capabilities.d.ts +26 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +9 -3
- package/dist/testing/cross_backend/cell_gated_create.d.ts +38 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +401 -5
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +145 -2
- package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +6 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +20 -19
- package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
- package/dist/testing/cross_backend/credential_deadline.js +185 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +13 -2
- package/dist/testing/cross_backend/expected_schema.json +0 -1
- package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +7 -1
- package/dist/testing/cross_backend/login_security.d.ts +16 -0
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +154 -0
- package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
- package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
- package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +6 -26
- package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
- package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_conformance.js +136 -0
- package/dist/testing/cross_backend/origin_conformance.json +1679 -0
- package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_scope.js +140 -0
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +24 -2
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +64 -0
- 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 -4
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +20 -1
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +23 -1
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +20 -7
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +25 -21
- package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
- package/dist/testing/cross_backend/transport_composition.js +364 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +21 -1
- package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_role_gated.js +142 -0
- package/dist/testing/entities.d.ts +23 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +37 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +3 -1
- package/dist/testing/middleware.d.ts +10 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +31 -7
- package/dist/testing/schema_introspect.js +1 -1
- package/dist/testing/schema_parity.d.ts +4 -3
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +4 -3
- package/dist/testing/schema_ready_fixture.d.ts +2 -4
- package/dist/testing/schema_ready_fixture.d.ts.map +1 -1
- package/dist/testing/schema_ready_fixture.js +2 -4
- package/dist/testing/stubs.d.ts +16 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +20 -7
- package/dist/testing/surface_invariants.js +2 -2
- package/dist/ui/auth_state.svelte.d.ts +10 -0
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +22 -4
- package/package.json +1 -1
- package/dist/db/cell_history_ddl.d.ts +0 -38
- package/dist/db/cell_history_ddl.d.ts.map +0 -1
- package/dist/db/cell_history_ddl.js +0 -61
|
@@ -13,6 +13,7 @@ import { z } from 'zod';
|
|
|
13
13
|
import { Uuid, bounded_string } from '../primitive_schemas.js';
|
|
14
14
|
import { FRACTIONAL_INDEX_LENGTH_MAX, FRACTIONAL_INDEX_REGEX } from '@fuzdev/fuz_util/fractional_index.ts';
|
|
15
15
|
import { ActingActor } from '../http/auth_shape.js';
|
|
16
|
+
import { CellRelationListLimit } from './cell_relation_list_limit.js';
|
|
16
17
|
// -- Error reasons ----------------------------------------------------------
|
|
17
18
|
/** Error reason — `cell_item_list` got neither `parent_id` nor `child_id`. */
|
|
18
19
|
export const ERROR_CELL_ITEM_LIST_REQUIRES_PARENT_OR_CHILD = 'cell_item_list_requires_parent_or_child';
|
|
@@ -101,10 +102,11 @@ export const CellItemDeleteOutput = z.strictObject({
|
|
|
101
102
|
* authz (child view-check gates the call; per-parent view-check filters
|
|
102
103
|
* the rows).
|
|
103
104
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
105
|
+
* Either direction returns a page of up to `limit` items (default
|
|
106
|
+
* `CELL_RELATION_LIST_LIMIT_DEFAULT`) whose other end the caller can view —
|
|
107
|
+
* the view check runs before the limit. Forward listing pages on via
|
|
108
|
+
* `position_after` (return rows with `position > position_after`); the
|
|
109
|
+
* reverse listing has no cursor, oldest item first.
|
|
108
110
|
*/
|
|
109
111
|
export const CellItemListInput = z
|
|
110
112
|
.strictObject({
|
|
@@ -117,9 +119,7 @@ export const CellItemListInput = z
|
|
|
117
119
|
position_after: CellItemPosition.optional().meta({
|
|
118
120
|
description: 'Cursor for forward pagination — return rows whose position > this.'
|
|
119
121
|
}),
|
|
120
|
-
limit:
|
|
121
|
-
description: 'Page size cap (max 500). Omit for unbounded — explicit list calls escape the bundled `cell_get` cap.'
|
|
122
|
-
}),
|
|
122
|
+
limit: CellRelationListLimit,
|
|
123
123
|
acting: ActingActor
|
|
124
124
|
})
|
|
125
125
|
.refine((v) => Boolean(v.parent_id) !== Boolean(v.child_id), {
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* collision-error shape as insert.
|
|
13
13
|
* - `cell_item_delete` — admin / owner / editor-grant on `parent`.
|
|
14
14
|
* Idempotent: `deleted: false` when no row matched.
|
|
15
|
-
* - `cell_item_list` — bidirectional
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* - `cell_item_list` — bidirectional, a page of up to `limit` items whose
|
|
16
|
+
* other end the caller may view (filtered in SQL, before the limit).
|
|
17
|
+
* Forward (pass `parent_id`) is gated on `can_view_cell(parent)` and
|
|
18
|
+
* pages the items whose child is viewable (strict target-visibility).
|
|
19
|
+
* Reverse (pass `child_id`) has 2-layer authz: gate on
|
|
20
|
+
* `can_view_cell(child)`, then page the items whose parent is viewable.
|
|
20
21
|
*
|
|
21
22
|
* IDOR-mask 404s on cell-miss / cell-unviewable, mirroring the existence-
|
|
22
23
|
* leak guards in `auth/cell_actions.ts`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_item_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_item_actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_item_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_item_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAeN,KAAK,QAAQ,EACb,MAAM,6BAA6B,CAAC;AAMrC,OAAO,EAMN,KAAK,WAAW,EAChB,MAAM,4BAA4B,CAAC;AAOpC,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,eAAO,MAAM,YAAY,GAAI,KAAK,WAAW,KAAG,QAS9C,CAAC;AAOH,iDAAiD;AACjD,eAAO,MAAM,wBAAwB,GAAI,MAAM,kBAAkB,KAAG,KAAK,CAAC,SAAS,CAwKlF,CAAC"}
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* collision-error shape as insert.
|
|
13
13
|
* - `cell_item_delete` — admin / owner / editor-grant on `parent`.
|
|
14
14
|
* Idempotent: `deleted: false` when no row matched.
|
|
15
|
-
* - `cell_item_list` — bidirectional
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* - `cell_item_list` — bidirectional, a page of up to `limit` items whose
|
|
16
|
+
* other end the caller may view (filtered in SQL, before the limit).
|
|
17
|
+
* Forward (pass `parent_id`) is gated on `can_view_cell(parent)` and
|
|
18
|
+
* pages the items whose child is viewable (strict target-visibility).
|
|
19
|
+
* Reverse (pass `child_id`) has 2-layer authz: gate on
|
|
20
|
+
* `can_view_cell(child)`, then page the items whose parent is viewable.
|
|
20
21
|
*
|
|
21
22
|
* IDOR-mask 404s on cell-miss / cell-unviewable, mirroring the existence-
|
|
22
23
|
* leak guards in `auth/cell_actions.ts`.
|
|
@@ -31,8 +32,8 @@ import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
|
|
|
31
32
|
import { is_pg_unique_violation } from '../db/pg_error.js';
|
|
32
33
|
import { cell_item_insert_action_spec, cell_item_move_action_spec, cell_item_delete_action_spec, cell_item_list_action_spec, ERROR_CELL_ITEM_POSITION_TAKEN } from './cell_item_action_specs.js';
|
|
33
34
|
import { ERROR_CELL_NOT_FOUND } from './cell_action_specs.js';
|
|
34
|
-
import { can_view_cell, can_edit_cell } from './cell_authorize.js';
|
|
35
|
-
import {
|
|
35
|
+
import { can_view_cell, can_edit_cell, cell_viewer_from_auth } from './cell_authorize.js';
|
|
36
|
+
import { CELL_RELATION_LIST_LIMIT_DEFAULT } from './cell_relation_list_limit.js';
|
|
36
37
|
import { query_cell_get } from '../db/cell_queries.js';
|
|
37
38
|
import { query_cell_grant_list_for_cell } from '../db/cell_grant_queries.js';
|
|
38
39
|
import { query_cell_item_insert, query_cell_item_move, query_cell_item_delete, query_cell_item_list_for_parent, query_cell_item_list_for_child } from '../db/cell_item_queries.js';
|
|
@@ -160,8 +161,11 @@ export const create_cell_item_actions = (deps) => {
|
|
|
160
161
|
};
|
|
161
162
|
const list_handler = async (input, ctx) => {
|
|
162
163
|
const auth = ctx.auth;
|
|
163
|
-
//
|
|
164
|
-
|
|
164
|
+
// an omitted limit is the default page, either direction
|
|
165
|
+
const limit = input.limit ?? CELL_RELATION_LIST_LIMIT_DEFAULT;
|
|
166
|
+
// Forward listing: gate on can_view_cell(parent), then page the items
|
|
167
|
+
// whose child the caller may view (strict target-visibility, in SQL
|
|
168
|
+
// before the limit).
|
|
165
169
|
if (input.parent_id !== undefined) {
|
|
166
170
|
const parent = await query_cell_get(ctx, input.parent_id);
|
|
167
171
|
if (!parent) {
|
|
@@ -171,12 +175,8 @@ export const create_cell_item_actions = (deps) => {
|
|
|
171
175
|
if (!can_view_cell(auth, parent, parent_grants)) {
|
|
172
176
|
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
173
177
|
}
|
|
174
|
-
const rows = await query_cell_item_list_for_parent(ctx, parent.id, {
|
|
175
|
-
|
|
176
|
-
position_after: input.position_after
|
|
177
|
-
});
|
|
178
|
-
const visible_children = await filter_visible_target_ids(ctx, auth, rows.map((r) => r.child_id));
|
|
179
|
-
return { items: rows.filter((r) => visible_children.has(r.child_id)).map(to_item_json) };
|
|
178
|
+
const rows = await query_cell_item_list_for_parent(ctx, cell_viewer_from_auth(auth), parent.id, { limit, position_after: input.position_after });
|
|
179
|
+
return { items: rows.map(to_item_json) };
|
|
180
180
|
}
|
|
181
181
|
// Reverse listing: 2-layer authz. First, can_view_cell(child).
|
|
182
182
|
const child = await query_cell_get(ctx, input.child_id);
|
|
@@ -187,13 +187,14 @@ export const create_cell_item_actions = (deps) => {
|
|
|
187
187
|
if (!can_view_cell(auth, child, child_grants)) {
|
|
188
188
|
throw jsonrpc_errors.not_found('cell', { reason: ERROR_CELL_NOT_FOUND });
|
|
189
189
|
}
|
|
190
|
-
// Then
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
const rows = await query_cell_item_list_for_child(ctx, child.id, {
|
|
195
|
-
|
|
196
|
-
|
|
190
|
+
// Then page the items whose parent the caller may view — filtered in
|
|
191
|
+
// SQL before the limit, so hidden parents never crowd out visible ones,
|
|
192
|
+
// and bounded so a heavily inbound-linked child can't force an
|
|
193
|
+
// unbounded read on this public endpoint.
|
|
194
|
+
const rows = await query_cell_item_list_for_child(ctx, cell_viewer_from_auth(auth), child.id, {
|
|
195
|
+
limit
|
|
196
|
+
});
|
|
197
|
+
return { items: rows.map(to_item_json) };
|
|
197
198
|
};
|
|
198
199
|
return [
|
|
199
200
|
rpc_action(cell_item_insert_action_spec, insert_handler),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page bound the two relation lists share — `cell_field_list` and
|
|
3
|
+
* `cell_item_list`, forward and reverse alike. A page is up to `limit` edges
|
|
4
|
+
* whose other end the caller can view. Both are public reads, so an omitted
|
|
5
|
+
* `limit` is the default page, never unbounded: anyone can pad a public
|
|
6
|
+
* cell's inbound links, and the reverse lists have no cursor.
|
|
7
|
+
*
|
|
8
|
+
* Twin of the Rust spine's `CELL_RELATION_LIST_LIMIT_MAX` /
|
|
9
|
+
* `CELL_RELATION_LIST_LIMIT_DEFAULT`.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
/** Cap on a relation-list page, forward and reverse alike. */
|
|
15
|
+
export declare const CELL_RELATION_LIST_LIMIT_MAX = 500;
|
|
16
|
+
/** The page size a relation list that omits `limit` gets, forward and reverse alike. */
|
|
17
|
+
export declare const CELL_RELATION_LIST_LIMIT_DEFAULT = 50;
|
|
18
|
+
/** The `limit` input of `cell_field_list` and `cell_item_list`. */
|
|
19
|
+
export declare const CellRelationListLimit: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
//# sourceMappingURL=cell_relation_list_limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell_relation_list_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_relation_list_limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8DAA8D;AAC9D,eAAO,MAAM,4BAA4B,MAAM,CAAC;AAEhD,wFAAwF;AACxF,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,4BAQ/B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page bound the two relation lists share — `cell_field_list` and
|
|
3
|
+
* `cell_item_list`, forward and reverse alike. A page is up to `limit` edges
|
|
4
|
+
* whose other end the caller can view. Both are public reads, so an omitted
|
|
5
|
+
* `limit` is the default page, never unbounded: anyone can pad a public
|
|
6
|
+
* cell's inbound links, and the reverse lists have no cursor.
|
|
7
|
+
*
|
|
8
|
+
* Twin of the Rust spine's `CELL_RELATION_LIST_LIMIT_MAX` /
|
|
9
|
+
* `CELL_RELATION_LIST_LIMIT_DEFAULT`.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
/** Cap on a relation-list page, forward and reverse alike. */
|
|
15
|
+
export const CELL_RELATION_LIST_LIMIT_MAX = 500;
|
|
16
|
+
/** The page size a relation list that omits `limit` gets, forward and reverse alike. */
|
|
17
|
+
export const CELL_RELATION_LIST_LIMIT_DEFAULT = 50;
|
|
18
|
+
/** The `limit` input of `cell_field_list` and `cell_item_list`. */
|
|
19
|
+
export const CellRelationListLimit = z
|
|
20
|
+
.number()
|
|
21
|
+
.int()
|
|
22
|
+
.positive()
|
|
23
|
+
.max(CELL_RELATION_LIST_LIMIT_MAX)
|
|
24
|
+
.optional()
|
|
25
|
+
.meta({
|
|
26
|
+
description: `The most edges a page holds, each one whose other end the caller can view — forward and reverse alike: default ${CELL_RELATION_LIST_LIMIT_DEFAULT}, max ${CELL_RELATION_LIST_LIMIT_MAX}.`
|
|
27
|
+
});
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* target the caller cannot view — otherwise an editor of a public parent
|
|
7
7
|
* could enumerate private children by id, or a public cell could leak the
|
|
8
8
|
* existence of private linked cells. This helper bulk-loads the target
|
|
9
|
-
* cells
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* cells' view facts (`CELL_VIEW_COLUMNS` — never the `data` payload) and
|
|
10
|
+
* their grants, then runs `can_view_cell` per target in memory, returning
|
|
11
|
+
* the set of viewable target ids. Batched (two queries for the whole
|
|
12
|
+
* id-set) to avoid the N+1 a naive per-row check would cause.
|
|
12
13
|
*
|
|
13
14
|
* @module
|
|
14
15
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_relation_visibility.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_relation_visibility.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_relation_visibility.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_relation_visibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,MAAM,cAAc,GAAG,IAAI,EAC3B,YAAY,aAAa,CAAC,IAAI,CAAC,KAC7B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAwBnB,CAAC"}
|
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
* target the caller cannot view — otherwise an editor of a public parent
|
|
7
7
|
* could enumerate private children by id, or a public cell could leak the
|
|
8
8
|
* existence of private linked cells. This helper bulk-loads the target
|
|
9
|
-
* cells
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* cells' view facts (`CELL_VIEW_COLUMNS` — never the `data` payload) and
|
|
10
|
+
* their grants, then runs `can_view_cell` per target in memory, returning
|
|
11
|
+
* the set of viewable target ids. Batched (two queries for the whole
|
|
12
|
+
* id-set) to avoid the N+1 a naive per-row check would cause.
|
|
12
13
|
*
|
|
13
14
|
* @module
|
|
14
15
|
*/
|
|
15
16
|
import { can_view_cell } from './cell_authorize.js';
|
|
16
|
-
import {
|
|
17
|
+
import { query_cell_view_load_many } from '../db/cell_queries.js';
|
|
17
18
|
import { query_cell_grant_list_for_cells } from '../db/cell_grant_queries.js';
|
|
18
19
|
/**
|
|
19
20
|
* Return the subset of `target_ids` the caller may view.
|
|
@@ -33,7 +34,7 @@ export const filter_visible_target_ids = async (deps, auth, target_ids) => {
|
|
|
33
34
|
if (target_ids.length === 0)
|
|
34
35
|
return visible;
|
|
35
36
|
const unique = [...new Set(target_ids)];
|
|
36
|
-
const cells = await
|
|
37
|
+
const cells = await query_cell_view_load_many(deps, unique);
|
|
37
38
|
// Grants only matter for authenticated callers — null auth admits via
|
|
38
39
|
// the public branch alone, so skip the grant load entirely.
|
|
39
40
|
const grants_by_cell = new Map();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The steward read arm, with the governing-root load it needs.
|
|
3
|
+
*
|
|
4
|
+
* `can_steward_cell` (`auth/cell_authorize.ts`) is pure over
|
|
5
|
+
* `(auth, cell, root)`; `cell_steward_admits` is the composition every steward
|
|
6
|
+
* site runs after `can_view_cell` denies: it loads the governing root (active
|
|
7
|
+
* rows only, so a soft-deleted root stewards nothing) and applies the
|
|
8
|
+
* predicate. It short-circuits with no query unless the caller is
|
|
9
|
+
* authenticated and the cell is a pending or rejected contribution.
|
|
10
|
+
*
|
|
11
|
+
* **Where the steward arm applies** — the surfaces a moderator works from:
|
|
12
|
+
* `cell_get`, `cell_moderate` (whose root-authority check it lets a non-admin
|
|
13
|
+
* root owner reach), the cell-scoped fact route, and `cell_list`'s general
|
|
14
|
+
* predicate (the SQL twin in `query_cell_list`).
|
|
15
|
+
*
|
|
16
|
+
* **Where it must not** — every other read keeps `can_view_cell` alone:
|
|
17
|
+
* `cell_clone` (a steward copying a contribution out from under its
|
|
18
|
+
* moderation), the parent check on `cell_create` (a steward contributing under
|
|
19
|
+
* a pending post), the relation endpoint gates, `filter_visible_target_ids`
|
|
20
|
+
* (the `cell_get` bundle), the field / item lists' SQL view filter
|
|
21
|
+
* (`db/cell_view_sql.ts`),
|
|
22
|
+
* `cell_list`'s `shared_with` semi-join (a grant footprint, not a moderation
|
|
23
|
+
* queue), and `cell_grant_revoke`'s `still_admitted` recompute. Widening one of
|
|
24
|
+
* these is a decision, not a sweep. Twin of the Rust `fuz_cell::cell_steward`.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { QueryDeps } from '../db/query_deps.js';
|
|
29
|
+
import { type CellRow } from '../db/cell_queries.js';
|
|
30
|
+
import type { RequestContext } from './request_context.js';
|
|
31
|
+
/**
|
|
32
|
+
* Whether `auth` stewards `cell` — loads the governing root and applies
|
|
33
|
+
* `can_steward_cell`.
|
|
34
|
+
*
|
|
35
|
+
* Resolves `false` without a query for an unauthenticated caller, a root, or a
|
|
36
|
+
* cell whose moderation isn't `'pending'` / `'rejected'`; a missing or
|
|
37
|
+
* soft-deleted root is `false` too.
|
|
38
|
+
*
|
|
39
|
+
* @param deps - query deps
|
|
40
|
+
* @param auth - request context, or `null` for unauthenticated callers
|
|
41
|
+
* @param cell - the cell the caller couldn't otherwise view
|
|
42
|
+
* @returns whether the steward arm admits the caller
|
|
43
|
+
*/
|
|
44
|
+
export declare const cell_steward_admits: (deps: QueryDeps, auth: RequestContext | null, cell: CellRow) => Promise<boolean>;
|
|
45
|
+
//# sourceMappingURL=cell_steward.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell_steward.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/cell_steward.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAkB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,SAAS,EACf,MAAM,cAAc,GAAG,IAAI,EAC3B,MAAM,OAAO,KACX,OAAO,CAAC,OAAO,CAMjB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The steward read arm, with the governing-root load it needs.
|
|
3
|
+
*
|
|
4
|
+
* `can_steward_cell` (`auth/cell_authorize.ts`) is pure over
|
|
5
|
+
* `(auth, cell, root)`; `cell_steward_admits` is the composition every steward
|
|
6
|
+
* site runs after `can_view_cell` denies: it loads the governing root (active
|
|
7
|
+
* rows only, so a soft-deleted root stewards nothing) and applies the
|
|
8
|
+
* predicate. It short-circuits with no query unless the caller is
|
|
9
|
+
* authenticated and the cell is a pending or rejected contribution.
|
|
10
|
+
*
|
|
11
|
+
* **Where the steward arm applies** — the surfaces a moderator works from:
|
|
12
|
+
* `cell_get`, `cell_moderate` (whose root-authority check it lets a non-admin
|
|
13
|
+
* root owner reach), the cell-scoped fact route, and `cell_list`'s general
|
|
14
|
+
* predicate (the SQL twin in `query_cell_list`).
|
|
15
|
+
*
|
|
16
|
+
* **Where it must not** — every other read keeps `can_view_cell` alone:
|
|
17
|
+
* `cell_clone` (a steward copying a contribution out from under its
|
|
18
|
+
* moderation), the parent check on `cell_create` (a steward contributing under
|
|
19
|
+
* a pending post), the relation endpoint gates, `filter_visible_target_ids`
|
|
20
|
+
* (the `cell_get` bundle), the field / item lists' SQL view filter
|
|
21
|
+
* (`db/cell_view_sql.ts`),
|
|
22
|
+
* `cell_list`'s `shared_with` semi-join (a grant footprint, not a moderation
|
|
23
|
+
* queue), and `cell_grant_revoke`'s `still_admitted` recompute. Widening one of
|
|
24
|
+
* these is a decision, not a sweep. Twin of the Rust `fuz_cell::cell_steward`.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { query_cell_get } from '../db/cell_queries.js';
|
|
29
|
+
import { cell_moderation_is_stewardable } from '../db/cell_moderation.js';
|
|
30
|
+
import { can_steward_cell } from './cell_authorize.js';
|
|
31
|
+
/**
|
|
32
|
+
* Whether `auth` stewards `cell` — loads the governing root and applies
|
|
33
|
+
* `can_steward_cell`.
|
|
34
|
+
*
|
|
35
|
+
* Resolves `false` without a query for an unauthenticated caller, a root, or a
|
|
36
|
+
* cell whose moderation isn't `'pending'` / `'rejected'`; a missing or
|
|
37
|
+
* soft-deleted root is `false` too.
|
|
38
|
+
*
|
|
39
|
+
* @param deps - query deps
|
|
40
|
+
* @param auth - request context, or `null` for unauthenticated callers
|
|
41
|
+
* @param cell - the cell the caller couldn't otherwise view
|
|
42
|
+
* @returns whether the steward arm admits the caller
|
|
43
|
+
*/
|
|
44
|
+
export const cell_steward_admits = async (deps, auth, cell) => {
|
|
45
|
+
if (auth === null || cell.root_id === null)
|
|
46
|
+
return false;
|
|
47
|
+
if (!cell_moderation_is_stewardable(cell.moderation))
|
|
48
|
+
return false;
|
|
49
|
+
const root = await query_cell_get(deps, cell.root_id);
|
|
50
|
+
if (!root)
|
|
51
|
+
return false;
|
|
52
|
+
return can_steward_cell(auth, cell, root);
|
|
53
|
+
};
|
package/dist/auth/cleanup.d.ts
CHANGED
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
* stream ends). Otherwise a swept session's connections would be reachable
|
|
23
23
|
* only by an account-wide revocation.
|
|
24
24
|
*
|
|
25
|
-
* The
|
|
26
|
-
* at
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* The sweep's close is the second on expiry, not the first: every connection
|
|
26
|
+
* already closes at its credential's deadline (`auth/credential_deadline.ts`)
|
|
27
|
+
* in the process holding it, whether or not a sweep runs. The sweep is for
|
|
28
|
+
* sessions only: an expired API token's row is not swept — its connections
|
|
29
|
+
* close at their deadline, and `account_token_revoke` still finds the row to
|
|
30
|
+
* close for.
|
|
31
31
|
*
|
|
32
32
|
* The closes run after the delete's commit, which is the rule every
|
|
33
33
|
* revocation close keeps (see `actions/connection_closer.ts`). An expired
|
|
@@ -118,9 +118,9 @@ import type { AuditEmitter } from './audit_emitter.js';
|
|
|
118
118
|
* `DEFAULT_AUTH_CLEANUP_INTERVAL`.
|
|
119
119
|
*
|
|
120
120
|
* An expired session or offer is already refused by every check that reads
|
|
121
|
-
* it,
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* it, and a connection closes at its credential's deadline on its own, so the
|
|
122
|
+
* interval bounds two things: how late an offer expiry's audit row lands, and
|
|
123
|
+
* how long dead rows linger.
|
|
124
124
|
*/
|
|
125
125
|
export declare const DEFAULT_AUTH_CLEANUP_INTERVAL_MS: number;
|
|
126
126
|
/**
|
package/dist/auth/cleanup.js
CHANGED
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
* stream ends). Otherwise a swept session's connections would be reachable
|
|
23
23
|
* only by an account-wide revocation.
|
|
24
24
|
*
|
|
25
|
-
* The
|
|
26
|
-
* at
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* The sweep's close is the second on expiry, not the first: every connection
|
|
26
|
+
* already closes at its credential's deadline (`auth/credential_deadline.ts`)
|
|
27
|
+
* in the process holding it, whether or not a sweep runs. The sweep is for
|
|
28
|
+
* sessions only: an expired API token's row is not swept — its connections
|
|
29
|
+
* close at their deadline, and `account_token_revoke` still finds the row to
|
|
30
|
+
* close for.
|
|
31
31
|
*
|
|
32
32
|
* The closes run after the delete's commit, which is the rule every
|
|
33
33
|
* revocation close keeps (see `actions/connection_closer.ts`). An expired
|
|
@@ -117,9 +117,9 @@ import { query_audit_log } from './audit_log_queries.js';
|
|
|
117
117
|
* `DEFAULT_AUTH_CLEANUP_INTERVAL`.
|
|
118
118
|
*
|
|
119
119
|
* An expired session or offer is already refused by every check that reads
|
|
120
|
-
* it,
|
|
121
|
-
*
|
|
122
|
-
*
|
|
120
|
+
* it, and a connection closes at its credential's deadline on its own, so the
|
|
121
|
+
* interval bounds two things: how late an offer expiry's audit row lands, and
|
|
122
|
+
* how long dead rows linger.
|
|
123
123
|
*/
|
|
124
124
|
export const DEFAULT_AUTH_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;
|
|
125
125
|
/**
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The credential deadline — the moment a resolved credential stops
|
|
3
|
+
* authenticating, which a long-lived connection opened on it must not outlive.
|
|
4
|
+
*
|
|
5
|
+
* A WebSocket or an SSE stream is authorized once, at open, and closed on
|
|
6
|
+
* revocation by identity. Expiry has no revocation event: nothing is written
|
|
7
|
+
* when a session or token passes its `expires_at`. So the auth middleware
|
|
8
|
+
* computes the deadline once (`CREDENTIAL_DEADLINE_KEY`, gathered onto
|
|
9
|
+
* `ResolvedAuth.credential_deadline`) and the transport closes the connection
|
|
10
|
+
* when it arrives (`schedule_credential_deadline`).
|
|
11
|
+
*
|
|
12
|
+
* The deadline is the earliest of what the credential carries:
|
|
13
|
+
*
|
|
14
|
+
* - **Session** — the signed cookie's embedded expiry and the `auth_session`
|
|
15
|
+
* row's `expires_at`.
|
|
16
|
+
* - **API token** — the `api_token` row's `expires_at`; an eternal token
|
|
17
|
+
* (`expires_at IS NULL`) has none.
|
|
18
|
+
* - **Daemon token** — none: it has no row and no expiry, and a rotation
|
|
19
|
+
* closes no connection.
|
|
20
|
+
*
|
|
21
|
+
* A row's expiry is read as **remaining time measured on the database clock**
|
|
22
|
+
* (`EXPIRES_IN_MS_SQL`, `expires_at - NOW()` in the same statement that checks
|
|
23
|
+
* `expires_at > NOW()`), then anchored to this process's monotonic clock
|
|
24
|
+
* (`performance.now()`) — so a skew between the database's clock and the
|
|
25
|
+
* server's moves no deadline. The cookie's expiry is a wall-clock second this
|
|
26
|
+
* server signed, read against the same wall clock the cookie check reads.
|
|
27
|
+
*
|
|
28
|
+
* The monotonic clock stops while the host is suspended, so a deadline on it
|
|
29
|
+
* alone would hold a connection past its credential by the time asleep. The
|
|
30
|
+
* wait (`schedule_credential_deadline`) therefore also carries the deadline on
|
|
31
|
+
* the wall clock (`Date.now()`), which keeps running through a suspend, and
|
|
32
|
+
* fires at whichever is reached first: after a resume, within one timer step
|
|
33
|
+
* (`CREDENTIAL_DEADLINE_TIMER_CAP_MS`, an hour). The wall deadline is taken
|
|
34
|
+
* from the time left when the wait starts, so a skew between the database's
|
|
35
|
+
* clock and the server's still moves nothing. A wall clock stepped forward
|
|
36
|
+
* can fire it early; the client reconnects through the gates, the fail-closed
|
|
37
|
+
* side of the trade.
|
|
38
|
+
*
|
|
39
|
+
* The twin of the Rust spine's `fuz_auth::ResolvedAuth::credential_deadline`
|
|
40
|
+
* and `fuz_realtime`'s deadline wait.
|
|
41
|
+
*
|
|
42
|
+
* @module
|
|
43
|
+
*/
|
|
44
|
+
/**
|
|
45
|
+
* SQL expression for a row's remaining lifetime in milliseconds, measured on
|
|
46
|
+
* the database clock — `NULL` when `expires_at` is `NULL` (an eternal API
|
|
47
|
+
* token). `float8`, so every driver reads it as a JS number.
|
|
48
|
+
*/
|
|
49
|
+
export declare const EXPIRES_IN_MS_SQL = "(EXTRACT(EPOCH FROM (expires_at - NOW())) * 1000)::float8";
|
|
50
|
+
/**
|
|
51
|
+
* The longest single timer the deadline wait sets — one hour, far inside
|
|
52
|
+
* `setTimeout`'s ceiling (2^31 - 1 ms, about 24.8 days, past which a timer
|
|
53
|
+
* fires at once) and the Rust twin's tokio ceiling, and short enough that a
|
|
54
|
+
* resume from suspend is noticed within it. A longer deadline is reached
|
|
55
|
+
* through several.
|
|
56
|
+
*/
|
|
57
|
+
export declare const CREDENTIAL_DEADLINE_TIMER_CAP_MS = 3600000;
|
|
58
|
+
/**
|
|
59
|
+
* The deadline `now` plus the shortest of `remaining_ms`, as a monotonic
|
|
60
|
+
* `performance.now()` timestamp, or `null` when no entry carries one.
|
|
61
|
+
*
|
|
62
|
+
* A negative or `NaN` entry — a row its own `expires_at > NOW()` check
|
|
63
|
+
* already refused, read at the boundary — counts as zero.
|
|
64
|
+
*
|
|
65
|
+
* @param remaining_ms - each source's remaining lifetime in ms, `null` for none
|
|
66
|
+
* @param now - the monotonic clock reading to anchor to
|
|
67
|
+
* @returns the deadline, or `null` for a credential with no expiry
|
|
68
|
+
*/
|
|
69
|
+
export declare const credential_deadline_after: (remaining_ms: ReadonlyArray<number | null>, now?: number) => number | null;
|
|
70
|
+
/**
|
|
71
|
+
* The next timer toward `deadline` from `now`: what remains, capped at
|
|
72
|
+
* `CREDENTIAL_DEADLINE_TIMER_CAP_MS`, or `null` once the deadline is reached.
|
|
73
|
+
*
|
|
74
|
+
* @param deadline - the monotonic deadline
|
|
75
|
+
* @param now - the monotonic clock reading
|
|
76
|
+
* @returns the next delay in ms, or `null` when the deadline has passed
|
|
77
|
+
*/
|
|
78
|
+
export declare const next_credential_deadline_delay: (deadline: number, now: number) => number | null;
|
|
79
|
+
/**
|
|
80
|
+
* Call `on_expire` once `deadline` has passed, waiting in timers of at most
|
|
81
|
+
* `CREDENTIAL_DEADLINE_TIMER_CAP_MS`, each re-reading the clock against the
|
|
82
|
+
* absolute deadline — so a session's 30 days (past `setTimeout`'s ceiling)
|
|
83
|
+
* does not fire at once. Each wake-up also checks the same deadline on the
|
|
84
|
+
* wall clock, so a host suspend, which stops the monotonic clock, delays the
|
|
85
|
+
* close by at most one timer after the resume (module doc).
|
|
86
|
+
*
|
|
87
|
+
* A `null` deadline schedules nothing. The returned function cancels the wait
|
|
88
|
+
* and is safe to call more than once, before or after it fires.
|
|
89
|
+
*
|
|
90
|
+
* @param deadline - the monotonic deadline (`ResolvedAuth.credential_deadline`)
|
|
91
|
+
* @param on_expire - run once, when the deadline passes
|
|
92
|
+
* @returns a cancel function
|
|
93
|
+
*/
|
|
94
|
+
export declare const schedule_credential_deadline: (deadline: number | null, on_expire: () => void) => (() => void);
|
|
95
|
+
//# sourceMappingURL=credential_deadline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential_deadline.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/credential_deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,8DAA8D,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAY,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GACrC,cAAc,aAAa,CAAC,MAAM,GAAG,IAAI,CAAC,EAC1C,MAAK,MAA0B,KAC7B,MAAM,GAAG,IAQX,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,GAAI,UAAU,MAAM,EAAE,KAAK,MAAM,KAAG,MAAM,GAAG,IAIvF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAU,MAAM,GAAG,IAAI,EACvB,WAAW,MAAM,IAAI,KACnB,CAAC,MAAM,IAAI,CA2Bb,CAAC"}
|