free-be-account 0.0.21 → 0.0.23

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/index.js CHANGED
@@ -7,7 +7,7 @@ var svgCaptcha = require('svg-captcha');
7
7
  const passport = require('passport');
8
8
  const LocalStrategy = require('passport-local').Strategy;
9
9
  const {v1: uuidv1} = require('uuid');
10
- const RedisStore = require("connect-redis").default;
10
+ const { RedisStore } = require("connect-redis");
11
11
  const session = require("express-session");
12
12
 
13
13
  const crypto = require("./crypto");
@@ -27,7 +27,7 @@ try {
27
27
  let __app_service_list_saved = false;
28
28
  let __saved_service_list;
29
29
 
30
- const __getServiceList = async (res, filter = { Enabled: true }) => {
30
+ const __getServiceList = async (res, filter = { Enabled: true }, scopeFilter) => {
31
31
  // add app.serviceList into db if not yet
32
32
  if (!__app_service_list_saved) {
33
33
  await res.app.modules.account.utils.saveServiceList(res.app);
@@ -36,7 +36,7 @@ const __getServiceList = async (res, filter = { Enabled: true }) => {
36
36
  return __saved_service_list;
37
37
  }
38
38
 
39
- const allPerms = await res.app.models.permission.find(filter);
39
+ const allPerms = await res.app.models.permission.find(filter).lean();
40
40
 
41
41
  const permList = {};
42
42
  if (allPerms && allPerms.length > 0) {
@@ -58,18 +58,39 @@ const __getServiceList = async (res, filter = { Enabled: true }) => {
58
58
  Description: doc.Description,
59
59
  Index: doc.Index,
60
60
  },
61
- // TODO: only add data scope when no filter provided, correct?
62
- filter ? {} : {
63
- Scope: filter ? undefined: doc.Scope.map(sc => {
64
- const dso = res.app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
65
- return dso ? {
61
+ ((doc.Scope || []).length <= 0) ? {} : {
62
+ Scope: doc.Scope.map(sc => {
63
+ const dso = app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
64
+
65
+ if (!dso) {
66
+ return {};
67
+ }
68
+
69
+ // the final scope options
70
+ let scopeOptions = [];
71
+
72
+ if (!scopeFilter) {
73
+ scopeOptions = dso.Options;
74
+ } else {
75
+ // get the scope filter of the current doc
76
+ const scopeFilterOfTheCurrentDoc = (scopeFilter && scopeFilter[doc.Path]) || {};
77
+ // get the option level of the filter
78
+ const filterOptionValue = scopeFilterOfTheCurrentDoc[sc.Name] || dso.Default;
79
+ const filterOption = dso.Options.find(o => o.Value === filterOptionValue);
80
+
81
+ if (filterOption && (filterOption.Level !== void 0)) {
82
+ scopeOptions = dso.Options.filter(o => (o.Level !== void 0) && (o.Level <= filterOption.Level));
83
+ }
84
+ }
85
+
86
+ return {
66
87
  Label: dso.Label || '',
67
88
  Field: `${sc.Name}`,
68
89
  Type: dso.Component || 'Select',
69
- Options: dso.Options || [],
90
+ Options: scopeOptions || [],
70
91
  Multiple: dso.Multiple || false,
71
- } : {};
72
- })
92
+ };
93
+ }),
73
94
  })
74
95
  }
75
96
  }
@@ -637,14 +658,17 @@ module.exports = (app) => ({
637
658
  {
638
659
  Label: mdl.t('Self'),
639
660
  Value: 'self',
661
+ Level: 1, // Level越大,越高权限
640
662
  },
641
663
  {
642
664
  Label: mdl.t('My Org'),
643
665
  Value: 'org',
666
+ Level: 10,
644
667
  },
645
668
  {
646
669
  Label: mdl.t('All'),
647
670
  Value: 'all',
671
+ Level: 20,
648
672
  }
649
673
  ],
650
674
  Default: 'self',
@@ -1139,6 +1163,7 @@ module.exports = (app) => ({
1139
1163
 
1140
1164
  res.addData({
1141
1165
  Name: (req.user.Profile && req.user.Profile.Name) || req.user.PhoneNumber || req.user.UserName || '',
1166
+ Account: req.user.PhoneNumber || req.user.UserName,
1142
1167
  Avatar: req.user.Profile && req.user.Profile.Avatar ? req.user.Profile.Avatar : '',
1143
1168
  Status: req.user.Status,
1144
1169
  }, false);
@@ -1451,12 +1476,22 @@ module.exports = (app) => ({
1451
1476
  }
1452
1477
 
1453
1478
  let filter;
1479
+ let scopeFilter = {};
1454
1480
  if (req.user.Permission !== '*') {
1455
1481
  const permPathList = getPermissionPathList(req.user.Permission);
1456
1482
  filter = { Path: { $in: permPathList } };
1483
+
1484
+ for (let i = 0; i < permPathList.length; i += 1) {
1485
+ const p = permPathList[i];
1486
+ const pScope = Object.nestValue(req.user.Permission, p.replace(/^\//,'').replace(/\//g, '.'));
1487
+
1488
+ if (pScope && pScope.Scope) {
1489
+ scopeFilter[p] = pScope.Scope;
1490
+ }
1491
+ }
1457
1492
  }
1458
1493
 
1459
- res.addData(await __getServiceList(res, filter));
1494
+ res.addData(await __getServiceList(res, filter, scopeFilter));
1460
1495
 
1461
1496
  return next();
1462
1497
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-be-account",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "main": "index.js",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -8,20 +8,21 @@
8
8
  "url": "https://github.com/freeeis/free-be-account.git"
9
9
  },
10
10
  "dependencies": {
11
- "@alicloud/pop-core": "^1.7.13",
12
- "axios": "^1.7.7",
13
- "bcrypt": "^5.1.1",
14
- "connect-redis": "^7.1.1",
11
+ "@alicloud/pop-core": "^1.8.0",
12
+ "axios": "^1.10.0",
13
+ "bcrypt": "^6.0.0",
14
+ "connect-redis": "^8.1.0",
15
15
  "crypto-js": "^4.2.0",
16
16
  "express-session": "^1.18.1",
17
17
  "js-md5": "^0.8.3",
18
- "minimatch": "^10.0.1",
19
- "nodemailer": "^6.9.16",
18
+ "minimatch": "^10.0.3",
19
+ "nodemailer": "^7.0.4",
20
20
  "passport": "^0.7.0",
21
21
  "passport-local": "^1.0.0",
22
- "semver": "^7.6.3",
22
+ "semver": "^7.7.2",
23
23
  "svg-captcha": "^1.4.0",
24
24
  "tar": "^7.4.3",
25
- "uuid": "^11.0.2"
25
+ "tencentcloud-sdk-nodejs-sms": "^4.1.71",
26
+ "uuid": "^11.1.0"
26
27
  }
27
28
  }
@@ -128,7 +128,7 @@ router.get('/:id',
128
128
  );
129
129
 
130
130
  router.post('/',
131
- (req, res, next) => {
131
+ async (req, res, next) => {
132
132
  req.body.Status = AccountAuditStatus.Passed;
133
133
  req.body.Saved = true;
134
134
 
@@ -144,7 +144,58 @@ router.post('/',
144
144
  // make sure the provided permission is in the scope of the current user permission!!
145
145
  if(req.user.Permission){
146
146
  if(req.user.Permission !== '*') {
147
+ // 根据当前账号的权限,检查所传入的权限中的数据权限配置是否合理,
148
+ // 如果合理,将传入的数据权限保存,并合并到最后的权限中
149
+ const permPathList = router.mdl.utils.getPermissionPathList(req.body.Permission);
150
+ const allPerms = await res.app.models.permission.find({
151
+ Path: { $in: permPathList } ,
152
+ Enabled: true
153
+ }).lean();
154
+
155
+ const dsScope = {};
156
+ for (let i = 0; i < allPerms.length; i += 1) {
157
+ const p = allPerms[i].Path;
158
+ const pDot = p.replace(/^\//, '').replace(/\//g, '.');
159
+ const uPerm = Object.nestValue(req.user.Permission, pDot);
160
+ const bPerm = Object.nestValue(req.body.Permission, pDot);
161
+
162
+ if (uPerm && uPerm.Scope && bPerm && bPerm.Scope) {
163
+ // 当前账号此权限中的数据权限定义
164
+ for (let j = 0; j < (allPerms[i].Scope || []).length; j += 1) {
165
+ const sc = allPerms[i].Scope[j];
166
+
167
+ if (!uPerm.Scope[sc.Name] || !bPerm.Scope[sc.Name]) {
168
+ continue;
169
+ }
170
+
171
+ const dso = res.app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
172
+
173
+ if (dso) {
174
+ const dsoOptions = dso.Options || [];
175
+
176
+ const dsOpOfCurrentUser = dsoOptions.find(o => o.Value === uPerm.Scope[sc.Name]);
177
+ const dsOpInBody = dsoOptions.find(o => o.Value === bPerm.Scope[sc.Name]);
178
+
179
+ if (dsOpOfCurrentUser
180
+ && dsOpInBody
181
+ ) {
182
+ if (
183
+ (dsOpOfCurrentUser.Level !== void 0)
184
+ && (dsOpInBody.Level !== void 0)
185
+ && (dsOpInBody.Level <= dsOpOfCurrentUser.Level)
186
+ ) {
187
+ Object.setValue(dsScope, `${pDot}.Scope.${sc.Name}`, dsOpInBody.Value);
188
+ } else {
189
+ Object.setValue(dsScope, `${pDot}.Scope.${sc.Name}`, dsOpOfCurrentUser.Value);
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ }
196
+
147
197
  req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
198
+ Object.assign(req.body.Permission, dsScope);
148
199
  }
149
200
  } else {
150
201
  delete req.body.Permission;
@@ -215,7 +266,7 @@ router.post('/audit',
215
266
  );
216
267
 
217
268
  router.put('/',
218
- (req, res, next) => {
269
+ async (req, res, next) => {
219
270
  if (req.body.Permission) {
220
271
  if (!clearPermission(req.body.Permission)) {
221
272
  req.body.Permission = {};
@@ -228,7 +279,58 @@ router.put('/',
228
279
  // make sure the provided permission is in the scope of the current user permission!!
229
280
  if(req.user.Permission){
230
281
  if(req.user.Permission !== '*') {
282
+ // 根据当前账号的权限,检查所传入的权限中的数据权限配置是否合理,
283
+ // 如果合理,将传入的数据权限保存,并合并到最后的权限中
284
+ const permPathList = router.mdl.utils.getPermissionPathList(req.body.Permission);
285
+ const allPerms = await res.app.models.permission.find({
286
+ Path: { $in: permPathList } ,
287
+ Enabled: true
288
+ }).lean();
289
+
290
+ const dsScope = {};
291
+ for (let i = 0; i < allPerms.length; i += 1) {
292
+ const p = allPerms[i].Path;
293
+ const pDot = p.replace(/^\//, '').replace(/\//g, '.');
294
+ const uPerm = Object.nestValue(req.user.Permission, pDot);
295
+ const bPerm = Object.nestValue(req.body.Permission, pDot);
296
+
297
+ if (uPerm && uPerm.Scope && bPerm && bPerm.Scope) {
298
+ // 当前账号此权限中的数据权限定义
299
+ for (let j = 0; j < (allPerms[i].Scope || []).length; j += 1) {
300
+ const sc = allPerms[i].Scope[j];
301
+
302
+ if (!uPerm.Scope[sc.Name] || !bPerm.Scope[sc.Name]) {
303
+ continue;
304
+ }
305
+
306
+ const dso = res.app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
307
+
308
+ if (dso) {
309
+ const dsoOptions = dso.Options || [];
310
+
311
+ const dsOpOfCurrentUser = dsoOptions.find(o => o.Value === uPerm.Scope[sc.Name]);
312
+ const dsOpInBody = dsoOptions.find(o => o.Value === bPerm.Scope[sc.Name]);
313
+
314
+ if (dsOpOfCurrentUser
315
+ && dsOpInBody
316
+ ) {
317
+ if (
318
+ (dsOpOfCurrentUser.Level !== void 0)
319
+ && (dsOpInBody.Level !== void 0)
320
+ && (dsOpInBody.Level <= dsOpOfCurrentUser.Level)
321
+ ) {
322
+ Object.setValue(dsScope, `${pDot}.Scope.${sc.Name}`, dsOpInBody.Value);
323
+ } else {
324
+ Object.setValue(dsScope, `${pDot}.Scope.${sc.Name}`, dsOpOfCurrentUser.Value);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+
231
332
  req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
333
+ Object.assign(req.body.Permission, dsScope);
232
334
  }
233
335
  } else {
234
336
  delete req.body.Permission;
@@ -79,7 +79,7 @@ router.post('/',
79
79
  }
80
80
  }
81
81
 
82
- req.body.Path = `${parent ? parent.Path + '/' : ''}${req.body.Name}`
82
+ req.body.Path = `${parent ? parent.Path : ''}/${req.body.Name}`
83
83
  req.body.BuiltIn = false;
84
84
 
85
85
  return next();
package/sms/index.js CHANGED
@@ -3,6 +3,9 @@ const path = require('path');
3
3
  const AliyunCore = require('@alicloud/pop-core');
4
4
  const nodemailer = require('nodemailer');
5
5
 
6
+ const submail = require('./platforms/submail');
7
+ const tencent = require('./platforms/tencent');
8
+
6
9
  let global;
7
10
 
8
11
  if (fs.existsSync(path.resolve(__dirname, '../../../global.js'))) {
@@ -66,7 +69,18 @@ const _sms_lib = {
66
69
  });
67
70
 
68
71
  var codeAndValue = {};
69
- codeAndValue[k.templateParamName] = v;
72
+ // codeAndValue[k.templateParamName] = v;
73
+
74
+ if (Array.isArray(k.templateParamName)) {
75
+ for (let i = 0; i < k.templateParamName.length; i += 1) {
76
+ const paramName = k.templateParamName[i];
77
+ if (paramName && v && v[paramName]) {
78
+ codeAndValue[paramName] = v[paramName];
79
+ }
80
+ }
81
+ } else {
82
+ codeAndValue[k.templateParamName] = v;
83
+ }
70
84
 
71
85
  var params = {
72
86
  "PhoneNumbers": p,
@@ -106,7 +120,7 @@ const _sms_lib = {
106
120
  }
107
121
  });
108
122
 
109
- const result = await MAIL_TRANS.sendMail({
123
+ const result = await MAIL_TRANS[k.mail].sendMail({
110
124
  from: k.from,
111
125
  to: p,
112
126
  subject: typeof k.title === 'function' ? k.title(v) : k.title,
@@ -121,6 +135,8 @@ const _sms_lib = {
121
135
  return result && result.accepted && result.accepted.length >= 1;
122
136
  }
123
137
  },
138
+ submail,
139
+ tencent,
124
140
  }
125
141
 
126
142
  module.exports = (app) => ({
@@ -137,7 +153,7 @@ module.exports = (app) => ({
137
153
 
138
154
  // const keys = (global && global.sms && global.sms[t]) || app.modules.account.config.sms.keys[t] || app.modules.account.config.sms.keys;
139
155
 
140
- if (keys.platform) {
156
+ if (keys && keys.platform) {
141
157
  // should not send too frequent!
142
158
  const lastSentTime = await app.cache.get(`${p}_lastSentTime`);
143
159
  if (lastSentTime && (Date.now() - lastSentTime) < (keys.resentGap || (60 * 1000))) {
@@ -182,7 +198,7 @@ module.exports = (app) => ({
182
198
  let v = _generateMSG(f);
183
199
  return await this.send(p, v, c, t);
184
200
  },
185
- verify: async function (p, v, del) {
201
+ verify: async function (p, v, del = true) {
186
202
  const cached = await app.cache.get(p);
187
203
 
188
204
  // clear cache if the code is correct
@@ -0,0 +1,70 @@
1
+ const axios = require('axios');
2
+ const crypto = require("crypto");
3
+
4
+ const client = axios.create({
5
+ baseURL: 'https://api-v4.mysubmail.com',
6
+ timeout: 308 * 1000,
7
+ headers: {
8
+ 'Content-Type':'application/json',
9
+ }
10
+ });
11
+
12
+ const sign = (appid, appkey, obj) => {
13
+ // 排序obj并转为json字符串
14
+ const bodyStr = Object.keys(obj).sort((a, b) => a > b ? 1 : (a < b ? -1 : 0)).map((k) => `${k}=${obj[k]}`).join('&');
15
+
16
+ // md5
17
+ var md5 = crypto.createHash('md5');
18
+
19
+ return md5.update(`${appid}${appkey}${bodyStr}${appid}${appkey}`).digest('hex');
20
+ };
21
+
22
+ // https://www.mysubmail.com/documents/OOVyh
23
+ module.exports = {
24
+ send: async function (k, p, v) {
25
+ if (!k || !k.templateCode || !k.templateParamName) {
26
+ throw new Error('SMS parameters not configured correctly for platform (Submail)');
27
+ }
28
+
29
+ var codeAndValue = {};
30
+
31
+ if (Array.isArray(k.templateParamName)) {
32
+ for (let i = 0; i < k.templateParamName.length; i += 1) {
33
+ const paramName = k.templateParamName[i];
34
+ if (paramName && v && v[paramName]) {
35
+ codeAndValue[paramName] = v[paramName];
36
+ }
37
+ }
38
+ } else {
39
+ codeAndValue[k.templateParamName] = v;
40
+ }
41
+
42
+ const requestBody = {
43
+ appid: k.appid, // 在 SUBMAIL 应用集成中创建的短信应用 ID
44
+ to: p, // 收件人手机号码
45
+ project: k.templateCode, // 模版 ID
46
+ timestamp: Math.floor(Date.now() / 1000), // Timestamp UNIX 时间戳
47
+ sign_type: 'md5', // md5 or sha1 or normal
48
+ sign_version: 2, // signature 加密计算方式(当 sign_version 传 2 时,vars 参数不参与加密计算)
49
+ };
50
+
51
+ const signature = sign(k.appid, k.appkey, requestBody);
52
+
53
+ return await client.post('/sms/xsend', {
54
+ ...requestBody,
55
+ signature, // 应用密匙或数字签名
56
+ vars: JSON.stringify(codeAndValue), // 使用文本变量动态控制短信中的文本。
57
+ // sms_signature: '', // 自定义短信签名,如果忽略此参数,将使用模板的默认签名作为签名(此参数不参与加密计算)
58
+ }).then(({data}) => {
59
+ if (data.status === 'success') {
60
+ return true;
61
+ } else {
62
+ console.error('SMS send error:', data.msg);
63
+ }
64
+
65
+ return false;
66
+ }).catch(() => {
67
+ return false;
68
+ });
69
+ }
70
+ };
@@ -0,0 +1,73 @@
1
+ const tencentcloud = require("tencentcloud-sdk-nodejs-sms");
2
+
3
+ const smsClient = tencentcloud.sms.v20210111.Client;
4
+
5
+ // https://cloud.tencent.com/document/product/382/55981
6
+ module.exports = {
7
+ send: async function (k, p, v) {
8
+ if (!k || !k.SecretId || !k.SecretKey || !k.appid || !k.templateCode || !k.signName || !k.templateParamName) {
9
+ throw new Error('SMS parameters not configured correctly for platform (Tencent)');
10
+ }
11
+
12
+ var paramValues = [];
13
+
14
+ if (Array.isArray(k.templateParamName)) {
15
+ for (let i = 0; i < k.templateParamName.length; i += 1) {
16
+ const paramName = k.templateParamName[i];
17
+ if (paramName && v && v[paramName]) {
18
+ paramValues.push(v[paramName]);
19
+ }
20
+ }
21
+ } else {
22
+ paramValues.push(v);
23
+ }
24
+
25
+ const client = new smsClient({
26
+ credential: {
27
+ secretId: k.SecretId,
28
+ secretKey: k.SecretKey,
29
+ },
30
+ region: "ap-beijing",
31
+ // 可选配置实例
32
+ profile: {
33
+ signMethod: "TC3-HMAC-SHA256", // 签名方法
34
+ httpProfile: {
35
+ reqMethod: "POST", // 请求方法
36
+ reqTimeout: 30, // 请求超时时间,默认60s
37
+ headers: {
38
+ Action: 'SendSms',
39
+ Region: 'ap-beijing',
40
+ Version: '2021-01-11',
41
+ Language: 'zh-CN',
42
+ },
43
+ },
44
+ },
45
+ });
46
+
47
+ return await client
48
+ .SendSms({
49
+ PhoneNumberSet: [p],
50
+
51
+ SmsSdkAppId: k.appid,
52
+ TemplateId: k.templateCode,
53
+ SignName: k.signName,
54
+
55
+ TemplateParamSet: paramValues,
56
+ })
57
+ .then((response) => {
58
+ const data = (response.SendStatusSet || [])[0] || {};
59
+
60
+ if (data.Code === 'Ok') {
61
+ return true;
62
+ } else {
63
+ console.error('SMS send error:', data.Code);
64
+ }
65
+
66
+ return false;
67
+ })
68
+ .catch((err) => {
69
+ console.log('SMS send error:', err);
70
+ return false;
71
+ });
72
+ }
73
+ };
package/utils.js CHANGED
@@ -49,7 +49,9 @@ function clearPermission(perm) {
49
49
  }
50
50
  });
51
51
 
52
- // TODO: add a field if nothing here, otherwise the db will not save it??? should be fixed!!
52
+ // add a field if nothing here, otherwise the db will not save it??? should be fixed!!
53
+ // fixed by disabling the minimize option in mongoose, but we still should add this field
54
+ // so we can indicate whether the account has this permission clearly
53
55
  if (Object.keys(p).length <= 0) {
54
56
  p.has = true;
55
57
  }