free-be-account 0.0.2 → 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 -49
- package/package.json +5 -3
- package/platforms/wx/index.js +16 -0
- package/routers/label/route.js +52 -2
- package/routers/mgmt/route.js +54 -6
- 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,29 +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
|
-
|
|
613
|
-
|
|
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
|
+
}
|
|
614
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
|
+
}
|
|
615
868
|
}
|
|
616
869
|
));
|
|
617
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
|
+
|
|
618
902
|
// login with the specified strategy
|
|
619
903
|
app.post(`${app.config['baseUrl'] || ''}/logedin`,
|
|
620
904
|
async (req, res) => {
|
|
@@ -655,6 +939,31 @@ module.exports = {
|
|
|
655
939
|
return next();
|
|
656
940
|
});
|
|
657
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
|
+
|
|
658
967
|
async function clear_cache_token_by_user_id (id) {
|
|
659
968
|
if (!id) return;
|
|
660
969
|
|
|
@@ -666,18 +975,11 @@ module.exports = {
|
|
|
666
975
|
let value = await app.cache.get(k);
|
|
667
976
|
if (value && value.userId && value.userId === id)
|
|
668
977
|
await app.cache.del(k);
|
|
669
|
-
// cache.del(k);
|
|
670
978
|
}
|
|
671
979
|
}
|
|
672
|
-
|
|
673
|
-
// cache.keys().forEach(async (k) => {
|
|
674
|
-
// let value = await app.cache.get(k);
|
|
675
|
-
// if (value && value.userId && value.userId === id)
|
|
676
|
-
// cache.del(k);
|
|
677
|
-
// });
|
|
678
980
|
}
|
|
679
981
|
|
|
680
|
-
async function generate_new_access_token_pwd (userId, oldToken, keepToken = '') {
|
|
982
|
+
async function generate_new_access_token_pwd (userId, oldToken, keepToken = '', isWx = false) {
|
|
681
983
|
let uuid = keepToken || uuidv1();
|
|
682
984
|
|
|
683
985
|
// remove the old one from cache
|
|
@@ -687,7 +989,7 @@ module.exports = {
|
|
|
687
989
|
|
|
688
990
|
// add the new one to the cache
|
|
689
991
|
|
|
690
|
-
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']);
|
|
691
993
|
// cache.put(uuid, { userId: userId, type: 'pwd' }, app.config['cacheTimeout']);
|
|
692
994
|
|
|
693
995
|
return uuid;
|
|
@@ -697,6 +999,21 @@ module.exports = {
|
|
|
697
999
|
app.post(`${app.config['baseUrl'] || ''}/login`,
|
|
698
1000
|
passport.authenticate(m.config['strategy'] || 'local', { session: false }),
|
|
699
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
|
+
}
|
|
700
1017
|
|
|
701
1018
|
// set token into cookie
|
|
702
1019
|
let access_token = req.cookies.token || req.header('Authorization');
|
|
@@ -706,10 +1023,10 @@ module.exports = {
|
|
|
706
1023
|
(req.user && req.user.PhoneNumber && m.config['keepTokenAccounts'].indexOf(req.user.PhoneNumber) >= 0)) {
|
|
707
1024
|
// keep token
|
|
708
1025
|
const kt = await app.cache.get(`_keep_token_${req.user.id}`);
|
|
709
|
-
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);
|
|
710
1027
|
app.cache.set(`_keep_token_${req.user.id}`, token);
|
|
711
1028
|
} else {
|
|
712
|
-
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);
|
|
713
1030
|
}
|
|
714
1031
|
|
|
715
1032
|
res.cookie('token', token, { maxAge: app.config['cookieTimeout'] });
|
|
@@ -718,7 +1035,6 @@ module.exports = {
|
|
|
718
1035
|
Name: (req.user.Profile && req.user.Profile.Name) || req.user.PhoneNumber || req.user.UserName || '',
|
|
719
1036
|
Avatar: req.user.Profile && req.user.Profile.Avatar ? req.user.Profile.Avatar : '',
|
|
720
1037
|
Status: req.user.Status,
|
|
721
|
-
Org: req.user.Org,
|
|
722
1038
|
}, false);
|
|
723
1039
|
|
|
724
1040
|
return next();
|
|
@@ -779,14 +1095,30 @@ module.exports = {
|
|
|
779
1095
|
return next('route');
|
|
780
1096
|
}
|
|
781
1097
|
const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
|
|
782
|
-
|
|
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');
|
|
783
1115
|
|
|
784
1116
|
if (!result) {
|
|
785
1117
|
res.makeError(500, 'Failed to send sms!', m);
|
|
786
1118
|
return next('route');
|
|
787
1119
|
}
|
|
788
1120
|
} catch (ex) {
|
|
789
|
-
res.makeError(500, 'Failed to send sms!', m);
|
|
1121
|
+
res.makeError(500, ex.message || 'Failed to send sms!', m);
|
|
790
1122
|
return next('route');
|
|
791
1123
|
}
|
|
792
1124
|
|
|
@@ -850,20 +1182,37 @@ module.exports = {
|
|
|
850
1182
|
return next('route');
|
|
851
1183
|
}
|
|
852
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
|
+
|
|
853
1199
|
const existPhone = await res.app.models.account.countDocuments({ PhoneNumber: phone });
|
|
854
1200
|
if (existPhone) {
|
|
855
|
-
res.makeError(404, 'The phone
|
|
1201
|
+
res.makeError(404, 'The phone number was used already!', m);
|
|
856
1202
|
return next('route');
|
|
857
1203
|
}
|
|
858
1204
|
|
|
859
1205
|
// only create with specified fields
|
|
860
1206
|
res.locals.body = {
|
|
1207
|
+
Saved: true,
|
|
861
1208
|
PhoneNumber: phone,
|
|
862
1209
|
Password: encryptPwd(password, m.config.pwdEncryptMethod || 'md5')
|
|
863
1210
|
}
|
|
864
1211
|
|
|
865
1212
|
if (!m.config.accountRequireAudit) {
|
|
866
1213
|
res.locals.body.Status = AccountAuditStatus.Passed;
|
|
1214
|
+
} else {
|
|
1215
|
+
res.locals.body.Status = AccountAuditStatus.Auditing;
|
|
867
1216
|
}
|
|
868
1217
|
|
|
869
1218
|
const defaultPerm = Object.assign({}, m.config.accountDefaultPermissions);
|
|
@@ -882,6 +1231,20 @@ module.exports = {
|
|
|
882
1231
|
res.makeError(400, 'Please provide phone number, sms code and the password!', m);
|
|
883
1232
|
return next('route');
|
|
884
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
|
+
|
|
885
1248
|
const phone = crypto.encoder.desDecode(req.body.PhoneNumber, m.config.desKey);
|
|
886
1249
|
const password = crypto.encoder.desDecode(req.body.Password, m.config.desKey);
|
|
887
1250
|
|
|
@@ -919,9 +1282,7 @@ module.exports = {
|
|
|
919
1282
|
let filter;
|
|
920
1283
|
if (req.user.Permission !== '*') {
|
|
921
1284
|
const permPathList = getPermissionPathList(req.user.Permission);
|
|
922
|
-
filter = { Path: { $in: permPathList }
|
|
923
|
-
} else {
|
|
924
|
-
filter = { Enabled: true };
|
|
1285
|
+
filter = { Path: { $in: permPathList } };
|
|
925
1286
|
}
|
|
926
1287
|
|
|
927
1288
|
res.addData(await __getServiceList(res, filter));
|
|
@@ -970,6 +1331,10 @@ module.exports = {
|
|
|
970
1331
|
for (let j = 0; j < sList.length; j += 1) {
|
|
971
1332
|
const service = sList[j];
|
|
972
1333
|
|
|
1334
|
+
if(ds.OnlyFor && ds.OnlyFor.findIndex(dsof => {
|
|
1335
|
+
return (dsof.startsWith('/') ? dsof : '/dsof').startsWith(service.Path);
|
|
1336
|
+
}) < 0) continue;
|
|
1337
|
+
|
|
973
1338
|
if (service.Path) {
|
|
974
1339
|
insertDataScopeMW(service.Path, `${service.Path.replace(/\//g, '_')}_${ds.Name}`, ds.Func(ds, service.Scope.find(ss => ss.Name === ds.Name), service.Path));
|
|
975
1340
|
}
|
|
@@ -987,6 +1352,9 @@ module.exports = {
|
|
|
987
1352
|
}
|
|
988
1353
|
|
|
989
1354
|
await m.models.account.create({
|
|
1355
|
+
Saved: true,
|
|
1356
|
+
Enabled: true,
|
|
1357
|
+
Deleted: false,
|
|
990
1358
|
UserName: m.config.defaultAccountName || 'admin',
|
|
991
1359
|
Password: crypto.MD5(m.config.defaultAccountPwd) || 'f6fdffe48c908deb0f4c3bd36c032e72',
|
|
992
1360
|
Permission: perms,
|
|
@@ -997,7 +1365,10 @@ module.exports = {
|
|
|
997
1365
|
});
|
|
998
1366
|
}
|
|
999
1367
|
|
|
1368
|
+
// clear all cached permission control (user permissions)
|
|
1369
|
+
m.clearCachedPermission(app);
|
|
1370
|
+
|
|
1000
1371
|
// TODO: remove service list which are in the white list
|
|
1001
1372
|
}
|
|
1002
1373
|
}
|
|
1003
|
-
}
|
|
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,20 +59,28 @@ 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];
|
|
@@ -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);
|
|
@@ -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
|
|