free-be-account 0.0.1

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 ADDED
@@ -0,0 +1,1002 @@
1
+ // 三种方式注册,简单用户名密码,不需要验证,只要不重复即可
2
+ // 手机号注册,验证码
3
+ // 邮箱注册,验证
4
+
5
+ // nodemailer send email
6
+ const passport = require('passport');
7
+ const LocalStrategy = require('passport-local').Strategy;
8
+ const {v1: uuidv1} = require('uuid');
9
+ const crypto = require("./crypto");
10
+ const { clearPermission, getPermissionPathList, verifyPassword, encryptPwd } = require('./utils');
11
+ const { AccountAuditStatus } = require('./enum');
12
+ const sms = require('./sms');
13
+
14
+ let __app_service_list_saved = false;
15
+ let __saved_service_list;
16
+
17
+ const __getServiceList = async (res, filter = { Enabled: true }) => {
18
+ // add app.serviceList into db if not yet
19
+ if (!__app_service_list_saved) {
20
+ await res.app.modules.account.utils.saveServiceList(res.app);
21
+ __app_service_list_saved = true;
22
+ } else {
23
+ return __saved_service_list;
24
+ }
25
+
26
+ const allPerms = await res.app.models.permission.find(filter);
27
+
28
+ const permList = {};
29
+ if (allPerms && allPerms.length > 0) {
30
+ // create permission object
31
+ allPerms.forEach(doc => {
32
+ if (!doc.Path) return;
33
+
34
+ const pathList = doc.Path.split('/');
35
+ let permo = permList;
36
+ for (let i = 0; i < pathList.length; i += 1) {
37
+ const pl = pathList[i];
38
+ if (!pl) continue;
39
+ permo[pl] = permo[pl] || {};
40
+ permo = permo[pl];
41
+
42
+ if (i >= pathList.length - 1) {
43
+ Object.assign(permo, {
44
+ Title: doc.Title,
45
+ Description: doc.Description,
46
+ Index: doc.Index,
47
+ Scope: doc.Scope.map(sc => {
48
+ const dso = res.app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
49
+ return {
50
+ Label: dso ? dso.Label : '',
51
+ Field: `${sc.Name}`,
52
+ Type: 'Select',
53
+ Options: dso ? dso.Options : []
54
+ }
55
+ }),
56
+ })
57
+ }
58
+ }
59
+ })
60
+ }
61
+
62
+ __saved_service_list = permList;
63
+
64
+ return permList;
65
+ }
66
+
67
+ /**
68
+ * 检验接口调用权限。
69
+ *
70
+ * @param app
71
+ * @param user_permission
72
+ * @param api_path
73
+ * @returns {boolean}
74
+ */
75
+ const verify_api_permission = async (app, mdl, user, api_path) => {
76
+ const service_list = await __getServiceList({ app });
77
+ if (!service_list || Object.keys(service_list).length <= 0) return false;
78
+
79
+ const user_permission = user ? user.Permission : {};
80
+
81
+ if (user_permission === '*') return true;
82
+
83
+ if (!service_list) return false;
84
+ if (!user_permission) return false;
85
+
86
+ // hooks from other modules
87
+ const customizedList = ((mdl.config && mdl.config['customizeControlList']) || []);
88
+ for (let i = 0; i < customizedList.length; i += 1) {
89
+ const cl = customizedList[i];
90
+
91
+ if (typeof cl !== 'object') continue;
92
+ if (!cl.pattern || !cl.replace) continue;
93
+
94
+ if (typeof cl.pattern === 'string')
95
+ api_path = api_path.replace(cl.pattern, cl.replace);
96
+
97
+ if (typeof cl.pattern === 'object')
98
+ api_path = api_path.replace(new RegExp(cl.pattern), cl.replace);
99
+ }
100
+
101
+ const qIndex = api_path.indexOf('?')
102
+ if (qIndex > 0) api_path = api_path.substr(0, qIndex)
103
+ if (api_path.startsWith('/api/')) api_path = api_path.slice('/api/'.length);
104
+ if (api_path.startsWith('api/')) api_path = api_path.slice('api/'.length);
105
+ const api_service_list = api_path.split('/');
106
+ let service = service_list;
107
+ let user_service = typeof user_permission === 'string' ? JSON.parse(user_permission.replace(/'/g, '"')) : user_permission;
108
+
109
+ // check permission
110
+ for (let i = 0; i < api_service_list.length; ++i) {
111
+ const api_service = api_service_list[i];
112
+
113
+ if (api_service !== '') {
114
+ if (service[api_service]) {
115
+ if (!user_service[api_service]) {
116
+ return false;
117
+ }
118
+
119
+ service = service[api_service];
120
+ user_service = user_service[api_service];
121
+ }
122
+ else {
123
+ return true;
124
+ }
125
+ }
126
+ }
127
+
128
+ return true; // TODO: secure enough??
129
+ }
130
+
131
+ module.exports = {
132
+ sms,
133
+ AccountAuditStatus,
134
+ config: {
135
+ routeRoot: 'account',
136
+ asRouteService: true,
137
+ defaultPassword: '12345678',
138
+ subAccountsHaveSameDateScope: true,
139
+ subAccountsHaveSubAccountsPermission: false,
140
+
141
+ accountRequireAudit: true,
142
+ accountDefaultPermissions: {},
143
+ accountDefaultPassword: '12345678',
144
+ accountDefaultPasswordRandom: false,
145
+ accountDefaultPasswordRandomLength: 6,
146
+ // accountDefaultPermissions: [
147
+ // // could from system config
148
+ // // {
149
+ // // condition: {},
150
+ // // permission: {}
151
+ // // }
152
+ // ],
153
+ whiteList: [],
154
+ userWhiteList: [],
155
+ customizeControlList: [],
156
+ keepTokenAccounts: ['demo'],
157
+ strategy: 'local',
158
+ defaultAccountName: 'admin',
159
+ defaultAccountPwd: 'adminadmin',
160
+ pwdEncryptMethod: ['sha1', 'bcrypt', 'md5'],
161
+ desKey: 'eis,is,s,2020',
162
+
163
+ permFields: [
164
+ {
165
+ Type: 'Category',
166
+ Label: '权限定义信息',
167
+ },
168
+ {
169
+ Name: 'Index',
170
+ Label: '排序号',
171
+ Type: 'Number',
172
+ },
173
+ {
174
+ Name: 'Name',
175
+ Label: '数据名称',
176
+ Type: 'String',
177
+ },
178
+ {
179
+ Name: 'Title',
180
+ Label: '显示名称',
181
+ Type: 'String',
182
+ },
183
+ {
184
+ Name: 'Description',
185
+ Label: '说明',
186
+ Type: 'Text',
187
+ },
188
+ ],
189
+
190
+ infoStepsDefinition: [
191
+ {
192
+ Name: '用户信息',
193
+ Index: 1,
194
+ Description: [{ Status: '1', Description: '已填写' }, { Status: '-1', Description: '未填写' }, { Status: '0', Description: '未填写' }],
195
+ Actions: [
196
+ {
197
+ Label: '保存',
198
+ Action: 'save',
199
+ },
200
+ {
201
+ Label: '提交审核',
202
+ Action: 'submit',
203
+ },
204
+ {
205
+ Label: '修改',
206
+ Action: 'edit',
207
+ },
208
+ ],
209
+ Fields: [
210
+ {
211
+ Type: 'Category',
212
+ Label: '账号信息',
213
+ },
214
+ {
215
+ Type: 'String',
216
+ Label: '登录手机号',
217
+ Name: 'PhoneNumber',
218
+ Index: 1,
219
+ ReadOnly: true,
220
+ },
221
+ {
222
+ Type: 'Category',
223
+ Label: '用户信息',
224
+ },
225
+ {
226
+ Type: 'String',
227
+ Label: '姓名',
228
+ Name: 'Profile.Name',
229
+ Index: 1,
230
+ },
231
+ {
232
+ Type: 'String',
233
+ Label: '昵称',
234
+ Name: 'Profile.NickName',
235
+ Index: 2,
236
+ },
237
+ {
238
+ Type: 'String',
239
+ Label: '邮箱',
240
+ Name: 'Profile.Email',
241
+ Index: 3,
242
+ },
243
+ {
244
+ Type: 'String',
245
+ Label: '职务',
246
+ Name: 'Profile.Title',
247
+ Index: 4,
248
+ },
249
+ ],
250
+ },
251
+ ],
252
+
253
+ sms: {
254
+ platform: '',
255
+ keys: {}
256
+ },
257
+
258
+ dependencies: [
259
+ 'core-modules'
260
+ ]
261
+ },
262
+ data: {
263
+ account: {
264
+ // account could have sub accounts
265
+ Parent: { type: 'ID', refer: 'account' },
266
+
267
+ // local login user name and password
268
+ PhoneNumber: { type: 'String', unique: true, sparse: true },
269
+ UserName: { type: 'String', unique: true, sparse: true },
270
+ Password: { type: 'String' },
271
+
272
+ // 3rd party login token
273
+ Secret: { type: 'String', unique: true, sparse: true },
274
+
275
+ // more info saved in profile
276
+ Profile: { type: 'Object' },
277
+
278
+ Enabled: { type: 'Boolean', default: true },
279
+
280
+ Permission: { type: 'Object', default: {} },
281
+
282
+ // Audit status
283
+ Status: { type: 'String' },
284
+
285
+ Org: { type: 'ID', refer: 'organization' },
286
+
287
+ Labels: { type: 'Array', default: [] }
288
+ },
289
+
290
+ organization: {
291
+ Parent: { type: 'ID', refer: 'organization' },
292
+ Name: { type: 'String', required: true },
293
+ Description: { type: 'String' },
294
+ Index: { type: 'Number', required: true },
295
+ IsVirtual: { type: 'Boolean', default: false },
296
+
297
+ Permission: { type: 'Object', default: {} },
298
+ },
299
+
300
+ permission: {
301
+ Parent: { type: "ID", refer: 'permission' },
302
+ Name: { type: 'String' },
303
+ Title: { type: 'String' },
304
+ Description: { type: 'String' },
305
+ Path: { type: 'String' },
306
+ Index: { type: 'Number', required: true },
307
+ Enabled: { type: 'Boolean', required: true, default: true },
308
+ BuiltIn: { type: "Boolean", required: true, default: true },
309
+ Scope: [
310
+ {
311
+ Name: { type: 'String', required: true },
312
+ Params: { type: 'Array' },
313
+ }
314
+ ],
315
+ },
316
+
317
+ plabel: {
318
+ Name: { type: 'String', required: true, unique: true },
319
+ Description: { type: 'String' },
320
+ Index: { type: 'Number', required: true },
321
+ Enabled: { type: 'Boolean', default: true },
322
+ Permission: { type: 'Object', default: {} },
323
+
324
+ // label could be nagtive, means a user with it will DO NOT has it's permissions
325
+ Negative: { type: 'Boolean', default: false },
326
+ },
327
+ },
328
+ utils: {
329
+ verify_api_permission,
330
+ ...require('./utils')
331
+ },
332
+ hasPermission: async (req, mdl) => {
333
+ // compare user permissions, completed permissions, and this api path
334
+ // all the other APIs
335
+ let access_token = req.cookies.token || req.header('Authorization');
336
+ let cacheData = (await req.app.cache.get(access_token)) || {};
337
+
338
+ let id = cacheData.userId;
339
+ let user;
340
+
341
+ // 用来做第三方集成身份认证的字段
342
+ let userid = req.body.UserId || req.header('UserId');
343
+ let appid = req.body.AppId || req.header('AppId');
344
+ let ts = req.body.Timestamp || req.header('Timestamp');
345
+ // md5(JSON.stringify({Timestamp:xxx, UserId: xxx, UserSecret:xxx }))
346
+ let sign = req.body.Sign || req.header('Sign');
347
+
348
+ // if (cacheData.type === 'wx') {
349
+ // // login with wechat
350
+ // user = await User.findOne({ WxOpenId: id }).lean();
351
+ // }
352
+ // else
353
+ if (cacheData.type === 'pwd') {
354
+ // login with username/email/phone and password
355
+ user = await req.app.models['account'].findOne({ id: id, Enabled: true, Deleted: false });
356
+ }
357
+ else if (userid && appid && sign && ts) {
358
+ // 第三方系统集成
359
+ const tmpUser = await req.app.models['account'].findOne({ id: userid, Enabled: true, Deleted: false });
360
+
361
+ if (!tmpUser) {
362
+ return false;
363
+ }
364
+
365
+ const tmpSign = crypto.MD5(JSON.stringify({
366
+ Timestamp: ts,
367
+ UserId: userid,
368
+ UserSecret: tmpUser.Secret
369
+ }));
370
+
371
+ if (tmpSign !== sign) {
372
+ req.app.logger.debug('user: ' + userid + ',sign: ' + sign + ',ts:' + ts + ',realSign: ' + tmpSign);
373
+ return false;
374
+ }
375
+
376
+ // 检查请求时间
377
+ if (tmpUser.LastCallTimestamp && tmpUser.LastCallTimestamp >= ts) {
378
+ // 上次请求时间大于当前时间戳
379
+ return false;
380
+ }
381
+
382
+ // 身份验证通过
383
+ tmpUser.isIntegration = true;
384
+ user = tmpUser;
385
+
386
+ // 更新时间戳
387
+ tmpUser.LastCallTimestamp = ts;
388
+ await tmpUser.save();
389
+ }
390
+ else {
391
+ return false;
392
+ }
393
+
394
+ if (!user) {
395
+ return false;
396
+ }
397
+
398
+ await req.app.cache.put(access_token, { userId: id, type: cacheData.type }, req.app.config['cacheTimeout']);
399
+ // cache.put(access_token, { userId: id, type: cacheData.type }, req.app.config['cacheTimeout']);
400
+ req.user = user;
401
+
402
+ // check user white list, urls in which will be public for all the login users
403
+ const whiteList = ((mdl.config && mdl.config['userWhiteList']) || []);
404
+ for (let i = 0; i < whiteList.length; i += 1) {
405
+ const wl = whiteList[i];
406
+
407
+ if (typeof wl === 'string' && wl.toLowerCase() === req.originalUrl.toLowerCase()) return true;
408
+
409
+ if (typeof wl === 'object' && new RegExp(wl).test(req.originalUrl)) return true;
410
+ }
411
+
412
+ // clear sub account permission for all sub accounts
413
+ if(!mdl.config.subAccountsHaveSubAccountsPermission && req.user && req.user.Parent && req.user.Permission && req.user.Permission.uc && req.user.Permission.uc.sub){
414
+ delete req.user.Permission.uc.sub;
415
+ }
416
+
417
+ // if has permission to access the API, then call next
418
+ const hasPerm = await mdl.utils.verify_api_permission(req.app, mdl, user, req.originalUrl);
419
+ if (!!req.originalUrl && hasPerm)
420
+ return true;
421
+
422
+ // otherwise return error
423
+ return false;
424
+ },
425
+ i18n: {
426
+ 'en-us': {
427
+ 'module-title': 'Account',
428
+ 'module-description': 'Manage all the accounts and related features in the system.',
429
+
430
+ 'module-mgmt-title': 'Account management',
431
+ 'module-mgmt-description': 'Manage all the accounts in the system.',
432
+
433
+ 'module-org-title': 'Organization management',
434
+ 'module-org-description': 'Manage all the organizations in the system.',
435
+ 'module-org-export-title': 'Export Organization',
436
+ 'module-org-export-description': 'Export all the organizations from the system.',
437
+
438
+
439
+ 'module-perm-title': 'Permission management',
440
+ 'module-perm-description': 'Manage all the permission definitions in the system.',
441
+ 'scope-field-label': 'Data Scope',
442
+ 'scope-params-header-label': 'Data Scope',
443
+ 'scope-params-label': 'Params',
444
+
445
+ 'Org Based Data Scope': 'Organization based data scope',
446
+ 'Data scope base on the organization':'Data scope base on the organization',
447
+ 'Self':'Self',
448
+ 'My Org': 'The organization',
449
+ 'All': 'All',
450
+ 'Account Field':'Account field',
451
+ 'Org Field':'Organization field',
452
+
453
+ 'module-label-title': 'Permission Label management',
454
+ 'module-label-description': 'Manage all the permission labels in the system.',
455
+
456
+ 'module-uc-title': 'User Center',
457
+ 'module-uc-description': '',
458
+ 'module-uc-sub-title': 'Sub Account',
459
+ 'module-uc-sub-description': '',
460
+ },
461
+ 'zh-cn': {
462
+ 'module-title': '账号管理',
463
+ 'module-description': '统一管理系统中的账号以及相关功能。',
464
+
465
+ 'module-mgmt-title': '管理',
466
+ 'module-mgmt-description': '统一管理系统中的账号。',
467
+
468
+ 'module-org-title': '组织机构管理',
469
+ 'module-org-description': '统一管理系统中的组织机构。',
470
+ 'module-org-export-title': '导出机构',
471
+ 'module-org-export-description': '导出系统中所有的机构配置数据。',
472
+
473
+ 'module-perm-title': '权限定义管理',
474
+ 'module-perm-description': '统一管理系统中的权限定义。',
475
+ 'scope-field-label': '数据权限',
476
+ 'scope-params-header-label': '数据权限',
477
+ 'scope-params-label': '关联参数',
478
+
479
+ 'Org Based Data Scope': '基于组织的数据权限',
480
+ 'Data scope base on the organization':'基于组织机构的数据权限控制。',
481
+ 'Self':'自己',
482
+ 'My Org': '所在机构',
483
+ 'All': '全部',
484
+ 'Account Field':'代表账号的字段名',
485
+ 'Org Field':'代表组织的字段名',
486
+
487
+ 'module-label-title': '权限标签管理',
488
+ 'module-label-description': '统一管理系统中的权限标签。',
489
+
490
+ 'module-uc-title': '用户中心',
491
+ 'module-uc-description': '',
492
+ 'module-uc-sub-title': '子账号管理',
493
+ 'module-uc-sub-description': '',
494
+ }
495
+ },
496
+ hooks: {
497
+ onBegin: (app) => {
498
+ app.use(passport.initialize());
499
+ },
500
+ onModulesReady: (app, mdl) => {
501
+ // register the data scope containers
502
+ app.registerContainer(null, 'DataScope', 'The data scope container which will contains all the data scope definitions.', (c, o) => {
503
+ if (!o.Options || !o.Func) {
504
+ app.logger.error(`Data scope ${o.name} should have options and func!!`)
505
+ }
506
+
507
+ return true;
508
+ });
509
+
510
+ app.addDataScope({
511
+ mdl: mdl,
512
+ Name: 'orgDataScope',
513
+ Label: mdl.t('Org Based Data Scope'),
514
+ Description: mdl.t('Data scope base on the organization'),
515
+ Options: [
516
+ {
517
+ Label: mdl.t('Self'),
518
+ Value: 'self',
519
+ },
520
+ {
521
+ Label: mdl.t('My Org'),
522
+ Value: 'org',
523
+ },
524
+ {
525
+ Label: mdl.t('All'),
526
+ Value: 'all',
527
+ }
528
+ ],
529
+ Default: 'self',
530
+ Params: [
531
+ {
532
+ Label: mdl.t('Account Field'),
533
+ Name: 'Account',
534
+ Type: 'String'
535
+ },
536
+ {
537
+ Label: mdl.t('Org Field'),
538
+ Name: 'Org',
539
+ Type: 'String',
540
+ }
541
+ ],
542
+ /**
543
+ * The function to generate the filter object base on the specified data scope
544
+ */
545
+ Func: (scope, pScope, p) => {
546
+ return (req, res, next) => {
547
+ // add filter according to the data scope
548
+ let val;
549
+ const filter = {};
550
+
551
+ // get user data scope for the current router
552
+ if (req.user && req.user.Permission && p) {
553
+ const pList = p.split('/');
554
+ let perm = req.user.Permission;
555
+ let userScope;
556
+
557
+ for (let i = 0; i < pList.length; i += 1) {
558
+ const pl = pList[i];
559
+
560
+ if (pl) {
561
+ if (perm[pl]) {
562
+ perm = perm[pl];
563
+ if (perm.Scope) {
564
+ userScope = perm.Scope;
565
+ }
566
+ }
567
+ }
568
+ }
569
+ val = userScope ? userScope['orgDataScope'] : undefined;
570
+ }
571
+
572
+ // make filter
573
+ if (typeof val !== 'undefined')
574
+ switch (val.toString()) {
575
+ case 'self':
576
+ if (req.user.Parent && mdl.config.subAccountsHaveSameDateScope){
577
+ filter[pScope.Params[0].Account] = {$in: [req.user.id, req.user.Parent]};
578
+ } else {
579
+ filter[pScope.Params[0].Account] = req.user.id;
580
+ }
581
+ res.locals.filter = Object.merge({}, res.locals.filter, filter);
582
+ break;
583
+ case 'org':
584
+ filter[pScope.Params[0].Org] = req.user.Org;
585
+ res.locals.filter = Object.merge({}, res.locals.filter, filter);
586
+ break;
587
+ default:
588
+ break;
589
+ }
590
+
591
+ return next();
592
+ }
593
+ }
594
+ });
595
+ },
596
+ onLoadRouters: async (app, m) => {
597
+ // define the local strategy
598
+ passport.use(new LocalStrategy(
599
+ function (uname, pwd, done) {
600
+ const username = crypto.encoder.desDecode(uname, m.config.desKey);
601
+ const password = crypto.encoder.desDecode(pwd, m.config.desKey);
602
+ app.models['account'].findOne(
603
+ {
604
+ $or: [{ PhoneNumber: username }, { UserName: username }],
605
+ // Password: password,
606
+ Enabled: true,
607
+ Deleted: false,
608
+ }, function (err, user) {
609
+ if (err) { return done(err); }
610
+ if (!user) { return done(null, false); }
611
+ if (!verifyPassword(password, user.Password, m.config.pwdEncryptMethod || 'md5')) { return done(null, false); }
612
+ return done(null, user);
613
+ });
614
+ }
615
+ ));
616
+
617
+ // login with the specified strategy
618
+ app.post(`${app.config['baseUrl'] || ''}/logedin`,
619
+ async (req, res) => {
620
+ // permission control
621
+ if (!await m.hasPermission(req, m)) {
622
+ await res.endWithErr(200, 401);
623
+ return;
624
+ } else {
625
+ res.endWithData({});
626
+ return;
627
+ }
628
+ }
629
+ );
630
+
631
+ // permission control
632
+ app.use(async (req, res, next) => {
633
+ // permission control
634
+ if (!await m.hasPermission(req, m)) {
635
+ const whiteList = ((m.config && m.config['whiteList']) || []).concat([`${app.config['baseUrl'] || ''}/login`]);
636
+ for (let i = 0; i < whiteList.length; i += 1) {
637
+ const wl = whiteList[i];
638
+
639
+ if (typeof wl === 'string' && wl.toLowerCase() === req.originalUrl.toLowerCase()) return next();
640
+
641
+ if (typeof wl === 'object' && new RegExp(wl).test(req.originalUrl)) return next();
642
+ }
643
+
644
+ if (req.user && req.user.id) {
645
+ await res.endWithErr(400, 401);
646
+ }
647
+ else {
648
+ await res.endWithErr(401);
649
+ }
650
+
651
+ return;
652
+ }
653
+
654
+ return next();
655
+ });
656
+
657
+ async function clear_cache_token_by_user_id (id) {
658
+ if (!id) return;
659
+
660
+ const cacheKeys = await app.cache.keys();
661
+ if (cacheKeys && cacheKeys.length) {
662
+ for (let i = 0; i < cacheKeys.length; i += 1) {
663
+ const k = cacheKeys[i];
664
+
665
+ let value = await app.cache.get(k);
666
+ if (value && value.userId && value.userId === id)
667
+ await app.cache.del(k);
668
+ // cache.del(k);
669
+ }
670
+ }
671
+
672
+ // cache.keys().forEach(async (k) => {
673
+ // let value = await app.cache.get(k);
674
+ // if (value && value.userId && value.userId === id)
675
+ // cache.del(k);
676
+ // });
677
+ }
678
+
679
+ async function generate_new_access_token_pwd (userId, oldToken, keepToken = '') {
680
+ let uuid = keepToken || uuidv1();
681
+
682
+ // remove the old one from cache
683
+ app.cache.del(oldToken);
684
+ // cache.del(oldToken);
685
+ await clear_cache_token_by_user_id(userId);
686
+
687
+ // add the new one to the cache
688
+
689
+ app.cache.put(uuid, { userId: userId, type: 'pwd' }, app.config['cacheTimeout']);
690
+ // cache.put(uuid, { userId: userId, type: 'pwd' }, app.config['cacheTimeout']);
691
+
692
+ return uuid;
693
+ }
694
+
695
+ // login with the specified strategy
696
+ app.post(`${app.config['baseUrl'] || ''}/login`,
697
+ passport.authenticate(m.config['strategy'] || 'local', { session: false }),
698
+ async (req, res, next) => {
699
+
700
+ // set token into cookie
701
+ let access_token = req.cookies.token || req.header('Authorization');
702
+ let token;
703
+
704
+ if ((req.user && req.user.UserName && m.config['keepTokenAccounts'].indexOf(req.user.UserName) >= 0) ||
705
+ (req.user && req.user.PhoneNumber && m.config['keepTokenAccounts'].indexOf(req.user.PhoneNumber) >= 0)) {
706
+ // keep token
707
+ const kt = await app.cache.get(`_keep_token_${req.user.id}`);
708
+ token = await generate_new_access_token_pwd(req.user.id, access_token, kt);
709
+ app.cache.set(`_keep_token_${req.user.id}`, token);
710
+ } else {
711
+ token = await generate_new_access_token_pwd(req.user.id, access_token);
712
+ }
713
+
714
+ res.cookie('token', token, { maxAge: app.config['cookieTimeout'] });
715
+
716
+ res.addData({
717
+ Name: (req.user.Profile && req.user.Profile.Name) || req.user.PhoneNumber || req.user.UserName || '',
718
+ Avatar: req.user.Profile && req.user.Profile.Avatar ? req.user.Profile.Avatar : '',
719
+ Status: req.user.Status,
720
+ Org: req.user.Org,
721
+ }, false);
722
+
723
+ return next();
724
+ }
725
+ );
726
+
727
+ app.post(`${app.config['baseUrl'] || ''}/logout`,
728
+ (req, res, next) => {
729
+ let access_token = req.cookies.token || req.header('Authorization');
730
+
731
+ // call logout of the passport
732
+ req.logout();
733
+
734
+ // clear the cached token
735
+ res.clearCookie('token');
736
+
737
+ // clear cached data when the account is not in the keep token list
738
+ // app.cache.del(access_token);
739
+ if (access_token && (req.user && req.user.UserName && m.config['keepTokenAccounts'].indexOf(req.user.UserName) < 0) &&
740
+ (req.user && req.user.PhoneNumber && m.config['keepTokenAccounts'].indexOf(req.user.PhoneNumber) < 0)) {
741
+ app.cache.del(access_token);
742
+ }
743
+
744
+ res.locals.data = {};
745
+
746
+ return next();
747
+ }
748
+ );
749
+
750
+ app.post(`${app.config['baseUrl'] || ''}/can_i`,
751
+ async (req, res, next) => {
752
+ const urls = (req.body.url || '').split(',');
753
+ let canDo = [];
754
+
755
+ for (let i = 0; i < urls.length; i += 1) {
756
+ const url = urls[i];
757
+
758
+ if (!url || !req.user || !await res.app.modules.account.utils.verify_api_permission(req.app, m, req.user, url)) {
759
+ canDo[i] = false;
760
+ } else {
761
+ canDo[i] = true;
762
+ }
763
+ }
764
+
765
+ if (canDo.length === 1) canDo = canDo[0];
766
+
767
+ res.addData({ can: canDo });
768
+
769
+ return next();
770
+ }
771
+ );
772
+
773
+ // send sms
774
+ app.post(`${(app.config['baseUrl'] || '')}/register/sms`, async (req, res, next) => {
775
+ try {
776
+ if (!req.body.PhoneNumber) {
777
+ res.makeError(408, 'Please provide phone number!', m);
778
+ return next('route');
779
+ }
780
+ const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
781
+ const result = await res.Module('sms').sendRandom(phone, undefined, true, 'register');
782
+
783
+ if (!result) {
784
+ res.makeError(500, 'Failed to send sms!', m);
785
+ return next('route');
786
+ }
787
+ } catch (ex) {
788
+ res.makeError(500, 'Failed to send sms!', m);
789
+ return next('route');
790
+ }
791
+
792
+ res.addData({});
793
+ return next();
794
+ })
795
+
796
+
797
+ // verfiy the sms code
798
+ app.post(`${(app.config['baseUrl'] || '')}/register/verify`, async (req, res, next) => {
799
+ if (!req.body.PhoneNumber || !req.body.code) {
800
+ res.makeError(409, 'Please provide phone number and the sms code!', m);
801
+ return next('route');
802
+ }
803
+ const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
804
+ const result = await res.Module('sms').verify(phone, req.body.code);
805
+ // app.logger.debug(cache.exportJson());
806
+
807
+ if (!result) {
808
+ res.makeError(403, 'Code verification failed!', m);
809
+ return next('route');
810
+ }
811
+
812
+ res.addData({});
813
+ return next();
814
+ })
815
+
816
+
817
+ // verify phone number (duplication) for register
818
+ app.post(`${(app.config['baseUrl'] || '')}/register/verify/phone`, async (req, res, next) => {
819
+ if (!req.body.PhoneNumber) {
820
+ res.makeError(408, 'Please provide phone number!', m);
821
+ return next('route');
822
+ }
823
+ const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
824
+ const exists = await res.app.models.account.findOne({
825
+ $or: [{ PhoneNumber: phone }, { UserName: phone }],
826
+ });
827
+ res.addData({ used: !!exists });
828
+ return next();
829
+ })
830
+
831
+ // register with username password
832
+ app.post(`${(app.config['baseUrl'] || '')}/register`,
833
+ async (req, res, next) => {
834
+ if (!req.body.Password || !req.body.PhoneNumber || !req.body.code) {
835
+ res.makeError(400, 'Please provide phone number, sms code and the password!', m);
836
+ return next('route');
837
+ }
838
+ const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
839
+ const password = crypto.encoder.desDecode(req.body.Password, m.config.desKey);
840
+ if (password.length < 6) {
841
+ res.makeError(402, 'Password does not meet the requirement!', m);
842
+ return next('route');
843
+ }
844
+
845
+ const result = await res.Module('sms').verify(phone, req.body.code);
846
+
847
+ if (!result) {
848
+ res.makeError(403, 'Code verification failed!', m);
849
+ return next('route');
850
+ }
851
+
852
+ const existPhone = await res.app.models.account.countDocuments({ PhoneNumber: phone });
853
+ if (existPhone) {
854
+ res.makeError(404, 'The phone use used already!', m);
855
+ return next('route');
856
+ }
857
+
858
+ // only create with specified fields
859
+ res.locals.body = {
860
+ PhoneNumber: phone,
861
+ Password: encryptPwd(password, m.config.pwdEncryptMethod || 'md5')
862
+ }
863
+
864
+ if (!m.config.accountRequireAudit) {
865
+ res.locals.body.Status = AccountAuditStatus.Passed;
866
+ }
867
+
868
+ const defaultPerm = Object.assign({}, m.config.accountDefaultPermissions);
869
+ clearPermission(defaultPerm);
870
+ res.locals.body.Permission = defaultPerm;
871
+
872
+ return next();
873
+ },
874
+ app.CreateDocument('account')
875
+ );
876
+
877
+ // recover password
878
+ app.post(`${(app.config['baseUrl'] || '')}/recover`,
879
+ async (req, res, next) => {
880
+ if (!req.body.Password || !req.body.PhoneNumber || !req.body.code) {
881
+ res.makeError(400, 'Please provide phone number, sms code and the password!', m);
882
+ return next('route');
883
+ }
884
+ const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
885
+ const password = crypto.encoder.desDecode(req.body.Password, m.config.desKey);
886
+
887
+ const result = await res.Module('sms').verify(phone, req.body.code);
888
+
889
+ if (!result) {
890
+ res.makeError(403, 'Code verification failed!', m);
891
+ return next('route');
892
+ }
893
+
894
+ // only create with specified fields
895
+ res.locals.body = {
896
+ Password: encryptPwd(password, m.config.pwdEncryptMethod || 'md5')
897
+ }
898
+
899
+ res.locals.filter = {
900
+ PhoneNumber: phone
901
+ }
902
+
903
+ return next();
904
+ },
905
+ app.UpdateDocument('account', false, (req, res) => {
906
+ res.locals.data = {};
907
+ })
908
+ );
909
+
910
+ // get service list base on the current user permission
911
+ app.get(`${app.config['baseUrl'] || ''}/_service_list`,
912
+ async (req, res, next) => {
913
+ if (!req.user || !req.user.Permission || Object.keys(req.user.Permission).length <= 0) {
914
+ res.addData({});
915
+ return next('route');
916
+ }
917
+
918
+ let filter;
919
+ if (req.user.Permission !== '*') {
920
+ const permPathList = getPermissionPathList(req.user.Permission);
921
+ filter = { Path: { $in: permPathList }, Enabled: true };
922
+ } else {
923
+ filter = { Enabled: true };
924
+ }
925
+
926
+ res.addData(await __getServiceList(res, filter));
927
+
928
+ return next();
929
+ },
930
+ );
931
+
932
+ // process configured data scope
933
+ app.use(function _data_scope_middleware_start (req, res, next) {
934
+ return next();
935
+ });
936
+ app.use(function _data_scope_middleware_end (req, res, next) {
937
+ return next();
938
+ });
939
+
940
+ const insertDataScopeMW = (p, n, mw) => {
941
+ // get existing flow routers markers
942
+ const firstIndex = app._router.stack.findIndex(r => r.name === '_data_scope_middleware_start');
943
+ let lastIndex = app._router.stack.findIndex(r => r.name === '_data_scope_middleware_end');
944
+
945
+ if (firstIndex < 0 || lastIndex < 0) {
946
+ app.logger.error('Cannot find the data scope marker middleware!!');
947
+ process.exit(-1);
948
+ }
949
+
950
+ // temp store rest middlewares
951
+ let restRouters = app._router.stack.splice(firstIndex + 1);
952
+
953
+ if (restRouters.findIndex(r => r.name === n) < 0) {
954
+ app.logger.debug(`Adding data scope mw: ${n}`)
955
+ Object.defineProperty(mw, 'name', { value: n });
956
+ app.use(`${app.config.baseUrl}${p}(/*)?`, mw);
957
+ }
958
+
959
+ // restore the rest middlewares
960
+ app._router.stack = app._router.stack.concat(restRouters);
961
+ };
962
+
963
+ const dataScopeList = app.getContainerContent('DataScope');
964
+ for (let i = 0; i < dataScopeList.length; i += 1) {
965
+ const ds = dataScopeList[i];
966
+
967
+ if (await app.models.permission.countDocuments({ "Scope.Name": ds.Name }) > 0) {
968
+ const sList = await app.models.permission.find({ "Scope.Name": ds.Name });
969
+ for (let j = 0; j < sList.length; j += 1) {
970
+ const service = sList[j];
971
+
972
+ if (service.Path) {
973
+ insertDataScopeMW(service.Path, `${service.Path.replace(/\//g, '_')}_${ds.Name}`, ds.Func(ds, service.Scope.find(ss => ss.Name === ds.Name), service.Path));
974
+ }
975
+ }
976
+ }
977
+
978
+ }
979
+ },
980
+ onRoutersReady: async (app, m) => {
981
+ // create default user if it's an empty db
982
+ if (await m.models['account'].countDocuments({}) <= 0) {
983
+ let perms = app.ctx.serviceList()
984
+ if (!clearPermission(perms)) {
985
+ perms = {}
986
+ }
987
+
988
+ await m.models.account.create({
989
+ UserName: m.config.defaultAccountName || 'admin',
990
+ Password: crypto.MD5(m.config.defaultAccountPwd) || 'f6fdffe48c908deb0f4c3bd36c032e72',
991
+ Permission: perms,
992
+ Status: AccountAuditStatus.Passed,
993
+ Profile: {
994
+ Name: 'SuperAdmin'
995
+ }
996
+ });
997
+ }
998
+
999
+ // TODO: remove service list which are in the white list
1000
+ }
1001
+ }
1002
+ }