@nocobase/acl 1.9.47 → 1.9.49

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.
Files changed (3) hide show
  1. package/lib/acl.d.ts +22 -8
  2. package/lib/acl.js +99 -72
  3. package/package.json +4 -4
package/lib/acl.d.ts CHANGED
@@ -15,6 +15,7 @@ import { ACLRole, ResourceActionsOptions, RoleActionParams } from './acl-role';
15
15
  import { AllowManager, ConditionFunc } from './allow-manager';
16
16
  import FixedParamsManager, { Merger } from './fixed-params-manager';
17
17
  import SnippetManager, { SnippetOptions } from './snippet-manager';
18
+ import Database, { Collection } from '@nocobase/database';
18
19
  export interface CanResult {
19
20
  role: string;
20
21
  resource: string;
@@ -44,6 +45,14 @@ export interface ListenerContext {
44
45
  params: RoleActionParams;
45
46
  }
46
47
  type Listener = (ctx: ListenerContext) => void;
48
+ export type UserProvider = (args: {
49
+ fields: string[];
50
+ }) => Promise<any>;
51
+ export interface ParseJsonTemplateOptions {
52
+ timezone?: string;
53
+ state?: any;
54
+ userProvider?: UserProvider;
55
+ }
47
56
  interface CanArgs {
48
57
  role?: string;
49
58
  resource: string;
@@ -104,10 +113,6 @@ export declare class ACL extends EventEmitter {
104
113
  * @deprecated
105
114
  */
106
115
  skip(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc): void;
107
- /**
108
- * @internal
109
- */
110
- parseJsonTemplate(json: any, ctx: any): Promise<any>;
111
116
  middleware(): (ctx: any, next: any) => Promise<void>;
112
117
  /**
113
118
  * @internal
@@ -115,11 +120,20 @@ export declare class ACL extends EventEmitter {
115
120
  getActionParams(ctx: any): Promise<void>;
116
121
  addFixedParams(resource: string, action: string, merger: Merger): void;
117
122
  registerSnippet(snippet: SnippetOptions): void;
118
- /**
119
- * @internal
120
- */
121
- filterParams(ctx: any, resourceName: any, params: any): any;
122
123
  protected addCoreMiddleware(): void;
123
124
  protected isAvailableAction(actionName: string): boolean;
124
125
  }
126
+ export declare function createUserProvider(options: {
127
+ db?: Database;
128
+ dataSourceManager?: any;
129
+ currentUser?: any;
130
+ }): UserProvider;
131
+ /**
132
+ * @internal
133
+ */
134
+ export declare function parseJsonTemplate(filter: any, options: ParseJsonTemplateOptions): Promise<any>;
135
+ /**
136
+ * @internal
137
+ */
138
+ export declare function checkFilterParams(collection: Collection, filter: any): void;
125
139
  export {};
package/lib/acl.js CHANGED
@@ -37,7 +37,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
37
37
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
38
  var acl_exports = {};
39
39
  __export(acl_exports, {
40
- ACL: () => ACL
40
+ ACL: () => ACL,
41
+ checkFilterParams: () => checkFilterParams,
42
+ createUserProvider: () => createUserProvider,
43
+ parseJsonTemplate: () => parseJsonTemplate
41
44
  });
42
45
  module.exports = __toCommonJS(acl_exports);
43
46
  var import_utils = require("@nocobase/utils");
@@ -276,31 +279,6 @@ const _ACL = class _ACL extends import_events.default {
276
279
  this.allowManager.allow(resourceName, actionName, condition);
277
280
  }
278
281
  }
279
- /**
280
- * @internal
281
- */
282
- async parseJsonTemplate(json, ctx) {
283
- var _a, _b, _c, _d, _e;
284
- if (json.filter) {
285
- (_b = (_a = ctx.logger) == null ? void 0 : _a.info) == null ? void 0 : _b.call(_a, "parseJsonTemplate.raw", JSON.parse(JSON.stringify(json.filter)));
286
- const timezone = (_c = ctx == null ? void 0 : ctx.get) == null ? void 0 : _c.call(ctx, "x-timezone");
287
- const state = JSON.parse(JSON.stringify(ctx.state));
288
- const filter = await (0, import_utils.parseFilter)(json.filter, {
289
- timezone,
290
- now: (/* @__PURE__ */ new Date()).toISOString(),
291
- vars: {
292
- ctx: {
293
- state
294
- },
295
- $user: getUser(ctx),
296
- $nRole: /* @__PURE__ */ __name(() => state.currentRole, "$nRole")
297
- }
298
- });
299
- json.filter = filter;
300
- (_e = (_d = ctx.logger) == null ? void 0 : _d.info) == null ? void 0 : _e.call(_d, "parseJsonTemplate.parsed", filter);
301
- }
302
- return json;
303
- }
304
282
  middleware() {
305
283
  const acl = this;
306
284
  return /* @__PURE__ */ __name(async function ACLMiddleware(ctx, next) {
@@ -371,40 +349,11 @@ const _ACL = class _ACL extends import_events.default {
371
349
  registerSnippet(snippet) {
372
350
  this.snippetManager.register(snippet);
373
351
  }
374
- /**
375
- * @internal
376
- */
377
- filterParams(ctx, resourceName, params) {
378
- var _a, _b, _c;
379
- if ((_a = params == null ? void 0 : params.filter) == null ? void 0 : _a.createdById) {
380
- const collection = ctx.db.getCollection(resourceName);
381
- if (!collection || !collection.getField("createdById")) {
382
- throw new import_no_permission_error.NoPermissionError("createdById field not found");
383
- }
384
- }
385
- if ((_c = (_b = params == null ? void 0 : params.filter) == null ? void 0 : _b.$or) == null ? void 0 : _c.length) {
386
- const checkCreatedById = /* @__PURE__ */ __name((items) => {
387
- return items.some(
388
- (x) => {
389
- var _a2, _b2;
390
- return "createdById" in x || ((_a2 = x.$or) == null ? void 0 : _a2.some((y) => "createdById" in y)) || ((_b2 = x.$and) == null ? void 0 : _b2.some((y) => "createdById" in y));
391
- }
392
- );
393
- }, "checkCreatedById");
394
- if (checkCreatedById(params.filter.$or)) {
395
- const collection = ctx.db.getCollection(resourceName);
396
- if (!collection || !collection.getField("createdById")) {
397
- throw new import_no_permission_error.NoPermissionError("createdById field not found");
398
- }
399
- }
400
- }
401
- return params;
402
- }
403
352
  addCoreMiddleware() {
404
353
  const acl = this;
405
354
  this.middlewares.add(
406
355
  async (ctx, next) => {
407
- var _a, _b, _c, _d;
356
+ var _a, _b, _c, _d, _e, _f;
408
357
  const resourcerAction = ctx.action;
409
358
  const { resourceName, actionName } = ctx.permission;
410
359
  const permission = ctx.permission;
@@ -417,10 +366,20 @@ const _ACL = class _ACL extends import_events.default {
417
366
  ((_c = ctx.log) == null ? void 0 : _c.debug) && ctx.log.debug("acl params", params);
418
367
  try {
419
368
  if (params && resourcerAction.mergeParams) {
420
- const filteredParams = acl.filterParams(ctx, resourceName, params);
421
- const parsedParams = await acl.parseJsonTemplate(filteredParams, ctx);
369
+ const db = ctx.database ?? ctx.db;
370
+ const collection = (_d = db == null ? void 0 : db.getCollection) == null ? void 0 : _d.call(db, resourceName);
371
+ checkFilterParams(collection, params == null ? void 0 : params.filter);
372
+ const parsedFilter = await parseJsonTemplate(params.filter, {
373
+ state: ctx.state,
374
+ timezone: getTimezone(ctx),
375
+ userProvider: createUserProvider({
376
+ db: ctx.db,
377
+ currentUser: (_e = ctx.state) == null ? void 0 : _e.currentUser
378
+ })
379
+ });
380
+ const parsedParams = params.filter ? { ...params, filter: parsedFilter ?? params.filter } : params;
422
381
  ctx.permission.parsedParams = parsedParams;
423
- ((_d = ctx.log) == null ? void 0 : _d.debug) && ctx.log.debug("acl parsedParams", parsedParams);
382
+ ((_f = ctx.log) == null ? void 0 : _f.debug) && ctx.log.debug("acl parsedParams", parsedParams);
424
383
  ctx.permission.rawParams = import_lodash.default.cloneDeep(resourcerAction.params);
425
384
  if (parsedParams.appends && resourcerAction.params.fields) {
426
385
  for (const queryField of resourcerAction.params.fields) {
@@ -471,31 +430,99 @@ const _ACL = class _ACL extends import_events.default {
471
430
  };
472
431
  __name(_ACL, "ACL");
473
432
  let ACL = _ACL;
474
- function getUser(ctx) {
475
- const dataSource = ctx.app.dataSourceManager.dataSources.get("main");
476
- const db = dataSource.collectionManager.db;
433
+ function getTimezone(ctx) {
434
+ var _a, _b, _c, _d, _e, _f;
435
+ return ((_b = (_a = ctx == null ? void 0 : ctx.request) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "x-timezone")) ?? ((_d = (_c = ctx == null ? void 0 : ctx.request) == null ? void 0 : _c.header) == null ? void 0 : _d["x-timezone"]) ?? ((_f = (_e = ctx == null ? void 0 : ctx.req) == null ? void 0 : _e.headers) == null ? void 0 : _f["x-timezone"]);
436
+ }
437
+ __name(getTimezone, "getTimezone");
438
+ function createUserProvider(options) {
439
+ var _a, _b, _c, _d, _e;
440
+ const db = options.db ?? ((_e = (_d = (_c = (_b = (_a = options.dataSourceManager) == null ? void 0 : _a.dataSources) == null ? void 0 : _b.get) == null ? void 0 : _c.call(_b, "main")) == null ? void 0 : _d.collectionManager) == null ? void 0 : _e.db);
441
+ const currentUser = options.currentUser;
477
442
  return async ({ fields }) => {
478
- var _a, _b;
479
- const userFields = fields.filter((f) => f && db.getFieldByPath("users." + f));
480
- (_a = ctx.logger) == null ? void 0 : _a.info("filter-parse: ", { userFields });
481
- if (!ctx.state.currentUser) {
443
+ if (!db) {
482
444
  return;
483
445
  }
446
+ if (!currentUser) {
447
+ return;
448
+ }
449
+ const userFields = fields.filter((f) => f && db.getFieldByPath("users." + f));
484
450
  if (!userFields.length) {
485
451
  return;
486
452
  }
487
453
  const user = await db.getRepository("users").findOne({
488
- filterByTk: ctx.state.currentUser.id,
454
+ filterByTk: currentUser.id,
489
455
  fields: userFields
490
456
  });
491
- (_b = ctx.logger) == null ? void 0 : _b.info("filter-parse: ", {
492
- $user: user == null ? void 0 : user.toJSON()
493
- });
494
457
  return user;
495
458
  };
496
459
  }
497
- __name(getUser, "getUser");
460
+ __name(createUserProvider, "createUserProvider");
461
+ function containsCreatedByIdFilter(input, seen = /* @__PURE__ */ new Set()) {
462
+ if (!input) {
463
+ return false;
464
+ }
465
+ if (Array.isArray(input)) {
466
+ return input.some((item) => containsCreatedByIdFilter(item, seen));
467
+ }
468
+ if (!import_lodash.default.isPlainObject(input)) {
469
+ return false;
470
+ }
471
+ if (seen.has(input)) {
472
+ return false;
473
+ }
474
+ seen.add(input);
475
+ for (const [key, value] of Object.entries(input)) {
476
+ if (isCreatedByIdKey(key)) {
477
+ return true;
478
+ }
479
+ if (containsCreatedByIdFilter(value, seen)) {
480
+ return true;
481
+ }
482
+ }
483
+ return false;
484
+ }
485
+ __name(containsCreatedByIdFilter, "containsCreatedByIdFilter");
486
+ function isCreatedByIdKey(key) {
487
+ return key === "createdById" || key.startsWith("createdById.") || key.startsWith("createdById$");
488
+ }
489
+ __name(isCreatedByIdKey, "isCreatedByIdKey");
490
+ async function parseJsonTemplate(filter, options) {
491
+ if (!filter) {
492
+ return filter;
493
+ }
494
+ const timezone = options == null ? void 0 : options.timezone;
495
+ const state = JSON.parse(JSON.stringify((options == null ? void 0 : options.state) || {}));
496
+ const parsedFilter = await (0, import_utils.parseFilter)(filter, {
497
+ timezone,
498
+ now: (/* @__PURE__ */ new Date()).toISOString(),
499
+ vars: {
500
+ ctx: {
501
+ state
502
+ },
503
+ $user: (options == null ? void 0 : options.userProvider) || (async () => void 0),
504
+ $nRole: /* @__PURE__ */ __name(() => state.currentRole, "$nRole")
505
+ }
506
+ });
507
+ return parsedFilter;
508
+ }
509
+ __name(parseJsonTemplate, "parseJsonTemplate");
510
+ function checkFilterParams(collection, filter) {
511
+ if (!filter) {
512
+ return;
513
+ }
514
+ if (!containsCreatedByIdFilter(filter)) {
515
+ return;
516
+ }
517
+ if (!collection || !collection.getField("createdById")) {
518
+ throw new import_no_permission_error.NoPermissionError("createdById field not found");
519
+ }
520
+ }
521
+ __name(checkFilterParams, "checkFilterParams");
498
522
  // Annotate the CommonJS export names for ESM import in node:
499
523
  0 && (module.exports = {
500
- ACL
524
+ ACL,
525
+ checkFilterParams,
526
+ createUserProvider,
527
+ parseJsonTemplate
501
528
  });
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@nocobase/acl",
3
- "version": "1.9.47",
3
+ "version": "1.9.49",
4
4
  "description": "",
5
5
  "license": "AGPL-3.0",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
8
8
  "dependencies": {
9
- "@nocobase/resourcer": "1.9.47",
10
- "@nocobase/utils": "1.9.47",
9
+ "@nocobase/resourcer": "1.9.49",
10
+ "@nocobase/utils": "1.9.49",
11
11
  "minimatch": "^5.1.1"
12
12
  },
13
13
  "repository": {
@@ -15,5 +15,5 @@
15
15
  "url": "git+https://github.com/nocobase/nocobase.git",
16
16
  "directory": "packages/acl"
17
17
  },
18
- "gitHead": "bc3787003d581cc1453444de53d23c7c41b862d8"
18
+ "gitHead": "b90fa583f837a84067c354a72574e738cc6c3281"
19
19
  }