@sixb/core 0.1.12 → 0.1.14
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/index.d.ts +2 -2
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +28 -26
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/read-facade.d.ts +14 -16
- package/dist/actions/read-facade.d.ts.map +1 -1
- package/dist/actions/request.d.ts.map +1 -1
- package/dist/actions/run-dispatch.d.ts +2 -0
- package/dist/actions/run-dispatch.d.ts.map +1 -1
- package/dist/actions/types/context.d.ts +18 -31
- package/dist/actions/types/context.d.ts.map +1 -1
- package/dist/actions/types/params.d.ts +2 -1
- package/dist/actions/types/params.d.ts.map +1 -1
- package/dist/actions/worker.js +1 -1
- package/dist/agents/index.js +41 -39
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/streams/index.js +2 -2
- package/dist/agents/streams/internal.js +2 -2
- package/dist/assert-never.d.ts +6 -0
- package/dist/assert-never.d.ts.map +1 -0
- package/dist/auth/client-address.d.ts +3 -0
- package/dist/auth/client-address.d.ts.map +1 -0
- package/dist/auth/csrf.d.ts +5 -1
- package/dist/auth/csrf.d.ts.map +1 -1
- package/dist/auth/errors.d.ts +12 -0
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/index.d.ts +4 -3
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +16 -8
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/runtime.d.ts +58 -41
- package/dist/auth/runtime.d.ts.map +1 -1
- package/dist/auth/session-cache.d.ts +10 -3
- package/dist/auth/session-cache.d.ts.map +1 -1
- package/dist/auth/sessions.d.ts +12 -0
- package/dist/auth/sessions.d.ts.map +1 -1
- package/dist/auth/strategy.d.ts +2 -0
- package/dist/auth/strategy.d.ts.map +1 -1
- package/dist/auth/strategy.js +4 -2
- package/dist/auth/strategy.js.map +1 -1
- package/dist/auth/types.d.ts +16 -15
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/authorization/index.js +3 -3
- package/dist/bootstrap/index.d.ts +1 -1
- package/dist/bootstrap/index.d.ts.map +1 -1
- package/dist/bootstrap/index.js +45 -43
- package/dist/bootstrap/index.js.map +1 -1
- package/dist/bootstrap/ontology-type-manifest.d.ts +8 -0
- package/dist/bootstrap/ontology-type-manifest.d.ts.map +1 -1
- package/dist/{chunk-m7cx05vp.js → chunk-00pfcqh4.js} +126 -59
- package/dist/{chunk-m7cx05vp.js.map → chunk-00pfcqh4.js.map} +13 -12
- package/dist/{chunk-nzsy3xrp.js → chunk-16jenmzw.js} +69 -44
- package/dist/{chunk-nzsy3xrp.js.map → chunk-16jenmzw.js.map} +7 -7
- package/dist/{chunk-y3ev53cq.js → chunk-2fv8sajb.js} +2 -2
- package/dist/{chunk-q5gz3g2j.js → chunk-2jntasmh.js} +13 -16
- package/dist/{chunk-q5gz3g2j.js.map → chunk-2jntasmh.js.map} +3 -3
- package/dist/chunk-3a338wrp.js +191 -0
- package/dist/chunk-3a338wrp.js.map +11 -0
- package/dist/{chunk-ybhz3j9g.js → chunk-3fpgxpsq.js} +104 -148
- package/dist/{chunk-ybhz3j9g.js.map → chunk-3fpgxpsq.js.map} +7 -8
- package/dist/{chunk-eyjzgav0.js → chunk-3nx8vj5a.js} +6 -4
- package/dist/chunk-3nx8vj5a.js.map +12 -0
- package/dist/{chunk-5tetw4hn.js → chunk-4tnrzqqh.js} +1 -1
- package/dist/{chunk-3ghw2t5q.js → chunk-4ypbe95r.js} +10 -16
- package/dist/chunk-4ypbe95r.js.map +10 -0
- package/dist/{chunk-2cxv584m.js → chunk-6rzb1g6h.js} +358 -248
- package/dist/chunk-6rzb1g6h.js.map +44 -0
- package/dist/{chunk-7zgecwy1.js → chunk-6x0pqh32.js} +12 -1
- package/dist/{chunk-7zgecwy1.js.map → chunk-6x0pqh32.js.map} +3 -3
- package/dist/{chunk-6ma8dvzc.js → chunk-728fp090.js} +49 -42
- package/dist/chunk-728fp090.js.map +15 -0
- package/dist/chunk-96yw3pwd.js.map +2 -2
- package/dist/{chunk-4k53sm9e.js → chunk-a0073121.js} +59 -59
- package/dist/{chunk-4k53sm9e.js.map → chunk-a0073121.js.map} +5 -5
- package/dist/{chunk-y1ahv8b7.js → chunk-bdhfn0ww.js} +1 -1
- package/dist/{chunk-kept9cdd.js → chunk-bxx3ms5t.js} +1 -1
- package/dist/{chunk-xtfryrfb.js → chunk-cbn96bve.js} +2 -2
- package/dist/{chunk-vsg0g7h9.js → chunk-d8yes9yx.js} +304 -45
- package/dist/chunk-d8yes9yx.js.map +20 -0
- package/dist/{chunk-vpxss818.js → chunk-drz59ve0.js} +9 -8
- package/dist/chunk-drz59ve0.js.map +10 -0
- package/dist/{chunk-7c2g3sz9.js → chunk-dsfw1bdr.js} +5 -5
- package/dist/{chunk-7c2g3sz9.js.map → chunk-dsfw1bdr.js.map} +1 -1
- package/dist/{chunk-qm535afa.js → chunk-dv4ct8mp.js} +66 -24
- package/dist/chunk-dv4ct8mp.js.map +15 -0
- package/dist/{chunk-t7dzd1tm.js → chunk-e2c5yxfe.js} +2 -2
- package/dist/{chunk-p6r1ex7t.js → chunk-ffwvdgk1.js} +5 -13
- package/dist/chunk-ffwvdgk1.js.map +10 -0
- package/dist/{chunk-tg4g4zh0.js → chunk-gct3m1da.js} +9 -9
- package/dist/{chunk-tg4g4zh0.js.map → chunk-gct3m1da.js.map} +3 -3
- package/dist/chunk-h89wz928.js +42 -0
- package/dist/chunk-h89wz928.js.map +10 -0
- package/dist/{chunk-b768c93y.js → chunk-hj0n3c2p.js} +83 -28
- package/dist/{chunk-b768c93y.js.map → chunk-hj0n3c2p.js.map} +5 -5
- package/dist/{chunk-hkw8rykk.js → chunk-ht4a7mjg.js} +74 -4
- package/dist/chunk-ht4a7mjg.js.map +15 -0
- package/dist/{chunk-7qnvhtcd.js → chunk-hxgj4kzd.js} +17 -17
- package/dist/{chunk-7qnvhtcd.js.map → chunk-hxgj4kzd.js.map} +5 -5
- package/dist/{chunk-bg2bs62d.js → chunk-jfdggg1p.js} +5 -5
- package/dist/{chunk-f06sf7rr.js → chunk-k4kkp58d.js} +18 -5
- package/dist/chunk-k4kkp58d.js.map +11 -0
- package/dist/{chunk-e1f1sqba.js → chunk-kxea54yg.js} +3 -3
- package/dist/{chunk-2h63nrde.js → chunk-ky7zpbtt.js} +5 -7
- package/dist/{chunk-2h63nrde.js.map → chunk-ky7zpbtt.js.map} +3 -4
- package/dist/chunk-m4ehq95f.js +9 -0
- package/dist/chunk-m4ehq95f.js.map +10 -0
- package/dist/{chunk-0mza4xb2.js → chunk-m8zkvep1.js} +2 -2
- package/dist/{chunk-xn686q2e.js → chunk-ma1mhh7w.js} +75 -57
- package/dist/{chunk-xn686q2e.js.map → chunk-ma1mhh7w.js.map} +3 -3
- package/dist/{chunk-yxb4c62x.js → chunk-mr60an99.js} +3 -2
- package/dist/{chunk-yxb4c62x.js.map → chunk-mr60an99.js.map} +3 -3
- package/dist/{chunk-g50nxqs5.js → chunk-pvgpckm1.js} +63 -20
- package/dist/chunk-pvgpckm1.js.map +20 -0
- package/dist/{chunk-jjwhy3rs.js → chunk-q2xghyft.js} +5 -5
- package/dist/chunk-rdghfsss.js.map +2 -2
- package/dist/{chunk-bbaa1fet.js → chunk-rzcjp5m2.js} +7 -3
- package/dist/chunk-rzcjp5m2.js.map +25 -0
- package/dist/{chunk-hzw7yv5g.js → chunk-rzkr70gr.js} +5 -1
- package/dist/{chunk-hzw7yv5g.js.map → chunk-rzkr70gr.js.map} +3 -3
- package/dist/{chunk-axndsymr.js → chunk-tw03bsmb.js} +2 -2
- package/dist/{chunk-axndsymr.js.map → chunk-tw03bsmb.js.map} +3 -3
- package/dist/{chunk-y0v18c2j.js → chunk-vef6qa14.js} +2 -2
- package/dist/{chunk-gzt6v1ja.js → chunk-vn3yh259.js} +1 -1
- package/dist/{chunk-sfypc7mk.js → chunk-wn7mznxc.js} +4 -4
- package/dist/{chunk-r5zythxp.js → chunk-wnxabtn4.js} +7 -7
- package/dist/{chunk-4ph94nhd.js → chunk-ww185kt6.js} +2 -2
- package/dist/{chunk-3h7ctayx.js → chunk-xgptywp0.js} +4 -3
- package/dist/chunk-xgptywp0.js.map +11 -0
- package/dist/{chunk-gswwr35f.js → chunk-y487ywrv.js} +4 -4
- package/dist/{chunk-bet1rb9h.js → chunk-y6vnvayj.js} +1 -1
- package/dist/{chunk-x9tmx3fc.js → chunk-ypj2wqtj.js} +3 -6
- package/dist/{chunk-x9tmx3fc.js.map → chunk-ypj2wqtj.js.map} +4 -4
- package/dist/{chunk-3bnt2wna.js → chunk-yvf2jqj8.js} +1 -1
- package/dist/{chunk-g2gezsg5.js → chunk-z9h698s8.js} +1 -1
- package/dist/{chunk-wjffn5m4.js → chunk-zdkcdshk.js} +3 -36
- package/dist/{chunk-wjffn5m4.js.map → chunk-zdkcdshk.js.map} +3 -4
- package/dist/{chunk-bfjv1adk.js → chunk-znw0zvy4.js} +1 -1
- package/dist/edits/types.d.ts +19 -19
- package/dist/edits/types.d.ts.map +1 -1
- package/dist/error-reporting/internal.js +3 -3
- package/dist/errors/catalog.d.ts +4 -0
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/internal.js +1 -1
- package/dist/events/envelope.d.ts +0 -12
- package/dist/events/envelope.d.ts.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +3 -3
- package/dist/events/service.d.ts +1 -2
- package/dist/events/service.d.ts.map +1 -1
- package/dist/events/toStoredEvent.d.ts +1 -2
- package/dist/events/toStoredEvent.d.ts.map +1 -1
- package/dist/events/types/index.d.ts +2 -1
- package/dist/events/types/index.d.ts.map +1 -1
- package/dist/execution/agent.d.ts +1 -2
- package/dist/execution/agent.d.ts.map +1 -1
- package/dist/execution/agent.js +39 -37
- package/dist/execution/agent.js.map +1 -1
- package/dist/execution/authorized-object-reader.d.ts.map +1 -1
- package/dist/execution/primitive.d.ts +1 -2
- package/dist/execution/primitive.d.ts.map +1 -1
- package/dist/execution/primitive.js +38 -36
- package/dist/execution/primitive.js.map +3 -3
- package/dist/execution/request.d.ts +1 -2
- package/dist/execution/request.d.ts.map +1 -1
- package/dist/execution/request.js +39 -37
- package/dist/execution/request.js.map +3 -3
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -50
- package/dist/index.js.map +1 -1
- package/dist/maintenance/ontology-maintenance.d.ts +11 -1
- package/dist/maintenance/ontology-maintenance.d.ts.map +1 -1
- package/dist/materialization/errors.d.ts +3 -1
- package/dist/materialization/errors.d.ts.map +1 -1
- package/dist/materialization/events.d.ts +27 -2
- package/dist/materialization/events.d.ts.map +1 -1
- package/dist/materializer/edits/commit.d.ts.map +1 -1
- package/dist/materializer/edits/operations.d.ts +2 -1
- package/dist/materializer/edits/operations.d.ts.map +1 -1
- package/dist/materializer/edits/plan.d.ts +2 -2
- package/dist/materializer/edits/plan.d.ts.map +1 -1
- package/dist/materializer/edits/user-refs.d.ts +30 -0
- package/dist/materializer/edits/user-refs.d.ts.map +1 -0
- package/dist/materializer/effective/build-events.d.ts +2 -2
- package/dist/materializer/effective/build-events.d.ts.map +1 -1
- package/dist/materializer/execution/scope.d.ts +11 -3
- package/dist/materializer/execution/scope.d.ts.map +1 -1
- package/dist/materializer/index.js +23 -21
- package/dist/materializer/index.js.map +1 -1
- package/dist/materializer/projections/replacement-plan.d.ts +2 -0
- package/dist/materializer/projections/replacement-plan.d.ts.map +1 -1
- package/dist/materializer/telemetry/plan.d.ts +2 -2
- package/dist/materializer/telemetry/plan.d.ts.map +1 -1
- package/dist/models/execution/index.js +23 -21
- package/dist/models/execution/index.js.map +1 -1
- package/dist/models/index.js +1 -1
- package/dist/objects/execution.d.ts +21 -27
- package/dist/objects/execution.d.ts.map +1 -1
- package/dist/objects/index.js +28 -26
- package/dist/objects/index.js.map +1 -1
- package/dist/objects/query/executor.d.ts.map +1 -1
- package/dist/objects/query/index.js +27 -25
- package/dist/objects/query/index.js.map +1 -1
- package/dist/objects/query/ir.d.ts +8 -1
- package/dist/objects/query/ir.d.ts.map +1 -1
- package/dist/objects/query/scalar-values.d.ts.map +1 -1
- package/dist/objects/query/validate.d.ts.map +1 -1
- package/dist/objects/sdk/object-handle.d.ts +1 -2
- package/dist/objects/sdk/object-handle.d.ts.map +1 -1
- package/dist/objects/sdk/object-set.d.ts +2 -3
- package/dist/objects/sdk/object-set.d.ts.map +1 -1
- package/dist/objects/sdk/query-builder.d.ts +8 -10
- package/dist/objects/sdk/query-builder.d.ts.map +1 -1
- package/dist/objects/sdk/telemetry-channel.d.ts +1 -2
- package/dist/objects/sdk/telemetry-channel.d.ts.map +1 -1
- package/dist/objects/sdk/where.d.ts +2 -3
- package/dist/objects/sdk/where.d.ts.map +1 -1
- package/dist/ontology/builders.d.ts +11 -10
- package/dist/ontology/builders.d.ts.map +1 -1
- package/dist/ontology/index.d.ts +4 -2
- package/dist/ontology/index.d.ts.map +1 -1
- package/dist/ontology/index.js +6 -4
- package/dist/ontology/index.js.map +1 -1
- package/dist/ontology/inference/index.d.ts +34 -22
- package/dist/ontology/inference/index.d.ts.map +1 -1
- package/dist/ontology/internal.d.ts +1 -0
- package/dist/ontology/internal.d.ts.map +1 -1
- package/dist/ontology/internal.js +9 -7
- package/dist/ontology/internal.js.map +1 -1
- package/dist/ontology/primitives.d.ts +38 -0
- package/dist/ontology/primitives.d.ts.map +1 -0
- package/dist/ontology/query-capabilities.d.ts +38 -0
- package/dist/ontology/query-capabilities.d.ts.map +1 -0
- package/dist/ontology/refs.d.ts +23 -2
- package/dist/ontology/refs.d.ts.map +1 -1
- package/dist/ontology/registered.d.ts +16 -0
- package/dist/ontology/registered.d.ts.map +1 -0
- package/dist/ontology/registry.d.ts.map +1 -1
- package/dist/ontology/tokens.d.ts +35 -1
- package/dist/ontology/tokens.d.ts.map +1 -1
- package/dist/ontology/types.d.ts +18 -4
- package/dist/ontology/types.d.ts.map +1 -1
- package/dist/ontology/user-ref.d.ts +35 -0
- package/dist/ontology/user-ref.d.ts.map +1 -0
- package/dist/ontology/validation/definition.d.ts.map +1 -1
- package/dist/ontology/validation/index.d.ts +1 -1
- package/dist/ontology/validation/index.d.ts.map +1 -1
- package/dist/ontology/validation/normalize.d.ts.map +1 -1
- package/dist/ontology/validation/properties.d.ts +6 -1
- package/dist/ontology/validation/properties.d.ts.map +1 -1
- package/dist/ontology/validation/query.d.ts.map +1 -1
- package/dist/ontology/validation/schema.d.ts.map +1 -1
- package/dist/pipelines/index.js +26 -24
- package/dist/pipelines/index.js.map +1 -1
- package/dist/predicates/types.d.ts +53 -3
- package/dist/predicates/types.d.ts.map +1 -1
- package/dist/projections/builders.d.ts +19 -1
- package/dist/projections/builders.d.ts.map +1 -1
- package/dist/projections/internal.js +23 -21
- package/dist/projections/internal.js.map +1 -1
- package/dist/projections/validation.d.ts.map +1 -1
- package/dist/query.js +2 -2
- package/dist/queues/in-memory.d.ts +1 -1
- package/dist/queues/in-memory.d.ts.map +1 -1
- package/dist/runtime/create.d.ts +1 -1
- package/dist/runtime/create.d.ts.map +1 -1
- package/dist/runtime/host.d.ts +5 -5
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/internal.js +27 -25
- package/dist/runtime/internal.js.map +1 -1
- package/dist/runtime/sixb.d.ts +5 -5
- package/dist/runtime/sixb.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +171 -188
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/sandboxes/index.js +5 -5
- package/dist/sandboxes/sandbox.d.ts +4 -1
- package/dist/sandboxes/sandbox.d.ts.map +1 -1
- package/dist/shared/params/types.d.ts +4 -3
- package/dist/shared/params/types.d.ts.map +1 -1
- package/dist/shared/params/user-refs.d.ts +19 -0
- package/dist/shared/params/user-refs.d.ts.map +1 -0
- package/dist/shares/compiler.d.ts.map +1 -1
- package/dist/shares/internal.js +24 -22
- package/dist/shares/internal.js.map +2 -2
- package/dist/storage/action-runs/failure.js +2 -2
- package/dist/storage/action-runs/types.d.ts +1 -1
- package/dist/storage/action-runs/types.d.ts.map +1 -1
- package/dist/storage/agents/provider.js +1 -1
- package/dist/storage/ai-limits/enforcement.js +2 -2
- package/dist/storage/auth/errors.d.ts +1 -1
- package/dist/storage/auth/errors.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/access-tokens.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/auth-storage.d.ts +2 -1
- package/dist/storage/auth/in-memory/auth-storage.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/device-authorizations.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/sessions.d.ts +8 -1
- package/dist/storage/auth/in-memory/sessions.d.ts.map +1 -1
- package/dist/storage/auth/in-memory/shared.d.ts.map +1 -1
- package/dist/storage/auth/index.d.ts +1 -1
- package/dist/storage/auth/index.d.ts.map +1 -1
- package/dist/storage/auth/types.d.ts +77 -20
- package/dist/storage/auth/types.d.ts.map +1 -1
- package/dist/storage/connector-connections/provider.js +1 -1
- package/dist/storage/executions/provider.js +1 -1
- package/dist/storage/file-upload-sessions/in-memory.d.ts +9 -18
- package/dist/storage/file-upload-sessions/in-memory.d.ts.map +1 -1
- package/dist/storage/file-upload-sessions/index.d.ts +1 -1
- package/dist/storage/file-upload-sessions/index.d.ts.map +1 -1
- package/dist/storage/file-upload-sessions/provider.d.ts +52 -0
- package/dist/storage/file-upload-sessions/provider.d.ts.map +1 -0
- package/dist/storage/file-upload-sessions/provider.js +24 -0
- package/dist/storage/file-upload-sessions/provider.js.map +9 -0
- package/dist/storage/file-upload-sessions/types.d.ts +19 -1
- package/dist/storage/file-upload-sessions/types.d.ts.map +1 -1
- package/dist/storage/file-upload-sessions/utils.d.ts +8 -2
- package/dist/storage/file-upload-sessions/utils.d.ts.map +1 -1
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +29 -25
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/internal.js +11 -9
- package/dist/storage/internal.js.map +1 -1
- package/dist/storage/objects/in-memory/query.d.ts.map +1 -1
- package/dist/storage/ontology/commits.d.ts +6 -2
- package/dist/storage/ontology/commits.d.ts.map +1 -1
- package/dist/storage/ontology/in-memory/materializations.d.ts.map +1 -1
- package/dist/storage/ontology/provider-work.d.ts.map +1 -1
- package/dist/storage/ontology/provider.d.ts +14 -1
- package/dist/storage/ontology/provider.d.ts.map +1 -1
- package/dist/storage/ontology/provider.js +8 -2
- package/dist/storage/ontology/provider.js.map +1 -1
- package/dist/storage/operation-scope.d.ts.map +1 -1
- package/dist/storage/operation-scope.js +1 -1
- package/dist/storage/projection-runs/provider.js +2 -2
- package/dist/storage/types.d.ts +1 -1
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/workflow-interventions/index.d.ts +1 -1
- package/dist/storage/workflow-interventions/index.d.ts.map +1 -1
- package/dist/storage/workflow-interventions/types.d.ts +5 -8
- package/dist/storage/workflow-interventions/types.d.ts.map +1 -1
- package/dist/storage/workflow-runs/provider.js +8 -8
- package/dist/syncs/index.js +27 -25
- package/dist/syncs/index.js.map +1 -1
- package/dist/testing/action-execution.d.ts +4 -0
- package/dist/testing/action-execution.d.ts.map +1 -1
- package/dist/testing/auth-storage-contract.d.ts.map +1 -1
- package/dist/testing/execution.d.ts +1 -2
- package/dist/testing/execution.d.ts.map +1 -1
- package/dist/testing/file-upload-session-storage-contract.d.ts +12 -0
- package/dist/testing/file-upload-session-storage-contract.d.ts.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2035 -1358
- package/dist/testing/index.js.map +15 -13
- package/dist/testing/materializer-storage-contract.d.ts.map +1 -1
- package/dist/testing/object-query-contract.d.ts.map +1 -1
- package/dist/testing/ontology-contract-fixture.d.ts +8 -0
- package/dist/testing/ontology-contract-fixture.d.ts.map +1 -1
- package/dist/testing/ontology-storage-contract.d.ts.map +1 -1
- package/dist/testing/sandboxes-contract.d.ts.map +1 -1
- package/dist/testing/workflow-intervention-storage-contract.d.ts +8 -0
- package/dist/testing/workflow-intervention-storage-contract.d.ts.map +1 -0
- package/dist/webhooks/internal.js +24 -22
- package/dist/webhooks/internal.js.map +2 -2
- package/dist/webhooks/types.d.ts +2 -2
- package/dist/webhooks/types.d.ts.map +1 -1
- package/dist/workers/index.js +1 -1
- package/dist/workflows/index.js +42 -40
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/run-dispatch.d.ts.map +1 -1
- package/dist/workflows/types.d.ts +2 -2
- package/dist/workflows/types.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/actions/index.ts +0 -3
- package/src/actions/read-facade.ts +102 -62
- package/src/actions/request.ts +13 -0
- package/src/actions/run-dispatch.ts +3 -0
- package/src/actions/types/context.ts +17 -65
- package/src/actions/types/params.ts +2 -1
- package/src/agents/authority.ts +1 -1
- package/src/assert-never.ts +7 -0
- package/src/auth/client-address.ts +12 -0
- package/src/auth/csrf.ts +14 -5
- package/src/auth/errors.ts +23 -0
- package/src/auth/index.ts +4 -3
- package/src/auth/runtime.ts +353 -297
- package/src/auth/session-cache.ts +18 -6
- package/src/auth/sessions.ts +30 -0
- package/src/auth/strategy.ts +2 -0
- package/src/auth/types.ts +15 -17
- package/src/bootstrap/index.ts +1 -0
- package/src/bootstrap/ontology-type-manifest.ts +112 -60
- package/src/edits/types.ts +47 -63
- package/src/errors/catalog.ts +4 -0
- package/src/events/envelope.ts +0 -13
- package/src/events/index.ts +1 -1
- package/src/events/service.ts +1 -11
- package/src/events/toStoredEvent.ts +1 -3
- package/src/events/types/index.ts +1 -1
- package/src/execution/agent.ts +1 -2
- package/src/execution/authorized-object-reader.ts +5 -7
- package/src/execution/primitive.ts +1 -2
- package/src/execution/request.ts +1 -2
- package/src/index.ts +8 -7
- package/src/maintenance/ontology-maintenance.ts +77 -1
- package/src/materialization/errors.ts +2 -0
- package/src/materialization/events.ts +27 -2
- package/src/materializer/README.md +2 -0
- package/src/materializer/edits/commit.ts +21 -12
- package/src/materializer/edits/operations.ts +32 -4
- package/src/materializer/edits/plan.ts +4 -5
- package/src/materializer/edits/user-refs.ts +115 -0
- package/src/materializer/effective/build-events.ts +4 -5
- package/src/materializer/execution/scope.ts +40 -7
- package/src/materializer/projections/replace.ts +2 -0
- package/src/materializer/projections/replacement-plan.ts +4 -0
- package/src/materializer/telemetry/append.ts +4 -12
- package/src/materializer/telemetry/plan.ts +4 -5
- package/src/objects/execution.ts +33 -62
- package/src/objects/query/executor.ts +12 -3
- package/src/objects/query/ir.ts +7 -0
- package/src/objects/query/planner.ts +1 -0
- package/src/objects/query/scalar-values.ts +19 -0
- package/src/objects/query/validate.ts +55 -44
- package/src/objects/sdk/object-handle.ts +8 -8
- package/src/objects/sdk/object-set.ts +8 -15
- package/src/objects/sdk/query-builder.ts +59 -71
- package/src/objects/sdk/telemetry-channel.ts +6 -7
- package/src/objects/sdk/where.ts +6 -13
- package/src/ontology/builders.ts +15 -11
- package/src/ontology/index.ts +4 -0
- package/src/ontology/inference/index.ts +56 -36
- package/src/ontology/internal.ts +1 -0
- package/src/ontology/json-schema.ts +12 -0
- package/src/ontology/primitives.ts +91 -0
- package/src/ontology/query-capabilities.ts +136 -0
- package/src/ontology/refs.ts +33 -6
- package/src/ontology/registered.ts +22 -0
- package/src/ontology/registry.ts +6 -3
- package/src/ontology/tokens.ts +41 -2
- package/src/ontology/types.ts +19 -3
- package/src/ontology/user-ref.ts +120 -0
- package/src/ontology/validation/definition.ts +2 -13
- package/src/ontology/validation/index.ts +1 -0
- package/src/ontology/validation/normalize.ts +39 -3
- package/src/ontology/validation/properties.ts +95 -19
- package/src/ontology/validation/query.ts +11 -42
- package/src/ontology/validation/schema.ts +16 -0
- package/src/predicates/types.ts +43 -33
- package/src/projections/builders.ts +24 -3
- package/src/projections/validation.ts +15 -1
- package/src/runtime/create.ts +2 -2
- package/src/runtime/host.ts +10 -12
- package/src/runtime/index.ts +3 -2
- package/src/runtime/sixb.ts +13 -23
- package/src/runtime/types.ts +277 -479
- package/src/sandboxes/sandbox.ts +4 -1
- package/src/shared/params/types.ts +4 -3
- package/src/shared/params/user-refs.ts +48 -0
- package/src/shares/compiler.ts +6 -16
- package/src/storage/action-runs/types.ts +1 -0
- package/src/storage/auth/errors.ts +0 -1
- package/src/storage/auth/in-memory/access-tokens.ts +5 -2
- package/src/storage/auth/in-memory/auth-storage.ts +49 -73
- package/src/storage/auth/in-memory/device-authorizations.ts +0 -2
- package/src/storage/auth/in-memory/sessions.ts +69 -1
- package/src/storage/auth/in-memory/shared.ts +13 -7
- package/src/storage/auth/index.ts +2 -1
- package/src/storage/auth/types.ts +72 -20
- package/src/storage/executions/validation.ts +1 -1
- package/src/storage/file-upload-sessions/in-memory.ts +64 -113
- package/src/storage/file-upload-sessions/index.ts +1 -0
- package/src/storage/file-upload-sessions/provider.ts +258 -0
- package/src/storage/file-upload-sessions/types.ts +20 -1
- package/src/storage/file-upload-sessions/utils.ts +25 -9
- package/src/storage/index.ts +3 -2
- package/src/storage/objects/in-memory/query.ts +13 -3
- package/src/storage/ontology/commits.ts +6 -2
- package/src/storage/ontology/in-memory/materializations.ts +6 -34
- package/src/storage/ontology/provider-header-validation.ts +57 -0
- package/src/storage/ontology/provider-work.ts +15 -3
- package/src/storage/ontology/provider.ts +59 -1
- package/src/storage/operation-scope.ts +1 -0
- package/src/storage/types.ts +0 -1
- package/src/storage/workflow-interventions/index.ts +0 -1
- package/src/storage/workflow-interventions/types.ts +5 -9
- package/src/testing/action-execution.ts +13 -3
- package/src/testing/auth-storage-contract.ts +253 -192
- package/src/testing/execution-storage-contract.ts +1 -2
- package/src/testing/execution.ts +4 -7
- package/src/testing/file-upload-session-storage-contract.ts +335 -0
- package/src/testing/index.ts +8 -0
- package/src/testing/materializer-storage-contract.ts +86 -4
- package/src/testing/object-query-contract.ts +121 -2
- package/src/testing/ontology-contract-fixture.ts +23 -3
- package/src/testing/ontology-storage-contract.ts +58 -0
- package/src/testing/sandboxes-contract.ts +27 -0
- package/src/testing/workflow-intervention-storage-contract.ts +203 -0
- package/src/webhooks/types.ts +2 -2
- package/src/workflows/run-dispatch.ts +12 -1
- package/src/workflows/snapshot.ts +16 -7
- package/src/workflows/types.ts +2 -2
- package/dist/chunk-2cxv584m.js.map +0 -43
- package/dist/chunk-3ghw2t5q.js.map +0 -10
- package/dist/chunk-3h7ctayx.js.map +0 -11
- package/dist/chunk-6ma8dvzc.js.map +0 -15
- package/dist/chunk-bbaa1fet.js.map +0 -25
- package/dist/chunk-eyjzgav0.js.map +0 -12
- package/dist/chunk-f06sf7rr.js.map +0 -11
- package/dist/chunk-g50nxqs5.js.map +0 -18
- package/dist/chunk-hkw8rykk.js.map +0 -15
- package/dist/chunk-p6r1ex7t.js.map +0 -10
- package/dist/chunk-qm535afa.js.map +0 -15
- package/dist/chunk-vpxss818.js.map +0 -10
- package/dist/chunk-vsg0g7h9.js.map +0 -17
- /package/dist/{chunk-y3ev53cq.js.map → chunk-2fv8sajb.js.map} +0 -0
- /package/dist/{chunk-5tetw4hn.js.map → chunk-4tnrzqqh.js.map} +0 -0
- /package/dist/{chunk-y1ahv8b7.js.map → chunk-bdhfn0ww.js.map} +0 -0
- /package/dist/{chunk-kept9cdd.js.map → chunk-bxx3ms5t.js.map} +0 -0
- /package/dist/{chunk-xtfryrfb.js.map → chunk-cbn96bve.js.map} +0 -0
- /package/dist/{chunk-t7dzd1tm.js.map → chunk-e2c5yxfe.js.map} +0 -0
- /package/dist/{chunk-bg2bs62d.js.map → chunk-jfdggg1p.js.map} +0 -0
- /package/dist/{chunk-e1f1sqba.js.map → chunk-kxea54yg.js.map} +0 -0
- /package/dist/{chunk-0mza4xb2.js.map → chunk-m8zkvep1.js.map} +0 -0
- /package/dist/{chunk-jjwhy3rs.js.map → chunk-q2xghyft.js.map} +0 -0
- /package/dist/{chunk-y0v18c2j.js.map → chunk-vef6qa14.js.map} +0 -0
- /package/dist/{chunk-gzt6v1ja.js.map → chunk-vn3yh259.js.map} +0 -0
- /package/dist/{chunk-sfypc7mk.js.map → chunk-wn7mznxc.js.map} +0 -0
- /package/dist/{chunk-r5zythxp.js.map → chunk-wnxabtn4.js.map} +0 -0
- /package/dist/{chunk-4ph94nhd.js.map → chunk-ww185kt6.js.map} +0 -0
- /package/dist/{chunk-gswwr35f.js.map → chunk-y487ywrv.js.map} +0 -0
- /package/dist/{chunk-bet1rb9h.js.map → chunk-y6vnvayj.js.map} +0 -0
- /package/dist/{chunk-3bnt2wna.js.map → chunk-yvf2jqj8.js.map} +0 -0
- /package/dist/{chunk-g2gezsg5.js.map → chunk-z9h698s8.js.map} +0 -0
- /package/dist/{chunk-bfjv1adk.js.map → chunk-znw0zvy4.js.map} +0 -0
package/src/auth/runtime.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
type AccessTokenRecord,
|
|
19
19
|
type AuthStorage,
|
|
20
20
|
AuthStorageError,
|
|
21
|
+
type CompleteDeviceAuthorizationInput,
|
|
22
|
+
type GroupMembershipRecord,
|
|
21
23
|
type InvitationRecord,
|
|
22
24
|
type ServiceAccountGroupMembershipRecord,
|
|
23
25
|
type ServiceAccountRecord,
|
|
@@ -31,7 +33,8 @@ import {
|
|
|
31
33
|
hashAccessTokenSecret,
|
|
32
34
|
parseAccessTokenValue,
|
|
33
35
|
} from "./access-tokens"
|
|
34
|
-
import type
|
|
36
|
+
import { type AuthSessionAudience, DEFAULT_AUTH_SESSION_AUDIENCE } from "./audience"
|
|
37
|
+
import { getRequestClientAddress } from "./client-address"
|
|
35
38
|
import {
|
|
36
39
|
getCookie,
|
|
37
40
|
type ResolvedAuthCookieOptions,
|
|
@@ -39,7 +42,15 @@ import {
|
|
|
39
42
|
} from "./cookies"
|
|
40
43
|
import { AuthRuntimeError } from "./errors"
|
|
41
44
|
import { SessionCache } from "./session-cache"
|
|
42
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
type BearerSessionTokens,
|
|
47
|
+
createBearerSessionTokens,
|
|
48
|
+
hashSessionSecret,
|
|
49
|
+
parseBearerSessionToken,
|
|
50
|
+
parseSessionCookieValue,
|
|
51
|
+
SESSION_ACCESS_TOKEN_PREFIX,
|
|
52
|
+
SESSION_REFRESH_TOKEN_PREFIX,
|
|
53
|
+
} from "./sessions"
|
|
43
54
|
import type {
|
|
44
55
|
AuthenticatedAuthSession,
|
|
45
56
|
AuthenticatedRequestAuthSession,
|
|
@@ -59,12 +70,12 @@ import type {
|
|
|
59
70
|
GetInvitationOptionsResult,
|
|
60
71
|
GetMembershipOptionsResult,
|
|
61
72
|
InvitationDeliveryAuthStrategy,
|
|
73
|
+
InvitationDestination,
|
|
62
74
|
InvitationGroupOption,
|
|
63
75
|
InvitationRecipientStatus,
|
|
64
76
|
InviteDeliveryResult,
|
|
65
77
|
InviteDeliveryStatus,
|
|
66
78
|
InviteUserInput,
|
|
67
|
-
InviteUserOptions,
|
|
68
79
|
InviteUserResult,
|
|
69
80
|
ListInvitationsInput,
|
|
70
81
|
ListInvitationsResult,
|
|
@@ -76,7 +87,6 @@ import type {
|
|
|
76
87
|
ListServiceAccountsResult,
|
|
77
88
|
MemberSummary,
|
|
78
89
|
MembershipCapabilities,
|
|
79
|
-
Principal,
|
|
80
90
|
ReactivateMemberInput,
|
|
81
91
|
ReactivateMemberResult,
|
|
82
92
|
ResolvedAuthConfig,
|
|
@@ -86,10 +96,10 @@ import type {
|
|
|
86
96
|
RevokePersonalAccessTokenInput,
|
|
87
97
|
RevokeServiceAccountAccessTokenInput,
|
|
88
98
|
RevokeServiceAccountAccessTokenResult,
|
|
89
|
-
SecurityContext,
|
|
90
99
|
SixbAuthConfig,
|
|
91
100
|
SuspendMemberInput,
|
|
92
101
|
SuspendMemberResult,
|
|
102
|
+
UnauthenticatedAuthSession,
|
|
93
103
|
UpdateMemberGroupsInput,
|
|
94
104
|
UpdateMemberGroupsResult,
|
|
95
105
|
} from "./types"
|
|
@@ -100,7 +110,6 @@ import {
|
|
|
100
110
|
resolveAuthConfig,
|
|
101
111
|
resolveAuthSessionAudience,
|
|
102
112
|
resolveInvitationExpiresAt,
|
|
103
|
-
sanitizeReturnTo,
|
|
104
113
|
} from "./validation"
|
|
105
114
|
|
|
106
115
|
export {
|
|
@@ -117,6 +126,12 @@ export {
|
|
|
117
126
|
const SESSION_TOUCH_INTERVAL_MS = 60_000
|
|
118
127
|
const ACCESS_TOKEN_TOUCH_INTERVAL_MS = 60_000
|
|
119
128
|
|
|
129
|
+
// A native client's access token is sent on every request, so a leaked one must stop working soon.
|
|
130
|
+
const BEARER_ACCESS_TOKEN_TTL_MS = 15 * 60_000
|
|
131
|
+
// A replaced refresh token still rotates this long after its replacement, so a client whose refresh
|
|
132
|
+
// response was lost can retry. Later, it means a copy is in use and the session is revoked.
|
|
133
|
+
const REFRESH_REUSE_GRACE_MS = 60_000
|
|
134
|
+
|
|
120
135
|
export interface AuthRuntimeOptions {
|
|
121
136
|
readonly projectId: string
|
|
122
137
|
readonly storage: Storage
|
|
@@ -211,7 +226,7 @@ export class AuthRuntime {
|
|
|
211
226
|
): Promise<AuthSessionResult>
|
|
212
227
|
async getSession(
|
|
213
228
|
request: Request,
|
|
214
|
-
options: AuthSessionResolutionOptions & { readonly credentialSource: "
|
|
229
|
+
options: AuthSessionResolutionOptions & { readonly credentialSource: "any" }
|
|
215
230
|
): Promise<AuthRequestResult>
|
|
216
231
|
async getSession(
|
|
217
232
|
request: Request,
|
|
@@ -226,22 +241,20 @@ export class AuthRuntime {
|
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
const audience = resolveAuthSessionAudience(options.audience)
|
|
229
|
-
const credentialSource = options.credentialSource ?? "session"
|
|
230
244
|
|
|
231
|
-
if (credentialSource
|
|
232
|
-
const authorizationHeader = request.headers.get("authorization")
|
|
245
|
+
if (options.credentialSource === "any") {
|
|
233
246
|
const tokenValue = getBearerAccessTokenValue(request)
|
|
247
|
+
if (tokenValue?.startsWith(SESSION_ACCESS_TOKEN_PREFIX)) {
|
|
248
|
+
return this.resolveBearerSession(tokenValue)
|
|
249
|
+
}
|
|
234
250
|
if (tokenValue) {
|
|
235
251
|
return this.resolveAccessTokenSession(request, tokenValue)
|
|
236
252
|
}
|
|
237
253
|
|
|
238
|
-
|
|
254
|
+
// An Authorization header that is not a usable token never falls back to the cookie.
|
|
255
|
+
if (request.headers.get("authorization")) {
|
|
239
256
|
return { authenticated: false, reason: "invalid_access_token" }
|
|
240
257
|
}
|
|
241
|
-
|
|
242
|
-
if (credentialSource === "accessToken") {
|
|
243
|
-
return { authenticated: false, reason: "missing_access_token" }
|
|
244
|
-
}
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
return this.resolveCookieSession(request, audience, options.sessionActivity)
|
|
@@ -269,6 +282,7 @@ export class AuthRuntime {
|
|
|
269
282
|
const cached = this.sessionCache?.get({
|
|
270
283
|
sessionId: parts.sessionId,
|
|
271
284
|
tokenHash,
|
|
285
|
+
transport: "cookie",
|
|
272
286
|
audience,
|
|
273
287
|
nowMs,
|
|
274
288
|
})
|
|
@@ -311,48 +325,142 @@ export class AuthRuntime {
|
|
|
311
325
|
sessionRenewed = true
|
|
312
326
|
}
|
|
313
327
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
328
|
+
if (!sessionRenewed) {
|
|
329
|
+
await this.touchSessionLastSeen(storage, session, now)
|
|
330
|
+
}
|
|
331
|
+
const result = await this.authenticateSession(storage, session, tokenHash, now)
|
|
332
|
+
return result.authenticated && sessionRenewed ? { ...result, sessionRenewed: true } : result
|
|
333
|
+
}
|
|
318
334
|
|
|
319
|
-
|
|
320
|
-
|
|
335
|
+
private async resolveBearerSession(tokenValue: string): Promise<AuthSessionResult> {
|
|
336
|
+
const parts = parseBearerSessionToken(SESSION_ACCESS_TOKEN_PREFIX, tokenValue)
|
|
337
|
+
if (!parts) {
|
|
338
|
+
return { authenticated: false, reason: "invalid_access_token" }
|
|
321
339
|
}
|
|
322
340
|
|
|
323
|
-
|
|
324
|
-
|
|
341
|
+
const tokenHash = hashSessionSecret(parts.sessionSecret)
|
|
342
|
+
const now = new Date()
|
|
343
|
+
const cached = this.sessionCache?.get({
|
|
344
|
+
sessionId: parts.sessionId,
|
|
345
|
+
tokenHash,
|
|
346
|
+
transport: "bearer",
|
|
347
|
+
nowMs: now.getTime(),
|
|
348
|
+
})
|
|
349
|
+
if (cached) {
|
|
350
|
+
return cached
|
|
325
351
|
}
|
|
326
352
|
|
|
327
|
-
const
|
|
353
|
+
const storage = this.requireAuthStorage()
|
|
354
|
+
const session = await storage.sessions.findValidByAccessTokenHash({
|
|
328
355
|
projectId: this.projectId,
|
|
329
|
-
|
|
356
|
+
id: parts.sessionId,
|
|
357
|
+
tokenHash,
|
|
358
|
+
now,
|
|
330
359
|
})
|
|
360
|
+
if (!session) {
|
|
361
|
+
return { authenticated: false, reason: "invalid_access_token" }
|
|
362
|
+
}
|
|
331
363
|
|
|
332
|
-
|
|
333
|
-
|
|
364
|
+
await this.touchSessionLastSeen(storage, session, now)
|
|
365
|
+
return this.authenticateSession(storage, session, tokenHash, now)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Resolve a live session's user and groups, the same way for cookies and bearer tokens. */
|
|
369
|
+
private async authenticateSession(
|
|
370
|
+
storage: AuthStorage,
|
|
371
|
+
session: SessionRecord,
|
|
372
|
+
tokenHash: string,
|
|
373
|
+
now: Date
|
|
374
|
+
): Promise<AuthSessionResult> {
|
|
375
|
+
const member = await this.loadActiveUser(storage, session.userId)
|
|
376
|
+
if (!member.authenticated) {
|
|
377
|
+
return member
|
|
334
378
|
}
|
|
335
379
|
|
|
336
380
|
const result: AuthenticatedAuthSession = {
|
|
337
381
|
authenticated: true,
|
|
338
382
|
credentialSource: "session",
|
|
339
|
-
principal: { type: "user", id: user.id },
|
|
340
|
-
user,
|
|
383
|
+
principal: { type: "user", id: member.user.id },
|
|
384
|
+
user: member.user,
|
|
341
385
|
session,
|
|
342
|
-
groupIds:
|
|
386
|
+
groupIds: member.groupIds,
|
|
343
387
|
}
|
|
344
|
-
|
|
345
388
|
this.sessionCache?.set({
|
|
346
|
-
sessionId:
|
|
389
|
+
sessionId: session.id,
|
|
347
390
|
tokenHash,
|
|
348
|
-
audience,
|
|
391
|
+
audience: session.audience,
|
|
349
392
|
session: result,
|
|
350
|
-
nowMs,
|
|
393
|
+
nowMs: now.getTime(),
|
|
351
394
|
sessionExpiresAtMs: session.expiresAt.getTime(),
|
|
352
395
|
sessionAbsoluteExpiresAtMs: session.absoluteExpiresAt?.getTime(),
|
|
353
396
|
})
|
|
397
|
+
return result
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The session a native client starts when the user approves it, and its first tokens. The caller
|
|
402
|
+
* stores the session in the same transaction that consumes the approval.
|
|
403
|
+
*/
|
|
404
|
+
prepareBearerSession(input: {
|
|
405
|
+
readonly userId: string
|
|
406
|
+
readonly clientName: string
|
|
407
|
+
readonly now: Date
|
|
408
|
+
}): {
|
|
409
|
+
readonly session: CompleteDeviceAuthorizationInput["session"]
|
|
410
|
+
readonly tokens: BearerSessionTokens
|
|
411
|
+
} {
|
|
412
|
+
const sessionId = `ses_${randomUUID()}`
|
|
413
|
+
const tokens = createBearerSessionTokens(sessionId)
|
|
414
|
+
return {
|
|
415
|
+
tokens,
|
|
416
|
+
session: {
|
|
417
|
+
id: sessionId,
|
|
418
|
+
projectId: this.projectId,
|
|
419
|
+
userId: input.userId,
|
|
420
|
+
strategyId: "device",
|
|
421
|
+
// A native session belongs to no web app. The column needs a value; nothing reads it.
|
|
422
|
+
audience: DEFAULT_AUTH_SESSION_AUDIENCE,
|
|
423
|
+
tokenHash: tokens.tokenHash,
|
|
424
|
+
createdAt: input.now,
|
|
425
|
+
...this.createSessionDeadlines(input.now),
|
|
426
|
+
bearer: {
|
|
427
|
+
clientName: input.clientName,
|
|
428
|
+
accessExpiresAt: new Date(input.now.getTime() + BEARER_ACCESS_TOKEN_TTL_MS),
|
|
429
|
+
refreshTokenHash: tokens.refreshTokenHash,
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
}
|
|
354
434
|
|
|
355
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Trade a native client's refresh token for new tokens. Returns null when the token is not
|
|
437
|
+
* accepted; a replaced token presented after the grace window also revokes its session.
|
|
438
|
+
*/
|
|
439
|
+
async refreshSession(
|
|
440
|
+
refreshToken: string
|
|
441
|
+
): Promise<{ readonly tokens: BearerSessionTokens; readonly session: SessionRecord } | null> {
|
|
442
|
+
const parts = parseBearerSessionToken(SESSION_REFRESH_TOKEN_PREFIX, refreshToken)
|
|
443
|
+
if (!parts || !this.isEnabled()) {
|
|
444
|
+
return null
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const now = new Date()
|
|
448
|
+
const tokens = createBearerSessionTokens(parts.sessionId)
|
|
449
|
+
const result = await this.requireAuthStorage().sessions.rotateRefreshToken({
|
|
450
|
+
projectId: this.projectId,
|
|
451
|
+
id: parts.sessionId,
|
|
452
|
+
refreshTokenHash: hashSessionSecret(parts.sessionSecret),
|
|
453
|
+
now,
|
|
454
|
+
reuseGraceMs: REFRESH_REUSE_GRACE_MS,
|
|
455
|
+
next: {
|
|
456
|
+
tokenHash: tokens.tokenHash,
|
|
457
|
+
accessExpiresAt: new Date(now.getTime() + BEARER_ACCESS_TOKEN_TTL_MS),
|
|
458
|
+
refreshTokenHash: tokens.refreshTokenHash,
|
|
459
|
+
expiresAt: new Date(now.getTime() + this.config.session.idleTimeoutMs),
|
|
460
|
+
},
|
|
461
|
+
})
|
|
462
|
+
this.sessionCache?.invalidate(parts.sessionId)
|
|
463
|
+
return result.status === "rotated" ? { tokens, session: result.session } : null
|
|
356
464
|
}
|
|
357
465
|
|
|
358
466
|
private canRenewSession(session: SessionRecord, nowMs: number): boolean {
|
|
@@ -397,41 +505,25 @@ export class AuthRuntime {
|
|
|
397
505
|
|
|
398
506
|
await this.touchAccessTokenLastUsed(storage, accessToken, request, now)
|
|
399
507
|
|
|
400
|
-
if (accessToken.
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
if (!user) {
|
|
407
|
-
return { authenticated: false, reason: "missing_user" }
|
|
508
|
+
if (accessToken.subject.type === "user") {
|
|
509
|
+
const member = await this.loadActiveUser(storage, accessToken.subject.id)
|
|
510
|
+
if (!member.authenticated) {
|
|
511
|
+
return member
|
|
408
512
|
}
|
|
409
513
|
|
|
410
|
-
if (user.status === "suspended") {
|
|
411
|
-
return { authenticated: false, reason: "suspended_user" }
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const memberships = await storage.groupMemberships.listForUser({
|
|
415
|
-
projectId: this.projectId,
|
|
416
|
-
userId: user.id,
|
|
417
|
-
})
|
|
418
|
-
|
|
419
514
|
return {
|
|
420
515
|
authenticated: true,
|
|
421
516
|
credentialSource: "accessToken",
|
|
422
|
-
principal: { type: "user", id: user.id },
|
|
423
|
-
user,
|
|
517
|
+
principal: { type: "user", id: member.user.id },
|
|
518
|
+
user: member.user,
|
|
424
519
|
accessToken,
|
|
425
|
-
groupIds: constrainTokenGroupIds(
|
|
426
|
-
memberships.map((membership) => membership.groupId),
|
|
427
|
-
accessToken
|
|
428
|
-
),
|
|
520
|
+
groupIds: constrainTokenGroupIds(member.groupIds, accessToken),
|
|
429
521
|
}
|
|
430
522
|
}
|
|
431
523
|
|
|
432
524
|
const serviceAccount = await storage.serviceAccounts.getById({
|
|
433
525
|
projectId: this.projectId,
|
|
434
|
-
id: accessToken.
|
|
526
|
+
id: accessToken.subject.id,
|
|
435
527
|
})
|
|
436
528
|
|
|
437
529
|
if (!serviceAccount) {
|
|
@@ -460,12 +552,32 @@ export class AuthRuntime {
|
|
|
460
552
|
}
|
|
461
553
|
}
|
|
462
554
|
|
|
463
|
-
/**
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
555
|
+
/** Resolve a user and their current groups the same way for every credential. */
|
|
556
|
+
private async loadActiveUser(
|
|
557
|
+
storage: AuthStorage,
|
|
558
|
+
userId: string
|
|
559
|
+
): Promise<
|
|
560
|
+
| UnauthenticatedAuthSession
|
|
561
|
+
| { readonly authenticated: true; readonly user: UserRecord; readonly groupIds: string[] }
|
|
562
|
+
> {
|
|
563
|
+
const user = await storage.users.getById({ projectId: this.projectId, id: userId })
|
|
564
|
+
if (!user) {
|
|
565
|
+
return { authenticated: false, reason: "missing_user" }
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (user.status === "suspended") {
|
|
569
|
+
return { authenticated: false, reason: "suspended_user" }
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const memberships = await storage.groupMemberships.listForUser({
|
|
573
|
+
projectId: this.projectId,
|
|
574
|
+
userId,
|
|
575
|
+
})
|
|
576
|
+
return {
|
|
577
|
+
authenticated: true,
|
|
578
|
+
user,
|
|
579
|
+
groupIds: memberships.map((membership) => membership.groupId),
|
|
580
|
+
}
|
|
469
581
|
}
|
|
470
582
|
|
|
471
583
|
// Best-effort refresh of `lastSeenAt` for the active-sessions view. Throttled,
|
|
@@ -503,109 +615,34 @@ export class AuthRuntime {
|
|
|
503
615
|
id: accessToken.id,
|
|
504
616
|
lastUsedAt: now,
|
|
505
617
|
userAgent: request.headers.get("user-agent")?.trim() || undefined,
|
|
506
|
-
ipAddress:
|
|
618
|
+
ipAddress: getRequestClientAddress(request),
|
|
507
619
|
})
|
|
508
620
|
} catch {
|
|
509
621
|
// Touch is non-critical; ignore failures so auth still succeeds.
|
|
510
622
|
}
|
|
511
623
|
}
|
|
512
624
|
|
|
513
|
-
async requirePrincipal(
|
|
514
|
-
request: Request,
|
|
515
|
-
options: AuthSessionResolutionOptions = {}
|
|
516
|
-
): Promise<Principal> {
|
|
517
|
-
const session = await this.getSession(request, options)
|
|
518
|
-
if (!session.authenticated) {
|
|
519
|
-
throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return session.principal
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
async requireUser(
|
|
526
|
-
request: Request,
|
|
527
|
-
options: AuthSessionResolutionOptions = {}
|
|
528
|
-
): Promise<AuthenticatedAuthSession> {
|
|
529
|
-
const session = await this.getSession(request, { ...options, credentialSource: "session" })
|
|
530
|
-
if (!session.authenticated) {
|
|
531
|
-
throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
return session
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
async requireUserRequest(
|
|
538
|
-
request: Request,
|
|
539
|
-
options: AuthSessionResolutionOptions = {}
|
|
540
|
-
): Promise<AuthenticatedUserRequestSession> {
|
|
541
|
-
const session = await this.getSession(request, { ...options, credentialSource: "any" })
|
|
542
|
-
if (!session.authenticated) {
|
|
543
|
-
throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if (!isAuthenticatedUserRequestSession(session)) {
|
|
547
|
-
throw new AuthRuntimeError("authorization_denied", "[Sixb] User authentication is required.")
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return session
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
async createSecurityContext(
|
|
554
|
-
request: Request,
|
|
555
|
-
options: AuthSessionResolutionOptions = {}
|
|
556
|
-
): Promise<SecurityContext> {
|
|
557
|
-
const session = await this.requireUser(request, options)
|
|
558
|
-
return {
|
|
559
|
-
principal: session.principal,
|
|
560
|
-
sessionId: session.session.id,
|
|
561
|
-
projectId: this.projectId,
|
|
562
|
-
correlationId: resolveCorrelationId(request),
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Resolve the authenticated principal's authorization context for a request.
|
|
568
|
-
*
|
|
569
|
-
* Grants resolve eagerly (`groups -> roles -> grants`, subtype-expanded), so the returned
|
|
570
|
-
* context supports synchronous checks when Core binds a request execution SDK.
|
|
571
|
-
*/
|
|
572
|
-
async createAuthorizationContext(
|
|
573
|
-
request: Request,
|
|
574
|
-
options: AuthSessionResolutionOptions = {}
|
|
575
|
-
): Promise<AuthorizationContext> {
|
|
576
|
-
const session = await this.getSession(request, options)
|
|
577
|
-
if (!session.authenticated) {
|
|
578
|
-
throw new AuthRuntimeError("authentication_required", "[Sixb] Authentication is required.")
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return this.contextFromSession(session)
|
|
582
|
-
}
|
|
583
|
-
|
|
584
625
|
/**
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
* the request twice.
|
|
626
|
+
* Resolve a caller's authorization context. Grants resolve eagerly
|
|
627
|
+
* (`groups -> roles -> grants`, subtype-expanded), so checks against it are synchronous.
|
|
588
628
|
*/
|
|
589
629
|
contextFromSession(session: AuthenticatedRequestAuthSession): AuthorizationContext {
|
|
590
630
|
return resolveAuthorizationContext({
|
|
591
631
|
principal: session.principal,
|
|
592
|
-
sessionId: session
|
|
632
|
+
sessionId: callerSessionId(session),
|
|
593
633
|
groupIds: session.groupIds,
|
|
594
634
|
roles: this.security.listResolvedRoles(),
|
|
595
635
|
})
|
|
596
636
|
}
|
|
597
637
|
|
|
598
638
|
async listPersonalAccessTokens(
|
|
599
|
-
|
|
600
|
-
options: AuthSessionResolutionOptions = {}
|
|
639
|
+
caller: AuthenticatedUserRequestSession
|
|
601
640
|
): Promise<ListPersonalAccessTokensResult> {
|
|
602
|
-
const session = await this.requireUserRequest(request, options)
|
|
603
641
|
const storage = this.requireAuthStorage()
|
|
604
642
|
const result = await storage.accessTokens.list({
|
|
605
643
|
projectId: this.projectId,
|
|
606
644
|
kind: "personal",
|
|
607
|
-
|
|
608
|
-
subjectId: session.user.id,
|
|
645
|
+
subject: { type: "user", id: caller.user.id },
|
|
609
646
|
includeRevoked: true,
|
|
610
647
|
order: "desc",
|
|
611
648
|
limit: 100,
|
|
@@ -615,14 +652,12 @@ export class AuthRuntime {
|
|
|
615
652
|
}
|
|
616
653
|
|
|
617
654
|
async createPersonalAccessToken(
|
|
618
|
-
|
|
619
|
-
input: CreatePersonalAccessTokenInput
|
|
620
|
-
options: AuthSessionResolutionOptions = {}
|
|
655
|
+
caller: AuthenticatedUserRequestSession,
|
|
656
|
+
input: CreatePersonalAccessTokenInput
|
|
621
657
|
): Promise<CreateAccessTokenResult> {
|
|
622
|
-
const session = await this.requireUserRequest(request, options)
|
|
623
658
|
const storage = this.requireAuthStorage()
|
|
624
659
|
// A personal token can only carry groups the caller currently belongs to.
|
|
625
|
-
const groupIds = constrainRequestedGroupIds(input.groupIds,
|
|
660
|
+
const groupIds = constrainRequestedGroupIds(input.groupIds, caller.groupIds, {
|
|
626
661
|
subject: "personal access token",
|
|
627
662
|
})
|
|
628
663
|
const credential = createAccessTokenCredential("personal")
|
|
@@ -631,12 +666,11 @@ export class AuthRuntime {
|
|
|
631
666
|
projectId: this.projectId,
|
|
632
667
|
name: input.name,
|
|
633
668
|
kind: "personal",
|
|
634
|
-
|
|
635
|
-
subjectId: session.user.id,
|
|
669
|
+
subject: { type: "user", id: caller.user.id },
|
|
636
670
|
tokenHash: credential.tokenHash,
|
|
637
671
|
groupIds,
|
|
638
|
-
createdByPrincipal:
|
|
639
|
-
createdBySessionId:
|
|
672
|
+
createdByPrincipal: caller.principal,
|
|
673
|
+
createdBySessionId: callerSessionId(caller),
|
|
640
674
|
createdAt: new Date(),
|
|
641
675
|
expiresAt: input.expiresAt,
|
|
642
676
|
})
|
|
@@ -645,11 +679,9 @@ export class AuthRuntime {
|
|
|
645
679
|
}
|
|
646
680
|
|
|
647
681
|
async revokePersonalAccessToken(
|
|
648
|
-
|
|
649
|
-
input: RevokePersonalAccessTokenInput
|
|
650
|
-
options: AuthSessionResolutionOptions = {}
|
|
682
|
+
caller: AuthenticatedUserRequestSession,
|
|
683
|
+
input: RevokePersonalAccessTokenInput
|
|
651
684
|
): Promise<RevokeAccessTokenResult> {
|
|
652
|
-
const session = await this.requireUserRequest(request, options)
|
|
653
685
|
const storage = this.requireAuthStorage()
|
|
654
686
|
const token = await storage.accessTokens.getById({
|
|
655
687
|
projectId: this.projectId,
|
|
@@ -660,8 +692,8 @@ export class AuthRuntime {
|
|
|
660
692
|
if (
|
|
661
693
|
!token ||
|
|
662
694
|
token.kind !== "personal" ||
|
|
663
|
-
token.
|
|
664
|
-
token.
|
|
695
|
+
token.subject.type !== "user" ||
|
|
696
|
+
token.subject.id !== caller.user.id
|
|
665
697
|
) {
|
|
666
698
|
throw missingAccessTokenError(input.tokenId, this.projectId)
|
|
667
699
|
}
|
|
@@ -676,10 +708,8 @@ export class AuthRuntime {
|
|
|
676
708
|
}
|
|
677
709
|
|
|
678
710
|
async listServiceAccounts(
|
|
679
|
-
|
|
680
|
-
options: AuthSessionResolutionOptions = {}
|
|
711
|
+
caller: AuthenticatedUserRequestSession
|
|
681
712
|
): Promise<ListServiceAccountsResult> {
|
|
682
|
-
const session = await this.requireUserRequest(request, options)
|
|
683
713
|
const storage = this.requireAuthStorage()
|
|
684
714
|
const result = await storage.serviceAccounts.list({
|
|
685
715
|
projectId: this.projectId,
|
|
@@ -697,21 +727,19 @@ export class AuthRuntime {
|
|
|
697
727
|
// listing never leaks the groups of more-privileged accounts.
|
|
698
728
|
return {
|
|
699
729
|
serviceAccounts: withGroups.filter(({ groupIds }) =>
|
|
700
|
-
callerCanManageServiceAccount(
|
|
730
|
+
callerCanManageServiceAccount(caller.groupIds, groupIds)
|
|
701
731
|
),
|
|
702
732
|
}
|
|
703
733
|
}
|
|
704
734
|
|
|
705
735
|
async createServiceAccount(
|
|
706
|
-
|
|
707
|
-
input: CreateServiceAccountInput
|
|
708
|
-
options: AuthSessionResolutionOptions = {}
|
|
736
|
+
caller: AuthenticatedUserRequestSession,
|
|
737
|
+
input: CreateServiceAccountInput
|
|
709
738
|
): Promise<CreateServiceAccountResult> {
|
|
710
|
-
const session = await this.requireUserRequest(request, options)
|
|
711
739
|
const storage = this.requireAuthStorage()
|
|
712
740
|
// A caller can only place a service account in groups it itself belongs to.
|
|
713
741
|
const groupIds =
|
|
714
|
-
constrainRequestedGroupIds(input.groupIds,
|
|
742
|
+
constrainRequestedGroupIds(input.groupIds, caller.groupIds, {
|
|
715
743
|
subject: "service account",
|
|
716
744
|
}) ?? []
|
|
717
745
|
const now = new Date()
|
|
@@ -720,8 +748,8 @@ export class AuthRuntime {
|
|
|
720
748
|
projectId: this.projectId,
|
|
721
749
|
name: input.name,
|
|
722
750
|
description: input.description,
|
|
723
|
-
createdByPrincipal:
|
|
724
|
-
createdBySessionId:
|
|
751
|
+
createdByPrincipal: caller.principal,
|
|
752
|
+
createdBySessionId: callerSessionId(caller),
|
|
725
753
|
createdAt: now,
|
|
726
754
|
updatedAt: now,
|
|
727
755
|
})
|
|
@@ -742,15 +770,13 @@ export class AuthRuntime {
|
|
|
742
770
|
}
|
|
743
771
|
|
|
744
772
|
async disableServiceAccount(
|
|
745
|
-
|
|
746
|
-
input: DisableServiceAccountInput
|
|
747
|
-
options: AuthSessionResolutionOptions = {}
|
|
773
|
+
caller: AuthenticatedUserRequestSession,
|
|
774
|
+
input: DisableServiceAccountInput
|
|
748
775
|
): Promise<DisableServiceAccountResult> {
|
|
749
|
-
const session = await this.requireUserRequest(request, options)
|
|
750
776
|
const storage = this.requireAuthStorage()
|
|
751
777
|
const { serviceAccount, groupIds } = await this.requireManageableServiceAccount(
|
|
752
778
|
storage,
|
|
753
|
-
|
|
779
|
+
caller.groupIds,
|
|
754
780
|
input.serviceAccountId
|
|
755
781
|
)
|
|
756
782
|
const updated = await storage.serviceAccounts.update({
|
|
@@ -764,22 +790,19 @@ export class AuthRuntime {
|
|
|
764
790
|
}
|
|
765
791
|
|
|
766
792
|
async listServiceAccountAccessTokens(
|
|
767
|
-
|
|
768
|
-
input: ListServiceAccountAccessTokensInput
|
|
769
|
-
options: AuthSessionResolutionOptions = {}
|
|
793
|
+
caller: AuthenticatedUserRequestSession,
|
|
794
|
+
input: ListServiceAccountAccessTokensInput
|
|
770
795
|
): Promise<ListServiceAccountAccessTokensResult> {
|
|
771
|
-
const session = await this.requireUserRequest(request, options)
|
|
772
796
|
const storage = this.requireAuthStorage()
|
|
773
797
|
const { serviceAccount } = await this.requireManageableServiceAccount(
|
|
774
798
|
storage,
|
|
775
|
-
|
|
799
|
+
caller.groupIds,
|
|
776
800
|
input.serviceAccountId
|
|
777
801
|
)
|
|
778
802
|
const result = await storage.accessTokens.list({
|
|
779
803
|
projectId: this.projectId,
|
|
780
804
|
kind: "serviceAccount",
|
|
781
|
-
|
|
782
|
-
subjectId: serviceAccount.id,
|
|
805
|
+
subject: { type: "serviceAccount", id: serviceAccount.id },
|
|
783
806
|
includeRevoked: true,
|
|
784
807
|
order: "desc",
|
|
785
808
|
limit: 100,
|
|
@@ -789,14 +812,12 @@ export class AuthRuntime {
|
|
|
789
812
|
}
|
|
790
813
|
|
|
791
814
|
async createServiceAccountAccessToken(
|
|
792
|
-
|
|
793
|
-
input: CreateServiceAccountAccessTokenInput
|
|
794
|
-
options: AuthSessionResolutionOptions = {}
|
|
815
|
+
caller: AuthenticatedUserRequestSession,
|
|
816
|
+
input: CreateServiceAccountAccessTokenInput
|
|
795
817
|
): Promise<CreateServiceAccountAccessTokenResult> {
|
|
796
|
-
const session = await this.requireUserRequest(request, options)
|
|
797
818
|
const storage = this.requireAuthStorage()
|
|
798
819
|
const { serviceAccount, groupIds: serviceAccountGroupIds } =
|
|
799
|
-
await this.requireManageableServiceAccount(storage,
|
|
820
|
+
await this.requireManageableServiceAccount(storage, caller.groupIds, input.serviceAccountId)
|
|
800
821
|
|
|
801
822
|
if (serviceAccount.status === "suspended") {
|
|
802
823
|
throw new AuthStorageError(
|
|
@@ -817,12 +838,11 @@ export class AuthRuntime {
|
|
|
817
838
|
projectId: this.projectId,
|
|
818
839
|
name: input.name,
|
|
819
840
|
kind: "serviceAccount",
|
|
820
|
-
|
|
821
|
-
subjectId: serviceAccount.id,
|
|
841
|
+
subject: { type: "serviceAccount", id: serviceAccount.id },
|
|
822
842
|
tokenHash: credential.tokenHash,
|
|
823
843
|
groupIds,
|
|
824
|
-
createdByPrincipal:
|
|
825
|
-
createdBySessionId:
|
|
844
|
+
createdByPrincipal: caller.principal,
|
|
845
|
+
createdBySessionId: callerSessionId(caller),
|
|
826
846
|
createdAt: new Date(),
|
|
827
847
|
expiresAt: input.expiresAt,
|
|
828
848
|
})
|
|
@@ -831,15 +851,13 @@ export class AuthRuntime {
|
|
|
831
851
|
}
|
|
832
852
|
|
|
833
853
|
async revokeServiceAccountAccessToken(
|
|
834
|
-
|
|
835
|
-
input: RevokeServiceAccountAccessTokenInput
|
|
836
|
-
options: AuthSessionResolutionOptions = {}
|
|
854
|
+
caller: AuthenticatedUserRequestSession,
|
|
855
|
+
input: RevokeServiceAccountAccessTokenInput
|
|
837
856
|
): Promise<RevokeServiceAccountAccessTokenResult> {
|
|
838
|
-
const session = await this.requireUserRequest(request, options)
|
|
839
857
|
const storage = this.requireAuthStorage()
|
|
840
858
|
const { serviceAccount } = await this.requireManageableServiceAccount(
|
|
841
859
|
storage,
|
|
842
|
-
|
|
860
|
+
caller.groupIds,
|
|
843
861
|
input.serviceAccountId
|
|
844
862
|
)
|
|
845
863
|
const token = await storage.accessTokens.getById({
|
|
@@ -850,8 +868,8 @@ export class AuthRuntime {
|
|
|
850
868
|
if (
|
|
851
869
|
!token ||
|
|
852
870
|
token.kind !== "serviceAccount" ||
|
|
853
|
-
token.
|
|
854
|
-
token.
|
|
871
|
+
token.subject.type !== "serviceAccount" ||
|
|
872
|
+
token.subject.id !== serviceAccount.id
|
|
855
873
|
) {
|
|
856
874
|
throw missingAccessTokenError(input.tokenId, this.projectId)
|
|
857
875
|
}
|
|
@@ -913,11 +931,9 @@ export class AuthRuntime {
|
|
|
913
931
|
}
|
|
914
932
|
|
|
915
933
|
async getInvitationOptions(
|
|
916
|
-
|
|
917
|
-
options: AuthSessionResolutionOptions = {}
|
|
934
|
+
caller: AuthenticatedUserRequestSession
|
|
918
935
|
): Promise<GetInvitationOptionsResult> {
|
|
919
|
-
const
|
|
920
|
-
const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
|
|
936
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
921
937
|
const inviteScope = scope.operations.invite
|
|
922
938
|
const groups = this.scopedGroupOptions(inviteScope.groupIds)
|
|
923
939
|
const hasInviteMembershipPolicy = inviteScope.policyIds.length > 0
|
|
@@ -950,11 +966,9 @@ export class AuthRuntime {
|
|
|
950
966
|
}
|
|
951
967
|
|
|
952
968
|
async getMembershipOptions(
|
|
953
|
-
|
|
954
|
-
options: AuthSessionResolutionOptions = {}
|
|
969
|
+
caller: AuthenticatedUserRequestSession
|
|
955
970
|
): Promise<GetMembershipOptionsResult> {
|
|
956
|
-
const
|
|
957
|
-
const scope = this.resolveMembershipPolicyScopeForUser(session.groupIds)
|
|
971
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
958
972
|
|
|
959
973
|
return {
|
|
960
974
|
// The member-admin edit-groups dialog assigns from the `assignGroups` scope.
|
|
@@ -964,13 +978,11 @@ export class AuthRuntime {
|
|
|
964
978
|
}
|
|
965
979
|
|
|
966
980
|
async listMembers(
|
|
967
|
-
|
|
968
|
-
input: ListMembersInput = {}
|
|
969
|
-
options: AuthSessionResolutionOptions = {}
|
|
981
|
+
caller: AuthenticatedUserRequestSession,
|
|
982
|
+
input: ListMembersInput = {}
|
|
970
983
|
): Promise<ListMembersResult> {
|
|
971
|
-
const session = await this.requireUser(request, options)
|
|
972
984
|
const storage = this.requireAuthStorage()
|
|
973
|
-
const scope = this.resolveMembershipPolicyScopeForUser(
|
|
985
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
974
986
|
const { limit, offset } = normalizePagination(input)
|
|
975
987
|
|
|
976
988
|
// First slice loads all users, attaches groups, then filters and paginates in
|
|
@@ -980,9 +992,9 @@ export class AuthRuntime {
|
|
|
980
992
|
|
|
981
993
|
const members: MemberSummary[] = []
|
|
982
994
|
for (const user of result.users) {
|
|
983
|
-
const groupIds = (
|
|
995
|
+
const { groupIds, managedGroupIds } = memberGroups(
|
|
984
996
|
await storage.groupMemberships.listForUser({ projectId: this.projectId, userId: user.id })
|
|
985
|
-
)
|
|
997
|
+
)
|
|
986
998
|
|
|
987
999
|
// Visibility is scope-based: a member is listed when the caller can assign
|
|
988
1000
|
// groups or suspend over the member's current groups. This keeps out-of-scope
|
|
@@ -993,10 +1005,11 @@ export class AuthRuntime {
|
|
|
993
1005
|
continue
|
|
994
1006
|
}
|
|
995
1007
|
|
|
996
|
-
const isSelf = user.id ===
|
|
1008
|
+
const isSelf = user.id === caller.user.id
|
|
997
1009
|
members.push({
|
|
998
1010
|
user,
|
|
999
1011
|
groupIds,
|
|
1012
|
+
managedGroupIds,
|
|
1000
1013
|
capabilities: {
|
|
1001
1014
|
assignGroups: canAssignGroups,
|
|
1002
1015
|
// A caller cannot suspend themselves; reactivation only applies to a
|
|
@@ -1012,24 +1025,21 @@ export class AuthRuntime {
|
|
|
1012
1025
|
}
|
|
1013
1026
|
|
|
1014
1027
|
async updateMemberGroups(
|
|
1015
|
-
|
|
1016
|
-
input: UpdateMemberGroupsInput
|
|
1017
|
-
options: AuthSessionResolutionOptions = {}
|
|
1028
|
+
caller: AuthenticatedUserRequestSession,
|
|
1029
|
+
input: UpdateMemberGroupsInput
|
|
1018
1030
|
): Promise<UpdateMemberGroupsResult> {
|
|
1019
|
-
const session = await this.requireUser(request, options)
|
|
1020
1031
|
const storage = this.requireAuthStorage()
|
|
1021
|
-
const scope = this.resolveMembershipPolicyScopeForUser(
|
|
1032
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
1022
1033
|
const userId = assertNonEmpty(input.userId, "User id")
|
|
1023
1034
|
|
|
1024
1035
|
// The target must exist and every group it currently holds must be assignable
|
|
1025
1036
|
// by the caller. Missing and out-of-scope targets raise the same error so a
|
|
1026
1037
|
// caller cannot probe which users exist.
|
|
1027
|
-
const {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
)
|
|
1038
|
+
const {
|
|
1039
|
+
user,
|
|
1040
|
+
groupIds: currentGroupIds,
|
|
1041
|
+
managedGroupIds: currentManagedGroupIds,
|
|
1042
|
+
} = await this.requireManageableMember(storage, scope, "assignGroups", userId)
|
|
1033
1043
|
|
|
1034
1044
|
const requestedGroupIds = this.resolveMemberGroupIds(input.groupIds)
|
|
1035
1045
|
// Every requested group must fall within the caller's assign scope.
|
|
@@ -1046,9 +1056,18 @@ export class AuthRuntime {
|
|
|
1046
1056
|
const additions = requestedGroupIds.filter((groupId) => !current.has(groupId))
|
|
1047
1057
|
const removals = currentGroupIds.filter((groupId) => !requested.has(groupId))
|
|
1048
1058
|
|
|
1059
|
+
// The identity provider would restore these at the user's next sign-in.
|
|
1060
|
+
const managedRemovals = removals.filter((groupId) => currentManagedGroupIds.includes(groupId))
|
|
1061
|
+
if (managedRemovals.length > 0) {
|
|
1062
|
+
throw new AuthRuntimeError(
|
|
1063
|
+
"invalid_auth_input",
|
|
1064
|
+
`[Sixb] Group(s) ${managedRemovals.join(", ")} are managed by the identity provider. Change them there; they sync at the user's next sign-in.`
|
|
1065
|
+
)
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1049
1068
|
// Self-protection: a caller may add in-scope groups to themselves but may not
|
|
1050
1069
|
// remove any of their own current groups, so they cannot lock themselves out.
|
|
1051
|
-
if (user.id ===
|
|
1070
|
+
if (user.id === caller.user.id && removals.length > 0) {
|
|
1052
1071
|
throw new AuthRuntimeError(
|
|
1053
1072
|
"authorization_denied",
|
|
1054
1073
|
"[Sixb] The current user cannot remove their own groups."
|
|
@@ -1075,27 +1094,30 @@ export class AuthRuntime {
|
|
|
1075
1094
|
|
|
1076
1095
|
// The user's cached session carries its old groups; drop it so the next
|
|
1077
1096
|
// request resolves the updated membership.
|
|
1078
|
-
this.
|
|
1097
|
+
this.sessionCache?.invalidateUser(user.id)
|
|
1079
1098
|
|
|
1080
|
-
const groupIds = (
|
|
1099
|
+
const { groupIds, managedGroupIds } = memberGroups(
|
|
1081
1100
|
await storage.groupMemberships.listForUser({ projectId: this.projectId, userId: user.id })
|
|
1082
|
-
)
|
|
1101
|
+
)
|
|
1083
1102
|
|
|
1084
|
-
return { user, groupIds }
|
|
1103
|
+
return { user, groupIds, managedGroupIds }
|
|
1085
1104
|
}
|
|
1086
1105
|
|
|
1087
1106
|
async suspendMember(
|
|
1088
|
-
|
|
1089
|
-
input: SuspendMemberInput
|
|
1090
|
-
options: AuthSessionResolutionOptions = {}
|
|
1107
|
+
caller: AuthenticatedUserRequestSession,
|
|
1108
|
+
input: SuspendMemberInput
|
|
1091
1109
|
): Promise<SuspendMemberResult> {
|
|
1092
|
-
const session = await this.requireUser(request, options)
|
|
1093
1110
|
const storage = this.requireAuthStorage()
|
|
1094
|
-
const scope = this.resolveMembershipPolicyScopeForUser(
|
|
1111
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
1095
1112
|
const userId = assertNonEmpty(input.userId, "User id")
|
|
1096
|
-
const { user, groupIds } = await this.requireManageableMember(
|
|
1113
|
+
const { user, groupIds, managedGroupIds } = await this.requireManageableMember(
|
|
1114
|
+
storage,
|
|
1115
|
+
scope,
|
|
1116
|
+
"suspend",
|
|
1117
|
+
userId
|
|
1118
|
+
)
|
|
1097
1119
|
|
|
1098
|
-
if (user.id ===
|
|
1120
|
+
if (user.id === caller.user.id) {
|
|
1099
1121
|
throw new AuthRuntimeError(
|
|
1100
1122
|
"authorization_denied",
|
|
1101
1123
|
"[Sixb] The current user cannot suspend themselves."
|
|
@@ -1109,21 +1131,24 @@ export class AuthRuntime {
|
|
|
1109
1131
|
})
|
|
1110
1132
|
// Storage revoked the user's sessions; drop cached copies so the suspended
|
|
1111
1133
|
// user stops authenticating immediately.
|
|
1112
|
-
this.
|
|
1134
|
+
this.sessionCache?.invalidateUser(user.id)
|
|
1113
1135
|
|
|
1114
|
-
return { user: suspended, groupIds }
|
|
1136
|
+
return { user: suspended, groupIds, managedGroupIds }
|
|
1115
1137
|
}
|
|
1116
1138
|
|
|
1117
1139
|
async reactivateMember(
|
|
1118
|
-
|
|
1119
|
-
input: ReactivateMemberInput
|
|
1120
|
-
options: AuthSessionResolutionOptions = {}
|
|
1140
|
+
caller: AuthenticatedUserRequestSession,
|
|
1141
|
+
input: ReactivateMemberInput
|
|
1121
1142
|
): Promise<ReactivateMemberResult> {
|
|
1122
|
-
const session = await this.requireUser(request, options)
|
|
1123
1143
|
const storage = this.requireAuthStorage()
|
|
1124
|
-
const scope = this.resolveMembershipPolicyScopeForUser(
|
|
1144
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
1125
1145
|
const userId = assertNonEmpty(input.userId, "User id")
|
|
1126
|
-
const { user, groupIds } = await this.requireManageableMember(
|
|
1146
|
+
const { user, groupIds, managedGroupIds } = await this.requireManageableMember(
|
|
1147
|
+
storage,
|
|
1148
|
+
scope,
|
|
1149
|
+
"suspend",
|
|
1150
|
+
userId
|
|
1151
|
+
)
|
|
1127
1152
|
|
|
1128
1153
|
// Reactivation restores access but not sessions; the user signs in again.
|
|
1129
1154
|
const reactivated = await storage.users.updateStatus({
|
|
@@ -1133,12 +1158,52 @@ export class AuthRuntime {
|
|
|
1133
1158
|
updatedAt: new Date(),
|
|
1134
1159
|
})
|
|
1135
1160
|
|
|
1136
|
-
return { user: reactivated, groupIds }
|
|
1161
|
+
return { user: reactivated, groupIds, managedGroupIds }
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/** The caller's active sessions across every audience, most recently active first. */
|
|
1165
|
+
async listSessions(caller: AuthenticatedUserRequestSession): Promise<readonly SessionRecord[]> {
|
|
1166
|
+
return this.requireAuthStorage().sessions.listActiveByUserId({
|
|
1167
|
+
projectId: this.projectId,
|
|
1168
|
+
userId: caller.user.id,
|
|
1169
|
+
now: new Date(),
|
|
1170
|
+
})
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* End one of the caller's own sessions. Returns false for a missing session or one that belongs
|
|
1175
|
+
* to another user, so a caller cannot probe other accounts.
|
|
1176
|
+
*/
|
|
1177
|
+
async revokeSession(
|
|
1178
|
+
caller: AuthenticatedUserRequestSession,
|
|
1179
|
+
sessionId: string
|
|
1180
|
+
): Promise<boolean> {
|
|
1181
|
+
const storage = this.requireAuthStorage()
|
|
1182
|
+
const session = await storage.sessions.getById({ projectId: this.projectId, id: sessionId })
|
|
1183
|
+
if (!session || session.userId !== caller.user.id) {
|
|
1184
|
+
return false
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
await storage.sessions.revoke({
|
|
1188
|
+
projectId: this.projectId,
|
|
1189
|
+
id: sessionId,
|
|
1190
|
+
revokedAt: new Date(),
|
|
1191
|
+
})
|
|
1192
|
+
this.sessionCache?.invalidate(sessionId)
|
|
1193
|
+
return true
|
|
1137
1194
|
}
|
|
1138
1195
|
|
|
1139
|
-
/**
|
|
1140
|
-
|
|
1141
|
-
this.
|
|
1196
|
+
/** End every active session of the caller, across audiences. Returns how many ended. */
|
|
1197
|
+
async revokeAllSessions(caller: AuthenticatedUserRequestSession): Promise<number> {
|
|
1198
|
+
const revoked = await this.requireAuthStorage().sessions.revokeActiveForUser({
|
|
1199
|
+
projectId: this.projectId,
|
|
1200
|
+
userId: caller.user.id,
|
|
1201
|
+
revokedAt: new Date(),
|
|
1202
|
+
})
|
|
1203
|
+
for (const session of revoked) {
|
|
1204
|
+
this.sessionCache?.invalidate(session.id)
|
|
1205
|
+
}
|
|
1206
|
+
return revoked.length
|
|
1142
1207
|
}
|
|
1143
1208
|
|
|
1144
1209
|
private scopedGroupOptions(groupIds: ReadonlySet<string>): InvitationGroupOption[] {
|
|
@@ -1165,13 +1230,15 @@ export class AuthRuntime {
|
|
|
1165
1230
|
scope: MembershipPolicyScope,
|
|
1166
1231
|
operation: MembershipOperation,
|
|
1167
1232
|
userId: string
|
|
1168
|
-
): Promise<{
|
|
1233
|
+
): Promise<{
|
|
1234
|
+
readonly user: UserRecord
|
|
1235
|
+
readonly groupIds: readonly string[]
|
|
1236
|
+
readonly managedGroupIds: readonly string[]
|
|
1237
|
+
}> {
|
|
1169
1238
|
const user = await storage.users.getById({ projectId: this.projectId, id: userId })
|
|
1170
|
-
const groupIds =
|
|
1171
|
-
?
|
|
1172
|
-
|
|
1173
|
-
)
|
|
1174
|
-
: []
|
|
1239
|
+
const { groupIds, managedGroupIds } = memberGroups(
|
|
1240
|
+
user ? await storage.groupMemberships.listForUser({ projectId: this.projectId, userId }) : []
|
|
1241
|
+
)
|
|
1175
1242
|
|
|
1176
1243
|
if (!user || !canPerformMembershipOperation(scope, operation, groupIds)) {
|
|
1177
1244
|
throw new AuthStorageError(
|
|
@@ -1180,7 +1247,7 @@ export class AuthRuntime {
|
|
|
1180
1247
|
)
|
|
1181
1248
|
}
|
|
1182
1249
|
|
|
1183
|
-
return { user, groupIds }
|
|
1250
|
+
return { user, groupIds, managedGroupIds }
|
|
1184
1251
|
}
|
|
1185
1252
|
|
|
1186
1253
|
private resolveMemberGroupIds(input: readonly string[]): readonly string[] {
|
|
@@ -1197,15 +1264,14 @@ export class AuthRuntime {
|
|
|
1197
1264
|
}
|
|
1198
1265
|
|
|
1199
1266
|
async invite(
|
|
1200
|
-
|
|
1267
|
+
caller: AuthenticatedUserRequestSession,
|
|
1201
1268
|
input: InviteUserInput,
|
|
1202
|
-
|
|
1269
|
+
destination: InvitationDestination
|
|
1203
1270
|
): Promise<InviteUserResult> {
|
|
1204
|
-
const session = await this.requireUser(request, options)
|
|
1205
1271
|
const authStorage = this.requireAuthStorage()
|
|
1206
1272
|
const now = new Date()
|
|
1207
1273
|
const groupIds = this.resolveInviteGroupIds(input)
|
|
1208
|
-
this.assertCanManageInvitationGroups(
|
|
1274
|
+
this.assertCanManageInvitationGroups(caller.groupIds, groupIds)
|
|
1209
1275
|
|
|
1210
1276
|
const strategy = this.getStrategy()
|
|
1211
1277
|
if (!isInvitationDeliveryAuthStrategy(strategy)) {
|
|
@@ -1216,18 +1282,15 @@ export class AuthRuntime {
|
|
|
1216
1282
|
}
|
|
1217
1283
|
|
|
1218
1284
|
await this.assertCanInviteRecipient(strategy, authStorage, input.email, now)
|
|
1219
|
-
|
|
1220
|
-
options.delivery?.audience ?? options.audience
|
|
1221
|
-
)
|
|
1222
|
-
this.assertInvitationApplicationAccess(groupIds, deliveryAudience)
|
|
1285
|
+
this.assertInvitationApplicationAccess(groupIds, destination.audience)
|
|
1223
1286
|
|
|
1224
1287
|
const invitation = await authStorage.invitations.createOrUpdateActive({
|
|
1225
1288
|
id: `inv_${randomUUID()}`,
|
|
1226
1289
|
projectId: this.projectId,
|
|
1227
1290
|
email: input.email,
|
|
1228
1291
|
groupIds,
|
|
1229
|
-
createdByPrincipal:
|
|
1230
|
-
createdBySessionId:
|
|
1292
|
+
createdByPrincipal: caller.principal,
|
|
1293
|
+
createdBySessionId: callerSessionId(caller),
|
|
1231
1294
|
createdAt: now,
|
|
1232
1295
|
updatedAt: now,
|
|
1233
1296
|
expiresAt: resolveInvitationExpiresAt(input.expiresAt, now),
|
|
@@ -1239,9 +1302,9 @@ export class AuthRuntime {
|
|
|
1239
1302
|
projectId: this.projectId,
|
|
1240
1303
|
authStorage,
|
|
1241
1304
|
invitation,
|
|
1242
|
-
audience:
|
|
1243
|
-
returnTo:
|
|
1244
|
-
requestOrigin:
|
|
1305
|
+
audience: destination.audience,
|
|
1306
|
+
returnTo: destination.returnTo,
|
|
1307
|
+
requestOrigin: destination.requestOrigin,
|
|
1245
1308
|
revealLink: input.revealLink,
|
|
1246
1309
|
now,
|
|
1247
1310
|
})
|
|
@@ -1270,11 +1333,9 @@ export class AuthRuntime {
|
|
|
1270
1333
|
}
|
|
1271
1334
|
|
|
1272
1335
|
async listInvitations(
|
|
1273
|
-
|
|
1274
|
-
input: ListInvitationsInput = {}
|
|
1275
|
-
options: AuthSessionResolutionOptions = {}
|
|
1336
|
+
caller: AuthenticatedUserRequestSession,
|
|
1337
|
+
input: ListInvitationsInput = {}
|
|
1276
1338
|
): Promise<ListInvitationsResult> {
|
|
1277
|
-
const session = await this.requireUser(request, options)
|
|
1278
1339
|
const authStorage = this.requireAuthStorage()
|
|
1279
1340
|
const { limit, offset } = normalizePagination(input)
|
|
1280
1341
|
const result = await authStorage.invitations.list({
|
|
@@ -1283,7 +1344,7 @@ export class AuthRuntime {
|
|
|
1283
1344
|
statuses: input.statuses,
|
|
1284
1345
|
order: input.order,
|
|
1285
1346
|
})
|
|
1286
|
-
const scope = this.resolveMembershipPolicyScopeForUser(
|
|
1347
|
+
const scope = this.resolveMembershipPolicyScopeForUser(caller.groupIds)
|
|
1287
1348
|
const manageable = result.invitations.filter((invitation) =>
|
|
1288
1349
|
canPerformMembershipOperation(scope, "invite", invitation.groupIds)
|
|
1289
1350
|
)
|
|
@@ -1297,11 +1358,9 @@ export class AuthRuntime {
|
|
|
1297
1358
|
}
|
|
1298
1359
|
|
|
1299
1360
|
async revokeInvitation(
|
|
1300
|
-
|
|
1301
|
-
input: RevokeInvitationInput
|
|
1302
|
-
options: AuthSessionResolutionOptions = {}
|
|
1361
|
+
caller: AuthenticatedUserRequestSession,
|
|
1362
|
+
input: RevokeInvitationInput
|
|
1303
1363
|
): Promise<RevokeInvitationResult> {
|
|
1304
|
-
const session = await this.requireUser(request, options)
|
|
1305
1364
|
const authStorage = this.requireAuthStorage()
|
|
1306
1365
|
const invitationId = assertNonEmpty(input.invitationId, "Invitation id")
|
|
1307
1366
|
const invitation = await authStorage.invitations.getById({
|
|
@@ -1316,7 +1375,7 @@ export class AuthRuntime {
|
|
|
1316
1375
|
)
|
|
1317
1376
|
}
|
|
1318
1377
|
|
|
1319
|
-
this.assertCanManageInvitationGroups(
|
|
1378
|
+
this.assertCanManageInvitationGroups(caller.groupIds, invitation.groupIds)
|
|
1320
1379
|
|
|
1321
1380
|
if (invitation.status !== "pending") {
|
|
1322
1381
|
throw new AuthRuntimeError(
|
|
@@ -1488,24 +1547,21 @@ export class AuthRuntime {
|
|
|
1488
1547
|
}
|
|
1489
1548
|
}
|
|
1490
1549
|
|
|
1491
|
-
function
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
)
|
|
1500
|
-
|
|
1550
|
+
function memberGroups(memberships: readonly GroupMembershipRecord[]): {
|
|
1551
|
+
readonly groupIds: readonly string[]
|
|
1552
|
+
readonly managedGroupIds: readonly string[]
|
|
1553
|
+
} {
|
|
1554
|
+
return {
|
|
1555
|
+
groupIds: memberships.map((membership) => membership.groupId),
|
|
1556
|
+
managedGroupIds: memberships
|
|
1557
|
+
.filter((membership) => membership.source === "directory")
|
|
1558
|
+
.map((membership) => membership.groupId),
|
|
1559
|
+
}
|
|
1501
1560
|
}
|
|
1502
1561
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
request.headers.get("x-real-ip")?.trim() ||
|
|
1507
|
-
undefined
|
|
1508
|
-
)
|
|
1562
|
+
/** The interactive session a caller authenticated with, when it used one. */
|
|
1563
|
+
function callerSessionId(caller: AuthenticatedRequestAuthSession): string | undefined {
|
|
1564
|
+
return caller.credentialSource === "session" ? caller.session.id : undefined
|
|
1509
1565
|
}
|
|
1510
1566
|
|
|
1511
1567
|
function constrainTokenGroupIds(
|