@rpcbase/db 0.135.0 → 0.137.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 CHANGED
@@ -51,6 +51,15 @@ const ItemB = await models.get("Item", {
51
51
 
52
52
  `ctx.tenantId` is checked first, then fallback is `ctx.req.session.user.currentTenantId`.
53
53
 
54
+ `models.get(...)` returns a model gateway. Bind the ability explicitly for each operation; `.acl(...)` returns a real Mongoose model backed by the rpcbase ACL driver:
55
+
56
+ ```ts
57
+ const Item = await models.get("Item", { tenantId })
58
+ const items = await Item.acl(ability).find({ status: "published" })
59
+ ```
60
+
61
+ Calling a protected model without `.acl(...)` fails closed. Passing `ctx.ability` remains supported as a shorthand when one ability is used for every operation on that handle. Use `models.getUnsafe(...)` only after an explicit authorization for DDL, index management, or another non-filterable operation.
62
+
54
63
  ### Transactions (MongoDB / Mongoose sessions)
55
64
 
56
65
  `@rpcbase/db` supports MongoDB transactions via Mongoose sessions.
@@ -62,7 +71,10 @@ import { models, withTransaction } from "@rpcbase/db"
62
71
 
63
72
  await withTransaction({ tenantId }, async ({ session, ctx, db }) => {
64
73
  const Item = await models.get("Item", ctx.tenant)
65
- const item = await Item.create({ uid: "u1", name: "Hello", createdAt: Date.now() }, { session })
74
+ const item = await Item.acl(ability).create(
75
+ { uid: "u1", name: "Hello", createdAt: Date.now() },
76
+ { session },
77
+ )
66
78
 
67
79
  const globalNative = db.global.db
68
80
  if (globalNative) {
@@ -110,12 +122,16 @@ import { buildSearchTextStage, ensureSearchIndex, searchMetaProjection } from "@
110
122
  Sample usage (sample-app) in `sample-app/src/api/search/items/handler.ts`:
111
123
 
112
124
  ```ts
113
- await ensureSearchIndex({ db: Item.db.db, collection: Item.collection.name, name: "item_name_text_v1", definition })
114
- const results = await Item.aggregate([
125
+ const UnsafeItem = await models.getUnsafe("Item", { tenantId })
126
+ await ensureSearchIndex({ db: UnsafeItem.db.db, collection: UnsafeItem.collection.name, name: "item_name_text_v1", definition })
127
+ const Item = await models.get("Item", { tenantId })
128
+ const results = await Item.acl(ability).aggregate([
115
129
  buildSearchTextStage({ index: "item_name_text_v1", query, path: "name", highlightPath: "name" }),
116
130
  { $limit: 20 },
117
131
  { $project: { name: 1, createdAt: 1, ...searchMetaProjection() } },
118
132
  ]).exec()
119
133
  ```
120
134
 
135
+ The ACL driver secures operations made through models returned by `.acl(...)`. It is not an in-process sandbox: direct `mongoose`/`mongodb` imports, database credentials, and handles returned by `models.getUnsafe(...)` are privileged capabilities and must stay in trusted infrastructure code.
136
+
121
137
  Dans `sample-app`, la config se trouve dans `sample-app/infrastructure` (`compose.yml`, `mongot/config.yml`, `mongot/mongodb-init.js`, `ensure-mongot-*`).
@@ -4,4 +4,5 @@ export * from './buildAbility';
4
4
  export * from './session';
5
5
  export * from './accessible';
6
6
  export * from './can';
7
+ export type { AclMongooseModel } from './mongooseAclModel';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/acl/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,OAAO,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/acl/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,OAAO,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { default as mongoose } from 'mongoose';
2
+ import { AclAction, AppAbility } from './types';
3
+ type PipelineStageLike = Record<string, unknown>;
4
+ export declare const injectAggregateMatch: (pipeline: PipelineStageLike[], match: Record<string, unknown>) => void;
5
+ export declare const resolveAggregateModel: (sourceModel: mongoose.Model<unknown>, collectionName: string) => mongoose.Model<unknown>;
6
+ export declare const secureAggregatePipeline: (pipeline: PipelineStageLike[], model: mongoose.Model<unknown>, ability: AppAbility | undefined, action: AclAction) => void;
7
+ export {};
8
+ //# sourceMappingURL=mongooseAclAggregate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclAggregate.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclAggregate.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAK/B,OAAO,KAAK,EAAE,SAAS,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAA;AAGpE,KAAK,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAiChD,eAAO,MAAM,oBAAoB,GAC/B,UAAU,iBAAiB,EAAE,EAC7B,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,IA2BF,CAAA;AAkBD,eAAO,MAAM,qBAAqB,GAChC,aAAa,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EACpC,gBAAgB,MAAM,KACrB,QAAQ,CAAC,KAAK,CAAC,OAAO,CAuBxB,CAAA;AAoID,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,EAAE,EAC7B,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAC9B,SAAS,UAAU,GAAG,SAAS,EAC/B,QAAQ,SAAS,KAChB,IAaF,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { default as mongoose } from 'mongoose';
2
+ import { AppAbility } from './types';
3
+ type NativeMongooseCollection = mongoose.Collection & {
4
+ collection: unknown;
5
+ conn: mongoose.Connection;
6
+ modelName?: string;
7
+ };
8
+ type NativeMongooseCollectionConstructor = new (name: string, connection: mongoose.Connection, options?: Record<string, unknown>) => NativeMongooseCollection;
9
+ declare const NativeCollection: NativeMongooseCollectionConstructor;
10
+ export declare class AclMongooseCollection extends NativeCollection {
11
+ constructor(name: string, connection: mongoose.Connection, options?: Record<string, unknown>);
12
+ }
13
+ export declare const installMongooseAclDriver: () => void;
14
+ export declare const runWithUnsafeMongooseAclDriver: <TResult>(callback: () => TResult) => TResult;
15
+ export declare const _private: {
16
+ getCollectionModels: (collection: NativeMongooseCollection) => Map<string, mongoose.Model<unknown>>;
17
+ getCollectionModelName: (collection: NativeMongooseCollection) => string | null;
18
+ secureBulkWriteOperations: (ability: AppAbility, model: mongoose.Model<unknown>, value: unknown) => Array<Record<string, unknown>>;
19
+ secureCollectionArguments: (collection: NativeMongooseCollection, ability: AppAbility, modelName: string, methodName: string, input: unknown[]) => unknown[];
20
+ };
21
+ export {};
22
+ //# sourceMappingURL=mongooseAclDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclDriver.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclDriver.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAa/B,OAAO,KAAK,EAAyC,UAAU,EAAE,MAAM,SAAS,CAAA;AAGhF,KAAK,wBAAwB,GAAG,QAAQ,CAAC,UAAU,GAAG;IACpD,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,KAAK,mCAAmC,GAAG,KACzC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,CAAC,UAAU,EAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC9B,wBAAwB,CAAA;AAiB7B,QAAA,MAAM,gBAAgB,qCAA0B,CAAA;AA0rBhD,qBAAa,qBAAsB,SAAQ,gBAAgB;gBAEvD,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,QAAQ,CAAC,UAAU,EAC/B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAKxC;AAID,eAAO,MAAM,wBAAwB,QAAO,IAa3C,CAAA;AAED,eAAO,MAAM,8BAA8B,GAAI,OAAO,EACpD,UAAU,MAAM,OAAO,KACtB,OAAoF,CAAA;AAEvF,eAAO,MAAM,QAAQ;sCAnoBP,wBAAwB,KACnC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;yCAgBK,wBAAwB,KAAG,MAAM,GAAG,IAAI;yCA6JzE,UAAU,SACZ,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,SACvB,OAAO,KACb,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;4CAyDnB,wBAAwB,WAC3B,UAAU,aACR,MAAM,cACL,MAAM,SACX,OAAO,EAAE,KACf,OAAO,EAAE;CAyZX,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { AppAbility } from './types';
2
+ export type MongooseAclDriverContext = {
3
+ mode: "ability";
4
+ ability: AppAbility;
5
+ } | {
6
+ mode: "unbound";
7
+ } | {
8
+ mode: "unsafe";
9
+ };
10
+ export declare const unsafeMongooseAclDriverContext: MongooseAclDriverContext;
11
+ export declare const unboundMongooseAclDriverContext: MongooseAclDriverContext;
12
+ export declare const bindMongooseAclDriverContext: (value: object, context: MongooseAclDriverContext) => void;
13
+ export declare const getMongooseAclDriverContext: (value?: object) => MongooseAclDriverContext | undefined;
14
+ export declare const bindMongooseAclRawConnection: (scopedConnection: object, rawConnection: object) => void;
15
+ export declare const getMongooseAclRawConnection: <TConnection extends object>(connection: TConnection) => TConnection;
16
+ export declare const runWithMongooseAclDriverContext: <TResult>(context: MongooseAclDriverContext, callback: () => TResult) => TResult;
17
+ //# sourceMappingURL=mongooseAclDriverContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclDriverContext.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclDriverContext.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC,MAAM,MAAM,wBAAwB,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAMtB,eAAO,MAAM,8BAA8B,EAAE,wBAE3C,CAAA;AAEF,eAAO,MAAM,+BAA+B,EAAE,wBAE5C,CAAA;AAEF,eAAO,MAAM,4BAA4B,GACvC,OAAO,MAAM,EACb,SAAS,wBAAwB,KAChC,IAEF,CAAA;AAED,eAAO,MAAM,2BAA2B,GACtC,QAAQ,MAAM,KACb,wBAAwB,GAAG,SAE7B,CAAA;AAED,eAAO,MAAM,4BAA4B,GACvC,kBAAkB,MAAM,EACxB,eAAe,MAAM,KACpB,IAEF,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,WAAW,SAAS,MAAM,EACpE,YAAY,WAAW,KACtB,WAA4E,CAAA;AAE/E,eAAO,MAAM,+BAA+B,GAAI,OAAO,EACrD,SAAS,wBAAwB,EACjC,UAAU,MAAM,OAAO,KACtB,OAAgD,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { default as mongoose } from 'mongoose';
2
+ import { MongooseAclDriverContext } from './mongooseAclDriverContext';
3
+ import { AppAbility } from './types';
4
+ declare const mongooseAclModelBrand: unique symbol;
5
+ declare module "mongoose" {
6
+ interface Model<TRawDocType, TQueryHelpers, TInstanceMethods, TVirtuals, THydratedDocumentType, TSchema, TLeanResultType> {
7
+ [mongooseAclModelBrand]?: [
8
+ TRawDocType,
9
+ TQueryHelpers,
10
+ TInstanceMethods,
11
+ TVirtuals,
12
+ THydratedDocumentType,
13
+ TSchema,
14
+ TLeanResultType
15
+ ];
16
+ acl: (ability: AppAbility) => this;
17
+ }
18
+ }
19
+ export declare const createAclMongooseModel: <TModel extends mongoose.Model<unknown>>(model: TModel, ability: AppAbility) => AclMongooseModel<TModel>;
20
+ export type AclMongooseModel<TModel extends mongoose.Model<unknown>> = TModel & {
21
+ acl: (ability: AppAbility) => AclMongooseModel<TModel>;
22
+ };
23
+ export declare const createAclMongooseModelGateway: <TModel extends mongoose.Model<unknown>>(model: TModel) => AclMongooseModel<TModel>;
24
+ export declare const createUnsafeMongooseModel: <TModel extends mongoose.Model<unknown>>(model: TModel) => TModel;
25
+ export declare const _private: {
26
+ createAclModelWithContext: <TModel extends mongoose.Model<unknown>>(model: TModel, context: MongooseAclDriverContext) => TModel;
27
+ resolveModelNameForCollection: (connection: mongoose.Connection, collectionName: string) => string | undefined;
28
+ setNativeCollection: (collection: object, nativeCollection: object) => void;
29
+ };
30
+ export {};
31
+ //# sourceMappingURL=mongooseAclModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclModel.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclModel.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAM/B,OAAO,EAKL,KAAK,wBAAwB,EAC9B,MAAM,4BAA4B,CAAA;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAGzC,OAAO,CAAC,MAAM,qBAAqB,EAAE,OAAO,MAAM,CAAA;AAElD,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,KAAK,CACb,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,qBAAqB,EACrB,OAAO,EACP,eAAe;QAEf,CAAC,qBAAqB,CAAC,CAAC,EAAE;YACxB,WAAW;YACX,aAAa;YACb,gBAAgB;YAChB,SAAS;YACT,qBAAqB;YACrB,OAAO;YACP,eAAe;SAChB,CAAA;QACD,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,IAAI,CAAA;KACnC;CACF;AAyfD,eAAO,MAAM,sBAAsB,GAAI,MAAM,SAAS,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAC3E,OAAO,MAAM,EACb,SAAS,UAAU,KAClB,gBAAgB,CAAC,MAAM,CAGG,CAAA;AAE7B,MAAM,MAAM,gBAAgB,CAAC,MAAM,SAAS,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,MAAM,GAAG;IAC9E,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,gBAAgB,CAAC,MAAM,CAAC,CAAA;CACvD,CAAA;AAED,eAAO,MAAM,6BAA6B,GAAI,MAAM,SAAS,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAClF,OAAO,MAAM,KACZ,gBAAgB,CAAC,MAAM,CAGG,CAAA;AAE7B,eAAO,MAAM,yBAAyB,GAAI,MAAM,SAAS,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAC9E,OAAO,MAAM,KACZ,MAA0E,CAAA;AAqB7E,eAAO,MAAM,QAAQ;gCAlWc,MAAM,SAAS,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,SAChE,MAAM,WACJ,wBAAwB,KAChC,MAAM;gDAnFK,QAAQ,CAAC,UAAU,kBACf,MAAM,KACrB,MAAM,GAAG,SAAS;sCAmbe,MAAM,oBAAoB,MAAM,KAAG,IAAI;CAI1E,CAAA"}
@@ -0,0 +1,6 @@
1
+ import { default as mongoose } from 'mongoose';
2
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
3
+ export declare const mergeMongoQuery: (left: unknown, right: Record<string, unknown>) => Record<string, unknown>;
4
+ export declare const cloneMongoValue: <TValue>(value: TValue) => TValue;
5
+ export declare const castMongoQuery: (model: mongoose.Model<unknown>, query: Record<string, unknown>) => Record<string, unknown>;
6
+ //# sourceMappingURL=mongooseAclUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongooseAclUtils.d.ts","sourceRoot":"","sources":["../../src/acl/mongooseAclUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAA;AAGpC,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAA;AAEtE,eAAO,MAAM,eAAe,GAC1B,MAAM,OAAO,EACb,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAQxB,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,EAAE,OAAO,MAAM,KAAG,MAyBvD,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,OAAO,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAC9B,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAMxB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ensureMongooseConnection.d.ts","sourceRoot":"","sources":["../src/ensureMongooseConnection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAqB/B,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CA+B1F,CAAA"}
1
+ {"version":3,"file":"ensureMongooseConnection.d.ts","sourceRoot":"","sources":["../src/ensureMongooseConnection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAA;AAwB/B,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CA+B1F,CAAA"}