@rebasepro/server 0.23.0 → 0.24.1-canary.g56af4a4
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/README.md +1 -1
- package/bin/rebase-server.js +4 -2
- package/dist/GCSStorageController-DivE9bAx.js +402 -0
- package/dist/GCSStorageController-DivE9bAx.js.map +1 -0
- package/dist/S3StorageController-D_vKs4kP.js +480 -0
- package/dist/S3StorageController-D_vKs4kP.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +92 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +38 -8
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +97 -11
- package/dist/api/rest/auth-collection-writes.d.ts +28 -21
- package/dist/api/rest/field-access-query.d.ts +28 -8
- package/dist/api/rest/field-ops.d.ts +19 -7
- package/dist/api/rest/query-body.d.ts +10 -0
- package/dist/api/rest/write-validation.d.ts +9 -7
- package/dist/api/schema-editor-routes.d.ts +10 -1
- package/dist/api/sql-export-routes.d.ts +8 -0
- package/dist/api/types.d.ts +28 -1
- package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-CSdL0iCk.js} +310 -12
- package/dist/ast-schema-editor-CSdL0iCk.js.map +1 -0
- package/dist/auth/access.d.ts +121 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +16 -1
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- package/dist/auth/admin-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +2 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +56 -55
- package/dist/auth/api-keys/api-key-routes.d.ts +41 -11
- package/dist/auth/api-keys/api-key-store.d.ts +31 -8
- package/dist/auth/api-keys/api-key-types.d.ts +14 -16
- package/dist/auth/api-keys/http-operation.d.ts +19 -0
- package/dist/auth/api-keys/index.d.ts +11 -11
- package/dist/auth/api-keys/key-grant.d.ts +41 -0
- package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
- package/dist/auth/auth-hooks.d.ts +46 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -0
- package/dist/auth/cookie-utils.d.ts +7 -0
- package/dist/auth/deliverable-address.d.ts +6 -0
- package/dist/auth/email-change-routes.d.ts +41 -0
- package/dist/auth/expired-token-sweep.d.ts +71 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +120 -59
- package/dist/auth/jwt-crypto.d.ts +7 -0
- package/dist/auth/jwt-keys.d.ts +4 -0
- package/dist/auth/jwt.d.ts +54 -3
- package/dist/auth/magic-link-routes.d.ts +2 -6
- package/dist/auth/mfa-routes.d.ts +2 -9
- package/dist/auth/middleware.d.ts +17 -5
- package/dist/auth/otp-routes.d.ts +2 -6
- package/dist/auth/passwordless-signup.d.ts +27 -0
- package/dist/auth/platform-token.d.ts +122 -0
- package/dist/auth/rate-limiter.d.ts +54 -1
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +11 -6
- package/dist/auth/token-revocation.d.ts +50 -1
- package/dist/auth/user-picking.d.ts +35 -0
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-B-GIMpDG.js → auth-OPq_FeMs.js} +3115 -638
- package/dist/auth-OPq_FeMs.js.map +1 -0
- package/dist/auth_adapter-CIItCTc8.js +21 -0
- package/dist/auth_adapter-CIItCTc8.js.map +1 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +24 -3
- package/dist/backup/backup-routes.d.ts +46 -5
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +16 -0
- package/dist/backup/index.d.ts +5 -1
- package/dist/backup-5d7XhLX3.js +581 -0
- package/dist/backup-5d7XhLX3.js.map +1 -0
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +56 -4
- package/dist/boot/http-server.d.ts +22 -0
- package/dist/boot/options.d.ts +16 -1
- package/dist/boot/runtime-cors.d.ts +17 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/static-routing.d.ts +65 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-CbFjuBwa.js → contract-routes-02lASyJI.js} +5 -8
- package/dist/contract-routes-02lASyJI.js.map +1 -0
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +94 -23
- package/dist/cron/cron-store.d.ts +31 -4
- package/dist/cron/define-cron.d.ts +6 -2
- package/dist/cron/index.d.ts +1 -1
- package/dist/{cron-loader-DfTj2Hbi.js → cron-loader-D7OmleAb.js} +11 -7
- package/dist/cron-loader-D7OmleAb.js.map +1 -0
- package/dist/{cron-routes-eE8nif_b.js → cron-routes-CBfiaqEL.js} +34 -8
- package/dist/cron-routes-CBfiaqEL.js.map +1 -0
- package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-C2vbETMo.js} +349 -65
- package/dist/cron-scheduler-C2vbETMo.js.map +1 -0
- package/dist/cron-store-B2Ih0gCN.js +540 -0
- package/dist/cron-store-B2Ih0gCN.js.map +1 -0
- package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BaqMSa4Y.js} +2 -2
- package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BaqMSa4Y.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/types.d.ts +44 -0
- package/dist/env.d.ts +1 -39
- package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
- package/dist/errors-D6_y86c5.js.map +1 -0
- package/dist/filter-dialect-REl-TjZr.js +1628 -0
- package/dist/filter-dialect-REl-TjZr.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-BDKKiBmy.js} +5 -4
- package/dist/function-loader-BDKKiBmy.js.map +1 -0
- package/dist/{function-routes-Chet4-lB.js → function-routes-DWB7BBsi.js} +25 -13
- package/dist/function-routes-DWB7BBsi.js.map +1 -0
- package/dist/functions/context.d.ts +26 -6
- package/dist/functions/define-function.d.ts +7 -4
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +2 -2
- package/dist/functions/index.js +114 -37
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/request-timeout.d.ts +7 -5
- package/dist/history/history-routes.d.ts +9 -0
- package/dist/history/retention-sweep.d.ts +81 -0
- package/dist/{history-recorder-B4MpJfJK.js → history-recorder-DHenr3lm.js} +4 -4
- package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-DHenr3lm.js.map} +1 -1
- package/dist/{history-store-BhxWOuz9.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-BhxWOuz9.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.es.js +23363 -17552
- package/dist/index.es.js.map +1 -1
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +20 -0
- package/dist/init.d.ts +70 -6
- package/dist/internal-tables-L2t39RdT.js +136 -0
- package/dist/internal-tables-L2t39RdT.js.map +1 -0
- package/dist/jobs/job-queue.d.ts +8 -0
- package/dist/jobs/job-store.d.ts +24 -0
- package/dist/jobs/types.d.ts +25 -4
- package/dist/{jobs-CazMYhyy.js → jobs-BpD4Q0qR.js} +235 -25
- package/dist/jobs-BpD4Q0qR.js.map +1 -0
- package/dist/{jwt-DnQHNFCl.js → jwt-Bqcu7cGt.js} +207 -77
- package/dist/jwt-Bqcu7cGt.js.map +1 -0
- package/dist/{keys-CogCQpxG.js → keys-GAVZqbqx.js} +3 -17
- package/dist/{keys-CogCQpxG.js.map → keys-GAVZqbqx.js.map} +1 -1
- package/dist/load-failure-UvzZgGXT.js +48 -0
- package/dist/load-failure-UvzZgGXT.js.map +1 -0
- package/dist/{logger-DO2PZc4i.js → logger-D-S-hO5e.js} +26 -3
- package/dist/logger-D-S-hO5e.js.map +1 -0
- package/dist/{logs-routes-Bj4TYYUl.js → logs-routes-DAdv37GI.js} +48 -8
- package/dist/logs-routes-DAdv37GI.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +7 -0
- package/dist/mcp/mcp-tools.d.ts +23 -9
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +7 -1
- package/dist/metrics/index.d.ts +18 -0
- package/dist/metrics/pool-wait.d.ts +50 -0
- package/dist/offline-8Ks3yp9n-CadBKm2D.js +3305 -0
- package/dist/offline-8Ks3yp9n-CadBKm2D.js.map +1 -0
- package/dist/offline-store-D9ytNNaa-BCr2vsFB.js +1903 -0
- package/dist/offline-store-D9ytNNaa-BCr2vsFB.js.map +1 -0
- package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-BqoUh4Ij.js} +455 -80
- package/dist/openapi-generator-BqoUh4Ij.js.map +1 -0
- package/dist/policy-PdzX-Opd.js +121 -0
- package/dist/policy-PdzX-Opd.js.map +1 -0
- package/dist/project_manifest-R6y_KHms.js +59 -0
- package/dist/project_manifest-R6y_KHms.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-qRlqeUmO.js} +2 -2
- package/dist/{proxy-Czngl3p9.js.map → proxy-qRlqeUmO.js.map} +1 -1
- package/dist/{query-parser-DGRVFNM3.js → query-parser-DK9eMkb9.js} +497 -52
- package/dist/query-parser-DK9eMkb9.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-Dnl2_OBc.js} +17 -10
- package/dist/request-timeout-Dnl2_OBc.js.map +1 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
- package/dist/schema-edit/apply-schema-change.d.ts +73 -3
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +20 -5
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/schema-editor-routes-BFf3i8jm.js +136 -0
- package/dist/schema-editor-routes-BFf3i8jm.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/outbound-post.d.ts +50 -0
- package/dist/services/outbound-url-guard.d.ts +34 -5
- package/dist/services/routed-realtime-service.d.ts +16 -0
- package/dist/services/webhook-service.d.ts +66 -14
- package/dist/soft-delete-params-47yBGs3_.js +668 -0
- package/dist/soft-delete-params-47yBGs3_.js.map +1 -0
- package/dist/sql-export-routes-BNNuGGLP.js +240 -0
- package/dist/sql-export-routes-BNNuGGLP.js.map +1 -0
- package/dist/{src-vkcwKXbT.js → src-CatHFUym.js} +439 -20
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +48 -2
- package/dist/storage/LocalStorageController.d.ts +69 -2
- package/dist/storage/S3StorageController.d.ts +39 -3
- package/dist/storage/image-transform.d.ts +5 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-keys.d.ts +15 -0
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +52 -14
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +76 -7
- package/dist/storage/types.d.ts +101 -7
- package/dist/storage/upload-validation.d.ts +13 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
- package/dist/strings.es-BeZWAodn.js +256 -0
- package/dist/strings.es-BeZWAodn.js.map +1 -0
- package/dist/upload-validation-Dd-fnsbA.js +172 -0
- package/dist/upload-validation-Dd-fnsbA.js.map +1 -0
- package/dist/utils/byte-size.d.ts +10 -0
- package/dist/utils/compression.d.ts +6 -0
- package/dist/utils/load-failure.d.ts +14 -0
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-CjrA4PMo.js +0 -242
- package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
- package/dist/S3StorageController-B6pKDNVj.js +0 -264
- package/dist/S3StorageController-B6pKDNVj.js.map +0 -1
- package/dist/admin-roles-vYdp_Pil.js +0 -36
- package/dist/admin-roles-vYdp_Pil.js.map +0 -1
- package/dist/admin_block-DxKLmdiv.js +0 -206
- package/dist/admin_block-DxKLmdiv.js.map +0 -1
- package/dist/ast-schema-editor-Mvr50v_S.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-B-GIMpDG.js.map +0 -1
- package/dist/backup-D7YR94N3.js +0 -253
- package/dist/backup-D7YR94N3.js.map +0 -1
- package/dist/contract-routes-CbFjuBwa.js.map +0 -1
- package/dist/cron-loader-DfTj2Hbi.js.map +0 -1
- package/dist/cron-routes-eE8nif_b.js.map +0 -1
- package/dist/cron-scheduler-B0pLfAix.js.map +0 -1
- package/dist/cron-store-TcoGz-xS.js +0 -305
- package/dist/cron-store-TcoGz-xS.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-loader-xnbDAPfa.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/jobs-CazMYhyy.js.map +0 -1
- package/dist/jwt-DnQHNFCl.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-Bj4TYYUl.js.map +0 -1
- package/dist/openapi-generator-O_O24MAT.js.map +0 -1
- package/dist/query-parser-DGRVFNM3.js.map +0 -1
- package/dist/request-timeout-C_4C2BeR.js.map +0 -1
- package/dist/schema-editor-routes-C5-lh_jO.js +0 -87
- package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
- package/dist/src-pmvW7BFx.js +0 -6688
- package/dist/src-pmvW7BFx.js.map +0 -1
- package/dist/src-vkcwKXbT.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- package/dist/types-BfKcm9do.js.map +0 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The access model this backend serves, and the one gate every scope check
|
|
3
|
+
* goes through.
|
|
4
|
+
*
|
|
5
|
+
* The model — the app's declared roles and scopes beside the built-in ones —
|
|
6
|
+
* comes from the users collection's `auth` block and is configured once at
|
|
7
|
+
* boot, like the JWT keys. A process serves one backend's auth, so one model.
|
|
8
|
+
*
|
|
9
|
+
* Who holds what:
|
|
10
|
+
*
|
|
11
|
+
* - A credential that narrows its person — an API key, an MCP token — puts
|
|
12
|
+
* its scopes in the request context (`c.set("scopes", …)`) when it is
|
|
13
|
+
* verified. That list is the whole answer for it.
|
|
14
|
+
* - Anyone else is a person (or the service key, which is the `admin` role),
|
|
15
|
+
* and holds what their roles hold: `scopesForRoles`.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { Context, MiddlewareHandler } from "hono";
|
|
20
|
+
import { type AccessModel, type CollectionConfig } from "@rebasepro/types";
|
|
21
|
+
import type { HonoEnv } from "../api/types.js";
|
|
22
|
+
import { ApiError } from "../api/errors.js";
|
|
23
|
+
import type { AuthRepository } from "./interfaces.js";
|
|
24
|
+
/** The account a personal key acts as, as it is now. Null when it no longer exists. */
|
|
25
|
+
export type KeyOwnerResolver = (uid: string) => Promise<{
|
|
26
|
+
roles: string[];
|
|
27
|
+
} | null>;
|
|
28
|
+
/**
|
|
29
|
+
* How a personal key finds its owner in an account store: the account as it
|
|
30
|
+
* is now — a missing or disabled one is no owner — and its roles.
|
|
31
|
+
*
|
|
32
|
+
* One read where the store answers both at once: `getAccountAccessState`, the
|
|
33
|
+
* statement every access token is already judged against, which reads roles
|
|
34
|
+
* and `disabled_at` together. Otherwise the account, then its roles — the two
|
|
35
|
+
* reads this always made, one round trip more per request.
|
|
36
|
+
*/
|
|
37
|
+
export declare function keyOwnerResolverFor(repo: Pick<AuthRepository, "getUserById" | "getUserRoleIds" | "getAccountAccessState">): KeyOwnerResolver;
|
|
38
|
+
/** The access model the running backend serves. Built-ins only until boot configures it. */
|
|
39
|
+
export declare function getAccessModel(): AccessModel;
|
|
40
|
+
/**
|
|
41
|
+
* How a personal key finds its owner, or undefined when personal keys are
|
|
42
|
+
* off — in which case every personal key, however valid, is refused.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getKeyOwnerResolver(): KeyOwnerResolver | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Install the access model, and — when the app enables `auth.personalKeys`
|
|
47
|
+
* and has an account store to read owners from — how personal keys find
|
|
48
|
+
* their owner. Called once by `initializeRebaseBackend`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function configureAccess(options: {
|
|
51
|
+
model: AccessModel;
|
|
52
|
+
resolveKeyOwner?: KeyOwnerResolver;
|
|
53
|
+
}): void;
|
|
54
|
+
/** A declaration the access model cannot be built from. Fails the boot. */
|
|
55
|
+
export declare class AccessModelError extends Error {
|
|
56
|
+
constructor(message: string);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the access model from the users collection's `auth` block, refusing
|
|
60
|
+
* anything that would read as a grant it is not.
|
|
61
|
+
*
|
|
62
|
+
* - `admin` may not be declared: it is built in and holds everything, and a
|
|
63
|
+
* declaration would either repeat that or quietly narrow it.
|
|
64
|
+
* - An app scope may not reuse a built-in resource name (`logs:export`): the
|
|
65
|
+
* built-in resources are Rebase's, and a reader of `logs:*` should not have
|
|
66
|
+
* to know which half is whose.
|
|
67
|
+
* - A role may not list a data-plane scope: a person's data access is the
|
|
68
|
+
* collection's `securityRules`, so `data:write` on a role would grant nothing
|
|
69
|
+
* and look like it granted something.
|
|
70
|
+
*/
|
|
71
|
+
export declare function accessModelFromCollections(collections: readonly CollectionConfig[]): AccessModel;
|
|
72
|
+
/**
|
|
73
|
+
* Everything the caller of this request may do. Empty for a request with no
|
|
74
|
+
* caller.
|
|
75
|
+
*/
|
|
76
|
+
export declare function callerScopes(c: Context<HonoEnv>): string[];
|
|
77
|
+
/**
|
|
78
|
+
* Whether this request's caller holds the `admin` role. The service key signs
|
|
79
|
+
* in as one.
|
|
80
|
+
*/
|
|
81
|
+
export declare function callerIsAdmin(c: Context<HonoEnv>): boolean;
|
|
82
|
+
/** Does this request's caller hold `scope`, on `target` when one is given? */
|
|
83
|
+
export declare function hasScope(c: Context<HonoEnv>, scope: string, target?: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* The refusal for a missing scope, worded for whoever is holding the
|
|
86
|
+
* credential. `details.requiredScope` names it for clients that branch.
|
|
87
|
+
*/
|
|
88
|
+
export declare function scopeMissing(c: Context<HonoEnv>, scope: string, target?: string): ApiError;
|
|
89
|
+
/**
|
|
90
|
+
* Refuse a request whose caller does not hold `scope`.
|
|
91
|
+
*
|
|
92
|
+
* `target` narrows the check to one collection, bucket, function or app
|
|
93
|
+
* resource, and may be read from the request. Mount after authentication: a
|
|
94
|
+
* request with no caller is refused as unauthenticated, not as unscoped.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* app.post("/deploy/:project", requireScope("project:deploy", c => c.req.param("project")), handler);
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function requireScope(scope: string, target?: string | ((c: Context<HonoEnv>) => string | undefined)): MiddlewareHandler<HonoEnv>;
|
|
102
|
+
/**
|
|
103
|
+
* Refuse a request unless its caller holds the scope for its HTTP method:
|
|
104
|
+
* `read` for GET and HEAD, `write` for everything else. For an admin surface
|
|
105
|
+
* whose reads and writes split exactly along the method.
|
|
106
|
+
*/
|
|
107
|
+
export declare function requireScopeByMethod(scopes: {
|
|
108
|
+
read: string;
|
|
109
|
+
write: string;
|
|
110
|
+
}): MiddlewareHandler<HonoEnv>;
|
|
111
|
+
/**
|
|
112
|
+
* Refuse to let the caller act on an account — edit it, reset its password,
|
|
113
|
+
* delete it — when that account holds anything the caller does not.
|
|
114
|
+
*
|
|
115
|
+
* `users:write` is the scope that lets a support role manage accounts, and
|
|
116
|
+
* without this it would also let that role reset an administrator's password
|
|
117
|
+
* and sign in as them.
|
|
118
|
+
*/
|
|
119
|
+
export declare function assertMayManageAccount(c: Context<HonoEnv>, targetRoles: readonly string[]): void;
|
|
120
|
+
/** Refuse to let the caller grant roles that hold anything the caller does not. */
|
|
121
|
+
export declare function assertMayGrantRoles(c: Context<HonoEnv>, roles: readonly string[]): void;
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* 3. Scopes the DataDriver via `withAuth()` for RLS
|
|
12
12
|
* 4. Rejects a presented-but-unverifiable token with 401, whatever
|
|
13
13
|
* `requireAuth` says
|
|
14
|
-
* 5.
|
|
14
|
+
* 5. Honours or refuses `x-rebase-impersonate` (`applyImpersonation`)
|
|
15
|
+
* 6. Enforces auth (401) when `requireAuth` is true and no user is found
|
|
15
16
|
*
|
|
16
17
|
* The behavior is identical to `createAuthMiddleware()` — only the
|
|
17
18
|
* token verification strategy is pluggable.
|
|
@@ -50,4 +50,19 @@ export declare function identityVouchesForAddress(identity: UserIdentityData, em
|
|
|
50
50
|
* on it is the takeover itself, and leaving the account unverified while
|
|
51
51
|
* signing the owner in would share the account all the same.
|
|
52
52
|
*/
|
|
53
|
-
export declare function confirmAddressOwnership(authRepo: AuthRepository, user: Pick<UserData, "id" | "email" | "passwordHash">, passwordHash: string | null
|
|
53
|
+
export declare function confirmAddressOwnership(authRepo: AuthRepository, user: Pick<UserData, "id" | "email" | "passwordHash">, passwordHash: string | null, options?: {
|
|
54
|
+
/**
|
|
55
|
+
* Whoever proved the address also holds a live session of this very
|
|
56
|
+
* account, so nothing on it is a stranger's: it was all put there by
|
|
57
|
+
* the person now holding the inbox. Nothing is removed and no session
|
|
58
|
+
* ends; the account is only marked verified. See `GET /auth/verify-email`.
|
|
59
|
+
*/
|
|
60
|
+
everythingProven?: boolean;
|
|
61
|
+
}): Promise<AddressProofOutcome>;
|
|
62
|
+
/** What a first proof of address took off the account. */
|
|
63
|
+
export interface AddressProofOutcome {
|
|
64
|
+
/** A password was on the account, nobody proved it, and it is gone. */
|
|
65
|
+
removedPassword: boolean;
|
|
66
|
+
/** The providers whose identities were detached for not vouching for the address. */
|
|
67
|
+
removedProviders: string[];
|
|
68
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Admin endpoint for listing
|
|
2
|
+
* Admin endpoint for listing roles.
|
|
3
3
|
*
|
|
4
|
-
* Mounts: GET /roles
|
|
4
|
+
* Mounts: GET /roles — the built-in `admin` role and the roles the app
|
|
5
|
+
* declares under `auth.roles` on the users collection, with the scopes each
|
|
6
|
+
* holds. Requires `users:read`.
|
|
5
7
|
*/
|
|
6
8
|
import { Hono } from "hono";
|
|
7
9
|
import type { AuthRepository } from "./interfaces.js";
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The admin role and scope matching, for code that may not import
|
|
3
|
+
* `@rebasepro/types`.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
* of them could then grant themselves real `admin` and keep it.
|
|
5
|
+
* The custom-functions surface (`@rebasepro/server/functions`) must bundle for
|
|
6
|
+
* a runtime with no Node built-ins and imports nothing but `hono`, so its
|
|
7
|
+
* guards cannot reach the canonical definitions in `@rebasepro/types`
|
|
8
|
+
* (`ADMIN_ROLE`, `hasAdminRole`, `scopeGrants`). These are the same rules,
|
|
9
|
+
* restated, and `test/admin-roles.test.ts` holds them equal.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
* Everything else imports the `@rebasepro/types` versions.
|
|
12
|
+
*/
|
|
13
|
+
/** The one built-in role. It holds every scope. */
|
|
14
|
+
export declare const ADMIN_ROLE_NAME = "admin";
|
|
15
|
+
/** Does this list of roles include the admin role? */
|
|
16
|
+
export declare function holdsAdminRole(roles: readonly string[] | null | undefined): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Does a set of held scopes grant `scope`, optionally on one `target`? The
|
|
19
|
+
* unqualified grant covers every target; `scope:target` covers its own.
|
|
18
20
|
*/
|
|
19
|
-
export declare
|
|
20
|
-
export type AdministrativeRole = (typeof ADMINISTRATIVE_ROLES)[number];
|
|
21
|
-
/** Does this single role name carry administrative privilege? */
|
|
22
|
-
export declare function isAdministrativeRole(role: string): role is AdministrativeRole;
|
|
23
|
-
/** Does any role in this list carry administrative privilege? */
|
|
24
|
-
export declare function hasAdministrativeRole(roles: readonly string[] | null | undefined): boolean;
|
|
21
|
+
export declare function heldScopesGrant(held: readonly string[], scope: string, target?: string): boolean;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import type { AuthRepository } from "./interfaces.js";
|
|
15
15
|
import type { EmailService, EmailConfig } from "../email/index.js";
|
|
16
16
|
import type { ResolvedAuthHooks } from "./auth-hooks.js";
|
|
17
|
-
import type { AuthAdapter, AuthCollectionConfig, UserCreationFinalizeResult, UserCreationPrepareResult } from "@rebasepro/types";
|
|
17
|
+
import type { AuthAdapter, AuthCollectionConfig, UserCreationFinalizeResult, UserCreationOptions, UserCreationPrepareResult } from "@rebasepro/types";
|
|
18
18
|
/**
|
|
19
19
|
* Generate a cryptographically secure random password that meets strength requirements.
|
|
20
20
|
*
|
|
@@ -54,6 +54,25 @@ export interface AdminUserPrepareResult {
|
|
|
54
54
|
/** Whether an invitation was sent (only relevant when hookHandledEmail is true). */
|
|
55
55
|
invitationSent: boolean;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* The body field with which an administrator waives the password policy for
|
|
59
|
+
* one create — an import of accounts whose passwords another system chose.
|
|
60
|
+
* An instruction to the door, never data: it reaches neither the row nor a
|
|
61
|
+
* create hook.
|
|
62
|
+
*/
|
|
63
|
+
export declare const SKIP_PASSWORD_POLICY_FIELD = "skipPasswordPolicy";
|
|
64
|
+
/**
|
|
65
|
+
* Hold a create body's chosen `password` to the password policy, as
|
|
66
|
+
* registration and every other door that sets a password are, and return the
|
|
67
|
+
* body without {@link SKIP_PASSWORD_POLICY_FIELD}.
|
|
68
|
+
*
|
|
69
|
+
* The flag waives the policy only when the door says the caller may waive it
|
|
70
|
+
* (`options.mayWaivePasswordPolicy`, an administrator); from anyone else it
|
|
71
|
+
* waives nothing, and a refusal says so.
|
|
72
|
+
*
|
|
73
|
+
* @throws ApiError 400 `WEAK_PASSWORD`
|
|
74
|
+
*/
|
|
75
|
+
export declare function enforceCreationPasswordPolicy(body: Record<string, unknown>, resolvedHooks: Pick<ResolvedAuthHooks, "validatePasswordStrength">, options?: UserCreationOptions): Record<string, unknown>;
|
|
57
76
|
/**
|
|
58
77
|
* Prepare user values for an admin-initiated user creation.
|
|
59
78
|
*
|
|
@@ -62,9 +81,12 @@ export interface AdminUserPrepareResult {
|
|
|
62
81
|
* 2. Backend-level `AuthHooks.onAdminCreateUser` — global override
|
|
63
82
|
* 3. Built-in default — generate password → hash → normalize email
|
|
64
83
|
*
|
|
84
|
+
* Before any of them, a chosen password is held to the password policy
|
|
85
|
+
* ({@link enforceCreationPasswordPolicy}).
|
|
86
|
+
*
|
|
65
87
|
* The caller is responsible for persisting (via `driver.save()`).
|
|
66
88
|
*/
|
|
67
|
-
export declare function prepareAdminUserValues(
|
|
89
|
+
export declare function prepareAdminUserValues(rawBody: Record<string, unknown>, ctx: AdminUserContext, options?: UserCreationOptions): Promise<AdminUserPrepareResult>;
|
|
68
90
|
/**
|
|
69
91
|
* Handle post-creation work for admin-created users.
|
|
70
92
|
*
|
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Authenticating requests that present an API key (`rk_`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* {@link resolveApiKey} turns a presented key into the identity it acts as and
|
|
5
|
+
* the scopes it holds; the HTTP middlewares, the realtime socket and `/mcp`
|
|
6
|
+
* all call it, so a key means the same thing on every surface.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* - A **service key** acts as `api-key:<id>`, with the RLS roles `service`
|
|
9
|
+
* plus whatever it was given, and holds exactly its scopes.
|
|
10
|
+
* - A **personal key** acts as its owner — their uid and their roles as they
|
|
11
|
+
* are *now*, read on every use — and holds its scopes narrowed to what the
|
|
12
|
+
* owner's roles still hold. Demote the owner and the key shrinks with them;
|
|
13
|
+
* delete the account and the key stops.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* independent one
|
|
16
|
-
* `rebase_user` role like any other caller. An `admin` key passes RLS via the
|
|
17
|
-
* injected `default_admin` policies; a non-admin key (roles `["service"]`,
|
|
18
|
-
* uid `api-key:<id>`) only sees rows that a policy explicitly grants to the
|
|
19
|
-
* `service` role or to the public. Owner-style policies
|
|
20
|
-
* (`owner_id = rebase.uid()`) never match an API key's synthetic uid.
|
|
15
|
+
* A key never bypasses RLS: the request's driver is scoped to the identity
|
|
16
|
+
* like any other caller's, so the scopes are one ceiling and the database's
|
|
17
|
+
* policies another, independent one.
|
|
21
18
|
*
|
|
22
19
|
* @module
|
|
23
20
|
*/
|
|
24
21
|
import type { Context, MiddlewareHandler } from "hono";
|
|
25
|
-
import type
|
|
22
|
+
import { type DataDriver } from "@rebasepro/types";
|
|
26
23
|
import type { HonoEnv } from "../../api/types.js";
|
|
27
24
|
import type { ApiKeyStore } from "./api-key-store.js";
|
|
25
|
+
import type { ApiKeyMasked } from "./api-key-types.js";
|
|
28
26
|
/**
|
|
29
27
|
* Check whether a token looks like a Rebase API key.
|
|
30
28
|
*/
|
|
31
29
|
export declare function isApiKeyToken(token: string): boolean;
|
|
30
|
+
/** Who a verified key acts as, and what it may do. */
|
|
31
|
+
export interface ApiKeyIdentity {
|
|
32
|
+
uid: string;
|
|
33
|
+
roles: string[];
|
|
34
|
+
scopes: string[];
|
|
35
|
+
apiKey: ApiKeyMasked;
|
|
36
|
+
}
|
|
37
|
+
/** A presented key that does not authenticate, and why. */
|
|
38
|
+
export interface ApiKeyRefusal {
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Verify a presented key: it exists, is live, and — for a personal key —
|
|
43
|
+
* personal keys are on and its owner still exists. Records the use.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveApiKey(store: ApiKeyStore, token: string): Promise<ApiKeyIdentity | ApiKeyRefusal>;
|
|
32
46
|
/**
|
|
33
47
|
* Options for the API key authentication handler.
|
|
34
48
|
*/
|
|
@@ -37,59 +51,46 @@ export interface ApiKeyAuthOptions {
|
|
|
37
51
|
driver: DataDriver;
|
|
38
52
|
}
|
|
39
53
|
/**
|
|
40
|
-
* Validate an API key token and populate the Hono context
|
|
41
|
-
*
|
|
42
|
-
* Returns `true` if the key is valid and context has been populated,
|
|
43
|
-
* or returns an error Response if the key is invalid.
|
|
54
|
+
* Validate an API key token and populate the Hono context: `user`, `apiKey`,
|
|
55
|
+
* `scopes` and the RLS-scoped `driver`.
|
|
44
56
|
*
|
|
45
|
-
*
|
|
46
|
-
* `createAuthMiddleware()` when a `rk_` prefixed token is detected.
|
|
57
|
+
* Returns `true` when the context is populated, or the error Response.
|
|
47
58
|
*/
|
|
48
59
|
export declare function validateApiKey(c: Context<HonoEnv>, token: string, options: ApiKeyAuthOptions): Promise<Response | true>;
|
|
49
60
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* Storage previously did not accept API keys at all (`rk_` tokens were
|
|
53
|
-
* misparsed as JWTs and 401'd). Now that the pre-auth middleware
|
|
54
|
-
* authenticates them, this guard decides what they may do: the key needs a
|
|
55
|
-
* `"storage"` permission entry (or the global `"*"` wildcard) covering the
|
|
56
|
-
* operation derived from the HTTP method. Requests not authenticated via an
|
|
57
|
-
* API key pass through to the storage router's own auth gates.
|
|
58
|
-
*
|
|
59
|
-
* TUS resumable-upload routes (`/tus`, `/tus/:id`) are classified as `write`
|
|
60
|
-
* for EVERY method: the protocol's offset check is a GET and its cancel is a
|
|
61
|
-
* DELETE, but both are steps of an upload — a write-scoped key must be able
|
|
62
|
-
* to complete (and abort) its own resumable upload without also holding
|
|
63
|
-
* `read`/`delete` on stored objects.
|
|
61
|
+
* The 403 for a credential that lacks a data-plane scope outside the REST
|
|
62
|
+
* generator (storage and functions). Names the scope that would grant it.
|
|
64
63
|
*/
|
|
65
|
-
export declare function
|
|
64
|
+
export declare function forbidScope(c: Context<HonoEnv>, scope: string, target?: string): Response;
|
|
66
65
|
/**
|
|
67
|
-
*
|
|
66
|
+
* Scope guard for the resumable-upload routes (`/tus/:id`).
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
|
|
68
|
+
* Every step of an upload — the offset check (HEAD), the chunks (PATCH), the
|
|
69
|
+
* cancel (DELETE) — is part of writing it, so all of them need
|
|
70
|
+
* `storage:write`. Which source the upload writes to was checked when it was
|
|
71
|
+
* created; these steps only reach an upload the same caller owns.
|
|
72
|
+
*/
|
|
73
|
+
export declare function createTusScopeGuard(): MiddlewareHandler<HonoEnv>;
|
|
74
|
+
/**
|
|
75
|
+
* Scope guard for the custom-functions router: a narrowed credential needs
|
|
76
|
+
* `functions:invoke` on the function it calls. The functions index — the
|
|
77
|
+
* listing at the mount point itself — needs the unqualified scope.
|
|
74
78
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
79
|
+
* People pass: what a person may do inside a function is the function's own
|
|
80
|
+
* business, as it always was.
|
|
77
81
|
*
|
|
78
82
|
* @param mountPrefix - The path the functions router is mounted at
|
|
79
83
|
* (e.g. `/api/functions`), used to extract the function
|
|
80
84
|
* name from the request path.
|
|
81
85
|
*/
|
|
82
|
-
export declare function
|
|
86
|
+
export declare function createFunctionScopeGuard(mountPrefix: string): MiddlewareHandler<HonoEnv>;
|
|
83
87
|
/**
|
|
84
|
-
*
|
|
88
|
+
* Pre-auth middleware for `rk_` bearer tokens.
|
|
85
89
|
*
|
|
86
|
-
* Routers whose auth gate is JWT-based (`
|
|
87
|
-
* the
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* the downstream gates then see the already-resolved user and apply their
|
|
91
|
-
* role checks (`requireAdmin`) as usual — so an `admin: true` key passes and
|
|
92
|
-
* a non-admin key is rejected with 403.
|
|
90
|
+
* Routers whose auth gate is JWT-based (`createRequireAuth` — the admin
|
|
91
|
+
* surfaces, the key routes) don't know about API keys. Mounted in front of
|
|
92
|
+
* them, this authenticates `rk_` tokens and populates the request context; the
|
|
93
|
+
* downstream gates then see the already-resolved caller and check its scopes.
|
|
93
94
|
*
|
|
94
95
|
* Requests without an `rk_` bearer token pass through untouched. An invalid,
|
|
95
96
|
* revoked, or expired `rk_` token is rejected here (401) rather than falling
|
|
@@ -1,32 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Routes for managing API keys.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* - {@link createApiKeyRoutes} — the project's service keys, under
|
|
5
|
+
* `/api/admin/api-keys`, for holders of `keys:read` / `keys:write`.
|
|
6
|
+
* - {@link createPersonalKeyRoutes} — the caller's own personal keys, under
|
|
7
|
+
* `/api/auth/keys`, when the app enables `auth.personalKeys`.
|
|
8
|
+
*
|
|
9
|
+
* All routes return masked keys (never the hash). The full plaintext key is
|
|
10
|
+
* returned exactly once, in the response that creates it. No API key may call
|
|
11
|
+
* either router.
|
|
7
12
|
*
|
|
8
13
|
* @module
|
|
9
14
|
*/
|
|
10
15
|
import { Hono } from "hono";
|
|
16
|
+
import type { AccessModel } from "@rebasepro/types";
|
|
11
17
|
import type { HonoEnv } from "../../api/types.js";
|
|
12
18
|
import type { ApiKeyStore } from "./api-key-store.js";
|
|
19
|
+
import { type KeyTargets } from "./key-grant.js";
|
|
13
20
|
export interface ApiKeyRouteOptions {
|
|
14
21
|
store: ApiKeyStore;
|
|
15
22
|
serviceKey?: string;
|
|
16
23
|
/**
|
|
17
24
|
* Read the caller's roles from the database rather than from their token.
|
|
18
25
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* See `createRequireAuth`.
|
|
26
|
+
* A key minted here may outlive the session that minted it, so the roles
|
|
27
|
+
* that bound what it may hold must be the caller's roles now — not the
|
|
28
|
+
* ones a token issued before a demotion still claims. See
|
|
29
|
+
* `createRequireAuth`.
|
|
24
30
|
*/
|
|
25
31
|
resolveRoles?: (uid: string) => Promise<string[]>;
|
|
26
32
|
/** Repository for the token-revocation watermark. See `createRequireAuth`. */
|
|
27
|
-
revocationRepo?:
|
|
33
|
+
revocationRepo?: import("../token-revocation.js").AccessJudgeRepository;
|
|
34
|
+
/** What a scope's target may name. Unset, targets are not checked for existence. */
|
|
35
|
+
targets?: KeyTargets;
|
|
36
|
+
/** The access model to validate scopes against. Defaults to the configured one. */
|
|
37
|
+
accessModel?: () => AccessModel;
|
|
28
38
|
}
|
|
29
39
|
/**
|
|
30
|
-
*
|
|
40
|
+
* The project's service keys: `GET` needs `keys:read`, everything else
|
|
41
|
+
* `keys:write`.
|
|
31
42
|
*/
|
|
32
43
|
export declare function createApiKeyRoutes(options: ApiKeyRouteOptions): Hono<HonoEnv>;
|
|
44
|
+
export interface PersonalKeyRouteOptions {
|
|
45
|
+
store: ApiKeyStore;
|
|
46
|
+
/** Whether the app enabled `auth.personalKeys`. Off, every route explains how to turn it on. */
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
/** Recognised only to be refused with a reason: the service key has no account. */
|
|
49
|
+
serviceKey?: string;
|
|
50
|
+
resolveRoles?: (uid: string) => Promise<string[]>;
|
|
51
|
+
revocationRepo?: import("../token-revocation.js").AccessJudgeRepository;
|
|
52
|
+
targets?: KeyTargets;
|
|
53
|
+
accessModel?: () => AccessModel;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The caller's own keys. Each acts as the caller — their account, their roles
|
|
57
|
+
* as they are when the key is used — and holds no scope the caller does not.
|
|
58
|
+
*
|
|
59
|
+
* For a signed-in account only: not a key, not the service key (it has no
|
|
60
|
+
* account to act as) and not a guest, whose account is one sign-out from gone.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createPersonalKeyRoutes(options: PersonalKeyRouteOptions): Hono<HonoEnv>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Database operations for
|
|
2
|
+
* Database operations for API keys.
|
|
3
3
|
*
|
|
4
4
|
* Uses the DataDriver's `admin.executeSql` capability (same pattern as
|
|
5
5
|
* the cron-store and ensure-tables modules). All data lives in the
|
|
@@ -8,22 +8,45 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import type { DataDriver } from "@rebasepro/types";
|
|
11
|
-
import type { ApiKey, ApiKeyMasked, ApiKeyWithSecret,
|
|
11
|
+
import type { ApiKey, ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, UpdateApiKeyRequest } from "./api-key-types.js";
|
|
12
|
+
/** What a new key row is made of, already validated by the route that asked. */
|
|
13
|
+
export interface NewApiKey {
|
|
14
|
+
name: string;
|
|
15
|
+
kind: ApiKeyKind;
|
|
16
|
+
scopes: string[];
|
|
17
|
+
/** RLS roles beside `service`. Always empty for a personal key. */
|
|
18
|
+
roles: string[];
|
|
19
|
+
/** The account a personal key acts as. */
|
|
20
|
+
owner_uid: string | null;
|
|
21
|
+
rate_limit: number | null;
|
|
22
|
+
expires_at: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Which keys a listing returns. */
|
|
25
|
+
export type ApiKeyFilter = {
|
|
26
|
+
kind: "service";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "personal";
|
|
29
|
+
owner_uid: string;
|
|
30
|
+
};
|
|
12
31
|
export interface ApiKeyStore {
|
|
13
32
|
/** Ensure the `rebase.api_keys` table exists. Called once on startup. */
|
|
14
33
|
ensureTable(): Promise<void>;
|
|
15
34
|
/** Create a new API key. Returns the full plaintext key exactly once. */
|
|
16
|
-
createApiKey(
|
|
35
|
+
createApiKey(key: NewApiKey, createdBy: string): Promise<ApiKeyWithSecret>;
|
|
17
36
|
/** Look up an API key by its SHA-256 hash. Returns `null` if not found. */
|
|
18
37
|
findByKeyHash(hash: string): Promise<ApiKey | null>;
|
|
19
|
-
/** List
|
|
20
|
-
listApiKeys(): Promise<ApiKeyMasked[]>;
|
|
38
|
+
/** List keys (masked, never includes hash), newest first. */
|
|
39
|
+
listApiKeys(filter: ApiKeyFilter): Promise<ApiKeyMasked[]>;
|
|
21
40
|
/** Get a single API key by ID (masked). */
|
|
22
41
|
getApiKeyById(id: string): Promise<ApiKeyMasked | null>;
|
|
23
|
-
/** Update name,
|
|
42
|
+
/** Update name, scopes, roles, rate_limit, or expires_at. */
|
|
24
43
|
updateApiKey(id: string, updates: UpdateApiKeyRequest): Promise<ApiKeyMasked | null>;
|
|
25
|
-
/**
|
|
26
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Soft-delete: set `revoked_at` to now. With `owner_uid`, only that
|
|
46
|
+
* account's personal key is revoked — the answer is false for anyone
|
|
47
|
+
* else's, so a route can 404 without saying whether the id exists.
|
|
48
|
+
*/
|
|
49
|
+
revokeApiKey(id: string, owner_uid?: string): Promise<boolean>;
|
|
27
50
|
/** Touch `last_used_at` to the current timestamp. */
|
|
28
51
|
updateLastUsed(id: string): Promise<void>;
|
|
29
52
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type definitions for
|
|
2
|
+
* Type definitions for API keys.
|
|
3
3
|
*
|
|
4
|
-
* The wire contract —
|
|
5
|
-
* lives in `@rebasepro/types`, because the client SDK needs the same shapes
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The wire contract — scopes, the masked key, the create/update payloads —
|
|
5
|
+
* lives in `@rebasepro/types`, because the client SDK needs the same shapes.
|
|
6
|
+
* Only {@link ApiKey}, the database row carrying `key_hash`, is server-side
|
|
7
|
+
* and stays here.
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import type {
|
|
12
|
-
export type {
|
|
11
|
+
import type { ApiKeyKind } from "@rebasepro/types";
|
|
12
|
+
export type { ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, CreatePersonalKeyRequest, UpdateApiKeyRequest } from "@rebasepro/types";
|
|
13
13
|
/**
|
|
14
14
|
* Full database row for an API key.
|
|
15
15
|
* The `key_hash` is never exposed via the API — only stored for lookup.
|
|
@@ -17,19 +17,17 @@ export type { ApiKeyPermission, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequ
|
|
|
17
17
|
export interface ApiKey {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
+
kind: ApiKeyKind;
|
|
20
21
|
/** First 12 characters of the plaintext key, for display only. */
|
|
21
22
|
key_prefix: string;
|
|
22
23
|
/** SHA-256 hash of the full plaintext key. */
|
|
23
24
|
key_hash: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* explicitly names that role.
|
|
31
|
-
*/
|
|
32
|
-
admin: boolean;
|
|
25
|
+
/** `resource:action[:target]` scope strings. */
|
|
26
|
+
scopes: string[];
|
|
27
|
+
/** RLS roles a service key runs as, beside `service`. Empty on a personal key. */
|
|
28
|
+
roles: string[];
|
|
29
|
+
/** The account a personal key acts as. Null on a service key. */
|
|
30
|
+
owner_uid: string | null;
|
|
33
31
|
/**
|
|
34
32
|
* Requests per 15-minute window. `null` means "no per-key override" —
|
|
35
33
|
* the data rate limiter then applies its default API-key limit
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which data-plane operation an HTTP request performs, for the scope it needs:
|
|
3
|
+
* `data:read`, `data:write` or `data:delete` (and the same for storage).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** The operation part of a data-plane scope. */
|
|
8
|
+
export type DataOperation = "read" | "write" | "delete";
|
|
9
|
+
/**
|
|
10
|
+
* Map an HTTP method to the operation it performs.
|
|
11
|
+
*
|
|
12
|
+
* - `GET`, `HEAD`, `OPTIONS` → `"read"`
|
|
13
|
+
* - `POST`, `PUT`, `PATCH` → `"write"`
|
|
14
|
+
* - `DELETE` → `"delete"`
|
|
15
|
+
*
|
|
16
|
+
* Any other method is a `"write"`: a verb this does not know is not assumed
|
|
17
|
+
* to be harmless.
|
|
18
|
+
*/
|
|
19
|
+
export declare function httpMethodToOperation(method: string): DataOperation;
|