@steedos/standard-permission 2.5.0-beta.3 → 2.5.0-beta.30

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.
@@ -0,0 +1,112 @@
1
+ name: copy
2
+ amis_schema: |-
3
+ {
4
+ "type": "service",
5
+ "body": [
6
+ {
7
+ "type": "button",
8
+ "label": "复制",
9
+ "id": "u:copy",
10
+ "onEvent": {
11
+ "click": {
12
+ "weight": 0,
13
+ "actions": [
14
+ {
15
+ "dialog": {
16
+ "type": "dialog",
17
+ "title": "复制 当前简档记录",
18
+ "body": [
19
+ {
20
+ "type": "form",
21
+ "title": "表单",
22
+ "body": [
23
+ {
24
+ "label": "显示名称",
25
+ "type": "input-text",
26
+ "value": "${record.label} 的副本",
27
+ "name": "label_input",
28
+ "id": "u:24e89a11d437",
29
+ "required": true
30
+ },
31
+ {
32
+ "type": "input-text",
33
+ "name": "name_input",
34
+ "value": "${record.name}_copy",
35
+ "label": "api名称",
36
+ "id": "u:27bd7cfe3e12",
37
+ "required": true
38
+ }
39
+ ],
40
+ "api": {
41
+ "url": "${context.rootUrl}/api/permission/permission_set/copy",
42
+ "method": "post",
43
+ "messages": {},
44
+ "requestAdaptor": "delete api.body.label_input;\ndelete api.body.name_input;",
45
+ "data": {
46
+ "&": "$$",
47
+ "label": "${label_input}",
48
+ "name": "${name_input}",
49
+ "originalPermissionSetId": "${record._id}"
50
+ },
51
+ "headers": {
52
+ "Authorization": "Bearer ${context.tenantId},${context.authToken}"
53
+ }
54
+ },
55
+ "onEvent": {
56
+ "submitSucc": {
57
+ "weight": 0,
58
+ "actions": [
59
+ {
60
+ "args": {
61
+ "url": "${context.rootUrl}/app/${appId}/${objectName}/view/${result.data.recordId}",
62
+ "blank": false
63
+ },
64
+ "actionType": "url"
65
+ }
66
+ ]
67
+ }
68
+ },
69
+ "id": "u:818f7b68417b",
70
+ "mode": "normal"
71
+ }
72
+ ],
73
+ "showCloseButton": true,
74
+ "showErrorMsg": true,
75
+ "showLoading": true,
76
+ "id": "u:9f9b042404a1",
77
+ "closeOnEsc": false,
78
+ "dataMapSwitch": false,
79
+ "size": "md"
80
+ },
81
+ "actionType": "dialog"
82
+ }
83
+ ]
84
+ }
85
+ }
86
+ }
87
+ ],
88
+ "regions": [
89
+ "body"
90
+ ],
91
+ "data": {
92
+ "context": {},
93
+ "app_id": "",
94
+ "tab_id": "",
95
+ "object_name": "",
96
+ "dataComponentId": "",
97
+ "record_id": "",
98
+ "record": {},
99
+ "permissions": {}
100
+ },
101
+ "bodyClassName": "p-0",
102
+ "id": "u:4b81eda72f12"
103
+ }
104
+ is_enable: true
105
+ label: 复制
106
+ 'on': record_only
107
+ type: amis_button
108
+ visible: !!js/function |
109
+ function (object_name, record_id, record_permissions, record) {
110
+
111
+ return record.record.type == 'profile';
112
+ }
@@ -1,6 +1,6 @@
1
1
  name: tab
2
2
  label: 选项卡
3
- type: lookup
3
+ type: master_detail
4
4
  reference_to: tabs
5
5
  reference_to_field: name
6
6
  required: true
@@ -2,8 +2,10 @@
2
2
  * @Author: sunhaolin@hotoa.com
3
3
  * @Date: 2022-05-26 16:56:54
4
4
  * @LastEditors: sunhaolin@hotoa.com
5
- * @LastEditTime: 2022-05-26 19:28:09
5
+ * @LastEditTime: 2023-05-13 17:00:38
6
6
  * @Description: 复制已有简档来创建新简档
7
+ * 使用mongodb的事务处理,保证数据的一致性
8
+ * 复制对象包括:简档、对象权限、字段权限、选项卡权限
7
9
  */
8
10
  'use strict';
9
11
  // @ts-check
@@ -12,6 +14,8 @@ const express = require("express");
12
14
  const router = express.Router();
13
15
  const core = require('@steedos/core');
14
16
  const objectql = require('@steedos/objectql');
17
+ const InternalData = require('@steedos/standard-objects').internalData;
18
+ const _ = require('underscore');
15
19
 
16
20
  /**
17
21
  * body {
@@ -38,7 +42,7 @@ router.post('/api/permission/permission_set/copy', core.requireAuthentication, a
38
42
 
39
43
  const psObj = objectql.getObject('permission_set');
40
44
  const poObj = objectql.getObject('permission_objects');
41
- const pfObj = objectql.getObject('permission_fields');
45
+ const ptObj = objectql.getObject('permission_tabs');
42
46
 
43
47
  const originalPermissionSet = await psObj.findOne(originalPermissionSetId, {}, userSession);
44
48
  if (!originalPermissionSet) {
@@ -49,65 +53,248 @@ router.post('/api/permission/permission_set/copy', core.requireAuthentication, a
49
53
  throw new Error("permission_set is not profile type");
50
54
  }
51
55
 
52
- // 基础信息
53
- const baseInfo = {
54
- created: new Date(),
55
- created_by: userId,
56
- modified: new Date(),
57
- modified_by: userId,
58
- space: spaceId,
59
- company_id: company_id
60
- };
61
-
62
- // 创建新简档
63
- delete originalPermissionSet._id;
64
- const newPermissionSet = await psObj.insert({
65
- ...originalPermissionSet,
66
- ...baseInfo,
67
- name,
68
- label
69
- });
56
+ const { name:originalPermissionSetName } = originalPermissionSet;
57
+
58
+ // API名称不能重复
59
+ const existPermissionSetCount = await psObj.count({
60
+ filters: [
61
+ ['name', '=', name],
62
+ ['space', '=', spaceId],
63
+ ]
64
+ }, userSession);
65
+ if (existPermissionSetCount > 0) {
66
+ throw new Error("API名称不能重复");
67
+ }
68
+
69
+ // 事务 https://www.mongodb.com/docs/v4.4/core/transactions-in-applications/#core-api
70
+ const config = objectql.getSteedosConfig();
71
+ let datasourceConfig = config.datasources['default'];
72
+ const driver = new objectql.SteedosMongoDriver(datasourceConfig.connection);
73
+ await driver.connect();
74
+ const client = driver._client;
75
+ const db = client.db();
76
+
77
+ // Start a session.
78
+ const session = client.startSession();
79
+
80
+ const permissionObjectsColl = db.collection('permission_objects');
81
+ const permissionFieldsColl = db.collection('permission_fields');
82
+ const permissionTabsColl = db.collection('permission_tabs');
83
+ const permissionSetColl = db.collection('permission_set');
84
+
85
+ // Start a transaction
86
+ session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } });
87
+
88
+ let newPermissionSet = null;
89
+
90
+ // Operations inside the transaction
91
+ try {
92
+ // 基础信息
93
+ const baseInfo = {
94
+ created: new Date(),
95
+ created_by: userId,
96
+ modified: new Date(),
97
+ modified_by: userId,
98
+ space: spaceId,
99
+ company_id: company_id,
100
+ company_ids: [company_id],
101
+ owner: userId,
102
+ };
103
+
104
+ // 创建新简档
105
+ let newPermissionSetData = {
106
+ ...originalPermissionSet,
107
+ ...baseInfo,
108
+ is_system: false,
109
+ name,
110
+ label,
111
+ _id: driver._makeNewID(),
112
+ copy_from: originalPermissionSetId,
113
+ };
70
114
 
71
- // 创建对象权限
72
- if (newPermissionSet) {
73
- const orininalPermissionSetName = originalPermissionSet.name;
74
- const { _id: newPermissionSetId, name: newPermissionSetName } = newPermissionSet;
75
- const originalPermissionObjects = await poObj.find({ filters: [['space', '=', spaceId], ['permission_set_id', '=', originalPermissionSetId]] }, userSession);
76
- // 遍历原有的对象权限
77
- for (const poDoc of originalPermissionObjects) {
78
- // 创建新的对象权限
79
- delete poDoc._id;
80
- const newPermissionObject = await poObj.insert({
81
- ...poDoc,
82
- ...baseInfo,
83
- permission_set_id: newPermissionSetId,
84
- });
85
- // 创建新的字段权限
86
- // 由于 15.permission_objects.observe.object.js 中的 _change 函数中,调用了 permission_fields.resetFieldPermissions 方法,会自动创建新的字段权限,为防止重复创建故这里不手动创建
87
- // if (newPermissionObject) {
88
- // const newPermissionObjectName = newPermissionObject.name;
89
- // const originalPermissionFields = await pfObj.find({ filters: [['space', '=', spaceId], ['permission_set_id', '=', orininalPermissionSetName], ['permission_object', '=', poDoc.name]] }, userSession);
90
- // // 遍历原有的字段权限
91
- // for (const pfDoc of originalPermissionFields) {
92
- // // 创建新的字段权限
93
- // delete pfDoc._id;
94
- // const newPermissionField = await pfObj.insert({
95
- // ...pfDoc,
96
- // ...baseInfo,
97
- // permission_set_id: newPermissionSetName,
98
- // permission_object_id: newPermissionObjectName
99
- // });
100
- // }
101
- // }
115
+ delete newPermissionSetData.record_permissions;
116
+ const insertPermissionSetResult = await permissionSetColl.insertOne(newPermissionSetData);
117
+ newPermissionSet = insertPermissionSetResult.ops[0];
118
+ // console.log('newPermissionSet', newPermissionSet)
119
+
120
+ // 创建对象权限和选项卡权限
121
+ if (newPermissionSet) {
122
+ const { _id: newPermissionSetId, name: newPermissionSetName } = newPermissionSet;
123
+ // 对象权限数据来源于两个部分,1、内存 2、数据库
124
+ const internalPermissionObjects = await getInternalPermissionObjects(originalPermissionSetId);
125
+ const dbPermissionObjects = await poObj.directFind({ filters: [['space', '=', spaceId], ['permission_set_id', '=', originalPermissionSetId]] });
126
+ const originalPermissionObjects = getCombinedPermissionObjects(internalPermissionObjects, dbPermissionObjects);
127
+ const newPermissionObjects = []
128
+ const newPermissionFields = []
129
+ // 遍历原有的对象权限
130
+ for (const poDoc of originalPermissionObjects) {
131
+ const fromId = poDoc._id;
132
+ const newId = driver._makeNewID();
133
+ // 由于 15.permission_objects.observe.object.js 中的 _change 函数中,调用了 permission_fields.resetFieldPermissions 方法,会阻塞redis查询,需要调整15.permission_objects.observe.object.js, 避免调用 resetFieldPermissions 方法
134
+ const newPermissionObject = {
135
+ ...poDoc,
136
+ ...baseInfo,
137
+ permission_set_id: newPermissionSetId,
138
+ name: `${poDoc.object_name}.${newPermissionSetName}`,
139
+ _id: newId,
140
+ copy_from: fromId
141
+ };
142
+ newPermissionObjects.push(newPermissionObject)
143
+ // 获取对象所有字段
144
+ const fields = await objectql.getObject('object_fields').find({ filters: [['object', '=', newPermissionObject.object_name]] });
145
+ for (const field of fields) {
146
+ const apiName = `${newPermissionSetName}.${newPermissionObject.object_name}.${field.name}`
147
+ const fieldPermission = await getFieldPermission(apiName)
148
+ newPermissionFields.push({
149
+ ...baseInfo,
150
+ name: apiName,
151
+ permission_set_id: newPermissionSetName,
152
+ permission_object: newPermissionObject.name,
153
+ object_name: newPermissionObject.object_name,
154
+ field: field.name,
155
+ editable: fieldPermission ? fieldPermission.editable : getFieldDefaultEditable(field),
156
+ readable: fieldPermission ? fieldPermission.readable : getFieldDefaultReadable(field),
157
+ _id: driver._makeNewID(),
158
+ copy_from: fieldPermission ? fieldPermission._id : `${originalPermissionSetName}.${newPermissionObject.object_name}.${field.name}`
159
+ })
160
+ }
161
+ }
162
+
163
+ const originalPermissionTabs = await ptObj.find({ filters: [['space', '=', spaceId], ['permission_set', '=', originalPermissionSet.name]] }, userSession);
164
+ const newPermissionTabs = [];
165
+ // 遍历原有的选项卡权限
166
+ for (const ptDoc of originalPermissionTabs) {
167
+ const fromId = ptDoc._id;
168
+ const newId = driver._makeNewID();
169
+ delete ptDoc.record_permissions;
170
+ newPermissionTabs.push({
171
+ ...ptDoc,
172
+ ...baseInfo,
173
+ permission_set: newPermissionSetName,
174
+ _id: newId,
175
+ is_system: false,
176
+ copy_from: fromId
177
+ })
178
+ }
179
+
180
+ // 批量创建对象权限
181
+ await permissionObjectsColl.insertMany(newPermissionObjects, { session });
182
+
183
+ // 批量创建字段权限
184
+ await permissionFieldsColl.insertMany(newPermissionFields, { session });
185
+
186
+ // 批量创建选项卡权限
187
+ await permissionTabsColl.insertMany(newPermissionTabs, { session });
188
+
189
+ // 批量注册字段权限
190
+ const schema = objectql.getSteedosSchema();
191
+ const objectName = "permission_fields";
192
+ const SERVICE_NAME = `~database-${objectName}`;
193
+ await objectql.registerPermissionFields.mregister(schema.broker, SERVICE_NAME, newPermissionFields)
102
194
  }
103
195
 
196
+ } catch (error) {
197
+ // Abort transaction on error
198
+ await session.abortTransaction();
199
+ throw error;
200
+ }
201
+
202
+ // Commit the transaction using write concern set at transaction start
203
+ await session.commitTransaction();
204
+ await session.endSession();
205
+ await client.close();
206
+
207
+ if (!newPermissionSet) {
208
+ throw new Error("create permission_set failed");
104
209
  }
105
210
 
106
- res.status(200).send({ message: 'success' });
211
+ res.status(200).send({
212
+ message: 'success',
213
+ recordId: newPermissionSet._id
214
+ });
215
+
107
216
  } catch (error) {
108
217
  console.error(error);
109
218
  res.status(500).send({ error: error.message });
110
219
  }
111
220
 
112
221
  });
113
- exports.default = router;
222
+ exports.default = router;
223
+
224
+ // 获取内存中的对象权限
225
+ async function getInternalPermissionObjects(permissionSetId) {
226
+ // 查找permissionSetId对应的permissionSet
227
+ const permissionSetDoc = (await objectql.getObject('permission_set').directFind({
228
+ filters: [
229
+ ['_id', '=', permissionSetId],
230
+ ]
231
+ })[0]);
232
+ // 如果库中有记录则使用库中的name作为判断条件,否则使用permissionSetId
233
+ const permissionSetName = permissionSetDoc ? permissionSetDoc.name : permissionSetId;
234
+
235
+ let objectsPermissions = [];
236
+ const datasources = objectql.getSteedosSchema().getDataSources();
237
+ for (const datasourceName in datasources) {
238
+ let datasource = datasources[datasourceName];
239
+ let datasourceObjects = await datasource.getObjects();
240
+ _.each(datasourceObjects, function (object) {
241
+ const objectJSON = object.metadata;
242
+ const objectName = objectJSON.name;
243
+ if (!objectJSON._id && !_.include(InternalData.hiddenObjects, objectName)) {
244
+ let permission_set = objectJSON.permission_set
245
+ _.each(permission_set, function (v, code) {
246
+ if (code === permissionSetName) {
247
+ objectsPermissions.push(Object.assign({}, v, { _id: `${code}_${objectName}`, name: `${code}_${objectName}`, permission_set_id: code, object_name: objectName }))
248
+ }
249
+ })
250
+ }
251
+ });
252
+ }
253
+ return objectsPermissions;
254
+ }
255
+ // 合并内存中的对象权限和数据库中的对象权限
256
+ // 如果dbPermissionObjects和internalPermissionObjects中permission_set_id存在相同的值,则以dbPermissionObjects中数据为准
257
+ function getCombinedPermissionObjects(internalPermissionObjects, dbPermissionObjects) {
258
+ const dbPermissionObjectsIds = _.pluck(dbPermissionObjects, 'permission_set_id');
259
+ const internalPermissionObjectsIds = _.pluck(internalPermissionObjects, 'permission_set_id');
260
+ const sameIds = _.intersection(dbPermissionObjectsIds, internalPermissionObjectsIds);
261
+ if (sameIds.length > 0) {
262
+ for (const sameId of sameIds) {
263
+ const dbPermissionObject = _.findWhere(dbPermissionObjects, { permission_set_id: sameId });
264
+ const internalPermissionObject = _.findWhere(internalPermissionObjects, { permission_set_id: sameId });
265
+ if (dbPermissionObject && internalPermissionObject) {
266
+ const index = internalPermissionObjects.indexOf(internalPermissionObject);
267
+ internalPermissionObjects.splice(index, 1, dbPermissionObject);
268
+ }
269
+ }
270
+ }
271
+ return [...internalPermissionObjects, ...dbPermissionObjects];
272
+ }
273
+
274
+ async function getFieldPermission(apiName) {
275
+ const schema = objectql.getSteedosSchema();
276
+ const config = await objectql.registerPermissionFields.get(schema.broker, apiName)
277
+ return config ? config.metadata : null;
278
+ }
279
+
280
+ const systemFields = ['owner', 'created', 'created_by', 'modified', 'modified_by', 'locked', 'company_id', 'company_ids', 'instance_state'];
281
+
282
+ const getFieldDefaultEditable = (field) => {
283
+ if (_.includes(systemFields, field.name) && (field.omit || field.hidden || field.readonly || field.disabled)) {
284
+ return false;
285
+ }
286
+ if (field.omit || field.hidden || field.readonly || field.disabled) {
287
+ return false;
288
+ }
289
+ return true;
290
+ }
291
+
292
+ const getFieldDefaultReadable = (field) => {
293
+ if (_.includes(systemFields, field.name) && !field.hidden) {
294
+ return true;
295
+ }
296
+ if (field.hidden) {
297
+ return false
298
+ }
299
+ return true;
300
+ }
@@ -91,6 +91,27 @@ module.exports = {
91
91
  this.data.values.push(doc);
92
92
  }
93
93
  })
94
+
95
+ // 为了解决默认简档自定义后生成新的_id,导致无法查询到数据的问题
96
+ if (_.isString(this.query.filters)) {
97
+ const filtersStr = this.query.filters;
98
+ const regex = /(?<=permission_set_id eq ')\w+(?=')/g;
99
+ const matches = filtersStr.match(regex);
100
+ if (matches) {
101
+ const permissionSetId = matches[0];
102
+ // 查找permissionSetId对应的permissionSet
103
+ const permissionSetDoc = (await objectql.getObject('permission_set').directFind({
104
+ filters: [
105
+ ['_id', '=', permissionSetId],
106
+ ]
107
+ })[0]);
108
+ if (permissionSetDoc) {
109
+ // 替换this.query.filters中全部的的permission_set_id为permissionSetDoc.name
110
+ this.query.filters = this.query.filters.replace(regex, permissionSetDoc.name).replace(`permission_set_id eq '${permissionSetDoc.name}'`, `(permission_set_id eq '${permissionSetDoc.name}') or (permission_set_id eq '${permissionSetId}')`);
111
+ }
112
+ }
113
+ }
114
+
94
115
  const records = objectql.getSteedosSchema().metadataDriver.find(this.data.values, this.query, spaceId);
95
116
  if (records.length > 0) {
96
117
  this.data.values = records;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steedos/standard-permission",
3
- "version": "2.5.0-beta.3",
3
+ "version": "2.5.0-beta.30",
4
4
  "main": "package.service.js",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -12,5 +12,5 @@
12
12
  "description": "steedos package",
13
13
  "repository": {},
14
14
  "license": "MIT",
15
- "gitHead": "48546b91b2a44dd6f6673af7f5ab707faff6e07c"
15
+ "gitHead": "d33d736a3009ecfe57981d03d7062e52eaf27609"
16
16
  }