@tachybase/module-acl 0.23.8
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/.turbo/turbo-build.log +14 -0
- package/LICENSE +201 -0
- package/README.md +11 -0
- package/README.zh-CN.md +11 -0
- package/client.d.ts +2 -0
- package/client.js +1 -0
- package/dist/client/NewRole.d.ts +2 -0
- package/dist/client/RolesManagement.d.ts +2 -0
- package/dist/client/RolesManagerProvider.d.ts +4 -0
- package/dist/client/RolesMenu.d.ts +7 -0
- package/dist/client/hooks/load-more-observer.d.ts +7 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.js +9 -0
- package/dist/client/locale.d.ts +1 -0
- package/dist/client/permissions/ActionPermissions.d.ts +4 -0
- package/dist/client/permissions/AvailableActions.d.ts +3 -0
- package/dist/client/permissions/GeneralPermissions.d.ts +4 -0
- package/dist/client/permissions/MenuItemsProvider.d.ts +4 -0
- package/dist/client/permissions/MenuPermissions.d.ts +4 -0
- package/dist/client/permissions/Permissions.d.ts +4 -0
- package/dist/client/permissions/PluginPermissions.d.ts +4 -0
- package/dist/client/permissions/RolesResourcesActions.d.ts +3 -0
- package/dist/client/permissions/ScopeSelect.d.ts +3 -0
- package/dist/client/permissions/StrategyActions.d.ts +2 -0
- package/dist/client/permissions/style.d.ts +1 -0
- package/dist/client/roles-manager.d.ts +10 -0
- package/dist/client/schemas/roles.d.ts +55 -0
- package/dist/client/schemas/scopes.d.ts +11 -0
- package/dist/externalVersion.js +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -0
- package/dist/locale/en-US.json +6 -0
- package/dist/locale/ko_KR.json +4 -0
- package/dist/locale/zh-CN.json +11 -0
- package/dist/server/actions/available-actions.d.ts +7 -0
- package/dist/server/actions/available-actions.js +42 -0
- package/dist/server/actions/role-check.d.ts +1 -0
- package/dist/server/actions/role-check.js +67 -0
- package/dist/server/actions/role-collections.d.ts +7 -0
- package/dist/server/actions/role-collections.js +75 -0
- package/dist/server/actions/user-setDefaultRole.d.ts +2 -0
- package/dist/server/actions/user-setDefaultRole.js +65 -0
- package/dist/server/collections/roles-users.d.ts +2 -0
- package/dist/server/collections/roles-users.js +30 -0
- package/dist/server/collections/roles.d.ts +2 -0
- package/dist/server/collections/roles.js +119 -0
- package/dist/server/collections/rolesResources.d.ts +2 -0
- package/dist/server/collections/rolesResources.js +55 -0
- package/dist/server/collections/rolesResourcesActions.d.ts +2 -0
- package/dist/server/collections/rolesResourcesActions.js +51 -0
- package/dist/server/collections/rolesResourcesScopes.d.ts +2 -0
- package/dist/server/collections/rolesResourcesScopes.js +45 -0
- package/dist/server/collections/users.d.ts +2 -0
- package/dist/server/collections/users.js +52 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +50 -0
- package/dist/server/middlewares/setCurrentRole.d.ts +2 -0
- package/dist/server/middlewares/setCurrentRole.js +74 -0
- package/dist/server/middlewares/with-acl-meta.d.ts +2 -0
- package/dist/server/middlewares/with-acl-meta.js +242 -0
- package/dist/server/migrations/20221214072638-set-role-snippets.d.ts +6 -0
- package/dist/server/migrations/20221214072638-set-role-snippets.js +43 -0
- package/dist/server/model/RoleModel.d.ts +8 -0
- package/dist/server/model/RoleModel.js +46 -0
- package/dist/server/model/RoleResourceActionModel.d.ts +12 -0
- package/dist/server/model/RoleResourceActionModel.js +85 -0
- package/dist/server/model/RoleResourceModel.d.ts +18 -0
- package/dist/server/model/RoleResourceModel.js +79 -0
- package/dist/server/server.d.ts +34 -0
- package/dist/server/server.js +648 -0
- package/dist/swagger/index.d.ts +366 -0
- package/dist/swagger/index.js +395 -0
- package/package.json +40 -0
- package/server.d.ts +3 -0
- package/server.js +1 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Plugin } from '@tachybase/server';
|
|
2
|
+
import { RoleModel } from './model/RoleModel';
|
|
3
|
+
import { RoleResourceActionModel } from './model/RoleResourceActionModel';
|
|
4
|
+
import { RoleResourceModel } from './model/RoleResourceModel';
|
|
5
|
+
export interface AssociationFieldAction {
|
|
6
|
+
associationActions: string[];
|
|
7
|
+
targetActions?: string[];
|
|
8
|
+
}
|
|
9
|
+
interface AssociationFieldActions {
|
|
10
|
+
[availableActionName: string]: AssociationFieldAction;
|
|
11
|
+
}
|
|
12
|
+
export interface AssociationFieldsActions {
|
|
13
|
+
[associationType: string]: AssociationFieldActions;
|
|
14
|
+
}
|
|
15
|
+
export declare class GrantHelper {
|
|
16
|
+
resourceTargetActionMap: Map<string, string[]>;
|
|
17
|
+
targetActionResourceMap: Map<string, string[]>;
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
export declare class PluginACL extends Plugin {
|
|
21
|
+
associationFieldsActions: AssociationFieldsActions;
|
|
22
|
+
grantHelper: GrantHelper;
|
|
23
|
+
get acl(): import("packages/acl/lib").ACL;
|
|
24
|
+
registerAssociationFieldAction(associationType: string, value: AssociationFieldActions): void;
|
|
25
|
+
registerAssociationFieldsActions(): void;
|
|
26
|
+
writeResourceToACL(resourceModel: RoleResourceModel, transaction: any): Promise<void>;
|
|
27
|
+
writeActionToACL(actionModel: RoleResourceActionModel, transaction: any): Promise<void>;
|
|
28
|
+
writeRolesToACL(options: any): Promise<void>;
|
|
29
|
+
writeRoleToACL(role: RoleModel, options?: any): Promise<void>;
|
|
30
|
+
beforeLoad(): Promise<void>;
|
|
31
|
+
install(): Promise<void>;
|
|
32
|
+
load(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export default PluginACL;
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var server_exports = {};
|
|
29
|
+
__export(server_exports, {
|
|
30
|
+
GrantHelper: () => GrantHelper,
|
|
31
|
+
PluginACL: () => PluginACL,
|
|
32
|
+
default: () => server_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(server_exports);
|
|
35
|
+
var import_path = require("path");
|
|
36
|
+
var import_worker_threads = require("worker_threads");
|
|
37
|
+
var import_actions = require("@tachybase/actions");
|
|
38
|
+
var import_database = require("@tachybase/database");
|
|
39
|
+
var import_server = require("@tachybase/server");
|
|
40
|
+
var import_async_mutex = require("async-mutex");
|
|
41
|
+
var import_lodash = __toESM(require("lodash"));
|
|
42
|
+
var import_available_actions = require("./actions/available-actions");
|
|
43
|
+
var import_role_check = require("./actions/role-check");
|
|
44
|
+
var import_role_collections = require("./actions/role-collections");
|
|
45
|
+
var import_user_setDefaultRole = require("./actions/user-setDefaultRole");
|
|
46
|
+
var import_setCurrentRole = require("./middlewares/setCurrentRole");
|
|
47
|
+
var import_with_acl_meta = require("./middlewares/with-acl-meta");
|
|
48
|
+
var import_RoleModel = require("./model/RoleModel");
|
|
49
|
+
var import_RoleResourceActionModel = require("./model/RoleResourceActionModel");
|
|
50
|
+
var import_RoleResourceModel = require("./model/RoleResourceModel");
|
|
51
|
+
class GrantHelper {
|
|
52
|
+
resourceTargetActionMap = /* @__PURE__ */ new Map();
|
|
53
|
+
targetActionResourceMap = /* @__PURE__ */ new Map();
|
|
54
|
+
constructor() {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
class PluginACL extends import_server.Plugin {
|
|
58
|
+
// association field actions config
|
|
59
|
+
associationFieldsActions = {};
|
|
60
|
+
grantHelper = new GrantHelper();
|
|
61
|
+
get acl() {
|
|
62
|
+
return this.app.acl;
|
|
63
|
+
}
|
|
64
|
+
registerAssociationFieldAction(associationType, value) {
|
|
65
|
+
this.associationFieldsActions[associationType] = value;
|
|
66
|
+
}
|
|
67
|
+
registerAssociationFieldsActions() {
|
|
68
|
+
this.registerAssociationFieldAction("hasOne", {
|
|
69
|
+
view: {
|
|
70
|
+
associationActions: ["list", "get", "view"]
|
|
71
|
+
},
|
|
72
|
+
create: {
|
|
73
|
+
associationActions: ["create", "set"]
|
|
74
|
+
},
|
|
75
|
+
update: {
|
|
76
|
+
associationActions: ["update", "remove", "set"]
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
this.registerAssociationFieldAction("hasMany", {
|
|
80
|
+
view: {
|
|
81
|
+
associationActions: ["list", "get", "view"]
|
|
82
|
+
},
|
|
83
|
+
create: {
|
|
84
|
+
associationActions: ["create", "set", "add"]
|
|
85
|
+
},
|
|
86
|
+
update: {
|
|
87
|
+
associationActions: ["update", "remove", "set"]
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
this.registerAssociationFieldAction("belongsTo", {
|
|
91
|
+
view: {
|
|
92
|
+
associationActions: ["list", "get", "view"]
|
|
93
|
+
},
|
|
94
|
+
create: {
|
|
95
|
+
associationActions: ["create", "set"]
|
|
96
|
+
},
|
|
97
|
+
update: {
|
|
98
|
+
associationActions: ["update", "remove", "set"]
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
this.registerAssociationFieldAction("belongsToMany", {
|
|
102
|
+
view: {
|
|
103
|
+
associationActions: ["list", "get", "view"]
|
|
104
|
+
},
|
|
105
|
+
create: {
|
|
106
|
+
associationActions: ["create", "set", "add"]
|
|
107
|
+
},
|
|
108
|
+
update: {
|
|
109
|
+
associationActions: ["update", "remove", "set", "toggle"]
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async writeResourceToACL(resourceModel, transaction) {
|
|
114
|
+
await resourceModel.writeToACL({
|
|
115
|
+
acl: this.acl,
|
|
116
|
+
associationFieldsActions: this.associationFieldsActions,
|
|
117
|
+
transaction,
|
|
118
|
+
grantHelper: this.grantHelper
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async writeActionToACL(actionModel, transaction) {
|
|
122
|
+
const resource = actionModel.get("resource");
|
|
123
|
+
const role = this.acl.getRole(resource.get("roleName"));
|
|
124
|
+
await actionModel.writeToACL({
|
|
125
|
+
acl: this.acl,
|
|
126
|
+
role,
|
|
127
|
+
resourceName: resource.get("name"),
|
|
128
|
+
associationFieldsActions: this.associationFieldsActions,
|
|
129
|
+
grantHelper: this.grantHelper
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async writeRolesToACL(options) {
|
|
133
|
+
const roles = await this.app.db.getRepository("roles").find({
|
|
134
|
+
appends: ["resources", "resources.actions"]
|
|
135
|
+
});
|
|
136
|
+
for (const role of roles) {
|
|
137
|
+
await this.writeRoleToACL(role, options);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async writeRoleToACL(role, options = {}) {
|
|
141
|
+
const transaction = options == null ? void 0 : options.transaction;
|
|
142
|
+
role.writeToAcl({ acl: this.acl, withOutStrategy: true });
|
|
143
|
+
if (options.withOutResources) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
let resources = role.get("resources");
|
|
147
|
+
if (!resources) {
|
|
148
|
+
resources = await role.getResources({ transaction });
|
|
149
|
+
}
|
|
150
|
+
for (const resource of resources) {
|
|
151
|
+
await this.writeResourceToACL(resource, transaction);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async beforeLoad() {
|
|
155
|
+
this.db.addMigrations({
|
|
156
|
+
namespace: this.name,
|
|
157
|
+
directory: (0, import_path.resolve)(__dirname, "./migrations"),
|
|
158
|
+
context: {
|
|
159
|
+
plugin: this
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
this.app.db.registerModels({
|
|
163
|
+
RoleResourceActionModel: import_RoleResourceActionModel.RoleResourceActionModel,
|
|
164
|
+
RoleResourceModel: import_RoleResourceModel.RoleResourceModel,
|
|
165
|
+
RoleModel: import_RoleModel.RoleModel
|
|
166
|
+
});
|
|
167
|
+
this.app.acl.registerSnippet({
|
|
168
|
+
name: `pm.${this.name}.roles`,
|
|
169
|
+
actions: [
|
|
170
|
+
"roles:*",
|
|
171
|
+
"roles.snippets:*",
|
|
172
|
+
"availableActions:list",
|
|
173
|
+
"roles.collections:list",
|
|
174
|
+
"roles.resources:*",
|
|
175
|
+
"uiSchemas:getProperties",
|
|
176
|
+
"roles.menuUiSchemas:*",
|
|
177
|
+
"roles.users:*"
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
this.app.acl.beforeGrantAction((ctx) => {
|
|
181
|
+
const actionName = this.app.acl.resolveActionAlias(ctx.actionName);
|
|
182
|
+
const collection = this.app.db.getCollection(ctx.resourceName);
|
|
183
|
+
if (!collection) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const fieldsParams = ctx.params.fields;
|
|
187
|
+
if (!fieldsParams) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (actionName === "view" || actionName === "export") {
|
|
191
|
+
const associationsFields = fieldsParams.filter((fieldName) => {
|
|
192
|
+
const field = collection.getField(fieldName);
|
|
193
|
+
return field instanceof import_database.RelationField;
|
|
194
|
+
});
|
|
195
|
+
ctx.params = {
|
|
196
|
+
...ctx.params,
|
|
197
|
+
fields: import_lodash.default.difference(fieldsParams, associationsFields),
|
|
198
|
+
appends: associationsFields
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
this.registerAssociationFieldsActions();
|
|
203
|
+
this.app.resourcer.define(import_available_actions.availableActionResource);
|
|
204
|
+
this.app.resourcer.define(import_role_collections.roleCollectionsResource);
|
|
205
|
+
this.app.resourcer.registerActionHandler("roles:check", import_role_check.checkAction);
|
|
206
|
+
this.app.resourcer.registerActionHandler(`users:setDefaultRole`, import_user_setDefaultRole.setDefaultRole);
|
|
207
|
+
this.db.on("users.afterCreateWithAssociations", async (model, options) => {
|
|
208
|
+
const { transaction } = options;
|
|
209
|
+
const repository = this.app.db.getRepository("roles");
|
|
210
|
+
const defaultRole = await repository.findOne({
|
|
211
|
+
filter: {
|
|
212
|
+
default: true
|
|
213
|
+
},
|
|
214
|
+
transaction
|
|
215
|
+
});
|
|
216
|
+
if (defaultRole && await model.countRoles({ transaction }) === 0) {
|
|
217
|
+
await model.addRoles(defaultRole, { transaction });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
this.app.on("acl:writeRoleToACL", async (roleModel) => {
|
|
221
|
+
await this.writeRoleToACL(roleModel, {
|
|
222
|
+
withOutResources: true
|
|
223
|
+
});
|
|
224
|
+
await this.app.db.getRepository("dataSourcesRoles").updateOrCreate({
|
|
225
|
+
values: {
|
|
226
|
+
roleName: roleModel.get("name"),
|
|
227
|
+
dataSourceKey: "main",
|
|
228
|
+
strategy: roleModel.get("strategy")
|
|
229
|
+
},
|
|
230
|
+
filterKeys: ["roleName", "dataSourceKey"]
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
this.app.db.on("roles.afterCreateWithAssociations", async (model, options) => {
|
|
234
|
+
const { transaction } = options;
|
|
235
|
+
await this.app.db.getRepository("dataSourcesRoles").updateOrCreate({
|
|
236
|
+
values: {
|
|
237
|
+
roleName: model.get("name"),
|
|
238
|
+
dataSourceKey: "main",
|
|
239
|
+
strategy: model.get("strategy")
|
|
240
|
+
},
|
|
241
|
+
filterKeys: ["roleName", "dataSourceKey"],
|
|
242
|
+
transaction
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
this.app.db.on("roles.afterSaveWithAssociations", async (model, options) => {
|
|
246
|
+
const { transaction } = options;
|
|
247
|
+
await this.writeRoleToACL(model, {
|
|
248
|
+
withOutResources: true
|
|
249
|
+
});
|
|
250
|
+
await this.app.db.getRepository("dataSourcesRoles").updateOrCreate({
|
|
251
|
+
values: {
|
|
252
|
+
roleName: model.get("name"),
|
|
253
|
+
dataSourceKey: "main"
|
|
254
|
+
},
|
|
255
|
+
filterKeys: ["roleName", "dataSourceKey"],
|
|
256
|
+
transaction
|
|
257
|
+
});
|
|
258
|
+
await this.app.emitAsync("acl:writeResources", {
|
|
259
|
+
roleName: model.get("name"),
|
|
260
|
+
transaction
|
|
261
|
+
});
|
|
262
|
+
if (model.get("default")) {
|
|
263
|
+
await this.app.db.getRepository("roles").update({
|
|
264
|
+
values: {
|
|
265
|
+
default: false
|
|
266
|
+
},
|
|
267
|
+
filter: {
|
|
268
|
+
"name.$ne": model.get("name")
|
|
269
|
+
},
|
|
270
|
+
hooks: false,
|
|
271
|
+
transaction
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
this.app.db.on("roles.afterDestroy", (model) => {
|
|
276
|
+
const roleName = model.get("name");
|
|
277
|
+
this.acl.removeRole(roleName);
|
|
278
|
+
});
|
|
279
|
+
this.app.db.on("rolesResources.afterSaveWithAssociations", async (model, options) => {
|
|
280
|
+
await this.writeResourceToACL(model, options.transaction);
|
|
281
|
+
});
|
|
282
|
+
this.app.db.on("rolesResourcesActions.afterUpdateWithAssociations", async (model, options) => {
|
|
283
|
+
const { transaction } = options;
|
|
284
|
+
const resource = await model.getResource({
|
|
285
|
+
transaction
|
|
286
|
+
});
|
|
287
|
+
await this.writeResourceToACL(resource, transaction);
|
|
288
|
+
});
|
|
289
|
+
this.app.db.on("collections.afterDestroy", async (model, options) => {
|
|
290
|
+
const { transaction } = options;
|
|
291
|
+
await this.app.db.getRepository("dataSourcesRolesResources").destroy({
|
|
292
|
+
filter: {
|
|
293
|
+
name: model.get("name"),
|
|
294
|
+
dataSourceKey: "main"
|
|
295
|
+
},
|
|
296
|
+
transaction
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
this.app.db.on("fields.afterCreate", async (model, options) => {
|
|
300
|
+
const { transaction } = options;
|
|
301
|
+
const collectionName = model.get("collectionName");
|
|
302
|
+
const fieldName = model.get("name");
|
|
303
|
+
const resourceActions = await this.app.db.getRepository("dataSourcesRolesResourcesActions").find({
|
|
304
|
+
filter: {
|
|
305
|
+
"resource.name": collectionName,
|
|
306
|
+
"resource.dataSourceKey": "main"
|
|
307
|
+
},
|
|
308
|
+
transaction,
|
|
309
|
+
appends: ["resource"]
|
|
310
|
+
});
|
|
311
|
+
for (const resourceAction of resourceActions) {
|
|
312
|
+
const fields = resourceAction.get("fields");
|
|
313
|
+
const newFields = [...fields, fieldName];
|
|
314
|
+
await this.app.db.getRepository("dataSourcesRolesResourcesActions").update({
|
|
315
|
+
filterByTk: resourceAction.get("id"),
|
|
316
|
+
values: {
|
|
317
|
+
fields: newFields
|
|
318
|
+
},
|
|
319
|
+
transaction
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
const mutex = new import_async_mutex.Mutex();
|
|
324
|
+
this.app.db.on("fields.afterDestroy", async (model, options) => {
|
|
325
|
+
await mutex.runExclusive(async () => {
|
|
326
|
+
const collectionName = model.get("collectionName");
|
|
327
|
+
const fieldName = model.get("name");
|
|
328
|
+
const resourceActions = await this.app.db.getRepository("dataSourcesRolesResourcesActions").find({
|
|
329
|
+
filter: {
|
|
330
|
+
"resource.name": collectionName,
|
|
331
|
+
"fields.$anyOf": [fieldName],
|
|
332
|
+
"resource.dataSourceKey": "main"
|
|
333
|
+
},
|
|
334
|
+
transaction: options.transaction
|
|
335
|
+
});
|
|
336
|
+
for (const resourceAction of resourceActions) {
|
|
337
|
+
const fields = resourceAction.get("fields");
|
|
338
|
+
const newFields = fields.filter((field) => field !== fieldName);
|
|
339
|
+
await this.app.db.getRepository("dataSourcesRolesResourcesActions").update({
|
|
340
|
+
filterByTk: resourceAction.get("id"),
|
|
341
|
+
values: {
|
|
342
|
+
fields: newFields
|
|
343
|
+
},
|
|
344
|
+
transaction: options.transaction
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
this.app.db.on("rolesUsers.afterSave", async (model) => {
|
|
350
|
+
const cache = this.app.cache;
|
|
351
|
+
await cache.del(`roles:${model.get("userId")}`);
|
|
352
|
+
});
|
|
353
|
+
this.app.db.on("rolesUsers.afterDestroy", async (model) => {
|
|
354
|
+
const cache = this.app.cache;
|
|
355
|
+
await cache.del(`roles:${model.get("userId")}`);
|
|
356
|
+
});
|
|
357
|
+
const writeRolesToACL = async (app, options) => {
|
|
358
|
+
const exists = await this.app.db.collectionExistsInDb("roles");
|
|
359
|
+
if (exists) {
|
|
360
|
+
this.log.info("write roles to ACL", { method: "writeRolesToACL" });
|
|
361
|
+
await this.writeRolesToACL(options);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
if (import_worker_threads.isMainThread) {
|
|
365
|
+
this.app.on("afterStart", async () => {
|
|
366
|
+
await writeRolesToACL(this.app, {
|
|
367
|
+
withOutResources: true
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
this.app.on("afterInstallPlugin", async (plugin) => {
|
|
372
|
+
if (plugin.getName() !== "user") {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const User = this.db.getCollection("users");
|
|
376
|
+
await User.repository.update({
|
|
377
|
+
filter: {
|
|
378
|
+
id: 1
|
|
379
|
+
},
|
|
380
|
+
values: {
|
|
381
|
+
roles: ["root", "admin", "member"]
|
|
382
|
+
},
|
|
383
|
+
forceUpdate: true
|
|
384
|
+
});
|
|
385
|
+
const RolesUsers = this.db.getCollection("rolesUsers");
|
|
386
|
+
await RolesUsers.repository.update({
|
|
387
|
+
filter: {
|
|
388
|
+
userId: 1,
|
|
389
|
+
roleName: "root"
|
|
390
|
+
},
|
|
391
|
+
values: {
|
|
392
|
+
default: true
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
this.app.on("beforeInstallPlugin", async (plugin) => {
|
|
397
|
+
if (plugin.getName() !== "user") {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const roles = this.app.db.getRepository("roles");
|
|
401
|
+
await roles.createMany({
|
|
402
|
+
records: [
|
|
403
|
+
{
|
|
404
|
+
name: "root",
|
|
405
|
+
title: '{{t("Root")}}',
|
|
406
|
+
hidden: true,
|
|
407
|
+
snippets: ["ui.*", "pm", "pm.*"]
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "admin",
|
|
411
|
+
title: '{{t("Admin")}}',
|
|
412
|
+
allowConfigure: true,
|
|
413
|
+
allowNewMenu: true,
|
|
414
|
+
strategy: { actions: ["create", "view", "update", "destroy"] },
|
|
415
|
+
snippets: ["ui.*", "pm", "pm.*"]
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: "member",
|
|
419
|
+
title: '{{t("Member")}}',
|
|
420
|
+
allowNewMenu: true,
|
|
421
|
+
strategy: { actions: ["view", "update:own", "destroy:own", "create"] },
|
|
422
|
+
default: true,
|
|
423
|
+
snippets: ["!ui.*", "!pm", "!pm.*"]
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
});
|
|
427
|
+
const rolesResourcesScopes = this.app.db.getRepository("dataSourcesRolesResourcesScopes");
|
|
428
|
+
await rolesResourcesScopes.createMany({
|
|
429
|
+
records: [
|
|
430
|
+
{
|
|
431
|
+
key: "all",
|
|
432
|
+
name: '{{t("All records")}}',
|
|
433
|
+
scope: {}
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
key: "own",
|
|
437
|
+
name: '{{t("Own records")}}',
|
|
438
|
+
scope: {
|
|
439
|
+
createdById: "{{ ctx.state.currentUser.id }}"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
]
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
this.app.on("beforeSignOut", ({ userId }) => {
|
|
446
|
+
this.app.cache.del(`roles:${userId}`);
|
|
447
|
+
});
|
|
448
|
+
this.app.resourcer.use(import_setCurrentRole.setCurrentRole, { tag: "setCurrentRole", before: "acl", after: "auth" });
|
|
449
|
+
this.app.acl.allow("users", "setDefaultRole", "loggedIn");
|
|
450
|
+
this.app.acl.allow("roles", "check", "loggedIn");
|
|
451
|
+
this.app.acl.allow("*", "*", (ctx) => {
|
|
452
|
+
return ctx.state.currentRole === "root";
|
|
453
|
+
});
|
|
454
|
+
this.app.acl.addFixedParams("collections", "destroy", () => {
|
|
455
|
+
return {
|
|
456
|
+
filter: {
|
|
457
|
+
$and: [{ "name.$ne": "roles" }, { "name.$ne": "rolesUsers" }]
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
});
|
|
461
|
+
this.app.acl.addFixedParams("rolesResourcesScopes", "destroy", () => {
|
|
462
|
+
return {
|
|
463
|
+
filter: {
|
|
464
|
+
$and: [{ "key.$ne": "all" }, { "key.$ne": "own" }]
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
});
|
|
468
|
+
this.app.acl.addFixedParams("rolesResourcesScopes", "update", () => {
|
|
469
|
+
return {
|
|
470
|
+
filter: {
|
|
471
|
+
$and: [{ "key.$ne": "all" }, { "key.$ne": "own" }]
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
});
|
|
475
|
+
this.app.acl.addFixedParams("roles", "destroy", () => {
|
|
476
|
+
return {
|
|
477
|
+
filter: {
|
|
478
|
+
$and: [{ "name.$ne": "root" }, { "name.$ne": "admin" }, { "name.$ne": "member" }]
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
});
|
|
482
|
+
this.app.resourcer.use(
|
|
483
|
+
async (ctx, next) => {
|
|
484
|
+
const { actionName, resourceName, params } = ctx.action;
|
|
485
|
+
const { showAnonymous } = params || {};
|
|
486
|
+
if (actionName === "list" && resourceName === "roles") {
|
|
487
|
+
if (!showAnonymous) {
|
|
488
|
+
ctx.action.mergeParams({
|
|
489
|
+
filter: {
|
|
490
|
+
"name.$ne": "anonymous"
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (actionName === "update" && resourceName === "roles.resources") {
|
|
496
|
+
ctx.action.mergeParams({
|
|
497
|
+
updateAssociationValues: ["actions"]
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
await next();
|
|
501
|
+
},
|
|
502
|
+
{ tag: "resource-association-update" }
|
|
503
|
+
);
|
|
504
|
+
this.app.acl.use(
|
|
505
|
+
async (ctx, next) => {
|
|
506
|
+
var _a, _b, _c, _d, _e;
|
|
507
|
+
const { actionName, resourceName } = ctx.action;
|
|
508
|
+
if (actionName === "get" || actionName === "list") {
|
|
509
|
+
if (!Array.isArray((_c = (_b = (_a = ctx == null ? void 0 : ctx.permission) == null ? void 0 : _a.can) == null ? void 0 : _b.params) == null ? void 0 : _c.fields)) {
|
|
510
|
+
return next();
|
|
511
|
+
}
|
|
512
|
+
let collection;
|
|
513
|
+
if (resourceName.includes(".")) {
|
|
514
|
+
const [collectionName, associationName] = resourceName.split(".");
|
|
515
|
+
const field = (_e = (_d = ctx.db.getCollection(collectionName)) == null ? void 0 : _d.getField) == null ? void 0 : _e.call(_d, associationName);
|
|
516
|
+
if (field.target) {
|
|
517
|
+
collection = ctx.db.getCollection(field.target);
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
collection = ctx.db.getCollection(resourceName);
|
|
521
|
+
}
|
|
522
|
+
if (collection && collection.hasField("createdById")) {
|
|
523
|
+
ctx.permission.can.params.fields.push("createdById");
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return next();
|
|
527
|
+
},
|
|
528
|
+
{ tag: "permission-management" }
|
|
529
|
+
);
|
|
530
|
+
const parseJsonTemplate = this.app.acl.parseJsonTemplate;
|
|
531
|
+
this.app.acl.beforeGrantAction(async (ctx) => {
|
|
532
|
+
const actionName = this.app.acl.resolveActionAlias(ctx.actionName);
|
|
533
|
+
if (import_lodash.default.isPlainObject(ctx.params)) {
|
|
534
|
+
if (actionName === "view" && ctx.params.fields) {
|
|
535
|
+
const appendFields = [];
|
|
536
|
+
const collection = this.app.db.getCollection(ctx.resourceName);
|
|
537
|
+
if (!collection) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (collection.model.primaryKeyAttribute) {
|
|
541
|
+
appendFields.push(collection.model.primaryKeyAttribute);
|
|
542
|
+
}
|
|
543
|
+
if (collection.model.rawAttributes["createdAt"]) {
|
|
544
|
+
appendFields.push("createdAt");
|
|
545
|
+
}
|
|
546
|
+
if (collection.model.rawAttributes["updatedAt"]) {
|
|
547
|
+
appendFields.push("updatedAt");
|
|
548
|
+
}
|
|
549
|
+
ctx.params = {
|
|
550
|
+
...import_lodash.default.omit(ctx.params, "fields"),
|
|
551
|
+
fields: [...ctx.params.fields, ...appendFields]
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
this.app.acl.use(
|
|
557
|
+
async (ctx, next) => {
|
|
558
|
+
var _a, _b, _c;
|
|
559
|
+
const { actionName, resourceName, resourceOf } = ctx.action;
|
|
560
|
+
if (resourceName.includes(".") && resourceOf) {
|
|
561
|
+
if (!((_b = (_a = ctx == null ? void 0 : ctx.permission) == null ? void 0 : _a.can) == null ? void 0 : _b.params)) {
|
|
562
|
+
return next();
|
|
563
|
+
}
|
|
564
|
+
delete ctx.permission.can.params.filter;
|
|
565
|
+
const [collectionName] = resourceName.split(".");
|
|
566
|
+
const action = ctx.can({ resource: collectionName, action: actionName });
|
|
567
|
+
const availableAction = this.app.acl.getAvailableAction(actionName);
|
|
568
|
+
if ((_c = availableAction == null ? void 0 : availableAction.options) == null ? void 0 : _c.onNewRecord) {
|
|
569
|
+
if (action) {
|
|
570
|
+
ctx.permission.skip = true;
|
|
571
|
+
} else {
|
|
572
|
+
ctx.permission.can = false;
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
const filteredParams = this.app.acl.filterParams(ctx, collectionName, (action == null ? void 0 : action.params) || {});
|
|
576
|
+
const params = await parseJsonTemplate(filteredParams, ctx);
|
|
577
|
+
const sourceInstance = await ctx.db.getRepository(collectionName).findOne({
|
|
578
|
+
filterByTk: resourceOf,
|
|
579
|
+
filter: params.filter || {}
|
|
580
|
+
});
|
|
581
|
+
if (!sourceInstance) {
|
|
582
|
+
ctx.permission.can = false;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
await next();
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
before: "core"
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
this.app.acl.use(
|
|
593
|
+
async (ctx, next) => {
|
|
594
|
+
var _a, _b;
|
|
595
|
+
const action = (_b = (_a = ctx.permission) == null ? void 0 : _a.can) == null ? void 0 : _b.action;
|
|
596
|
+
if (action === "destroy" && !ctx.action.resourceName.includes(".")) {
|
|
597
|
+
const repository = import_actions.utils.getRepositoryFromParams(ctx);
|
|
598
|
+
if (!repository) {
|
|
599
|
+
await next();
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const filteredCount = await repository.count(ctx.permission.mergedParams);
|
|
603
|
+
const queryCount = await repository.count(ctx.permission.rawParams);
|
|
604
|
+
if (queryCount > filteredCount) {
|
|
605
|
+
ctx.throw(403, "No permissions");
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
await next();
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
after: "core",
|
|
613
|
+
group: "after"
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
const withACLMeta = (0, import_with_acl_meta.createWithACLMetaMiddleware)();
|
|
617
|
+
this.app.use(
|
|
618
|
+
async (ctx, next) => {
|
|
619
|
+
try {
|
|
620
|
+
await withACLMeta(ctx, next);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
ctx.logger.error(error);
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
{ after: "restApi", group: "after" }
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
async install() {
|
|
629
|
+
const repo = this.db.getRepository("collections");
|
|
630
|
+
if (repo) {
|
|
631
|
+
await repo.db2cm("roles");
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async load() {
|
|
635
|
+
await this.importCollections((0, import_path.resolve)(__dirname, "collections"));
|
|
636
|
+
this.db.extendCollection({
|
|
637
|
+
name: "rolesUischemas",
|
|
638
|
+
dumpRules: "required",
|
|
639
|
+
origin: this.options.packageName
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
var server_default = PluginACL;
|
|
644
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
645
|
+
0 && (module.exports = {
|
|
646
|
+
GrantHelper,
|
|
647
|
+
PluginACL
|
|
648
|
+
});
|