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

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.
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
- "@nocobase/client": "2.1.0-alpha.20",
11
+ "@nocobase/client": "2.1.0-alpha.22",
12
12
  "antd": "5.24.2",
13
13
  "react": "18.2.0",
14
14
  "react-i18next": "11.18.6",
@@ -17,16 +17,16 @@ module.exports = {
17
17
  "@formily/react": "2.3.7",
18
18
  "@ant-design/icons": "5.6.1",
19
19
  "lodash": "4.18.1",
20
- "@nocobase/utils": "2.1.0-alpha.20",
21
- "@nocobase/client-v2": "2.1.0-alpha.20",
22
- "@nocobase/actions": "2.1.0-alpha.20",
23
- "@nocobase/cache": "2.1.0-alpha.20",
24
- "@nocobase/database": "2.1.0-alpha.20",
25
- "@nocobase/server": "2.1.0-alpha.20",
26
- "@nocobase/acl": "2.1.0-alpha.20",
27
- "@nocobase/test": "2.1.0-alpha.20",
20
+ "@nocobase/utils": "2.1.0-alpha.22",
21
+ "@nocobase/client-v2": "2.1.0-alpha.22",
22
+ "@nocobase/actions": "2.1.0-alpha.22",
23
+ "@nocobase/cache": "2.1.0-alpha.22",
24
+ "@nocobase/database": "2.1.0-alpha.22",
25
+ "@nocobase/server": "2.1.0-alpha.22",
26
+ "@nocobase/acl": "2.1.0-alpha.22",
27
+ "@nocobase/test": "2.1.0-alpha.22",
28
28
  "@formily/core": "2.3.7",
29
29
  "@formily/antd-v5": "1.2.3",
30
30
  "antd-style": "3.7.1",
31
- "@nocobase/flow-engine": "2.1.0-alpha.20"
31
+ "@nocobase/flow-engine": "2.1.0-alpha.22"
32
32
  };
@@ -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 { Context, Next } from '@nocobase/actions';
10
+ export declare function applyDataPermissions(ctx: Context, next: Next): Promise<void>;
@@ -0,0 +1,208 @@
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
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var apply_data_permissions_exports = {};
28
+ __export(apply_data_permissions_exports, {
29
+ applyDataPermissions: () => applyDataPermissions
30
+ });
31
+ module.exports = __toCommonJS(apply_data_permissions_exports);
32
+ function ensureString(value, fieldName) {
33
+ if (typeof value !== "string" || !value.trim()) {
34
+ throw new Error(`Invalid ${fieldName}`);
35
+ }
36
+ return value.trim();
37
+ }
38
+ function normalizeFields(fields) {
39
+ if (!Array.isArray(fields)) {
40
+ return void 0;
41
+ }
42
+ const normalized = [
43
+ ...new Set(fields.filter((field) => typeof field === "string" && !!field.trim()))
44
+ ];
45
+ return normalized.length ? normalized : [];
46
+ }
47
+ function normalizeScopeId(scopeId) {
48
+ if (scopeId === null) {
49
+ return null;
50
+ }
51
+ if (scopeId === void 0) {
52
+ return void 0;
53
+ }
54
+ const parsed = Number(scopeId);
55
+ if (!Number.isInteger(parsed) || parsed <= 0) {
56
+ throw new Error(`Invalid scopeId: ${scopeId}`);
57
+ }
58
+ return parsed;
59
+ }
60
+ async function resolveScopeIdByKey(options) {
61
+ const scope = await options.ctx.db.getRepository("dataSourcesRolesResourcesScopes").findOne({
62
+ filter: {
63
+ dataSourceKey: options.dataSourceKey,
64
+ key: options.scopeKey
65
+ },
66
+ transaction: options.transaction
67
+ });
68
+ if (!scope) {
69
+ throw new Error(`Scope key "${options.scopeKey}" not found in data source "${options.dataSourceKey}"`);
70
+ }
71
+ return scope.get("id");
72
+ }
73
+ async function normalizeAction(options) {
74
+ const name = ensureString(options.action.name, "action.name");
75
+ let scopeId = normalizeScopeId(options.action.scopeId);
76
+ if (scopeId === void 0 && options.action.scope && typeof options.action.scope.id === "number") {
77
+ scopeId = normalizeScopeId(options.action.scope.id);
78
+ }
79
+ let scopeKey;
80
+ if (typeof options.action.scopeKey === "string" && options.action.scopeKey.trim()) {
81
+ scopeKey = options.action.scopeKey.trim();
82
+ } else if (options.action.scope && typeof options.action.scope.key === "string" && options.action.scope.key.trim()) {
83
+ scopeKey = options.action.scope.key.trim();
84
+ }
85
+ if (scopeId === void 0 && scopeKey) {
86
+ scopeId = await resolveScopeIdByKey({
87
+ ctx: options.ctx,
88
+ dataSourceKey: options.dataSourceKey,
89
+ scopeKey,
90
+ transaction: options.transaction
91
+ });
92
+ }
93
+ if (scopeId === void 0) {
94
+ scopeId = null;
95
+ }
96
+ const normalized = {
97
+ name,
98
+ fields: normalizeFields(options.action.fields),
99
+ scopeId
100
+ };
101
+ const output = {
102
+ name: normalized.name
103
+ };
104
+ if (normalized.fields !== void 0) {
105
+ output.fields = normalized.fields;
106
+ }
107
+ if (normalized.scopeId !== void 0) {
108
+ output.scopeId = normalized.scopeId;
109
+ }
110
+ return output;
111
+ }
112
+ async function applyDataPermissions(ctx, next) {
113
+ const roleName = ensureString(ctx.action.params.filterByTk, "filterByTk");
114
+ const values = ctx.action.params.values || {};
115
+ const dataSourceKey = ensureString(values.dataSourceKey || "main", "dataSourceKey");
116
+ const resources = values.resources;
117
+ if (!Array.isArray(resources) || resources.length === 0) {
118
+ throw new Error("`resources` must be a non-empty array");
119
+ }
120
+ const role = await ctx.db.getRepository("roles").findOne({
121
+ filterByTk: roleName
122
+ });
123
+ if (!role) {
124
+ throw new Error(`Role "${roleName}" not found`);
125
+ }
126
+ const roleResourceRepository = ctx.db.getRepository("roles.resources", roleName);
127
+ const appliedResources = [];
128
+ await ctx.db.sequelize.transaction(async (transaction) => {
129
+ for (const resourceInput of resources) {
130
+ const resourceName = ensureString(resourceInput == null ? void 0 : resourceInput.name, "resource.name");
131
+ const usingActionsConfig = (resourceInput == null ? void 0 : resourceInput.usingActionsConfig) ?? true;
132
+ const actionsInput = Array.isArray(resourceInput == null ? void 0 : resourceInput.actions) ? resourceInput.actions : [];
133
+ const normalizedActions = [];
134
+ for (const action of actionsInput) {
135
+ normalizedActions.push(
136
+ await normalizeAction({
137
+ ctx,
138
+ dataSourceKey,
139
+ action,
140
+ transaction
141
+ })
142
+ );
143
+ }
144
+ const existingResource = await roleResourceRepository.findOne({
145
+ filter: {
146
+ name: resourceName,
147
+ dataSourceKey
148
+ },
149
+ appends: ["actions"],
150
+ transaction
151
+ });
152
+ const writeValues = {
153
+ name: resourceName,
154
+ dataSourceKey,
155
+ usingActionsConfig,
156
+ actions: normalizedActions
157
+ };
158
+ let savedResource;
159
+ if (existingResource) {
160
+ await roleResourceRepository.update({
161
+ filterByTk: existingResource.get("id"),
162
+ values: writeValues,
163
+ updateAssociationValues: ["actions"],
164
+ transaction
165
+ });
166
+ savedResource = await roleResourceRepository.findOne({
167
+ filterByTk: existingResource.get("id"),
168
+ appends: ["actions"],
169
+ transaction
170
+ });
171
+ } else {
172
+ const created = await roleResourceRepository.create({
173
+ values: writeValues,
174
+ transaction
175
+ });
176
+ savedResource = await roleResourceRepository.findOne({
177
+ filterByTk: created.get("id"),
178
+ appends: ["actions"],
179
+ transaction
180
+ });
181
+ }
182
+ const actionModels = savedResource.get("actions") || [];
183
+ appliedResources.push({
184
+ id: savedResource.get("id"),
185
+ name: savedResource.get("name"),
186
+ dataSourceKey: savedResource.get("dataSourceKey"),
187
+ usingActionsConfig: !!savedResource.get("usingActionsConfig"),
188
+ actions: actionModels.map((actionModel) => ({
189
+ id: actionModel.get("id"),
190
+ name: actionModel.get("name"),
191
+ fields: actionModel.get("fields") || [],
192
+ scopeId: actionModel.get("scopeId") ?? null
193
+ }))
194
+ });
195
+ }
196
+ });
197
+ ctx.body = {
198
+ roleName,
199
+ dataSourceKey,
200
+ resources: appliedResources,
201
+ count: appliedResources.length
202
+ };
203
+ await next();
204
+ }
205
+ // Annotate the CommonJS export names for ESM import in node:
206
+ 0 && (module.exports = {
207
+ applyDataPermissions
208
+ });
@@ -0,0 +1,13 @@
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 { Context, Next } from '@nocobase/actions';
10
+ export declare function guardRolesDataSourcesCollectionsList(ctx: Context, next: Next): Promise<void>;
11
+ export declare function guardRolesDataSourceResourcesCreate(ctx: Context, next: Next): Promise<void>;
12
+ export declare function guardRolesDataSourceResourcesGet(ctx: Context, next: Next): Promise<void>;
13
+ export declare function guardRolesDataSourceResourcesUpdate(ctx: Context, next: Next): Promise<void>;
@@ -0,0 +1,189 @@
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
+
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+ var data_source_compat_exports = {};
38
+ __export(data_source_compat_exports, {
39
+ guardRolesDataSourceResourcesCreate: () => guardRolesDataSourceResourcesCreate,
40
+ guardRolesDataSourceResourcesGet: () => guardRolesDataSourceResourcesGet,
41
+ guardRolesDataSourceResourcesUpdate: () => guardRolesDataSourceResourcesUpdate,
42
+ guardRolesDataSourcesCollectionsList: () => guardRolesDataSourcesCollectionsList
43
+ });
44
+ module.exports = __toCommonJS(data_source_compat_exports);
45
+ var import_lodash = __toESM(require("lodash"));
46
+ function normalizeString(value) {
47
+ if (typeof value !== "string") {
48
+ return void 0;
49
+ }
50
+ const trimmed = value.trim();
51
+ return trimmed ? trimmed : void 0;
52
+ }
53
+ function normalizeFilter(input) {
54
+ if (!import_lodash.default.isPlainObject(input)) {
55
+ return {};
56
+ }
57
+ return { ...input };
58
+ }
59
+ function applyLocatorFromQuery(params, filter) {
60
+ const dataSourceKeyFromQuery = normalizeString(params.dataSourceKey);
61
+ if (dataSourceKeyFromQuery) {
62
+ filter.dataSourceKey = filter.dataSourceKey || dataSourceKeyFromQuery;
63
+ }
64
+ const nameFromQuery = normalizeString(params.name);
65
+ if (nameFromQuery) {
66
+ filter.name = filter.name || nameFromQuery;
67
+ }
68
+ }
69
+ function normalizeNumericTk(value) {
70
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
71
+ return value;
72
+ }
73
+ const normalized = normalizeString(value);
74
+ if (!normalized || !/^\d+$/.test(normalized)) {
75
+ return void 0;
76
+ }
77
+ return normalized;
78
+ }
79
+ function deriveNameFromPrefixedTk(value) {
80
+ const normalized = normalizeString(value);
81
+ if (!normalized) {
82
+ return void 0;
83
+ }
84
+ const matched = normalized.match(/^[a-zA-Z]+_(.+)$/);
85
+ if (!matched) {
86
+ return void 0;
87
+ }
88
+ return normalizeString(matched[1]);
89
+ }
90
+ async function resolveLocatorFromFilterByTk(ctx, roleName, filter) {
91
+ const rawFilterByTk = ctx.action.params.filterByTk;
92
+ if (rawFilterByTk === void 0 || rawFilterByTk === null || rawFilterByTk === "") {
93
+ return;
94
+ }
95
+ const numericFilterByTk = normalizeNumericTk(rawFilterByTk);
96
+ if (numericFilterByTk === void 0) {
97
+ if (!normalizeString(filter.name)) {
98
+ const derivedName = deriveNameFromPrefixedTk(rawFilterByTk);
99
+ if (derivedName) {
100
+ filter.name = derivedName;
101
+ }
102
+ }
103
+ return;
104
+ }
105
+ const resource = await ctx.db.getRepository("dataSourcesRolesResources").findOne({
106
+ filterByTk: numericFilterByTk
107
+ });
108
+ if (!resource) {
109
+ ctx.throw(404, `Resource permission not found by filterByTk "${rawFilterByTk}"`);
110
+ return;
111
+ }
112
+ const targetRoleName = resource.get("roleName");
113
+ if (targetRoleName !== roleName) {
114
+ ctx.throw(400, `Resource permission "${rawFilterByTk}" does not belong to role "${roleName}"`);
115
+ return;
116
+ }
117
+ if (!normalizeString(filter.dataSourceKey)) {
118
+ filter.dataSourceKey = resource.get("dataSourceKey");
119
+ }
120
+ if (!normalizeString(filter.name)) {
121
+ filter.name = resource.get("name");
122
+ }
123
+ }
124
+ async function normalizeRoleDataSourceResourceLocator(ctx) {
125
+ const roleName = normalizeString(ctx.action.params.associatedIndex);
126
+ if (!roleName) {
127
+ ctx.throw(400, "Role name is required");
128
+ return;
129
+ }
130
+ const filter = normalizeFilter(ctx.action.params.filter);
131
+ applyLocatorFromQuery(ctx.action.params, filter);
132
+ await resolveLocatorFromFilterByTk(ctx, roleName, filter);
133
+ const dataSourceKey = normalizeString(filter.dataSourceKey);
134
+ const name = normalizeString(filter.name);
135
+ if (!dataSourceKey || !name) {
136
+ ctx.throw(
137
+ 400,
138
+ "Missing resource locator: provide --filter-by-tk, or both --data-source-key and --name (or filter.{dataSourceKey,name})"
139
+ );
140
+ return;
141
+ }
142
+ ctx.action.params.filter = {
143
+ ...filter,
144
+ dataSourceKey,
145
+ name
146
+ };
147
+ }
148
+ async function guardRolesDataSourcesCollectionsList(ctx, next) {
149
+ const filter = normalizeFilter(ctx.action.params.filter);
150
+ applyLocatorFromQuery(ctx.action.params, filter);
151
+ const dataSourceKey = normalizeString(filter.dataSourceKey);
152
+ if (!dataSourceKey) {
153
+ ctx.throw(400, "dataSourceKey is required: pass --data-source-key or filter.dataSourceKey");
154
+ return;
155
+ }
156
+ ctx.action.params.filter = {
157
+ ...filter,
158
+ dataSourceKey
159
+ };
160
+ await next();
161
+ }
162
+ async function guardRolesDataSourceResourcesCreate(ctx, next) {
163
+ const values = normalizeFilter(ctx.action.params.values);
164
+ const dataSourceKeyFromQuery = normalizeString(ctx.action.params.dataSourceKey);
165
+ if (!values.dataSourceKey && dataSourceKeyFromQuery) {
166
+ values.dataSourceKey = dataSourceKeyFromQuery;
167
+ }
168
+ if (!normalizeString(values.dataSourceKey)) {
169
+ ctx.throw(400, "dataSourceKey is required for roles.dataSourceResources:create");
170
+ return;
171
+ }
172
+ ctx.action.params.values = values;
173
+ await next();
174
+ }
175
+ async function guardRolesDataSourceResourcesGet(ctx, next) {
176
+ await normalizeRoleDataSourceResourceLocator(ctx);
177
+ await next();
178
+ }
179
+ async function guardRolesDataSourceResourcesUpdate(ctx, next) {
180
+ await normalizeRoleDataSourceResourceLocator(ctx);
181
+ await next();
182
+ }
183
+ // Annotate the CommonJS export names for ESM import in node:
184
+ 0 && (module.exports = {
185
+ guardRolesDataSourceResourcesCreate,
186
+ guardRolesDataSourceResourcesGet,
187
+ guardRolesDataSourceResourcesUpdate,
188
+ guardRolesDataSourcesCollectionsList
189
+ });
@@ -46,6 +46,8 @@ var import_server = require("@nocobase/server");
46
46
  var import_lodash = __toESM(require("lodash"));
47
47
  var import_path = require("path");
48
48
  var import_available_actions = require("./actions/available-actions");
49
+ var import_apply_data_permissions = require("./actions/apply-data-permissions");
50
+ var import_data_source_compat = require("./actions/data-source-compat");
49
51
  var import_role_check = require("./actions/role-check");
50
52
  var import_role_collections = require("./actions/role-collections");
51
53
  var import_user_setDefaultRole = require("./actions/user-setDefaultRole");
@@ -181,7 +183,21 @@ class PluginACLServer extends import_server.Plugin {
181
183
  this.app.resourcer.define(import_available_actions.availableActionResource);
182
184
  this.app.resourcer.define(import_role_collections.roleCollectionsResource);
183
185
  this.app.resourcer.registerActionHandler("roles:setSystemRoleMode", import_union_role.setSystemRoleMode);
186
+ this.app.resourcer.registerActionHandler("roles:applyDataPermissions", import_apply_data_permissions.applyDataPermissions);
184
187
  this.app.resourcer.registerActionHandler("roles:check", import_role_check.checkAction);
188
+ this.app.resourcer.registerPreActionHandler(
189
+ "roles.dataSourcesCollections:list",
190
+ import_data_source_compat.guardRolesDataSourcesCollectionsList
191
+ );
192
+ this.app.resourcer.registerPreActionHandler(
193
+ "roles.dataSourceResources:create",
194
+ import_data_source_compat.guardRolesDataSourceResourcesCreate
195
+ );
196
+ this.app.resourcer.registerPreActionHandler("roles.dataSourceResources:get", import_data_source_compat.guardRolesDataSourceResourcesGet);
197
+ this.app.resourcer.registerPreActionHandler(
198
+ "roles.dataSourceResources:update",
199
+ import_data_source_compat.guardRolesDataSourceResourcesUpdate
200
+ );
185
201
  this.app.resourcer.registerActionHandler(`users:setDefaultRole`, import_user_setDefaultRole.setDefaultRole);
186
202
  this.db.on("users.afterCreateWithAssociations", async (model, options) => {
187
203
  const { transaction } = options;
@@ -198,6 +198,38 @@ declare const _default: {
198
198
  };
199
199
  };
200
200
  };
201
+ '/roles:applyDataPermissions': {
202
+ post: {
203
+ tags: string[];
204
+ summary: string;
205
+ description: string;
206
+ parameters: {
207
+ $ref: string;
208
+ }[];
209
+ requestBody: {
210
+ required: boolean;
211
+ content: {
212
+ 'application/json': {
213
+ schema: {
214
+ $ref: string;
215
+ };
216
+ };
217
+ };
218
+ };
219
+ responses: {
220
+ 200: {
221
+ description: string;
222
+ content: {
223
+ 'application/json': {
224
+ schema: {
225
+ $ref: string;
226
+ };
227
+ };
228
+ };
229
+ };
230
+ };
231
+ };
232
+ };
201
233
  '/roles/{roleName}/users:list': {
202
234
  get: {
203
235
  tags: string[];
@@ -744,6 +776,14 @@ declare const _default: {
744
776
  type: string;
745
777
  };
746
778
  };
779
+ DataSourceKeyQuery: {
780
+ name: string;
781
+ in: string;
782
+ description: string;
783
+ schema: {
784
+ type: string;
785
+ };
786
+ };
747
787
  MembershipUserFilterByTkQuery: {
748
788
  name: string;
749
789
  in: string;
@@ -789,6 +829,14 @@ declare const _default: {
789
829
  type: string;
790
830
  };
791
831
  };
832
+ DataSourceResourceNameQuery: {
833
+ name: string;
834
+ in: string;
835
+ description: string;
836
+ schema: {
837
+ type: string;
838
+ };
839
+ };
792
840
  ScopePkQuery: {
793
841
  name: string;
794
842
  in: string;
@@ -951,6 +999,107 @@ declare const _default: {
951
999
  };
952
1000
  required: string[];
953
1001
  };
1002
+ RoleDataPermissionActionWrite: {
1003
+ type: string;
1004
+ properties: {
1005
+ name: {
1006
+ type: string;
1007
+ description: string;
1008
+ };
1009
+ fields: {
1010
+ type: string;
1011
+ items: {
1012
+ type: string;
1013
+ };
1014
+ description: string;
1015
+ };
1016
+ scopeId: {
1017
+ type: string;
1018
+ nullable: boolean;
1019
+ description: string;
1020
+ };
1021
+ scopeKey: {
1022
+ type: string;
1023
+ description: string;
1024
+ };
1025
+ scope: {
1026
+ type: string;
1027
+ properties: {
1028
+ id: {
1029
+ type: string;
1030
+ };
1031
+ key: {
1032
+ type: string;
1033
+ };
1034
+ };
1035
+ additionalProperties: boolean;
1036
+ description: string;
1037
+ };
1038
+ };
1039
+ required: string[];
1040
+ additionalProperties: boolean;
1041
+ };
1042
+ RoleDataPermissionResourceWrite: {
1043
+ type: string;
1044
+ properties: {
1045
+ name: {
1046
+ type: string;
1047
+ description: string;
1048
+ };
1049
+ usingActionsConfig: {
1050
+ type: string;
1051
+ description: string;
1052
+ };
1053
+ actions: {
1054
+ type: string;
1055
+ items: {
1056
+ $ref: string;
1057
+ };
1058
+ description: string;
1059
+ };
1060
+ };
1061
+ required: string[];
1062
+ additionalProperties: boolean;
1063
+ };
1064
+ RoleDataPermissionsApplyWrite: {
1065
+ type: string;
1066
+ properties: {
1067
+ dataSourceKey: {
1068
+ type: string;
1069
+ description: string;
1070
+ };
1071
+ resources: {
1072
+ type: string;
1073
+ items: {
1074
+ $ref: string;
1075
+ };
1076
+ description: string;
1077
+ };
1078
+ };
1079
+ required: string[];
1080
+ additionalProperties: boolean;
1081
+ };
1082
+ RoleDataPermissionsApplyResult: {
1083
+ type: string;
1084
+ properties: {
1085
+ roleName: {
1086
+ type: string;
1087
+ };
1088
+ dataSourceKey: {
1089
+ type: string;
1090
+ };
1091
+ count: {
1092
+ type: string;
1093
+ };
1094
+ resources: {
1095
+ type: string;
1096
+ items: {
1097
+ $ref: string;
1098
+ };
1099
+ };
1100
+ };
1101
+ additionalProperties: boolean;
1102
+ };
954
1103
  DataSourceRole: {
955
1104
  type: string;
956
1105
  properties: {
@@ -213,6 +213,36 @@ var swagger_default = {
213
213
  }
214
214
  }
215
215
  },
216
+ "/roles:applyDataPermissions": {
217
+ post: {
218
+ tags: ["roles"],
219
+ summary: "Apply collection-level independent ACL permissions in batch for a role",
220
+ description: "Apply one or more collection-level independent ACL permission configs for a role in one request. Supports action scope binding by `scopeId` or `scopeKey`.",
221
+ parameters: [{ $ref: "#/components/parameters/RoleNameQuery" }],
222
+ requestBody: {
223
+ required: true,
224
+ content: {
225
+ "application/json": {
226
+ schema: {
227
+ $ref: "#/components/schemas/RoleDataPermissionsApplyWrite"
228
+ }
229
+ }
230
+ }
231
+ },
232
+ responses: {
233
+ 200: {
234
+ description: "OK",
235
+ content: {
236
+ "application/json": {
237
+ schema: {
238
+ $ref: "#/components/schemas/RoleDataPermissionsApplyResult"
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ },
216
246
  "/roles/{roleName}/users:list": {
217
247
  get: {
218
248
  tags: ["roles.users"],
@@ -412,9 +442,10 @@ var swagger_default = {
412
442
  get: {
413
443
  tags: ["roles.dataSourcesCollections"],
414
444
  summary: "List data-source collections in role permission configuration",
415
- description: "List collections in the target data source for the given role and indicate whether they use global permissions or collection-level independent permissions.",
445
+ description: "List collections in the target data source for the given role and indicate whether they use global permissions or collection-level independent permissions. You can pass `dataSourceKey` directly, or through `filter.dataSourceKey` for compatibility.",
416
446
  parameters: [
417
447
  { $ref: "#/components/parameters/RoleNamePath" },
448
+ { $ref: "#/components/parameters/DataSourceKeyQuery" },
418
449
  { $ref: "#/components/parameters/PageQuery" },
419
450
  { $ref: "#/components/parameters/PageSizeQuery" },
420
451
  { $ref: "#/components/parameters/SortQuery" },
@@ -483,9 +514,12 @@ var swagger_default = {
483
514
  get: {
484
515
  tags: ["roles.dataSourceResources"],
485
516
  summary: "Get one collection-level independent ACL permission for a role",
486
- description: "Get one collection-level independent permission config. Target it with a `filter` such as `{ name, dataSourceKey }`. Do not rely on `filterByTk` for this endpoint.",
517
+ description: "Get one collection-level independent permission config. You can target it by `filterByTk`, or by `name` + `dataSourceKey`. `filter` with `{ name, dataSourceKey }` is still supported for compatibility.",
487
518
  parameters: [
488
519
  { $ref: "#/components/parameters/RoleNamePath" },
520
+ { $ref: "#/components/parameters/ResourcePermissionTkQuery" },
521
+ { $ref: "#/components/parameters/DataSourceKeyQuery" },
522
+ { $ref: "#/components/parameters/DataSourceResourceNameQuery" },
489
523
  { $ref: "#/components/parameters/FilterQuery" },
490
524
  { $ref: "#/components/parameters/AppendsQuery" }
491
525
  ],
@@ -507,8 +541,14 @@ var swagger_default = {
507
541
  post: {
508
542
  tags: ["roles.dataSourceResources"],
509
543
  summary: "Update collection-level independent ACL permissions for a role",
510
- description: "Update one collection-level independent permission config. Target it with a `filter` such as `{ name, dataSourceKey }`. Do not rely on `filterByTk` for this endpoint.",
511
- parameters: [{ $ref: "#/components/parameters/RoleNamePath" }, { $ref: "#/components/parameters/FilterQuery" }],
544
+ description: "Update one collection-level independent permission config. You can target it by `filterByTk`, or by `name` + `dataSourceKey`. `filter` with `{ name, dataSourceKey }` is still supported for compatibility.",
545
+ parameters: [
546
+ { $ref: "#/components/parameters/RoleNamePath" },
547
+ { $ref: "#/components/parameters/ResourcePermissionTkQuery" },
548
+ { $ref: "#/components/parameters/DataSourceKeyQuery" },
549
+ { $ref: "#/components/parameters/DataSourceResourceNameQuery" },
550
+ { $ref: "#/components/parameters/FilterQuery" }
551
+ ],
512
552
  requestBody: {
513
553
  required: true,
514
554
  content: {
@@ -765,6 +805,12 @@ var swagger_default = {
765
805
  required: true,
766
806
  schema: { type: "string" }
767
807
  },
808
+ DataSourceKeyQuery: {
809
+ name: "dataSourceKey",
810
+ in: "query",
811
+ description: "Data source key, for example `main`.",
812
+ schema: { type: "string" }
813
+ },
768
814
  MembershipUserFilterByTkQuery: {
769
815
  name: "filterByTk",
770
816
  in: "query",
@@ -807,6 +853,12 @@ var swagger_default = {
807
853
  required: true,
808
854
  schema: { type: "string" }
809
855
  },
856
+ DataSourceResourceNameQuery: {
857
+ name: "name",
858
+ in: "query",
859
+ description: "Resource name, typically the collection name.",
860
+ schema: { type: "string" }
861
+ },
810
862
  ScopePkQuery: {
811
863
  name: "filterByTk",
812
864
  in: "query",
@@ -919,6 +971,89 @@ var swagger_default = {
919
971
  },
920
972
  required: ["roleMode"]
921
973
  },
974
+ RoleDataPermissionActionWrite: {
975
+ type: "object",
976
+ properties: {
977
+ name: {
978
+ type: "string",
979
+ description: "ACL action name, such as `view`, `create`, `update`, or `destroy`."
980
+ },
981
+ fields: {
982
+ type: "array",
983
+ items: { type: "string" },
984
+ description: "Optional allowed field list for field-configurable actions."
985
+ },
986
+ scopeId: {
987
+ type: "integer",
988
+ nullable: true,
989
+ description: "Optional scope id. Use `null` to clear the scope binding."
990
+ },
991
+ scopeKey: {
992
+ type: "string",
993
+ description: "Optional built-in/custom scope key. When provided, server resolves it to `scopeId`."
994
+ },
995
+ scope: {
996
+ type: "object",
997
+ properties: {
998
+ id: { type: "integer" },
999
+ key: { type: "string" }
1000
+ },
1001
+ additionalProperties: true,
1002
+ description: "Optional compatibility payload from existing role-resource readback."
1003
+ }
1004
+ },
1005
+ required: ["name"],
1006
+ additionalProperties: true
1007
+ },
1008
+ RoleDataPermissionResourceWrite: {
1009
+ type: "object",
1010
+ properties: {
1011
+ name: {
1012
+ type: "string",
1013
+ description: "Collection name."
1014
+ },
1015
+ usingActionsConfig: {
1016
+ type: "boolean",
1017
+ description: "Whether the collection uses independent action config."
1018
+ },
1019
+ actions: {
1020
+ type: "array",
1021
+ items: { $ref: "#/components/schemas/RoleDataPermissionActionWrite" },
1022
+ description: "Independent action config list for the collection."
1023
+ }
1024
+ },
1025
+ required: ["name"],
1026
+ additionalProperties: true
1027
+ },
1028
+ RoleDataPermissionsApplyWrite: {
1029
+ type: "object",
1030
+ properties: {
1031
+ dataSourceKey: {
1032
+ type: "string",
1033
+ description: "Data source key, default is `main` when omitted."
1034
+ },
1035
+ resources: {
1036
+ type: "array",
1037
+ items: { $ref: "#/components/schemas/RoleDataPermissionResourceWrite" },
1038
+ description: "Collection-level independent permission payloads to apply in batch."
1039
+ }
1040
+ },
1041
+ required: ["resources"],
1042
+ additionalProperties: true
1043
+ },
1044
+ RoleDataPermissionsApplyResult: {
1045
+ type: "object",
1046
+ properties: {
1047
+ roleName: { type: "string" },
1048
+ dataSourceKey: { type: "string" },
1049
+ count: { type: "integer" },
1050
+ resources: {
1051
+ type: "array",
1052
+ items: { $ref: "#/components/schemas/RoleDataSourceResource" }
1053
+ }
1054
+ },
1055
+ additionalProperties: true
1056
+ },
922
1057
  DataSourceRole: {
923
1058
  type: "object",
924
1059
  properties: {
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Based on roles, resources, and actions, access control can precisely manage interface configuration permissions, data operation permissions, menu access permissions, and plugin permissions.",
7
7
  "description.ru-RU": "На основе ролей, ресурсов и действий система контроля доступа может точно управлять разрешениями на изменение интерфейса, работу с данными, доступ к меню и разрешениями для подключаемых модулей.",
8
8
  "description.zh-CN": "基于角色、资源和操作的权限控制,可以精确控制界面配置权限、数据操作权限、菜单访问权限、插件权限。",
9
- "version": "2.1.0-alpha.20",
9
+ "version": "2.1.0-alpha.22",
10
10
  "license": "Apache-2.0",
11
11
  "main": "./dist/server/index.js",
12
12
  "homepage": "https://docs.nocobase.com/handbook/acl",
@@ -46,5 +46,5 @@
46
46
  "url": "git+https://github.com/nocobase/nocobase.git",
47
47
  "directory": "packages/plugins/acl"
48
48
  },
49
- "gitHead": "3d1535db6bf93ca23257faf474afee0d565f54c6"
49
+ "gitHead": "81ed83f158f172cca607b36beaf8428b14ba16ad"
50
50
  }