@objectstack/rest 11.0.0 → 11.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -227,6 +227,19 @@ declare class RestServer {
|
|
|
227
227
|
*/
|
|
228
228
|
private readonly hostnameCache;
|
|
229
229
|
private readonly hostnameCacheTtlMs;
|
|
230
|
+
/**
|
|
231
|
+
* Request-scoped memoization for `resolveExecCtx`. A single HTTP request
|
|
232
|
+
* resolves the SAME execution context (identity + RBAC/RLS + localization)
|
|
233
|
+
* many times — the data operation itself, app-nav RBAC filtering, dashboard
|
|
234
|
+
* widget gating, the auth gate, etc. Each resolution is ~16 sequential
|
|
235
|
+
* queries (the `resolveAuthzContext` aggregation plus localization), so a
|
|
236
|
+
* request that resolves twice pays for duplicate authz and repeated
|
|
237
|
+
* localization. Keyed by the per-request `req` object (a `WeakMap`, so the
|
|
238
|
+
* entry is collected with the request — naturally request-scoped, no TTL,
|
|
239
|
+
* no cross-request leak) and the input `environmentId`. We cache the
|
|
240
|
+
* in-flight Promise so concurrent callers share one resolution.
|
|
241
|
+
*/
|
|
242
|
+
private readonly execCtxMemo;
|
|
230
243
|
private defaultEnvironmentIdProvider?;
|
|
231
244
|
private authServiceProvider?;
|
|
232
245
|
private objectQLProvider?;
|
|
@@ -330,6 +343,8 @@ declare class RestServer {
|
|
|
330
343
|
* to the protocol layer (the SecurityPlugin treats undefined as anon).
|
|
331
344
|
*/
|
|
332
345
|
private resolveExecCtx;
|
|
346
|
+
/** Heavy path behind `resolveExecCtx` — resolve identity + RBAC/RLS + localization. */
|
|
347
|
+
private computeExecCtx;
|
|
333
348
|
/**
|
|
334
349
|
* Filter an `App` metadata item by the current user's `systemPermissions`.
|
|
335
350
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -227,6 +227,19 @@ declare class RestServer {
|
|
|
227
227
|
*/
|
|
228
228
|
private readonly hostnameCache;
|
|
229
229
|
private readonly hostnameCacheTtlMs;
|
|
230
|
+
/**
|
|
231
|
+
* Request-scoped memoization for `resolveExecCtx`. A single HTTP request
|
|
232
|
+
* resolves the SAME execution context (identity + RBAC/RLS + localization)
|
|
233
|
+
* many times — the data operation itself, app-nav RBAC filtering, dashboard
|
|
234
|
+
* widget gating, the auth gate, etc. Each resolution is ~16 sequential
|
|
235
|
+
* queries (the `resolveAuthzContext` aggregation plus localization), so a
|
|
236
|
+
* request that resolves twice pays for duplicate authz and repeated
|
|
237
|
+
* localization. Keyed by the per-request `req` object (a `WeakMap`, so the
|
|
238
|
+
* entry is collected with the request — naturally request-scoped, no TTL,
|
|
239
|
+
* no cross-request leak) and the input `environmentId`. We cache the
|
|
240
|
+
* in-flight Promise so concurrent callers share one resolution.
|
|
241
|
+
*/
|
|
242
|
+
private readonly execCtxMemo;
|
|
230
243
|
private defaultEnvironmentIdProvider?;
|
|
231
244
|
private authServiceProvider?;
|
|
232
245
|
private objectQLProvider?;
|
|
@@ -330,6 +343,8 @@ declare class RestServer {
|
|
|
330
343
|
* to the protocol layer (the SecurityPlugin treats undefined as anon).
|
|
331
344
|
*/
|
|
332
345
|
private resolveExecCtx;
|
|
346
|
+
/** Heavy path behind `resolveExecCtx` — resolve identity + RBAC/RLS + localization. */
|
|
347
|
+
private computeExecCtx;
|
|
333
348
|
/**
|
|
334
349
|
* Filter an `App` metadata item by the current user's `systemPermissions`.
|
|
335
350
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/rest-server.ts
|
|
2
|
-
import { resolveAuthzContext, resolveLocalizationContext } from "@objectstack/core";
|
|
2
|
+
import { resolveAuthzContext, resolveLocalizationContext, isAuthGateAllowlisted } from "@objectstack/core";
|
|
3
3
|
|
|
4
4
|
// src/route-manager.ts
|
|
5
5
|
var RouteManager = class {
|
|
@@ -460,6 +460,19 @@ var RestServer = class {
|
|
|
460
460
|
*/
|
|
461
461
|
this.hostnameCache = /* @__PURE__ */ new Map();
|
|
462
462
|
this.hostnameCacheTtlMs = 3e4;
|
|
463
|
+
/**
|
|
464
|
+
* Request-scoped memoization for `resolveExecCtx`. A single HTTP request
|
|
465
|
+
* resolves the SAME execution context (identity + RBAC/RLS + localization)
|
|
466
|
+
* many times — the data operation itself, app-nav RBAC filtering, dashboard
|
|
467
|
+
* widget gating, the auth gate, etc. Each resolution is ~16 sequential
|
|
468
|
+
* queries (the `resolveAuthzContext` aggregation plus localization), so a
|
|
469
|
+
* request that resolves twice pays for duplicate authz and repeated
|
|
470
|
+
* localization. Keyed by the per-request `req` object (a `WeakMap`, so the
|
|
471
|
+
* entry is collected with the request — naturally request-scoped, no TTL,
|
|
472
|
+
* no cross-request leak) and the input `environmentId`. We cache the
|
|
473
|
+
* in-flight Promise so concurrent callers share one resolution.
|
|
474
|
+
*/
|
|
475
|
+
this.execCtxMemo = /* @__PURE__ */ new WeakMap();
|
|
463
476
|
/**
|
|
464
477
|
* Lazily load the OpenAPI spec JSON shipped by @objectstack/spec.
|
|
465
478
|
* Cached after first read. Resilient to missing files / parse errors
|
|
@@ -633,6 +646,11 @@ var RestServer = class {
|
|
|
633
646
|
* requests (they're internal-only), so they cannot bypass this gate.
|
|
634
647
|
*/
|
|
635
648
|
enforceAuth(req, res, context) {
|
|
649
|
+
const gate = context?.authGate;
|
|
650
|
+
if (gate && req?.method !== "OPTIONS" && !isAuthGateAllowlisted(req?.path)) {
|
|
651
|
+
res.status(403).json({ error: { code: gate.code, message: gate.message } });
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
636
654
|
if (!this.config.api.requireAuth) return false;
|
|
637
655
|
if (context?.userId) return false;
|
|
638
656
|
if (req?.method === "OPTIONS") return false;
|
|
@@ -703,6 +721,21 @@ var RestServer = class {
|
|
|
703
721
|
* to the protocol layer (the SecurityPlugin treats undefined as anon).
|
|
704
722
|
*/
|
|
705
723
|
async resolveExecCtx(environmentId, req) {
|
|
724
|
+
if (!req || typeof req !== "object") return this.computeExecCtx(environmentId, req);
|
|
725
|
+
const key = environmentId ?? "\0default";
|
|
726
|
+
let perReq = this.execCtxMemo.get(req);
|
|
727
|
+
if (!perReq) {
|
|
728
|
+
perReq = /* @__PURE__ */ new Map();
|
|
729
|
+
this.execCtxMemo.set(req, perReq);
|
|
730
|
+
}
|
|
731
|
+
const cached = perReq.get(key);
|
|
732
|
+
if (cached) return cached;
|
|
733
|
+
const pending = this.computeExecCtx(environmentId, req);
|
|
734
|
+
perReq.set(key, pending);
|
|
735
|
+
return pending;
|
|
736
|
+
}
|
|
737
|
+
/** Heavy path behind `resolveExecCtx` — resolve identity + RBAC/RLS + localization. */
|
|
738
|
+
async computeExecCtx(environmentId, req) {
|
|
706
739
|
try {
|
|
707
740
|
if (!environmentId && req && this.envRegistry && this.kernelManager) {
|
|
708
741
|
const host = this.extractHostname(req);
|
|
@@ -779,6 +812,14 @@ var RestServer = class {
|
|
|
779
812
|
tenantId: authz.tenantId,
|
|
780
813
|
userId: authz.userId
|
|
781
814
|
});
|
|
815
|
+
let authGate;
|
|
816
|
+
try {
|
|
817
|
+
if (typeof authService.isAuthGateActive === "function" && authService.isAuthGateActive()) {
|
|
818
|
+
const gatedSession = await getSession(headers).catch(() => void 0);
|
|
819
|
+
if (gatedSession?.user?.authGate) authGate = gatedSession.user.authGate;
|
|
820
|
+
}
|
|
821
|
+
} catch {
|
|
822
|
+
}
|
|
782
823
|
return {
|
|
783
824
|
userId: authz.userId,
|
|
784
825
|
tenantId: authz.tenantId,
|
|
@@ -789,6 +830,7 @@ var RestServer = class {
|
|
|
789
830
|
...authz.tabPermissions ? { tabPermissions: authz.tabPermissions } : {},
|
|
790
831
|
isSystem: false,
|
|
791
832
|
org_user_ids: authz.org_user_ids,
|
|
833
|
+
...authGate ? { authGate } : {},
|
|
792
834
|
...localization.timezone ? { timezone: localization.timezone } : {},
|
|
793
835
|
...localization.locale ? { locale: localization.locale } : {},
|
|
794
836
|
...localization.currency ? { currency: localization.currency } : {},
|