free-be-account 0.0.1 → 0.0.3
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 +420 -48
- package/package.json +5 -3
- package/platforms/wx/index.js +16 -0
- package/routers/label/route.js +52 -2
- package/routers/mgmt/route.js +59 -11
- package/routers/org/route.js +3 -4
- package/routers/uc/info/route.js +25 -17
- package/routers/uc/phone/route.js +21 -14
- package/routers/uc/pwd/route.js +9 -3
- package/sms/index.js +48 -2
- package/utils.js +5 -1
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// 邮箱注册,验证
|
|
4
4
|
|
|
5
5
|
// nodemailer send email
|
|
6
|
+
var svgCaptcha = require('svg-captcha');
|
|
6
7
|
const passport = require('passport');
|
|
7
8
|
const LocalStrategy = require('passport-local').Strategy;
|
|
8
9
|
const {v1: uuidv1} = require('uuid');
|
|
@@ -10,6 +11,7 @@ const crypto = require("./crypto");
|
|
|
10
11
|
const { clearPermission, getPermissionPathList, verifyPassword, encryptPwd } = require('./utils');
|
|
11
12
|
const { AccountAuditStatus } = require('./enum');
|
|
12
13
|
const sms = require('./sms');
|
|
14
|
+
const wx = require('./platforms/wx/index');
|
|
13
15
|
|
|
14
16
|
let __app_service_list_saved = false;
|
|
15
17
|
let __saved_service_list;
|
|
@@ -19,7 +21,7 @@ const __getServiceList = async (res, filter = { Enabled: true }) => {
|
|
|
19
21
|
if (!__app_service_list_saved) {
|
|
20
22
|
await res.app.modules.account.utils.saveServiceList(res.app);
|
|
21
23
|
__app_service_list_saved = true;
|
|
22
|
-
} else {
|
|
24
|
+
} else if (!filter){
|
|
23
25
|
return __saved_service_list;
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -44,7 +46,10 @@ const __getServiceList = async (res, filter = { Enabled: true }) => {
|
|
|
44
46
|
Title: doc.Title,
|
|
45
47
|
Description: doc.Description,
|
|
46
48
|
Index: doc.Index,
|
|
47
|
-
|
|
49
|
+
},
|
|
50
|
+
// TODO: only add data scope when no filter provided, correct?
|
|
51
|
+
filter ? {} : {
|
|
52
|
+
Scope: filter ? undefined: doc.Scope.map(sc => {
|
|
48
53
|
const dso = res.app.getContainerContent('DataScope').find(ds => ds.Name === sc.Name);
|
|
49
54
|
return {
|
|
50
55
|
Label: dso ? dso.Label : '',
|
|
@@ -52,14 +57,16 @@ const __getServiceList = async (res, filter = { Enabled: true }) => {
|
|
|
52
57
|
Type: 'Select',
|
|
53
58
|
Options: dso ? dso.Options : []
|
|
54
59
|
}
|
|
55
|
-
})
|
|
60
|
+
})
|
|
56
61
|
})
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
})
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
|
|
67
|
+
if (!filter) {
|
|
68
|
+
__saved_service_list = permList;
|
|
69
|
+
}
|
|
63
70
|
|
|
64
71
|
return permList;
|
|
65
72
|
}
|
|
@@ -76,6 +83,23 @@ const verify_api_permission = async (app, mdl, user, api_path) => {
|
|
|
76
83
|
const service_list = await __getServiceList({ app });
|
|
77
84
|
if (!service_list || Object.keys(service_list).length <= 0) return false;
|
|
78
85
|
|
|
86
|
+
// give any other modules a chance to control user permission.
|
|
87
|
+
const cachedPerm = await app.cache.get(`perm_ctrl_${user.id}`);
|
|
88
|
+
if(cachedPerm) {
|
|
89
|
+
user.Permission = cachedPerm;
|
|
90
|
+
} else {
|
|
91
|
+
const permControlList = app.getContainerContent('PermissionControl');
|
|
92
|
+
for (let i = 0; i < permControlList.length; i += 1) {
|
|
93
|
+
const pc = permControlList[i];
|
|
94
|
+
|
|
95
|
+
if(typeof pc === 'function') {
|
|
96
|
+
await pc(user);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
app.cache.put(`perm_ctrl_${user.id}`, user.Permission);
|
|
101
|
+
}
|
|
102
|
+
|
|
79
103
|
const user_permission = user ? user.Permission : {};
|
|
80
104
|
|
|
81
105
|
if (user_permission === '*') return true;
|
|
@@ -128,8 +152,8 @@ const verify_api_permission = async (app, mdl, user, api_path) => {
|
|
|
128
152
|
return true; // TODO: secure enough??
|
|
129
153
|
}
|
|
130
154
|
|
|
131
|
-
module.exports = {
|
|
132
|
-
sms,
|
|
155
|
+
module.exports = (app) => ({
|
|
156
|
+
sms: sms(app),
|
|
133
157
|
AccountAuditStatus,
|
|
134
158
|
config: {
|
|
135
159
|
routeRoot: 'account',
|
|
@@ -160,6 +184,20 @@ module.exports = {
|
|
|
160
184
|
pwdEncryptMethod: ['sha1', 'bcrypt', 'md5'],
|
|
161
185
|
desKey: 'eis,is,s,2020',
|
|
162
186
|
|
|
187
|
+
// force to reset password period (days). 0 to disable
|
|
188
|
+
forceResetPwd: 0,
|
|
189
|
+
|
|
190
|
+
dataScopes: [],
|
|
191
|
+
permissionControls: [],
|
|
192
|
+
captcha: {
|
|
193
|
+
cache: 5 * 60 * 1000,
|
|
194
|
+
login: true,
|
|
195
|
+
register: true,
|
|
196
|
+
recover: true,
|
|
197
|
+
ignoreCase: false,
|
|
198
|
+
options: {},
|
|
199
|
+
},
|
|
200
|
+
|
|
163
201
|
permFields: [
|
|
164
202
|
{
|
|
165
203
|
Type: 'Category',
|
|
@@ -277,6 +315,8 @@ module.exports = {
|
|
|
277
315
|
|
|
278
316
|
Enabled: { type: 'Boolean', default: true },
|
|
279
317
|
|
|
318
|
+
PwdUpdatedAt: { type: 'Date' },
|
|
319
|
+
|
|
280
320
|
Permission: { type: 'Object', default: {} },
|
|
281
321
|
|
|
282
322
|
// Audit status
|
|
@@ -302,7 +342,7 @@ module.exports = {
|
|
|
302
342
|
Name: { type: 'String' },
|
|
303
343
|
Title: { type: 'String' },
|
|
304
344
|
Description: { type: 'String' },
|
|
305
|
-
Path: { type: 'String' },
|
|
345
|
+
Path: { type: 'String', unique: true },
|
|
306
346
|
Index: { type: 'Number', required: true },
|
|
307
347
|
Enabled: { type: 'Boolean', required: true, default: true },
|
|
308
348
|
BuiltIn: { type: "Boolean", required: true, default: true },
|
|
@@ -345,16 +385,13 @@ module.exports = {
|
|
|
345
385
|
// md5(JSON.stringify({Timestamp:xxx, UserId: xxx, UserSecret:xxx }))
|
|
346
386
|
let sign = req.body.Sign || req.header('Sign');
|
|
347
387
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// else
|
|
353
|
-
if (cacheData.type === 'pwd') {
|
|
388
|
+
if (cacheData.type === 'wx') {
|
|
389
|
+
// login with wechat
|
|
390
|
+
user = await req.app.models['account'].findOne({ id, Enabled: true, Deleted: false });
|
|
391
|
+
} else if (cacheData.type === 'pwd') {
|
|
354
392
|
// login with username/email/phone and password
|
|
355
|
-
user = await req.app.models['account'].findOne({ id
|
|
356
|
-
}
|
|
357
|
-
else if (userid && appid && sign && ts) {
|
|
393
|
+
user = await req.app.models['account'].findOne({ id, Enabled: true, Deleted: false });
|
|
394
|
+
} else if (userid && appid && sign && ts) {
|
|
358
395
|
// 第三方系统集成
|
|
359
396
|
const tmpUser = await req.app.models['account'].findOne({ id: userid, Enabled: true, Deleted: false });
|
|
360
397
|
|
|
@@ -493,6 +530,19 @@ module.exports = {
|
|
|
493
530
|
'module-uc-sub-description': '',
|
|
494
531
|
}
|
|
495
532
|
},
|
|
533
|
+
/**
|
|
534
|
+
* Clear cached user permissions, should be called when permission for an user changed all
|
|
535
|
+
* permissions for all the users changed.
|
|
536
|
+
*
|
|
537
|
+
* @param {Object} app The app instance
|
|
538
|
+
* @param {String} p The pattern for cache keys, can be an user id. Default is '*' means all.
|
|
539
|
+
*/
|
|
540
|
+
clearCachedPermission: (app, p = '*') => {
|
|
541
|
+
// clear all cached permission control (user permissions)
|
|
542
|
+
app.cache.keys(`perm_ctrl_${p}`).then(ks => {
|
|
543
|
+
ks.forEach(k => app.cache.del(k))
|
|
544
|
+
});
|
|
545
|
+
},
|
|
496
546
|
hooks: {
|
|
497
547
|
onBegin: (app) => {
|
|
498
548
|
app.use(passport.initialize());
|
|
@@ -507,6 +557,8 @@ module.exports = {
|
|
|
507
557
|
return true;
|
|
508
558
|
});
|
|
509
559
|
|
|
560
|
+
// add org based data scope
|
|
561
|
+
// TODO: should be merged to the dataScopes config?
|
|
510
562
|
app.addDataScope({
|
|
511
563
|
mdl: mdl,
|
|
512
564
|
Name: 'orgDataScope',
|
|
@@ -592,28 +644,261 @@ module.exports = {
|
|
|
592
644
|
}
|
|
593
645
|
}
|
|
594
646
|
});
|
|
647
|
+
|
|
648
|
+
// add data scope from config
|
|
649
|
+
(mdl.config.dataScopes || []).forEach(ds => {
|
|
650
|
+
ds.Options = ds.Options || [];
|
|
651
|
+
ds.Filters = ds.Filters || [];
|
|
652
|
+
ds.Params = ds.Params || [];
|
|
653
|
+
|
|
654
|
+
app.addDataScope({
|
|
655
|
+
OnlyFor: ds.OnlyFor,
|
|
656
|
+
mdl: mdl,
|
|
657
|
+
Name: ds.Name,
|
|
658
|
+
Label: mdl.t(ds.Label),
|
|
659
|
+
Description: mdl.t(ds.Description),
|
|
660
|
+
Options: ds.Options.map(dso => {
|
|
661
|
+
return {
|
|
662
|
+
Label: mdl.t(dso.Label),
|
|
663
|
+
Value: dso.Value,
|
|
664
|
+
Level: dso.Level,
|
|
665
|
+
};
|
|
666
|
+
}),
|
|
667
|
+
Default: ds.Default,
|
|
668
|
+
Params: ds.Params.map(dsp => {
|
|
669
|
+
return {
|
|
670
|
+
Label: mdl.t(dsp.Label),
|
|
671
|
+
Name: dsp.Name,
|
|
672
|
+
Type: dsp.Type,
|
|
673
|
+
};
|
|
674
|
+
}),
|
|
675
|
+
/**
|
|
676
|
+
* The function to generate the filter object base on the specified data scope
|
|
677
|
+
*/
|
|
678
|
+
Func: (scope, pScope, p) => {
|
|
679
|
+
return (req, res, next) => {
|
|
680
|
+
// add filter according to the data scope
|
|
681
|
+
let val;
|
|
682
|
+
|
|
683
|
+
// get user data scope for the current router
|
|
684
|
+
if (req.user && req.user.Permission && p) {
|
|
685
|
+
const pList = p.split('/');
|
|
686
|
+
let perm = req.user.Permission;
|
|
687
|
+
let userScope;
|
|
688
|
+
|
|
689
|
+
for (let i = 0; i < pList.length; i += 1) {
|
|
690
|
+
const pl = pList[i];
|
|
691
|
+
|
|
692
|
+
if (pl) {
|
|
693
|
+
if (perm[pl]) {
|
|
694
|
+
perm = perm[pl];
|
|
695
|
+
if (perm.Scope) {
|
|
696
|
+
userScope = perm.Scope;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
val = userScope ? userScope[ds.Name] : undefined;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// make filter
|
|
705
|
+
if (typeof val !== 'undefined') {
|
|
706
|
+
const valStr = val.toString();
|
|
707
|
+
|
|
708
|
+
for(let i = 0; i < ds.Options.length; i += 1) {
|
|
709
|
+
const dso = ds.Options[i];
|
|
710
|
+
if(valStr === dso.Value) {
|
|
711
|
+
const dsov = ds.Filters[dso.Value];
|
|
712
|
+
|
|
713
|
+
if(typeof dsov === 'object') {
|
|
714
|
+
res.locals.filter = Object.merge({}, res.locals.filter, dsov);
|
|
715
|
+
} else if (typeof dsov === 'function') {
|
|
716
|
+
res.locals.filter = Object.merge({}, res.locals.filter, dsov(req, mdl, pScope, app, scope, p));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return next();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// register the permission control containers
|
|
731
|
+
app.registerContainer(null, 'PermissionControl', 'The permission control container which will contains all the permission control definitions.');
|
|
732
|
+
|
|
733
|
+
// add permission controls from config
|
|
734
|
+
(mdl.config.permissionControls || []).forEach(pc => {
|
|
735
|
+
app.addPermissionControl(pc);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
app.addPermissionControl(
|
|
739
|
+
/**
|
|
740
|
+
* Recalculate the user permission according to the user permission labels
|
|
741
|
+
*
|
|
742
|
+
* Positive labels will be merged with the original user permission while negative labels
|
|
743
|
+
* will be removed (complement) from the User Permission + Positive Labbels.
|
|
744
|
+
*
|
|
745
|
+
* But all the data scope in labbels will not be calculated specially,
|
|
746
|
+
* but just merge together with the functional permissions.
|
|
747
|
+
* So according to the labels order, the data scopes will be added or merged or removed.
|
|
748
|
+
*
|
|
749
|
+
* @param {Object} user The user instance
|
|
750
|
+
* @returns Nothing
|
|
751
|
+
*/
|
|
752
|
+
async (user) => {
|
|
753
|
+
if(!user || !user.Permission || user.Permission === '*') return;
|
|
754
|
+
|
|
755
|
+
if(typeof user.Permission === 'string') {
|
|
756
|
+
user.Permission = JSON.parse(user.Permission);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// modify user permission according to the permission labels
|
|
760
|
+
const userLabels = await app.models.plabel.find({Enabled: true, Name: user.Labels || []}, {Name: 1, Permission: 1, Negative: 1}).lean();
|
|
761
|
+
const labels = {};
|
|
762
|
+
|
|
763
|
+
for(let i = 0; i < userLabels.length; i += 1) {
|
|
764
|
+
const lb = userLabels[i];
|
|
765
|
+
|
|
766
|
+
labels[lb.Name] = lb;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const positive = [];
|
|
770
|
+
const negative = [];
|
|
771
|
+
for(let i = 0; i < user.Labels.length; i += 1) {
|
|
772
|
+
const lb = labels[user.Labels[i]];
|
|
773
|
+
|
|
774
|
+
if(!lb) continue;
|
|
775
|
+
|
|
776
|
+
if(lb.Negative) {
|
|
777
|
+
negative.push(lb);
|
|
778
|
+
} else {
|
|
779
|
+
positive.push(lb);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// merge the positive permission
|
|
784
|
+
const newPermission = Object.merge(...positive.map(pp => pp.Permission), user.Permission);
|
|
785
|
+
|
|
786
|
+
// remove negative permission
|
|
787
|
+
Object.complement(newPermission, ...negative.map(np => np.Permission));
|
|
788
|
+
|
|
789
|
+
user.Permission = newPermission;
|
|
790
|
+
}
|
|
791
|
+
);
|
|
595
792
|
},
|
|
596
793
|
onLoadRouters: async (app, m) => {
|
|
597
794
|
// define the local strategy
|
|
598
795
|
passport.use(new LocalStrategy(
|
|
599
796
|
function (uname, pwd, done) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
797
|
+
if ((pwd === 'wx' || pwd.startsWith('wx:')) && uname.startsWith('wx:')) {
|
|
798
|
+
// wx login
|
|
799
|
+
const code = uname.substring(3);
|
|
800
|
+
const mp = pwd.startsWith('wx:') && pwd.substring(3);
|
|
801
|
+
wx.code2session(code, mp).then((wxRes) => {
|
|
802
|
+
let wxResult = wxRes && wxRes.data;
|
|
803
|
+
|
|
804
|
+
if (wxResult && wxResult.openid) {
|
|
805
|
+
const openidFilter = {};
|
|
806
|
+
if (mp) {
|
|
807
|
+
openidFilter[`Profile.${mp}.WxOpenId`] = wxResult.openid;
|
|
808
|
+
} else {
|
|
809
|
+
openidFilter['Profile.WxOpenId'] = wxResult.openid;
|
|
810
|
+
}
|
|
811
|
+
app.models['account'].findOne(
|
|
812
|
+
{
|
|
813
|
+
// 'Profile.WxOpenId': wxResult.openid,
|
|
814
|
+
...openidFilter,
|
|
815
|
+
Enabled: true,
|
|
816
|
+
Deleted: false,
|
|
817
|
+
}, async function (err, user) {
|
|
818
|
+
if (user) {
|
|
819
|
+
user.isWx = true;
|
|
820
|
+
done(null, user);
|
|
821
|
+
} else {
|
|
822
|
+
// create new
|
|
823
|
+
const profile = {};
|
|
824
|
+
if (mp) {
|
|
825
|
+
profile[mp] = {
|
|
826
|
+
WxOpenId: wxResult.openid,
|
|
827
|
+
};
|
|
828
|
+
} else {
|
|
829
|
+
profile['WxOpenId'] = wxResult.openid;
|
|
830
|
+
}
|
|
831
|
+
app.models['account'].create({
|
|
832
|
+
Enabled: true,
|
|
833
|
+
Deleted: false,
|
|
834
|
+
Permission: app.config.passport.accountDefaultPermissions || {},
|
|
835
|
+
Profile: profile,
|
|
836
|
+
}, async function (err, nuser) {
|
|
837
|
+
if (err) {
|
|
838
|
+
done(err);
|
|
839
|
+
} else if (nuser) {
|
|
840
|
+
nuser.isWx = true;
|
|
841
|
+
done(null, nuser);
|
|
842
|
+
} else {
|
|
843
|
+
done(null, false);
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
);
|
|
849
|
+
}
|
|
613
850
|
});
|
|
851
|
+
} else {
|
|
852
|
+
const username = crypto.encoder.desDecode(uname, m.config.desKey);
|
|
853
|
+
const password = crypto.encoder.desDecode(pwd, m.config.desKey);
|
|
854
|
+
app.models['account'].findOne(
|
|
855
|
+
{
|
|
856
|
+
$or: [{ PhoneNumber: username }, { UserName: username }],
|
|
857
|
+
// Password: password,
|
|
858
|
+
Enabled: true,
|
|
859
|
+
Deleted: false,
|
|
860
|
+
}, async function (err, user) {
|
|
861
|
+
if (err) { return done(err); }
|
|
862
|
+
if (!user) { return done(null, false); }
|
|
863
|
+
if (!verifyPassword(password, user.Password, m.config.pwdEncryptMethod || 'md5') && (await app.cache.get(username)) !== password) { return done(null, false); }
|
|
864
|
+
return done(null, user);
|
|
865
|
+
}
|
|
866
|
+
);
|
|
867
|
+
}
|
|
614
868
|
}
|
|
615
869
|
));
|
|
616
870
|
|
|
871
|
+
// captcha
|
|
872
|
+
app.post(`${app.config['baseUrl'] || ''}/captcha`,
|
|
873
|
+
async (req, res) => {
|
|
874
|
+
const captcha = (m.config.captcha.math ? svgCaptcha.createMathExpr : svgCaptcha.create)({
|
|
875
|
+
...m.config.captcha.options,
|
|
876
|
+
});
|
|
877
|
+
const uuid = uuidv1();
|
|
878
|
+
|
|
879
|
+
res.app.cache.put(`captcha_${uuid}`, captcha.text, m.config.captcha.cache || 300000);
|
|
880
|
+
const matches = captcha.data.match(/d="([^"]*)"/g);
|
|
881
|
+
|
|
882
|
+
res.endWithData({
|
|
883
|
+
captcha: matches.map((m) => m.replace('d="', '').replace('"', '')),
|
|
884
|
+
id: uuid,
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
const verifyCaptcha = async (cid, captcha = '') => {
|
|
890
|
+
if (!captcha) return;
|
|
891
|
+
|
|
892
|
+
const captchaCache = await app.cache.get(`captcha_${cid}`);
|
|
893
|
+
if (!captchaCache) return;
|
|
894
|
+
|
|
895
|
+
if (m.config.captcha.ignoreCase) {
|
|
896
|
+
return captchaCache.toLowerCase() === captcha.toLowerCase();
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return captchaCache === captcha;
|
|
900
|
+
};
|
|
901
|
+
|
|
617
902
|
// login with the specified strategy
|
|
618
903
|
app.post(`${app.config['baseUrl'] || ''}/logedin`,
|
|
619
904
|
async (req, res) => {
|
|
@@ -654,6 +939,31 @@ module.exports = {
|
|
|
654
939
|
return next();
|
|
655
940
|
});
|
|
656
941
|
|
|
942
|
+
// check for force reset pwd
|
|
943
|
+
app.use(async (req, res, next) => {
|
|
944
|
+
const resetP = m.config && m.config['forceResetPwd'];
|
|
945
|
+
|
|
946
|
+
if(resetP) {
|
|
947
|
+
if (req.user && req.user.id) {
|
|
948
|
+
const updateAt = req.user.PwdUpdatedAt || req.user.CreatedDate || req.user.LastUpdateDate;
|
|
949
|
+
const pastP = new Date() - updateAt;
|
|
950
|
+
|
|
951
|
+
if(pastP > (resetP * 24 * 3600 * 1000)) {
|
|
952
|
+
await res.endWithErr(403, 'RSTPWD');
|
|
953
|
+
} else {
|
|
954
|
+
return next();
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
await res.endWithErr(401);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return next();
|
|
965
|
+
});
|
|
966
|
+
|
|
657
967
|
async function clear_cache_token_by_user_id (id) {
|
|
658
968
|
if (!id) return;
|
|
659
969
|
|
|
@@ -665,18 +975,11 @@ module.exports = {
|
|
|
665
975
|
let value = await app.cache.get(k);
|
|
666
976
|
if (value && value.userId && value.userId === id)
|
|
667
977
|
await app.cache.del(k);
|
|
668
|
-
// cache.del(k);
|
|
669
978
|
}
|
|
670
979
|
}
|
|
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
980
|
}
|
|
678
981
|
|
|
679
|
-
async function generate_new_access_token_pwd (userId, oldToken, keepToken = '') {
|
|
982
|
+
async function generate_new_access_token_pwd (userId, oldToken, keepToken = '', isWx = false) {
|
|
680
983
|
let uuid = keepToken || uuidv1();
|
|
681
984
|
|
|
682
985
|
// remove the old one from cache
|
|
@@ -686,7 +989,7 @@ module.exports = {
|
|
|
686
989
|
|
|
687
990
|
// add the new one to the cache
|
|
688
991
|
|
|
689
|
-
app.cache.put(uuid, { userId: userId, type: 'pwd' }, app.config['cacheTimeout']);
|
|
992
|
+
app.cache.put(uuid, { userId: userId, type: isWx ? 'wx' : 'pwd' }, app.config['cacheTimeout']);
|
|
690
993
|
// cache.put(uuid, { userId: userId, type: 'pwd' }, app.config['cacheTimeout']);
|
|
691
994
|
|
|
692
995
|
return uuid;
|
|
@@ -696,6 +999,21 @@ module.exports = {
|
|
|
696
999
|
app.post(`${app.config['baseUrl'] || ''}/login`,
|
|
697
1000
|
passport.authenticate(m.config['strategy'] || 'local', { session: false }),
|
|
698
1001
|
async (req, res, next) => {
|
|
1002
|
+
if (res._headerSent) return;
|
|
1003
|
+
|
|
1004
|
+
// check captcha
|
|
1005
|
+
if(m.config.captcha.login && !((req.body.password === 'wx' || req.body.password.startsWith('wx:')) && req.body.username.startsWith('wx:'))) {
|
|
1006
|
+
const { captcha, id : cid } = req.body.captcha || {};
|
|
1007
|
+
if (!captcha || !cid) {
|
|
1008
|
+
res.makeError(400, 'Please provide captcha code!', m);
|
|
1009
|
+
return next('route');
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (!await verifyCaptcha(cid, captcha)) {
|
|
1013
|
+
res.makeError(400, 'Captcha code is incorrect!', m);
|
|
1014
|
+
return next('route');
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
699
1017
|
|
|
700
1018
|
// set token into cookie
|
|
701
1019
|
let access_token = req.cookies.token || req.header('Authorization');
|
|
@@ -705,10 +1023,10 @@ module.exports = {
|
|
|
705
1023
|
(req.user && req.user.PhoneNumber && m.config['keepTokenAccounts'].indexOf(req.user.PhoneNumber) >= 0)) {
|
|
706
1024
|
// keep token
|
|
707
1025
|
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);
|
|
1026
|
+
token = await generate_new_access_token_pwd(req.user.id, access_token, kt, req.user.isWx);
|
|
709
1027
|
app.cache.set(`_keep_token_${req.user.id}`, token);
|
|
710
1028
|
} else {
|
|
711
|
-
token = await generate_new_access_token_pwd(req.user.id, access_token);
|
|
1029
|
+
token = await generate_new_access_token_pwd(req.user.id, access_token, null, req.user.isWx);
|
|
712
1030
|
}
|
|
713
1031
|
|
|
714
1032
|
res.cookie('token', token, { maxAge: app.config['cookieTimeout'] });
|
|
@@ -717,7 +1035,6 @@ module.exports = {
|
|
|
717
1035
|
Name: (req.user.Profile && req.user.Profile.Name) || req.user.PhoneNumber || req.user.UserName || '',
|
|
718
1036
|
Avatar: req.user.Profile && req.user.Profile.Avatar ? req.user.Profile.Avatar : '',
|
|
719
1037
|
Status: req.user.Status,
|
|
720
|
-
Org: req.user.Org,
|
|
721
1038
|
}, false);
|
|
722
1039
|
|
|
723
1040
|
return next();
|
|
@@ -778,14 +1095,30 @@ module.exports = {
|
|
|
778
1095
|
return next('route');
|
|
779
1096
|
}
|
|
780
1097
|
const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
|
|
781
|
-
|
|
1098
|
+
|
|
1099
|
+
// check user existance if necessary
|
|
1100
|
+
const existsCount = await res.app.models.account.countDocuments({$or: [
|
|
1101
|
+
{ PhoneNumber: phone },
|
|
1102
|
+
{ 'Profile.Email': phone },
|
|
1103
|
+
]});
|
|
1104
|
+
|
|
1105
|
+
if (req.body.exists && existsCount <= 0) {
|
|
1106
|
+
res.makeError(409, 'User not exists!', m);
|
|
1107
|
+
return next('route');
|
|
1108
|
+
}
|
|
1109
|
+
if (!req.body.exists && existsCount > 0) {
|
|
1110
|
+
res.makeError(410, 'User aleady exists!', m);
|
|
1111
|
+
return next('route');
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
const result = await res.Module('sms').sendRandom(phone, undefined, true, req.body.smsTemp || 'register');
|
|
782
1115
|
|
|
783
1116
|
if (!result) {
|
|
784
1117
|
res.makeError(500, 'Failed to send sms!', m);
|
|
785
1118
|
return next('route');
|
|
786
1119
|
}
|
|
787
1120
|
} catch (ex) {
|
|
788
|
-
res.makeError(500, 'Failed to send sms!', m);
|
|
1121
|
+
res.makeError(500, ex.message || 'Failed to send sms!', m);
|
|
789
1122
|
return next('route');
|
|
790
1123
|
}
|
|
791
1124
|
|
|
@@ -849,20 +1182,37 @@ module.exports = {
|
|
|
849
1182
|
return next('route');
|
|
850
1183
|
}
|
|
851
1184
|
|
|
1185
|
+
// check captcha
|
|
1186
|
+
if(m.config.captcha.register) {
|
|
1187
|
+
const { captcha, id : cid } = req.body.captcha || {};
|
|
1188
|
+
if (!captcha || !cid) {
|
|
1189
|
+
res.makeError(400, 'Please provide captcha code!', m);
|
|
1190
|
+
return next('route');
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
if (!await verifyCaptcha(cid, captcha)) {
|
|
1194
|
+
res.makeError(400, 'Captcha code is incorrect!', m);
|
|
1195
|
+
return next('route');
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
852
1199
|
const existPhone = await res.app.models.account.countDocuments({ PhoneNumber: phone });
|
|
853
1200
|
if (existPhone) {
|
|
854
|
-
res.makeError(404, 'The phone
|
|
1201
|
+
res.makeError(404, 'The phone number was used already!', m);
|
|
855
1202
|
return next('route');
|
|
856
1203
|
}
|
|
857
1204
|
|
|
858
1205
|
// only create with specified fields
|
|
859
1206
|
res.locals.body = {
|
|
1207
|
+
Saved: true,
|
|
860
1208
|
PhoneNumber: phone,
|
|
861
1209
|
Password: encryptPwd(password, m.config.pwdEncryptMethod || 'md5')
|
|
862
1210
|
}
|
|
863
1211
|
|
|
864
1212
|
if (!m.config.accountRequireAudit) {
|
|
865
1213
|
res.locals.body.Status = AccountAuditStatus.Passed;
|
|
1214
|
+
} else {
|
|
1215
|
+
res.locals.body.Status = AccountAuditStatus.Auditing;
|
|
866
1216
|
}
|
|
867
1217
|
|
|
868
1218
|
const defaultPerm = Object.assign({}, m.config.accountDefaultPermissions);
|
|
@@ -881,6 +1231,20 @@ module.exports = {
|
|
|
881
1231
|
res.makeError(400, 'Please provide phone number, sms code and the password!', m);
|
|
882
1232
|
return next('route');
|
|
883
1233
|
}
|
|
1234
|
+
// check captcha
|
|
1235
|
+
if(m.config.captcha.recover) {
|
|
1236
|
+
const { captcha, id : cid } = req.body.captcha || {};
|
|
1237
|
+
if (!captcha || !cid) {
|
|
1238
|
+
res.makeError(400, 'Please provide captcha code!', m);
|
|
1239
|
+
return next('route');
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (!await verifyCaptcha(cid, captcha)) {
|
|
1243
|
+
res.makeError(400, 'Captcha code is incorrect!', m);
|
|
1244
|
+
return next('route');
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
884
1248
|
const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
|
|
885
1249
|
const password = crypto.encoder.desDecode(req.body.Password, m.config.desKey);
|
|
886
1250
|
|
|
@@ -918,9 +1282,7 @@ module.exports = {
|
|
|
918
1282
|
let filter;
|
|
919
1283
|
if (req.user.Permission !== '*') {
|
|
920
1284
|
const permPathList = getPermissionPathList(req.user.Permission);
|
|
921
|
-
filter = { Path: { $in: permPathList }
|
|
922
|
-
} else {
|
|
923
|
-
filter = { Enabled: true };
|
|
1285
|
+
filter = { Path: { $in: permPathList } };
|
|
924
1286
|
}
|
|
925
1287
|
|
|
926
1288
|
res.addData(await __getServiceList(res, filter));
|
|
@@ -969,6 +1331,10 @@ module.exports = {
|
|
|
969
1331
|
for (let j = 0; j < sList.length; j += 1) {
|
|
970
1332
|
const service = sList[j];
|
|
971
1333
|
|
|
1334
|
+
if(ds.OnlyFor && ds.OnlyFor.findIndex(dsof => {
|
|
1335
|
+
return (dsof.startsWith('/') ? dsof : '/dsof').startsWith(service.Path);
|
|
1336
|
+
}) < 0) continue;
|
|
1337
|
+
|
|
972
1338
|
if (service.Path) {
|
|
973
1339
|
insertDataScopeMW(service.Path, `${service.Path.replace(/\//g, '_')}_${ds.Name}`, ds.Func(ds, service.Scope.find(ss => ss.Name === ds.Name), service.Path));
|
|
974
1340
|
}
|
|
@@ -986,6 +1352,9 @@ module.exports = {
|
|
|
986
1352
|
}
|
|
987
1353
|
|
|
988
1354
|
await m.models.account.create({
|
|
1355
|
+
Saved: true,
|
|
1356
|
+
Enabled: true,
|
|
1357
|
+
Deleted: false,
|
|
989
1358
|
UserName: m.config.defaultAccountName || 'admin',
|
|
990
1359
|
Password: crypto.MD5(m.config.defaultAccountPwd) || 'f6fdffe48c908deb0f4c3bd36c032e72',
|
|
991
1360
|
Permission: perms,
|
|
@@ -996,7 +1365,10 @@ module.exports = {
|
|
|
996
1365
|
});
|
|
997
1366
|
}
|
|
998
1367
|
|
|
1368
|
+
// clear all cached permission control (user permissions)
|
|
1369
|
+
m.clearCachedPermission(app);
|
|
1370
|
+
|
|
999
1371
|
// TODO: remove service list which are in the white list
|
|
1000
1372
|
}
|
|
1001
1373
|
}
|
|
1002
|
-
}
|
|
1374
|
+
})
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-be-account",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@alicloud/pop-core": "^1.7.
|
|
7
|
+
"@alicloud/pop-core": "^1.7.12",
|
|
8
8
|
"bcrypt": "^5.0.1",
|
|
9
|
-
"crypto-js": "^4.
|
|
9
|
+
"crypto-js": "^4.1.1",
|
|
10
10
|
"js-md5": "^0.7.3",
|
|
11
|
+
"nodemailer": "^6.9.1",
|
|
11
12
|
"passport": "^0.5.0",
|
|
12
13
|
"passport-local": "^1.0.0",
|
|
14
|
+
"svg-captcha": "^1.4.0",
|
|
13
15
|
"uuid": "^8.3.2"
|
|
14
16
|
}
|
|
15
17
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const { wx } = require('../../../../global');
|
|
3
|
+
|
|
4
|
+
const wxAgent = axios.create({
|
|
5
|
+
baseURL: 'https://api.weixin.qq.com',
|
|
6
|
+
timeout: 30 * 1000,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
code2session: (code, mp) => {
|
|
11
|
+
const appid = (mp && wx[mp] && wx[mp].appid) || wx.appid;
|
|
12
|
+
const secret = (mp && wx[mp] && wx[mp].secret) || wx.secret;
|
|
13
|
+
|
|
14
|
+
return wxAgent.get(`/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`);
|
|
15
|
+
},
|
|
16
|
+
}
|
package/routers/label/route.js
CHANGED
|
@@ -10,6 +10,8 @@ router.get('/',
|
|
|
10
10
|
'Name',
|
|
11
11
|
'Index',
|
|
12
12
|
'Enabled',
|
|
13
|
+
'Description',
|
|
14
|
+
'Negative',
|
|
13
15
|
'Permission'
|
|
14
16
|
];
|
|
15
17
|
res.locals.filter = {
|
|
@@ -24,9 +26,57 @@ router.get('/',
|
|
|
24
26
|
router.FindAllDocuments('plabel')
|
|
25
27
|
);
|
|
26
28
|
|
|
27
|
-
router.post('/',
|
|
29
|
+
router.post('/',
|
|
30
|
+
(req, res, next) => {
|
|
31
|
+
if(req.body.Permission) {
|
|
32
|
+
if (!res.app.modules['passport'].utils.clearPermission(req.body.Permission)) {
|
|
33
|
+
req.body.Permission = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// permission changed, clear all cached account permission
|
|
37
|
+
// TODO: should be optimized??
|
|
38
|
+
res.app.modules['passport'].clearCachedPermission(res.app);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// when create or update plabel, provided permission should NOT exceed the permission of the current user
|
|
42
|
+
if(req.user.Permission){
|
|
43
|
+
if(req.user.Permission !== '*') {
|
|
44
|
+
req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
delete req.body.Permission;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return next();
|
|
51
|
+
},
|
|
52
|
+
router.CreateDocument('plabel')
|
|
53
|
+
);
|
|
28
54
|
|
|
29
|
-
router.put('/',
|
|
55
|
+
router.put('/',
|
|
56
|
+
(req, res, next) => {
|
|
57
|
+
if(req.body.Permission) {
|
|
58
|
+
if (!res.app.modules['passport'].utils.clearPermission(req.body.Permission)) {
|
|
59
|
+
req.body.Permission = {};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// permission changed, clear all cached account permission
|
|
63
|
+
// TODO: should be optimized??
|
|
64
|
+
res.app.modules['passport'].clearCachedPermission(res.app);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// when create or update plabel, provided permission should NOT exceed the permission of the current user
|
|
68
|
+
if(req.user.Permission){
|
|
69
|
+
if(req.user.Permission !== '*') {
|
|
70
|
+
req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
delete req.body.Permission;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return next();
|
|
77
|
+
},
|
|
78
|
+
router.UpdateDocument('plabel')
|
|
79
|
+
);
|
|
30
80
|
|
|
31
81
|
router.delete('/', router.DeleteDocument('plabel'));
|
|
32
82
|
|
package/routers/mgmt/route.js
CHANGED
|
@@ -59,25 +59,33 @@ router.get('/', async (req, res, next) => {
|
|
|
59
59
|
'Enabled',
|
|
60
60
|
'Org',
|
|
61
61
|
'Labels',
|
|
62
|
+
'Status'
|
|
62
63
|
];
|
|
63
64
|
|
|
64
65
|
res.locals.filter = Object.assign({ Saved: true }, res.app.modules['core-modules'].generateQueryFilter(accountFilters, req.query), res.locals.filter);
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
// add summary
|
|
68
|
+
if (router.mdl.config.accountRequireAudit) {
|
|
69
|
+
res.locals.data.summary = {};
|
|
70
|
+
res.locals.data.summary.auditing = await res.app.models['account'].countDocuments({...res.locals.filter, Saved: true, Status: AccountAuditStatus.Auditing });
|
|
71
|
+
res.locals.data.summary.passed = await res.app.models['account'].countDocuments({...res.locals.filter, Saved: true, Status: AccountAuditStatus.Passed });
|
|
72
|
+
res.locals.data.summary.failed = await res.app.models['account'].countDocuments({...res.locals.filter, Saved: true, Status: AccountAuditStatus.Failed });
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
return next();
|
|
72
76
|
|
|
73
77
|
}, router.FindDocuments('account', false, async (req, res) => {
|
|
74
78
|
res.locals.data.Filters = accountFilters;
|
|
75
79
|
|
|
80
|
+
res.locals.data.summary = {};
|
|
81
|
+
res.locals.data.summary.passed = await res.app.models['account'].countDocuments({ Saved: true, Enabled: true });
|
|
82
|
+
res.locals.data.summary.failed = await res.app.models['account'].countDocuments({ Saved: true, Enabled: false });
|
|
83
|
+
|
|
76
84
|
if (res.locals.data && res.locals.data.total) {
|
|
77
85
|
for (let i = 0; i < res.locals.data.docs.length; i += 1) {
|
|
78
86
|
const doc = res.locals.data.docs[i];
|
|
79
87
|
if (doc && doc.Org) {
|
|
80
|
-
const org = await app.models.organization.findOne({ id: doc.Org });
|
|
88
|
+
const org = await res.app.models.organization.findOne({ id: doc.Org });
|
|
81
89
|
if (org) {
|
|
82
90
|
doc.Org = {
|
|
83
91
|
id: org.id,
|
|
@@ -122,13 +130,26 @@ router.get('/:id',
|
|
|
122
130
|
router.post('/',
|
|
123
131
|
(req, res, next) => {
|
|
124
132
|
req.body.Status = AccountAuditStatus.Passed;
|
|
133
|
+
req.body.Saved = true;
|
|
125
134
|
|
|
126
135
|
if (req.body.Permission) {
|
|
127
136
|
if (!clearPermission(req.body.Permission)) {
|
|
128
137
|
req.body.Permission = {};
|
|
129
138
|
}
|
|
139
|
+
|
|
140
|
+
// permission changed, clear cached account permission
|
|
141
|
+
router.mdl.clearCachedPermission(res.app, req.body.id);
|
|
130
142
|
}
|
|
131
143
|
|
|
144
|
+
// make sure the provided permission is in the scope of the current user permission!!
|
|
145
|
+
if(req.user.Permission){
|
|
146
|
+
if(req.user.Permission !== '*') {
|
|
147
|
+
req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
delete req.body.Permission;
|
|
151
|
+
}
|
|
152
|
+
|
|
132
153
|
// pwd
|
|
133
154
|
if (req.body.Password) {
|
|
134
155
|
const password = crypto.encoder.desDecode(req.body.Password, router.mdl.config.desKey);
|
|
@@ -172,13 +193,13 @@ router.post('/audit',
|
|
|
172
193
|
// set permission
|
|
173
194
|
// try to use default account permission in the config first
|
|
174
195
|
// if not found use the permission of the org of the account (if have org module loaded)
|
|
175
|
-
if (req.body.Status === app.modules.account.AccountAuditStatus.Passed && req.body.id) {
|
|
176
|
-
const account = await app.models.account.findOne({ id: req.body.id });
|
|
196
|
+
if (req.body.Status === res.app.modules.account.AccountAuditStatus.Passed && req.body.id) {
|
|
197
|
+
const account = await res.app.models.account.findOne({ id: req.body.id });
|
|
177
198
|
if (account && account.Org) {
|
|
178
|
-
const accountOrg = await app.models.organization.findOne({ id: account.Org });
|
|
199
|
+
const accountOrg = await res.app.models.organization.findOne({ id: account.Org });
|
|
179
200
|
if (accountOrg && accountOrg.Permission) {
|
|
180
201
|
const p = Object.assign({}, accountOrg.Permission);
|
|
181
|
-
if (app.modules.account.utils.clearPermission(p)) {
|
|
202
|
+
if (res.app.modules.account.utils.clearPermission(p)) {
|
|
182
203
|
const op = res.locals.CURD.find(op => op.method === 'U' && op.model === 'account');
|
|
183
204
|
if (op) {
|
|
184
205
|
op.ctx.body.Permission = p;
|
|
@@ -194,6 +215,33 @@ router.post('/audit',
|
|
|
194
215
|
);
|
|
195
216
|
|
|
196
217
|
router.put('/',
|
|
218
|
+
(req, res, next) => {
|
|
219
|
+
if (req.body.Permission) {
|
|
220
|
+
if (!clearPermission(req.body.Permission)) {
|
|
221
|
+
req.body.Permission = {};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// permission changed, clear cached account permission
|
|
225
|
+
router.mdl.clearCachedPermission(res.app, req.body.id);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// make sure the provided permission is in the scope of the current user permission!!
|
|
229
|
+
if(req.user.Permission){
|
|
230
|
+
if(req.user.Permission !== '*') {
|
|
231
|
+
req.body.Permission = Object.intersection(req.body.Permission, req.user.Permission);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
delete req.body.Permission;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// pwd
|
|
238
|
+
if (req.body.Password) {
|
|
239
|
+
const password = crypto.encoder.desDecode(req.body.Password, router.mdl.config.desKey);
|
|
240
|
+
req.body.Password = encryptPwd(password, router.mdl.config.pwdEncryptMethod || 'md5');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return next();
|
|
244
|
+
},
|
|
197
245
|
router.UpdateDocument('account', false, (req, res) => {
|
|
198
246
|
// clear return data
|
|
199
247
|
if (res.locals.data && res.locals.data.id) {
|
|
@@ -254,7 +302,7 @@ router.get(`/search`,
|
|
|
254
302
|
res.locals.filter.id = req.query.id;
|
|
255
303
|
}
|
|
256
304
|
else if (req.query.search) {
|
|
257
|
-
let keyword = RegExp.quote(req.query.search);
|
|
305
|
+
let keyword = RegExp.quote(req.query.search, 'i');
|
|
258
306
|
res.locals.filter.$or = [
|
|
259
307
|
{ Name: keyword },
|
|
260
308
|
];
|
package/routers/org/route.js
CHANGED
|
@@ -24,7 +24,7 @@ router.get('/',
|
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
// router.get('/search',
|
|
27
|
-
// (req, res, next) => {
|
|
27
|
+
// async (req, res, next) => {
|
|
28
28
|
// res.locals = res.locals || {};
|
|
29
29
|
|
|
30
30
|
// res.locals.filter = {};
|
|
@@ -32,10 +32,9 @@ router.get('/',
|
|
|
32
32
|
// res.locals.filter.id = req.query.id;
|
|
33
33
|
// }
|
|
34
34
|
// else if (req.query.search) {
|
|
35
|
-
//
|
|
36
|
-
// // let keyword = new RegExp(req.query.search);
|
|
35
|
+
// let keyword = RegExp.quote(req.query.search, 'i');
|
|
37
36
|
// res.locals.filter.$or = [
|
|
38
|
-
// { Name:
|
|
37
|
+
// { Name: keyword },
|
|
39
38
|
// ];
|
|
40
39
|
// } else {
|
|
41
40
|
// await res.endWithErr(400);
|
package/routers/uc/info/route.js
CHANGED
|
@@ -69,27 +69,35 @@ router.post('/edit', async (req, res, next) => {
|
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
// submit to audit
|
|
72
|
-
router.post('/submit',
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
router.post('/submit',
|
|
73
|
+
(req, res, next) => {
|
|
74
|
+
const user = req.user;
|
|
75
|
+
|
|
76
|
+
// save changes first
|
|
77
|
+
res.locals.body = {};
|
|
78
|
+
if (req.body.Profile) {
|
|
79
|
+
res.locals.body.Profile = {...user.Profile, ...req.body.Profile};
|
|
80
|
+
}
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
res.locals.body.Status = res.app.modules.passport.AccountAuditStatus.Auditing;
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// set to default permission
|
|
85
|
+
const p = res.app.modules.passport.config.accountDefaultPermissions;
|
|
86
|
+
res.app.modules.passport.utils.clearPermission(p);
|
|
87
|
+
res.locals.body.Permission = p;
|
|
86
88
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
res.locals.filters = { id: req.user.id };
|
|
90
|
+
res.locals.fields = [
|
|
91
|
+
'Profile',
|
|
92
|
+
'Status',
|
|
93
|
+
'Permission',
|
|
94
|
+
];
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
res.addData({});
|
|
91
97
|
|
|
92
|
-
|
|
93
|
-
}
|
|
98
|
+
return next();
|
|
99
|
+
},
|
|
100
|
+
router.UpdateDocument('account'),
|
|
101
|
+
);
|
|
94
102
|
|
|
95
103
|
module.exports = router;
|
|
@@ -16,28 +16,29 @@ router.put('/',
|
|
|
16
16
|
return next('route');
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// TODO: validate the phone number with correct logic
|
|
20
20
|
if (!req.body.phone || req.body.phone.length < 11) {
|
|
21
21
|
res.makeError(301, 'New phone number is incorrect!', router.mdl);
|
|
22
22
|
return next('route');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// update phone number
|
|
26
|
-
|
|
26
|
+
res.locals.body = {};
|
|
27
|
+
res.locals.body.PhoneNumber = res.app.modules.passport.utils.crypto.encoder.desDecode(req.body.phone, res.app.modules.passport.config.desKey);
|
|
27
28
|
|
|
28
29
|
const oResult = await res.Module('sms').verify(ophone, req.body.ocode);
|
|
29
30
|
if (!oResult) {
|
|
30
31
|
res.makeError(400, 'Verification code for the old phone is incorrect!', router.mdl);
|
|
31
32
|
await res.app.cache.del(ophone);
|
|
32
|
-
await res.app.cache.del(
|
|
33
|
+
await res.app.cache.del(res.locals.body.PhoneNumber);
|
|
33
34
|
return next('route');
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
const result = await res.Module('
|
|
37
|
+
const result = await res.Module('account').sms.verify(res.locals.body.PhoneNumber, req.body.code);
|
|
37
38
|
if (!result) {
|
|
38
39
|
res.makeError(405, 'Verification code for the new phone is incorrect!', router.mdl);
|
|
39
40
|
await res.app.cache.del(ophone);
|
|
40
|
-
await res.app.cache.del(
|
|
41
|
+
await res.app.cache.del(res.locals.body.PhoneNumber);
|
|
41
42
|
return next('route');
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -45,28 +46,34 @@ router.put('/',
|
|
|
45
46
|
if (req.body.Password) {
|
|
46
47
|
const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
|
|
47
48
|
if (password) {
|
|
48
|
-
|
|
49
|
+
res.locals.body.Password = res.app.modules.account.utils.encryptPwd(password, res.app.modules.account.config.pwdEncryptMethod || 'md5');
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
const existPhone = await res.app.models.account.countDocuments({ PhoneNumber:
|
|
53
|
+
const existPhone = await res.app.models.account.countDocuments({ PhoneNumber: res.locals.body.PhoneNumber });
|
|
53
54
|
if (existPhone) {
|
|
54
55
|
res.makeError(402, 'Phone number is already in use!', router.mdl);
|
|
55
56
|
return next('route');
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
res.locals.body.Profile = req.user.Profile || {};
|
|
60
|
+
res.locals.body.Profile.Mobile = res.locals.body.PhoneNumber;
|
|
60
61
|
// if user name is the old phone number, set it to the new phone number
|
|
61
|
-
if (
|
|
62
|
-
|
|
62
|
+
if (res.locals.body.UserName === ophone) {
|
|
63
|
+
res.locals.body.UserName = res.locals.body.PhoneNumber;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
res.locals.filters = { id: req.user.id };
|
|
67
|
+
res.locals.fields = [
|
|
68
|
+
'PhoneNumber',
|
|
69
|
+
'Password',
|
|
70
|
+
'UserName',
|
|
71
|
+
'Profile',
|
|
72
|
+
];
|
|
67
73
|
|
|
68
74
|
return next();
|
|
69
|
-
}
|
|
75
|
+
},
|
|
76
|
+
router.UpdateDocument('account'),
|
|
70
77
|
);
|
|
71
78
|
|
|
72
79
|
module.exports = router;
|
package/routers/uc/pwd/route.js
CHANGED
|
@@ -31,11 +31,17 @@ router.put('/',
|
|
|
31
31
|
}
|
|
32
32
|
const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
res.locals.body = {};
|
|
35
|
+
res.locals.body.Password = res.app.modules.passport.utils.encryptPwd(password, res.app.modules.passport.config.pwdEncryptMethod || 'md5');
|
|
36
|
+
|
|
37
|
+
res.locals.filters = { id: req.user.id };
|
|
38
|
+
res.locals.fields = [
|
|
39
|
+
'password',
|
|
40
|
+
];
|
|
36
41
|
|
|
37
42
|
return next();
|
|
38
|
-
}
|
|
43
|
+
},
|
|
44
|
+
router.UpdateDocument('account'),
|
|
39
45
|
);
|
|
40
46
|
|
|
41
47
|
module.exports = router;
|
package/sms/index.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
1
3
|
const AliyunCore = require('@alicloud/pop-core');
|
|
4
|
+
const nodemailer = require('nodemailer');
|
|
5
|
+
|
|
6
|
+
let global;
|
|
7
|
+
|
|
8
|
+
if (fs.existsSync(path.resolve(__dirname, '../../global.js'))) {
|
|
9
|
+
global = require('../../global');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let MAIL_TRANS = undefined;
|
|
2
13
|
|
|
3
14
|
function _generateMSG (f = '4n') {
|
|
4
15
|
if (typeof f !== 'string' || f.length < 2) {
|
|
@@ -79,12 +90,47 @@ const _sms_lib = {
|
|
|
79
90
|
send: async function () {
|
|
80
91
|
return true;
|
|
81
92
|
}
|
|
82
|
-
}
|
|
93
|
+
},
|
|
94
|
+
mail: {
|
|
95
|
+
send: async function(k, p, v) {
|
|
96
|
+
if(!k || !k.host || !k.secret || !k.mail || !k.from || !k.title) throw new Error('MMail configuration incorrect!');
|
|
97
|
+
|
|
98
|
+
MAIL_TRANS = MAIL_TRANS || nodemailer.createTransport({
|
|
99
|
+
host: k.host,
|
|
100
|
+
secureConnection: true,
|
|
101
|
+
port: 465,
|
|
102
|
+
secure: true,
|
|
103
|
+
auth: {
|
|
104
|
+
user: k.mail,
|
|
105
|
+
pass: k.secret,
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const result = await MAIL_TRANS.sendMail({
|
|
110
|
+
from: k.from,
|
|
111
|
+
to: p,
|
|
112
|
+
subject: k.title,
|
|
113
|
+
html: typeof k.template === 'function' ? k.template(v) : k.template,
|
|
114
|
+
envelope: {
|
|
115
|
+
from: k.from,
|
|
116
|
+
to: p,
|
|
117
|
+
cc: k.from,
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return result && result.accepted && result.accepted.length >= 1;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
83
124
|
}
|
|
84
125
|
|
|
85
126
|
module.exports = (app) => ({
|
|
86
127
|
send: async function (p, value, c = true, t = 'default') {
|
|
87
|
-
|
|
128
|
+
if (p.indexOf('@') > 0) {
|
|
129
|
+
t = `${t}_mail`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const keys = (global && global.sms && global.sms[t]) || app.modules.account.config.sms.keys[t] || app.modules.account.config.sms.keys;
|
|
133
|
+
|
|
88
134
|
if (keys.platform) {
|
|
89
135
|
// if the cached code still there, we should not re-send!
|
|
90
136
|
if (await app.cache.get(p)) {
|
package/utils.js
CHANGED
|
@@ -147,7 +147,11 @@ async function saveServiceList (app, clean=false) {
|
|
|
147
147
|
};
|
|
148
148
|
if (parent) newDoc.Parent = parent;
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
try{
|
|
151
|
+
newCreated = await app.models.permission.create(newDoc);
|
|
152
|
+
} catch(ex) {
|
|
153
|
+
app.logger.error(ex.message);
|
|
154
|
+
}
|
|
151
155
|
} else {
|
|
152
156
|
newCreated = (await app.models['permission'].findOne({ Name: p, Path: `${pt}/${p}`}));
|
|
153
157
|
|