@nocobase/plugin-acl 2.1.0-alpha.2 → 2.1.0-alpha.20

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 (37) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/client-v2.d.ts +2 -0
  4. package/client-v2.js +1 -0
  5. package/dist/client/949.7ad4ad3b554e5452.js +10 -0
  6. package/dist/client/971.50ecf7b6ac572080.js +10 -0
  7. package/dist/client/index.js +1 -1
  8. package/dist/client-v2/139.929bc11d582ef7d4.js +10 -0
  9. package/dist/client-v2/193.3245b23f17b4c9f8.js +10 -0
  10. package/dist/client-v2/366.069b6cf12cfb9a67.js +10 -0
  11. package/dist/client-v2/627.ce101823deb86dd6.js +10 -0
  12. package/dist/client-v2/index.d.ts +9 -0
  13. package/dist/client-v2/index.js +10 -0
  14. package/dist/client-v2/plugin.d.ts +5 -0
  15. package/dist/client-v2/routes/AppInfoDemoRoute.d.ts +10 -0
  16. package/dist/client-v2/routes/DemoHomepageRoute.d.ts +10 -0
  17. package/dist/client-v2/routes/FlowSettingsComponentLoaderDemoRoute.d.ts +2 -0
  18. package/dist/client-v2/settings/DemoFlowSettingsLazyField.d.ts +10 -0
  19. package/dist/externalVersion.js +11 -9
  20. package/dist/server/index.d.ts +1 -0
  21. package/dist/server/index.js +2 -0
  22. package/dist/server/middlewares/check-association-operate.js +14 -5
  23. package/dist/server/middlewares/check-change-with-association.d.ts +21 -0
  24. package/dist/server/middlewares/check-change-with-association.js +327 -246
  25. package/dist/server/middlewares/check-query-permission.d.ts +10 -0
  26. package/dist/server/middlewares/check-query-permission.js +64 -0
  27. package/dist/server/middlewares/with-acl-meta.js +7 -2
  28. package/dist/server/migrations/20251119225252-update-member-default-permission.js +1 -1
  29. package/dist/server/query/apply-query-permission.d.ts +27 -0
  30. package/dist/server/query/apply-query-permission.js +242 -0
  31. package/dist/server/server.d.ts +6 -1
  32. package/dist/server/server.js +8 -1
  33. package/dist/swagger/index.d.ts +962 -143
  34. package/dist/swagger/index.js +854 -183
  35. package/package.json +5 -3
  36. package/dist/client/0655d5ded9f45bb1.js +0 -10
  37. package/dist/client/50204a14518b3a0d.js +0 -10
@@ -36,42 +36,260 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var check_change_with_association_exports = {};
38
38
  __export(check_change_with_association_exports, {
39
- checkChangesWithAssociation: () => checkChangesWithAssociation
39
+ checkChangesWithAssociation: () => checkChangesWithAssociation,
40
+ sanitizeAssociationValues: () => sanitizeAssociationValues
40
41
  });
41
42
  module.exports = __toCommonJS(check_change_with_association_exports);
42
43
  var import_acl = require("@nocobase/acl");
43
44
  var import_lodash = __toESM(require("lodash"));
45
+ async function sanitizeAssociationValues(options) {
46
+ var _a, _b, _c;
47
+ const {
48
+ acl,
49
+ resourceName,
50
+ actionName,
51
+ values,
52
+ updateAssociationValues = [],
53
+ protectedKeys = [],
54
+ aclParams
55
+ } = options;
56
+ if (import_lodash.default.isEmpty(values)) {
57
+ return values;
58
+ }
59
+ const collection = options.collection ?? ((_b = (_a = options.database ?? options.db) == null ? void 0 : _a.getCollection) == null ? void 0 : _b.call(_a, resourceName));
60
+ if (!collection) {
61
+ return values;
62
+ }
63
+ const params = aclParams ?? (acl ? (_c = acl.fixedParamsManager) == null ? void 0 : _c.getParams(resourceName, actionName) : void 0);
64
+ const roles = options.roles;
65
+ const can = (canOptions) => (acl == null ? void 0 : acl.can({ roles: (roles == null ? void 0 : roles.length) ? roles : ["anonymous"], ...canOptions })) ?? null;
66
+ const parseOptions = {
67
+ timezone: options.timezone,
68
+ userProvider: options.userProvider,
69
+ state: {
70
+ ...options.state || {},
71
+ currentRole: options.currentRole,
72
+ currentRoles: options.roles,
73
+ currentUser: options.currentUser
74
+ }
75
+ };
76
+ return await processValues({
77
+ values,
78
+ updateAssociationValues,
79
+ aclParams: params,
80
+ collection,
81
+ lastFieldPath: "",
82
+ protectedKeys,
83
+ can,
84
+ parseOptions
85
+ });
86
+ }
87
+ const checkChangesWithAssociation = async (ctx, next) => {
88
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
89
+ const timezone = ((_b = (_a = ctx.request) == null ? void 0 : _a.get) == null ? void 0 : _b.call(_a, "x-timezone")) ?? ((_d = (_c = ctx.request) == null ? void 0 : _c.header) == null ? void 0 : _d["x-timezone"]) ?? ((_f = (_e = ctx.req) == null ? void 0 : _e.headers) == null ? void 0 : _f["x-timezone"]);
90
+ const { resourceName, actionName } = ctx.action;
91
+ if (!["create", "firstOrCreate", "updateOrCreate", "update"].includes(actionName)) {
92
+ return next();
93
+ }
94
+ if ((_g = ctx.permission) == null ? void 0 : _g.skip) {
95
+ return next();
96
+ }
97
+ const roles = ctx.state.currentRoles;
98
+ if (roles.includes("root")) {
99
+ return next();
100
+ }
101
+ const acl = ctx.acl;
102
+ for (const role of roles) {
103
+ const aclRole = acl.getRole(role);
104
+ if (aclRole == null ? void 0 : aclRole.snippetAllowed(`${resourceName}:${actionName}`)) {
105
+ return next();
106
+ }
107
+ }
108
+ const params = ctx.action.params || {};
109
+ const rawValues = params.values;
110
+ if (import_lodash.default.isEmpty(rawValues)) {
111
+ return next();
112
+ }
113
+ const protectedKeys = ["firstOrCreate", "updateOrCreate"].includes(actionName) ? params.filterKeys || [] : [];
114
+ const collection = (_i = (_h = ctx.database ?? ctx.db) == null ? void 0 : _h.getCollection) == null ? void 0 : _i.call(_h, resourceName);
115
+ const processed = await sanitizeAssociationValues({
116
+ acl,
117
+ collection,
118
+ resourceName,
119
+ actionName,
120
+ values: rawValues,
121
+ updateAssociationValues: params.updateAssociationValues || [],
122
+ protectedKeys,
123
+ roles,
124
+ currentRole: ctx.state.currentRole,
125
+ currentUser: ctx.state.currentUser,
126
+ state: import_lodash.default.clone(ctx.state),
127
+ aclParams: (_k = (_j = ctx.permission) == null ? void 0 : _j.can) == null ? void 0 : _k.params,
128
+ timezone,
129
+ userProvider: (0, import_acl.createUserProvider)({
130
+ dataSourceManager: (_l = ctx.app) == null ? void 0 : _l.dataSourceManager,
131
+ currentUser: (_m = ctx.state) == null ? void 0 : _m.currentUser
132
+ })
133
+ });
134
+ ctx.action.params.values = processed;
135
+ await next();
136
+ };
137
+ async function processValues(options) {
138
+ var _a, _b;
139
+ const {
140
+ values,
141
+ updateAssociationValues,
142
+ aclParams,
143
+ collection,
144
+ lastFieldPath = "",
145
+ protectedKeys = [],
146
+ can,
147
+ parseOptions
148
+ } = options;
149
+ if (Array.isArray(values)) {
150
+ const result = [];
151
+ for (const item of values) {
152
+ if (!import_lodash.default.isPlainObject(item)) {
153
+ result.push(item);
154
+ continue;
155
+ }
156
+ const processed = await processValues({
157
+ values: item,
158
+ updateAssociationValues,
159
+ aclParams,
160
+ collection,
161
+ lastFieldPath,
162
+ protectedKeys,
163
+ can,
164
+ parseOptions
165
+ });
166
+ if (processed !== null && processed !== void 0) {
167
+ result.push(processed);
168
+ }
169
+ }
170
+ return result;
171
+ }
172
+ if (!values || !import_lodash.default.isPlainObject(values)) {
173
+ return values;
174
+ }
175
+ if (!collection) {
176
+ return values;
177
+ }
178
+ let v = values;
179
+ if (aclParams == null ? void 0 : aclParams.whitelist) {
180
+ const combined = import_lodash.default.uniq([...aclParams.whitelist, ...protectedKeys]);
181
+ v = import_lodash.default.pick(values, combined);
182
+ }
183
+ for (const [fieldName, fieldValue] of Object.entries(v)) {
184
+ if (protectedKeys.includes(fieldName)) {
185
+ continue;
186
+ }
187
+ const field = collection.getField(fieldName);
188
+ const isAssociation = field && ["hasOne", "hasMany", "belongsTo", "belongsToMany", "belongsToArray"].includes(field.type);
189
+ if (!isAssociation) {
190
+ continue;
191
+ }
192
+ const targetCollection = collection.db.getCollection(field.target);
193
+ if (!targetCollection) {
194
+ delete v[fieldName];
195
+ continue;
196
+ }
197
+ const fieldPath = lastFieldPath ? `${lastFieldPath}.${fieldName}` : fieldName;
198
+ const recordKey = field.type === "hasOne" ? targetCollection.model.primaryKeyAttribute : field.targetKey;
199
+ const canUpdateAssociation = updateAssociationValues.includes(fieldPath);
200
+ if (!canUpdateAssociation) {
201
+ const normalized = normalizeAssociationValue(fieldValue, recordKey);
202
+ if (normalized === void 0 && !protectedKeys.includes(fieldName)) {
203
+ delete v[fieldName];
204
+ } else {
205
+ v[fieldName] = normalized;
206
+ }
207
+ continue;
208
+ }
209
+ const createParams = can == null ? void 0 : can({
210
+ resource: field.target,
211
+ action: "create"
212
+ });
213
+ const updateParams = can == null ? void 0 : can({
214
+ resource: field.target,
215
+ action: "update"
216
+ });
217
+ if (Array.isArray(fieldValue)) {
218
+ const processed = [];
219
+ let allowedRecordKeys;
220
+ let existingRecordKeys;
221
+ if (updateParams) {
222
+ const allowedResult = await collectAllowedRecordKeys(
223
+ fieldValue,
224
+ recordKey,
225
+ (_a = updateParams == null ? void 0 : updateParams.params) == null ? void 0 : _a.filter,
226
+ targetCollection,
227
+ parseOptions
228
+ );
229
+ allowedRecordKeys = allowedResult == null ? void 0 : allowedResult.allowedKeys;
230
+ if (createParams && ((_b = allowedResult == null ? void 0 : allowedResult.missingKeys) == null ? void 0 : _b.size)) {
231
+ existingRecordKeys = await collectExistingRecordKeys(recordKey, targetCollection, allowedResult.missingKeys);
232
+ }
233
+ }
234
+ for (const item of fieldValue) {
235
+ const r2 = await processAssociationChild({
236
+ value: item,
237
+ recordKey,
238
+ updateAssociationValues,
239
+ createParams,
240
+ updateParams,
241
+ target: targetCollection,
242
+ fieldPath,
243
+ allowedRecordKeys,
244
+ existingRecordKeys,
245
+ can,
246
+ parseOptions
247
+ });
248
+ if (r2 !== null && r2 !== void 0) {
249
+ processed.push(r2);
250
+ }
251
+ }
252
+ v[fieldName] = processed;
253
+ continue;
254
+ }
255
+ const r = await processAssociationChild({
256
+ value: fieldValue,
257
+ recordKey,
258
+ updateAssociationValues,
259
+ createParams,
260
+ updateParams,
261
+ target: targetCollection,
262
+ fieldPath,
263
+ can,
264
+ parseOptions
265
+ });
266
+ v[fieldName] = r;
267
+ }
268
+ return v;
269
+ }
44
270
  function normalizeAssociationValue(value, recordKey) {
45
271
  if (!value) {
46
272
  return value;
47
273
  }
48
274
  if (Array.isArray(value)) {
49
275
  const result = value.map((v) => typeof v === "number" || typeof v === "string" ? v : v[recordKey]).filter((v) => v !== null && v !== void 0);
50
- return result.length > 0 ? result : void 0;
51
- } else {
52
- return typeof value === "number" || typeof value === "string" ? value : value[recordKey];
53
- }
54
- }
55
- async function resolveScopeFilter(ctx, target, params) {
56
- if (!params) {
57
- return {};
276
+ return result;
58
277
  }
59
- const filteredParams = ctx.acl.filterParams(ctx, target, params);
60
- const parsedParams = await ctx.acl.parseJsonTemplate(filteredParams, ctx);
61
- return parsedParams.filter || {};
278
+ return typeof value === "number" || typeof value === "string" ? value : value[recordKey];
62
279
  }
63
- async function collectAllowedRecordKeys(ctx, items, recordKey, updateParams, target) {
64
- const repo = ctx.database.getRepository(target);
65
- if (!repo) {
66
- return void 0;
280
+ async function collectAllowedRecordKeys(items, recordKey, filter, collection, parseOptions) {
281
+ if (!collection) {
282
+ return;
67
283
  }
284
+ const { repository } = collection;
68
285
  const keys = items.map((item) => import_lodash.default.isPlainObject(item) ? item[recordKey] : void 0).filter((key) => key !== void 0 && key !== null);
69
286
  if (!keys.length) {
70
- return void 0;
287
+ return;
71
288
  }
72
289
  try {
73
- const scopedFilter = await resolveScopeFilter(ctx, target, updateParams == null ? void 0 : updateParams.params);
74
- const records = await repo.find({
290
+ (0, import_acl.checkFilterParams)(collection, filter);
291
+ const scopedFilter = filter ? await (0, import_acl.parseJsonTemplate)(filter, parseOptions) : {};
292
+ const records = await repository.find({
75
293
  filter: {
76
294
  ...scopedFilter,
77
295
  [`${recordKey}.$in`]: keys
@@ -96,16 +314,16 @@ async function collectAllowedRecordKeys(ctx, items, recordKey, updateParams, tar
96
314
  throw e;
97
315
  }
98
316
  }
99
- async function collectExistingRecordKeys(ctx, recordKey, target, keys) {
100
- const repo = ctx.database.getRepository(target);
101
- if (!repo) {
317
+ async function collectExistingRecordKeys(recordKey, collection, keys) {
318
+ const { repository } = collection;
319
+ if (!repository) {
102
320
  return /* @__PURE__ */ new Set();
103
321
  }
104
322
  const keyList = Array.from(keys);
105
323
  if (!keyList.length) {
106
324
  return /* @__PURE__ */ new Set();
107
325
  }
108
- const records = await repo.find({
326
+ const records = await repository.find({
109
327
  filter: {
110
328
  [`${recordKey}.$in`]: keyList
111
329
  }
@@ -119,43 +337,55 @@ async function collectExistingRecordKeys(ctx, recordKey, target, keys) {
119
337
  }
120
338
  return existingKeys;
121
339
  }
122
- async function recordExistsWithoutScope(ctx, target, recordKey, keyValue) {
123
- const repo = ctx.database.getRepository(target);
124
- if (!repo) {
340
+ async function recordExistsWithoutScope(collection, recordKey, keyValue) {
341
+ const { repository } = collection;
342
+ if (!repository) {
125
343
  return false;
126
344
  }
127
- const record = await repo.findOne({
345
+ const record = await repository.findOne({
128
346
  filter: {
129
347
  [recordKey]: keyValue
130
348
  }
131
349
  });
132
350
  return Boolean(record);
133
351
  }
134
- async function processAssociationChild(ctx, value, recordKey, updateAssociationValues, createParams, updateParams, target, fieldPath, allowedRecordKeys, existingRecordKeys) {
352
+ async function processAssociationChild(options) {
353
+ var _a, _b;
354
+ const {
355
+ value,
356
+ recordKey,
357
+ updateAssociationValues,
358
+ createParams,
359
+ updateParams,
360
+ target,
361
+ fieldPath,
362
+ allowedRecordKeys,
363
+ existingRecordKeys,
364
+ can,
365
+ parseOptions
366
+ } = options;
135
367
  const keyValue = value == null ? void 0 : value[recordKey];
136
368
  const fallbackToCreate = async () => {
137
369
  if (!createParams) {
138
370
  return keyValue;
139
371
  }
140
- ctx.log.debug(`Association record missing, fallback to create`, {
141
- fieldPath,
142
- value,
143
- target
372
+ return await processValues({
373
+ values: value,
374
+ updateAssociationValues,
375
+ aclParams: createParams.params,
376
+ collection: target,
377
+ lastFieldPath: fieldPath,
378
+ protectedKeys: [],
379
+ can,
380
+ parseOptions
144
381
  });
145
- return await processValues(ctx, value, updateAssociationValues, createParams.params, target, fieldPath, []);
146
382
  };
147
383
  const tryFallbackToCreate = async (reason, knownExists) => {
148
384
  if (!createParams) {
149
385
  return void 0;
150
386
  }
151
- const recordExists = typeof knownExists === "boolean" ? knownExists : await recordExistsWithoutScope(ctx, target, recordKey, keyValue);
387
+ const recordExists = typeof knownExists === "boolean" ? knownExists : await recordExistsWithoutScope(target, recordKey, keyValue);
152
388
  if (!recordExists) {
153
- ctx.log.debug(reason, {
154
- fieldPath,
155
- value,
156
- createParams,
157
- updateParams
158
- });
159
389
  return await fallbackToCreate();
160
390
  }
161
391
  return void 0;
@@ -166,225 +396,76 @@ async function processAssociationChild(ctx, value, recordKey, updateAssociationV
166
396
  if (created !== void 0) {
167
397
  return created;
168
398
  }
169
- ctx.log.debug(`No permission to update association`, { fieldPath, value, updateParams });
170
399
  return keyValue;
171
- } else {
172
- const repo = ctx.database.getRepository(target);
173
- if (!repo) {
174
- ctx.log.debug(`Repository not found for association target`, { fieldPath, target });
175
- return keyValue;
176
- }
177
- try {
178
- if (allowedRecordKeys) {
179
- if (!allowedRecordKeys.has(keyValue)) {
180
- const created = await tryFallbackToCreate(
181
- `No permission to update association due to scope, try create not exist record`,
182
- existingRecordKeys ? existingRecordKeys.has(keyValue) : void 0
183
- );
184
- if (created !== void 0) {
185
- return created;
186
- }
187
- ctx.log.debug(`No permission to update association due to scope`, { fieldPath, value, updateParams });
188
- return keyValue;
189
- }
190
- } else {
191
- const filter = await resolveScopeFilter(ctx, target, updateParams.params);
192
- const record = await repo.findOne({
193
- filter: {
194
- ...filter,
195
- [recordKey]: keyValue
196
- }
197
- });
198
- if (!record) {
199
- const created = await tryFallbackToCreate(
200
- `No permission to update association due to scope, try create not exist record`
201
- );
202
- if (created !== void 0) {
203
- return created;
204
- }
205
- ctx.log.debug(`No permission to update association due to scope`, { fieldPath, value, updateParams });
206
- return keyValue;
400
+ }
401
+ const { repository } = target;
402
+ if (!repository) {
403
+ return keyValue;
404
+ }
405
+ try {
406
+ if (allowedRecordKeys) {
407
+ if (!allowedRecordKeys.has(keyValue)) {
408
+ const created = await tryFallbackToCreate(
409
+ `No permission to update association due to scope, try create not exist record`,
410
+ existingRecordKeys ? existingRecordKeys.has(keyValue) : void 0
411
+ );
412
+ if (created !== void 0) {
413
+ return created;
207
414
  }
208
- }
209
- return await processValues(ctx, value, updateAssociationValues, updateParams.params, target, fieldPath, []);
210
- } catch (e) {
211
- if (e instanceof import_acl.NoPermissionError) {
212
415
  return keyValue;
213
416
  }
214
- throw e;
215
- }
216
- }
217
- }
218
- if (createParams) {
219
- return await processValues(ctx, value, updateAssociationValues, createParams.params, target, fieldPath, []);
220
- }
221
- ctx.log.debug(`No permission to create association`, { fieldPath, value, createParams });
222
- return null;
223
- }
224
- async function processValues(ctx, values, updateAssociationValues, aclParams, collectionName, lastFieldPath = "", protectedKeys = []) {
225
- var _a;
226
- if (Array.isArray(values)) {
227
- const result = [];
228
- for (const item of values) {
229
- if (!import_lodash.default.isPlainObject(item)) {
230
- result.push(item);
231
- continue;
232
- }
233
- const processed = await processValues(
234
- ctx,
235
- item,
236
- updateAssociationValues,
237
- aclParams,
238
- collectionName,
239
- lastFieldPath,
240
- protectedKeys
241
- );
242
- if (processed !== null && processed !== void 0) {
243
- result.push(processed);
244
- }
245
- }
246
- return result;
247
- }
248
- if (!values || !import_lodash.default.isPlainObject(values)) {
249
- return values;
250
- }
251
- const db = ctx.database;
252
- const collection = db.getCollection(collectionName);
253
- if (!collection) {
254
- return values;
255
- }
256
- if (aclParams == null ? void 0 : aclParams.whitelist) {
257
- const combined = import_lodash.default.uniq([...aclParams.whitelist, ...protectedKeys]);
258
- values = import_lodash.default.pick(values, combined);
259
- }
260
- for (const [fieldName, fieldValue] of Object.entries(values)) {
261
- if (protectedKeys.includes(fieldName)) {
262
- continue;
263
- }
264
- const field = collection.getField(fieldName);
265
- const isAssociation = field && ["hasOne", "hasMany", "belongsTo", "belongsToMany", "belongsToArray"].includes(field.type);
266
- if (!isAssociation) {
267
- continue;
268
- }
269
- const targetCollection = db.getCollection(field.target);
270
- if (!targetCollection) {
271
- delete values[fieldName];
272
- continue;
273
- }
274
- const fieldPath = lastFieldPath ? `${lastFieldPath}.${fieldName}` : fieldName;
275
- const recordKey = field.type === "hasOne" ? targetCollection.model.primaryKeyAttribute : field.targetKey;
276
- const canUpdateAssociation = updateAssociationValues.includes(fieldPath);
277
- if (!canUpdateAssociation) {
278
- const normalized = normalizeAssociationValue(fieldValue, recordKey);
279
- if (normalized === void 0 && !protectedKeys.includes(fieldName)) {
280
- delete values[fieldName];
281
417
  } else {
282
- values[fieldName] = normalized;
418
+ (0, import_acl.checkFilterParams)(target, (_a = updateParams.params) == null ? void 0 : _a.filter);
419
+ const filter = await (0, import_acl.parseJsonTemplate)((_b = updateParams.params) == null ? void 0 : _b.filter, parseOptions);
420
+ const record = await repository.findOne({
421
+ filter: {
422
+ ...filter,
423
+ [recordKey]: keyValue
424
+ }
425
+ });
426
+ if (!record) {
427
+ const created = await tryFallbackToCreate(
428
+ `No permission to update association due to scope, try create not exist record`
429
+ );
430
+ if (created !== void 0) {
431
+ return created;
432
+ }
433
+ return keyValue;
434
+ }
283
435
  }
284
- ctx.log.debug(`Not allow to update association, only keep keys`, {
285
- fieldPath,
286
- fieldValue,
436
+ return await processValues({
437
+ values: value,
287
438
  updateAssociationValues,
288
- recordKey,
289
- normalizedValue: values[fieldName]
439
+ aclParams: updateParams.params,
440
+ collection: target,
441
+ lastFieldPath: fieldPath,
442
+ protectedKeys: [],
443
+ can,
444
+ parseOptions
290
445
  });
291
- continue;
292
- }
293
- const createParams = ctx.can({
294
- roles: ctx.state.currentRoles,
295
- resource: field.target,
296
- action: "create"
297
- });
298
- const updateParams = ctx.can({
299
- roles: ctx.state.currentRoles,
300
- resource: field.target,
301
- action: "update"
302
- });
303
- if (Array.isArray(fieldValue)) {
304
- const processed = [];
305
- let allowedRecordKeys;
306
- let existingRecordKeys;
307
- if (updateParams) {
308
- const allowedResult = await collectAllowedRecordKeys(ctx, fieldValue, recordKey, updateParams, field.target);
309
- allowedRecordKeys = allowedResult == null ? void 0 : allowedResult.allowedKeys;
310
- if (createParams && ((_a = allowedResult == null ? void 0 : allowedResult.missingKeys) == null ? void 0 : _a.size)) {
311
- existingRecordKeys = await collectExistingRecordKeys(ctx, recordKey, field.target, allowedResult.missingKeys);
312
- }
313
- }
314
- for (const item of fieldValue) {
315
- const r2 = await processAssociationChild(
316
- ctx,
317
- item,
318
- recordKey,
319
- updateAssociationValues,
320
- createParams,
321
- updateParams,
322
- field.target,
323
- fieldPath,
324
- allowedRecordKeys,
325
- existingRecordKeys
326
- );
327
- if (r2 !== null && r2 !== void 0) {
328
- processed.push(r2);
329
- }
446
+ } catch (e) {
447
+ if (e instanceof import_acl.NoPermissionError) {
448
+ return keyValue;
330
449
  }
331
- values[fieldName] = processed;
332
- continue;
450
+ throw e;
333
451
  }
334
- const r = await processAssociationChild(
335
- ctx,
336
- fieldValue,
337
- recordKey,
452
+ }
453
+ if (createParams) {
454
+ return await processValues({
455
+ values: value,
338
456
  updateAssociationValues,
339
- createParams,
340
- updateParams,
341
- field.target,
342
- fieldPath
343
- );
344
- values[fieldName] = r;
457
+ aclParams: createParams.params,
458
+ collection: target,
459
+ lastFieldPath: fieldPath,
460
+ protectedKeys: [],
461
+ can,
462
+ parseOptions
463
+ });
345
464
  }
346
- return values;
465
+ return null;
347
466
  }
348
- const checkChangesWithAssociation = async (ctx, next) => {
349
- var _a, _b;
350
- const { resourceName, actionName } = ctx.action;
351
- if (!["create", "firstOrCreate", "updateOrCreate", "update"].includes(actionName)) {
352
- return next();
353
- }
354
- if ((_a = ctx.permission) == null ? void 0 : _a.skip) {
355
- return next();
356
- }
357
- const roles = ctx.state.currentRoles;
358
- if (roles.includes("root")) {
359
- return next();
360
- }
361
- const acl = ctx.acl;
362
- for (const role of roles) {
363
- const aclRole = acl.getRole(role);
364
- if (aclRole.snippetAllowed(`${resourceName}:${actionName}`)) {
365
- return next();
366
- }
367
- }
368
- const params = ctx.action.params || {};
369
- const rawValues = params.values;
370
- if (import_lodash.default.isEmpty(rawValues)) {
371
- return next();
372
- }
373
- const protectedKeys = ["firstOrCreate", "updateOrCreate"].includes(actionName) ? params.filterKeys || [] : [];
374
- const aclParams = ((_b = ctx.permission.can) == null ? void 0 : _b.params) || ctx.acl.fixedParamsManager.getParams(resourceName, actionName);
375
- const processed = await processValues(
376
- ctx,
377
- rawValues,
378
- params.updateAssociationValues || [],
379
- aclParams,
380
- resourceName,
381
- "",
382
- protectedKeys
383
- );
384
- ctx.action.params.values = processed;
385
- await next();
386
- };
387
467
  // Annotate the CommonJS export names for ESM import in node:
388
468
  0 && (module.exports = {
389
- checkChangesWithAssociation
469
+ checkChangesWithAssociation,
470
+ sanitizeAssociationValues
390
471
  });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import type { Next } from '@nocobase/actions';
10
+ export declare function checkQueryPermission(ctx: any, next: Next): Promise<void>;