@aooth/arbac-moost 0.2.2 → 0.2.4

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.
@@ -1,4 +1,4 @@
1
- import { r as AoothArbacClaims, t as ArbacUserProvider } from "../user.provider-BbYgv9RC.mjs";
1
+ import { r as AoothArbacClaims, t as ArbacUserProvider } from "../user.provider-D0cJ_iMd.mjs";
2
2
  import { TAtscriptAnnotatedType, TAtscriptTypeObject, TMetadataMap, TValidatorOptions, Validator } from "@atscript/typescript/utils";
3
3
  import { AoothUserCredentials } from "@aooth/user/atscript-db/model.as";
4
4
  /**
@@ -475,7 +475,7 @@ var AoothArbacUserCredentials = class {
475
475
  throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
476
476
  }
477
477
  };
478
- $ak("db.index.fulltext", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
479
- $nr("aooth.auth.metadata", "aooth.user.email", "aooth.user.phone", "arbac.attenuate.attr", "arbac.attenuate.role", "arbac.attribute", "arbac.role", "arbac.userId", "db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
478
+ $ak("db.index.fulltext", "db.index.fulltext.language", "db.index.fulltext.match", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
479
+ $nr("aooth.auth.metadata", "aooth.user.email", "aooth.user.phone", "arbac.attenuate.attr", "arbac.attenuate.role", "arbac.attribute", "arbac.role", "arbac.userId", "db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.fulltext.language", "db.index.fulltext.match", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
480
480
  defineAnnotatedType("object", AoothArbacUserCredentials).prop("id", $f(AoothUserCredentials, "id")).prop("username", $f(AoothUserCredentials, "username")).prop("version", $f(AoothUserCredentials, "version")).prop("password", $f(AoothUserCredentials, "password")).prop("account", $f(AoothUserCredentials, "account")).prop("mfa", $f(AoothUserCredentials, "mfa")).prop("trustedDevices", $f(AoothUserCredentials, "trustedDevices")).prop("seenDevices", $f(AoothUserCredentials, "seenDevices")).prop("roles", defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).annotate("arbac.role", true).$type);
481
481
  export { AoothArbacUserCredentials, ArbacUserRecordCache, AtscriptArbacUserProvider, extractAttenuation, getAoothUserHandleSpec, getArbacAttenuationSpec, validateAttenuationTargets };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { _ as ProjectionOf, a as conjoinArbacDbScopes, b as TArbacMeta, c as ArbacDbScope, d as extractUsedControlValues, f as applyAllowedFieldsAndSet, g as OwnFieldKey, h as NavTarget, i as ConjoinArbacDbScopesOptions, l as AsArbacDbController, m as NavRelationKey, n as ArbacUserProviderToken, o as RefForeignKey, p as ControlsOf, r as AoothArbacClaims, s as RefTableSource, t as ArbacUserProvider, u as enforceControlsPolicy, v as ARBAC_DELEGATED_AUTH, x as getArbacMate, y as ArbacDelegatedAuth } from "./user.provider-BbYgv9RC.mjs";
1
+ import { _ as ProjectionOf, a as conjoinArbacDbScopes, b as TArbacMeta, c as ArbacDbScope, d as extractUsedControlValues, f as applyAllowedFieldsAndSet, g as OwnFieldKey, h as NavTarget, i as ConjoinArbacDbScopesOptions, l as AsArbacDbController, m as NavRelationKey, n as ArbacUserProviderToken, o as RefForeignKey, p as ControlsOf, r as AoothArbacClaims, s as RefTableSource, t as ArbacUserProvider, u as enforceControlsPolicy, v as ARBAC_DELEGATED_AUTH, x as getArbacMate, y as ArbacDelegatedAuth } from "./user.provider-D0cJ_iMd.mjs";
2
2
  import { Arbac, Arbac as Arbac$1, TArbacCompiledRule, TArbacEvalResult, TArbacEvalResult as TArbacEvalResult$1, TArbacRole, TArbacRoleForResource, TArbacRule, arbacPatternToRegex } from "@aooth/arbac-core";
3
3
  import { ControlGate, ControlGate as ControlGate$1, TProjection, TScopeFieldRules, TScopeFilter, conjoinScopeFilters } from "@aooth/arbac";
4
- import { TAuthGuardDef } from "@moostjs/event-http";
4
+ import { HttpError, TAuthGuardDef } from "@moostjs/event-http";
5
5
  import { EventContext } from "@wooksjs/event-core";
6
6
  import { FilterExpr, TDbRemoveGuardContext, TDbWriteCheckContext, TDbWriteGuardContext, TDeleteOptions, TMetaResponse, TRowResolveOptions, TWriteOptions, UniqueryControls } from "@atscript/db";
7
7
  import { AsDbReadableController, AsJsonValueHelpController, AsValueHelpController, TDbActionScopeContext, TDbControlsType, TDbIndexFieldPaths, TDbRequestContext, ValueHelpSelect } from "@atscript/moost-db";
@@ -224,6 +224,37 @@ export interface ArbacEvaluateOptions {
224
224
  */
225
225
  table?: VisibilityTableSource;
226
226
  }
227
+ /** The current user as ARBAC sees it — what `useArbac().principal()` returns. */
228
+ export interface ArbacPrincipal<TUserAttrs extends object = object> {
229
+ /** The user id the `ArbacUserProvider` resolved for this event. */
230
+ userId: string;
231
+ /**
232
+ * The roles the user HOLDS (`ArbacUserProvider.getRoles`), before
233
+ * credential attenuation. Don't authorize on it — an attenuated credential
234
+ * may act with fewer roles; use {@link effectiveRoles} or `evaluate()`.
235
+ */
236
+ roles: string[];
237
+ /**
238
+ * The roles this request acts with: {@link roles} narrowed by the
239
+ * credential's attenuation (`roles ∩ attenuation.roles`; all held roles when
240
+ * the attenuation names none). A claimed role the user does not hold is
241
+ * never included, even with `allowUnheldRoles` — that "view as" mode only
242
+ * ever narrows through `evaluate()`. Use this for role checks in app code.
243
+ */
244
+ effectiveRoles: string[];
245
+ /** The user's attributes (`ArbacUserProvider.getAttrs`), loaded on the first call. */
246
+ attrs: () => Promise<TUserAttrs>;
247
+ /**
248
+ * The credential's restrict-only attenuation
249
+ * (`ArbacUserProvider.getAttenuation`), when it narrows the user's
250
+ * authority; absent otherwise.
251
+ */
252
+ attenuation?: {
253
+ roles?: string[];
254
+ attrs?: Record<string, unknown>;
255
+ allowUnheldRoles?: boolean;
256
+ };
257
+ }
227
258
  interface ArbacBindings {
228
259
  getScopes: <TScope extends object>() => TScope[] | undefined;
229
260
  setScopes: <TScope extends object>(scope: TScope[] | undefined) => void;
@@ -246,6 +277,14 @@ interface ArbacBindings {
246
277
  scopes?: TScope[];
247
278
  userId: string;
248
279
  }>;
280
+ /**
281
+ * The current principal — user id, held and effective roles, lazy
282
+ * attributes and credential attenuation — read through the
283
+ * `ArbacUserProvider`, the same source `evaluate()` uses. Loaded fresh on
284
+ * every call (like `evaluate()`), so a role change applies immediately.
285
+ * Decide on `effectiveRoles` (or `evaluate()`), never on `roles`.
286
+ */
287
+ principal: <TUserAttrs extends object = object>() => Promise<ArbacPrincipal<TUserAttrs>>;
249
288
  resource: string;
250
289
  action: string;
251
290
  isPublic: boolean;
@@ -576,9 +615,41 @@ export declare function isScopedFieldVisible(scopes: ArbacDbScope[], path: strin
576
615
  * caller's readable rows match the query; the action itself is authorized
577
616
  * by its source controller on every batch.
578
617
  *
618
+ * A read endpoint (`query`, `pages`, `geo`, `one`) under a moost-db write
619
+ * handler (`insert`, `update`, `replace`, `remove`, `removeComposite`) is
620
+ * served only inside {@link readBackUnderReadGrant} (since 0.2.4), under the
621
+ * read grant it installs — otherwise a 403, never the write grant. moost-db's
622
+ * insert read-back (`Prefer: return=representation`) then answers the plain
623
+ * insert result.
624
+ *
579
625
  * @since 0.1.72
580
626
  */
581
627
  export declare function prepareArbacRequest(ctx: TDbRequestContext, readable?: VisibilityTableSource): Promise<void>;
628
+ /**
629
+ * Read back the row `id` after a write, exactly as `GET /one/:id` would
630
+ * answer the caller (since 0.2.4) — the body of
631
+ * `AsArbacDbController._readRepresentation`, and the way for a write handler
632
+ * override to answer the written row:
633
+ *
634
+ * ```ts
635
+ * await super.update(payload);
636
+ * return readBackUnderReadGrant(id, () => this.getOne(String(id), ""));
637
+ * ```
638
+ *
639
+ * `read` calls the controller's `/one` handler: `getOne` for a scalar `id`,
640
+ * `getOneComposite` for a key object. The caller's grant on that handler is
641
+ * evaluated (its `@ArbacResource` / `@ArbacAction`, credential attenuation
642
+ * included) and installed as the request's scopes while `read` runs — its
643
+ * row filter, projection and controls apply, and an overridden handler body
644
+ * runs too — then the write's scopes are restored.
645
+ *
646
+ * Answers `undefined` instead of the row when: the caller has no grant on
647
+ * that handler; the handler carries method-level interceptors (they would
648
+ * not run on a direct call); `read` answers or throws an `HttpError` (e.g.
649
+ * the row is outside the read filter → 404); or anything else fails (logged
650
+ * as a warning, never answered — the write is already committed).
651
+ */
652
+ export declare function readBackUnderReadGrant<R>(id: unknown, read: () => Promise<R>): Promise<Exclude<R, HttpError> | undefined>;
582
653
  /**
583
654
  * `authorizeForm` for the ARBAC DB controllers: a form is served iff the
584
655
  * caller may run at least one of the actions that take it as input — each
package/dist/index.mjs CHANGED
@@ -21,6 +21,15 @@ function getOrCreate(map, key, create) {
21
21
  function hasSelfOrAncestor(set, path) {
22
22
  return set.has(path) || findAncestorInSet(path, set) !== void 0;
23
23
  }
24
+ /**
25
+ * A key object (a composite id, read by `getOneComposite`) — a plain object.
26
+ * A class instance such as a MongoDB `ObjectId` is a scalar id: `String(id)`.
27
+ */
28
+ function isKeyObject(id) {
29
+ if (id === null || typeof id !== "object") return false;
30
+ const proto = Object.getPrototypeOf(id);
31
+ return proto === Object.prototype || proto === null;
32
+ }
24
33
  /** A patch (`update` / `updateMany`) — only the keys it carries are written. */
25
34
  function isPatchAction(action) {
26
35
  return action === "update" || action === "updateMany";
@@ -871,6 +880,11 @@ async function loadPrincipal(user) {
871
880
  };
872
881
  }
873
882
  const arbacDeps = cachedBy((_controller, ctx) => Promise.all([useControllerContext(ctx).instantiate(ArbacUserProviderToken), useControllerContext(ctx).instantiate(MoostArbac)]));
883
+ /** Load the current principal through the controller's user provider (fresh, no memo). */
884
+ async function loadCurrentPrincipal(ctx) {
885
+ const [user] = await arbacDeps(useControllerContext(ctx).getController(), ctx);
886
+ return loadPrincipal(user);
887
+ }
874
888
  const principalKey = key("arbac.principal");
875
889
  function isHttpEvent(ctx) {
876
890
  return ctx.has(eventTypeKey) && ctx.get(eventTypeKey) === "http";
@@ -1044,11 +1058,23 @@ const useArbac = (_ctx) => {
1044
1058
  allowed: true
1045
1059
  };
1046
1060
  };
1061
+ const principal = async () => {
1062
+ const p = await loadCurrentPrincipal(ctx);
1063
+ const claimed = p.attenuate?.roles;
1064
+ return {
1065
+ userId: p.userId,
1066
+ roles: p.roles,
1067
+ effectiveRoles: claimed ? p.roles.filter((r) => claimed.includes(r)) : p.roles,
1068
+ attrs: () => p.attrs(p.userId),
1069
+ ...p.attenuate && { attenuation: p.attenuate }
1070
+ };
1071
+ };
1047
1072
  return {
1048
1073
  getScopes,
1049
1074
  setScopes,
1050
1075
  evaluate,
1051
1076
  evaluateOrThrow,
1077
+ principal,
1052
1078
  resource,
1053
1079
  action,
1054
1080
  isPublic
@@ -2586,10 +2612,11 @@ function cachedRequestScopes() {
2586
2612
  */
2587
2613
  async function resolveRequestScopes() {
2588
2614
  const ctx = current();
2615
+ const prior = resolved();
2616
+ if (prior?.readBack) return prior.scopes;
2589
2617
  const arbac = useArbac(ctx);
2590
2618
  const cachedScopes = arbac.getScopes();
2591
2619
  const fromInterceptor = cachedScopes && scopesOwnedBy(cachedScopes, arbac.resource, arbac.action) ? cachedScopes : void 0;
2592
- const prior = resolved();
2593
2620
  if (prior && prior.resource === arbac.resource && prior.action === arbac.action && prior.source === fromInterceptor) return prior.scopes;
2594
2621
  const scopes = normalizeScopes(fromInterceptor !== void 0 ? {
2595
2622
  allowed: true,
@@ -2667,6 +2694,13 @@ function isScopedFieldVisible(scopes, path, source) {
2667
2694
  * caller's readable rows match the query; the action itself is authorized
2668
2695
  * by its source controller on every batch.
2669
2696
  *
2697
+ * A read endpoint (`query`, `pages`, `geo`, `one`) under a moost-db write
2698
+ * handler (`insert`, `update`, `replace`, `remove`, `removeComposite`) is
2699
+ * served only inside {@link readBackUnderReadGrant} (since 0.2.4), under the
2700
+ * read grant it installs — otherwise a 403, never the write grant. moost-db's
2701
+ * insert read-back (`Prefer: return=representation`) then answers the plain
2702
+ * insert result.
2703
+ *
2670
2704
  * @since 0.1.72
2671
2705
  */
2672
2706
  async function prepareArbacRequest(ctx, readable) {
@@ -2678,10 +2712,67 @@ async function prepareArbacRequest(ctx, readable) {
2678
2712
  await authorizeDelegatedQueryTarget();
2679
2713
  return;
2680
2714
  }
2715
+ if (READ_ENDPOINTS.has(ctx.endpoint) && !resolved()?.readBack && underWriteHandler()) {
2716
+ const { resource, action } = useArbac();
2717
+ throw insufficientPrivileges(resource, action);
2718
+ }
2681
2719
  const scopes = await resolveRequestScopes();
2682
2720
  if (ctx.endpoint === "insert" && ctx.onConflict === "ignore") resolved().insertIgnore = true;
2683
2721
  if (READ_ENDPOINTS.has(ctx.endpoint)) enforceRelationGates(await resolveRequestRelations(scopes, ctx.controls, readable, ctx.filter), visibilityFor(scopes, readable));
2684
2722
  }
2723
+ /** moost-db's write handler methods — a read under one of them must be a read-back. */
2724
+ const WRITE_HANDLERS = new Set([...WRITE_CRUD_OPS].flatMap((op) => DB_CRUD_HANDLERS[op]));
2725
+ function underWriteHandler() {
2726
+ const method = useControllerContext().getMethod();
2727
+ return method !== void 0 && WRITE_HANDLERS.has(method);
2728
+ }
2729
+ /**
2730
+ * Read back the row `id` after a write, exactly as `GET /one/:id` would
2731
+ * answer the caller (since 0.2.4) — the body of
2732
+ * `AsArbacDbController._readRepresentation`, and the way for a write handler
2733
+ * override to answer the written row:
2734
+ *
2735
+ * ```ts
2736
+ * await super.update(payload);
2737
+ * return readBackUnderReadGrant(id, () => this.getOne(String(id), ""));
2738
+ * ```
2739
+ *
2740
+ * `read` calls the controller's `/one` handler: `getOne` for a scalar `id`,
2741
+ * `getOneComposite` for a key object. The caller's grant on that handler is
2742
+ * evaluated (its `@ArbacResource` / `@ArbacAction`, credential attenuation
2743
+ * included) and installed as the request's scopes while `read` runs — its
2744
+ * row filter, projection and controls apply, and an overridden handler body
2745
+ * runs too — then the write's scopes are restored.
2746
+ *
2747
+ * Answers `undefined` instead of the row when: the caller has no grant on
2748
+ * that handler; the handler carries method-level interceptors (they would
2749
+ * not run on a direct call); `read` answers or throws an `HttpError` (e.g.
2750
+ * the row is outside the read filter → 404); or anything else fails (logged
2751
+ * as a warning, never answered — the write is already committed).
2752
+ */
2753
+ async function readBackUnderReadGrant(id, read) {
2754
+ const ctx = current();
2755
+ const prior = resolved();
2756
+ if (!prior) return void 0;
2757
+ try {
2758
+ const instance = useControllerContext(ctx).getController();
2759
+ const method = isKeyObject(id) ? "getOneComposite" : "getOne";
2760
+ if (getMoostMate().read(instance, method)?.interceptors?.length) return void 0;
2761
+ const ids = resolveHandlerArbacIds(instance, method);
2762
+ const [outcome] = await evaluateHandlerOutcomes([ids]);
2763
+ const scopes = normalizeScopes(outcome);
2764
+ if (!scopes) return void 0;
2765
+ storeResolved(ctx, ids, void 0, scopes);
2766
+ ctx.get(requestScopesKey).readBack = true;
2767
+ const row = await read();
2768
+ return row instanceof HttpError ? void 0 : row;
2769
+ } catch (error) {
2770
+ if (!(error instanceof HttpError)) useLogger("arbac", ctx).warn(`Read-back of the written row failed: ${String(error)}`);
2771
+ return;
2772
+ } finally {
2773
+ ctx.set(requestScopesKey, prior);
2774
+ }
2775
+ }
2685
2776
  /**
2686
2777
  * `authorizeForm` for the ARBAC DB controllers: a form is served iff the
2687
2778
  * caller may run at least one of the actions that take it as input — each
@@ -3188,6 +3279,15 @@ let AsArbacDbController = class AsArbacDbController extends AsDbController {
3188
3279
  return prepareArbacRequest(ctx, this.readable);
3189
3280
  }
3190
3281
  /**
3282
+ * The read-back of `Prefer: return=representation` on `POST /` (since
3283
+ * 0.2.4; opt in with `returnRepresentation()`): this controller's own
3284
+ * `/one` handler under the caller's grant on it — see
3285
+ * {@link readBackUnderReadGrant}.
3286
+ */
3287
+ _readRepresentation(id) {
3288
+ return readBackUnderReadGrant(id, () => isKeyObject(id) ? this.getOneComposite(textKey(id), "") : this.getOne(String(id), ""));
3289
+ }
3290
+ /**
3191
3291
  * The request filter ∧ the union of the scopes' `filter`s. Computed
3192
3292
  * synchronously, but typed with moost-db's async-capable contract
3193
3293
  * (`FilterExpr | Promise<FilterExpr>`) so a subclass may override it
@@ -3375,6 +3475,10 @@ let AsArbacDbController = class AsArbacDbController extends AsDbController {
3375
3475
  }
3376
3476
  };
3377
3477
  AsArbacDbController = __decorate([Inherit()], AsArbacDbController);
3478
+ /** A key object as `GET /one?…` receives it — every value as text. */
3479
+ function textKey(id) {
3480
+ return Object.fromEntries(Object.entries(id).map(([k, v]) => [k, String(v)]));
3481
+ }
3378
3482
  const preservedFieldsCache = /* @__PURE__ */ new WeakMap();
3379
3483
  /**
3380
3484
  * Test-friendly internal helper — exported for unit tests and helper
@@ -3796,4 +3900,4 @@ async function useArbacDbScope() {
3796
3900
  })
3797
3901
  };
3798
3902
  }
3799
- export { ARBAC_DELEGATED_AUTH, Arbac, ArbacAction, ArbacAuthorize, ArbacResource, ArbacUserProvider, ArbacUserProviderToken, AsArbacDbController, AsArbacDbReadableController, AsArbacJsonValueHelpController, AsArbacValueHelpController, MoostArbac, applyAllowedFieldsAndSet, applyArbacControls, applyArbacMetaOverlay, applyArbacProjection, applyArbacRelationScopes, arbacActionRowScope, arbacAllowedActions, arbacAuthorizeInterceptor, arbacPatternToRegex, arbacRelationFilter, arbacRowFilter, authorizeArbacForm, buildScopeVisibility, cachedRequestScopes, checkArbacWrite, collectMethodNames, collectWithGrantNames, collectWritableFields, conjoinArbacDbScopes, conjoinScopeFilters, enforceControlsPolicy, extractUsedControlValues, getArbacMate, getArbacScopes, guardArbacRemove, guardArbacWrite, isMetaFieldVisible, isScopedFieldVisible, metaAlwaysVisibleFields, prepareArbacRequest, pruneMetaByVisibility, registerArbacDbTarget, requestFieldVisible, requireRequestScopes, resolveHandlerArbacIds, resolveRequestScopes, unionScopeProjection, useArbac, useArbacDbScope };
3903
+ export { ARBAC_DELEGATED_AUTH, Arbac, ArbacAction, ArbacAuthorize, ArbacResource, ArbacUserProvider, ArbacUserProviderToken, AsArbacDbController, AsArbacDbReadableController, AsArbacJsonValueHelpController, AsArbacValueHelpController, MoostArbac, applyAllowedFieldsAndSet, applyArbacControls, applyArbacMetaOverlay, applyArbacProjection, applyArbacRelationScopes, arbacActionRowScope, arbacAllowedActions, arbacAuthorizeInterceptor, arbacPatternToRegex, arbacRelationFilter, arbacRowFilter, authorizeArbacForm, buildScopeVisibility, cachedRequestScopes, checkArbacWrite, collectMethodNames, collectWithGrantNames, collectWritableFields, conjoinArbacDbScopes, conjoinScopeFilters, enforceControlsPolicy, extractUsedControlValues, getArbacMate, getArbacScopes, guardArbacRemove, guardArbacWrite, isMetaFieldVisible, isScopedFieldVisible, metaAlwaysVisibleFields, prepareArbacRequest, pruneMetaByVisibility, readBackUnderReadGrant, registerArbacDbTarget, requestFieldVisible, requireRequestScopes, resolveHandlerArbacIds, resolveRequestScopes, unionScopeProjection, useArbac, useArbacDbScope };
@@ -268,6 +268,13 @@ declare class AsArbacDbController<T extends TAtscriptAnnotatedType = TAtscriptAn
268
268
  * is resolved for the requested relations.
269
269
  */
270
270
  protected prepareRequest(ctx: TDbRequestContext): Promise<void>;
271
+ /**
272
+ * The read-back of `Prefer: return=representation` on `POST /` (since
273
+ * 0.2.4; opt in with `returnRepresentation()`): this controller's own
274
+ * `/one` handler under the caller's grant on it — see
275
+ * {@link readBackUnderReadGrant}.
276
+ */
277
+ protected _readRepresentation(id: unknown): Promise<TAtscriptDataType<T> | undefined>;
271
278
  /**
272
279
  * The request filter ∧ the union of the scopes' `filter`s. Computed
273
280
  * synchronously, but typed with moost-db's async-capable contract
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aooth/arbac-moost",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Moost RBAC integration for aoothjs (migrated from @moostjs/arbac)",
5
5
  "keywords": [
6
6
  "abac",
@@ -60,30 +60,30 @@
60
60
  "access": "public"
61
61
  },
62
62
  "dependencies": {
63
- "@uniqu/core": "^0.1.15",
64
- "@aooth/arbac": "0.2.2",
65
- "@aooth/user": "0.2.2",
66
- "@aooth/arbac-core": "0.2.2"
63
+ "@uniqu/core": "^0.2.0",
64
+ "@aooth/arbac": "0.2.4",
65
+ "@aooth/user": "0.2.4",
66
+ "@aooth/arbac-core": "0.2.4"
67
67
  },
68
68
  "devDependencies": {
69
- "@atscript/core": "^0.2.3",
70
- "@atscript/db": "^0.1.167",
71
- "@atscript/db-sql-tools": "^0.1.167",
72
- "@atscript/db-sqlite": "^0.1.167",
73
- "@atscript/moost-db": "^0.1.167",
74
- "@atscript/typescript": "^0.2.3",
75
- "@moostjs/event-http": "^0.7.1",
69
+ "@atscript/core": "^0.2.4",
70
+ "@atscript/db": "^0.1.169",
71
+ "@atscript/db-sql-tools": "^0.1.169",
72
+ "@atscript/db-sqlite": "^0.1.169",
73
+ "@atscript/moost-db": "^0.1.169",
74
+ "@atscript/typescript": "^0.2.4",
75
+ "@moostjs/event-http": "^0.7.2",
76
76
  "@types/better-sqlite3": "^7.6.13",
77
77
  "better-sqlite3": "^12.6.2",
78
- "unplugin-atscript": "^0.2.3"
78
+ "unplugin-atscript": "^0.2.4"
79
79
  },
80
80
  "peerDependencies": {
81
- "@atscript/db": "^0.1.167",
82
- "@atscript/moost-db": "^0.1.167",
83
- "@atscript/typescript": "^0.2.3",
84
- "@moostjs/event-http": "^0.7.1",
81
+ "@atscript/db": "^0.1.169",
82
+ "@atscript/moost-db": "^0.1.169",
83
+ "@atscript/typescript": "^0.2.4",
84
+ "@moostjs/event-http": "^0.7.2",
85
85
  "@wooksjs/event-core": "^0.8.0",
86
- "moost": "^0.7.1"
86
+ "moost": "^0.7.2"
87
87
  },
88
88
  "peerDependenciesMeta": {
89
89
  "@atscript/db": {