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.
@@ -0,0 +1,72 @@
1
+ const express = require(require('path').resolve('./') + "/node_modules/express");
2
+ const router = express.Router();
3
+
4
+ // change phone number
5
+ router.put('/',
6
+ async (req, res, next) => {
7
+ if (!req.body.ophone || !req.body.phone) {
8
+ res.makeError(300, 'Phone number is incorrect!', router.mdl);
9
+ return next('route');
10
+ }
11
+
12
+ const ophone = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.ophone, res.app.modules.account.config.desKey);
13
+
14
+ if (ophone !== req.user.PhoneNumber || ophone.length < 11) {
15
+ res.makeError(300, 'Phone number is incorrect!', router.mdl);
16
+ return next('route');
17
+ }
18
+
19
+
20
+ if (!req.body.phone || req.body.phone.length < 11) {
21
+ res.makeError(301, 'New phone number is incorrect!', router.mdl);
22
+ return next('route');
23
+ }
24
+
25
+ // update phone number
26
+ req.user.PhoneNumber = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.phone, res.app.modules.account.config.desKey);
27
+
28
+ const oResult = await res.Module('sms').verify(ophone, req.body.ocode);
29
+ if (!oResult) {
30
+ res.makeError(400, 'Verification code for the old phone is incorrect!', router.mdl);
31
+ await res.app.cache.del(ophone);
32
+ await res.app.cache.del(req.user.PhoneNumber);
33
+ return next('route');
34
+ }
35
+
36
+ const result = await res.Module('sms').verify(req.user.PhoneNumber, req.body.code);
37
+ if (!result) {
38
+ res.makeError(405, 'Verification code for the new phone is incorrect!', router.mdl);
39
+ await res.app.cache.del(ophone);
40
+ await res.app.cache.del(req.user.PhoneNumber);
41
+ return next('route');
42
+ }
43
+
44
+ // update password
45
+ if (req.body.Password) {
46
+ const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
47
+ if (password) {
48
+ req.user.Password = res.app.modules.account.utils.encryptPwd(password, res.app.modules.account.config.pwdEncryptMethod || 'md5');
49
+ }
50
+ }
51
+
52
+ const existPhone = await res.app.models.account.countDocuments({ PhoneNumber: req.user.PhoneNumber });
53
+ if (existPhone) {
54
+ res.makeError(402, 'Phone number is already in use!', router.mdl);
55
+ return next('route');
56
+ }
57
+
58
+ req.user.Profile = req.user.Profile || {};
59
+ req.user.Profile.Mobile = req.user.PhoneNumber;
60
+ // if user name is the old phone number, set it to the new phone number
61
+ if (req.user.UserName === ophone) {
62
+ req.user.UserName = req.user.PhoneNumber;
63
+ }
64
+
65
+ // update db
66
+ await req.user.save();
67
+
68
+ return next();
69
+ }
70
+ );
71
+
72
+ module.exports = router;
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ title: '修改密码',
3
+ description: '',
4
+ };
@@ -0,0 +1,41 @@
1
+ const express = require(require('path').resolve('./') + "/node_modules/express");
2
+ const router = express.Router();
3
+
4
+ // change password
5
+ router.put('/',
6
+ async (req, res, next) => {
7
+ if (!req.body.phone) {
8
+ res.makeError(300, 'Phone number is incorrect!', router.mdl);
9
+ return next('route');
10
+ }
11
+
12
+ const phone = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.phone, res.app.modules.account.config.desKey);
13
+
14
+ if (phone !== req.user.PhoneNumber || phone.length < 11) {
15
+ res.makeError(301, 'Phone number is incorrect!', router.mdl);
16
+ return next('route');
17
+ }
18
+
19
+ const result = await res.Module('sms').verify(phone, req.body.code);
20
+ // app.logger.debug(cache.exportJson());
21
+
22
+ if (!result) {
23
+ res.makeError(402, 'Verification code is incorrect!', router.mdl);
24
+ return next('route');
25
+ }
26
+
27
+ // update password
28
+ if (!req.body.Password) {
29
+ res.makeError(403, 'Please provide the password!', router.mdl);
30
+ return next('route');
31
+ }
32
+ const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
33
+
34
+ req.user.Password = res.app.modules.account.utils.encryptPwd(password, res.app.modules.account.config.pwdEncryptMethod || 'md5');
35
+ await req.user.save();
36
+
37
+ return next();
38
+ }
39
+ );
40
+
41
+ module.exports = router;
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ title: 'module-uc-sub-title',
3
+ description: 'module-uc-sub-description',
4
+ };
@@ -0,0 +1,158 @@
1
+ const path = require('path');
2
+ const express = require(path.resolve('./') + "/node_modules/express");
3
+ const router = express.Router();
4
+
5
+ const subAccountFilters = [
6
+ {
7
+ Name: 'LastUpdateDate',
8
+ Type: 'DateRange',
9
+ Label: '更新日期',
10
+ Placeholder: '请选择',
11
+ },
12
+ {
13
+ Name: 'Enabled',
14
+ Type: 'Select',
15
+ Label: '激活状态',
16
+ Placeholder: '请选择',
17
+ Options: [
18
+ {
19
+ Label: '已激活',
20
+ Value: true,
21
+ },
22
+ {
23
+ Label: '未激活',
24
+ Value: false,
25
+ },
26
+ ],
27
+ },
28
+ {
29
+ Name: 'Profile.Name',
30
+ Type: 'String',
31
+ Label: '姓名',
32
+ },
33
+ {
34
+ Name: 'Profile.Title',
35
+ Type: 'String',
36
+ Label: '职务',
37
+ },
38
+ {
39
+ Name: 'PhoneNumber',
40
+ Type: 'String',
41
+ Label: '手机号',
42
+ },
43
+ ];
44
+ // sub account list
45
+ router.get('/', (req, res, next) => {
46
+ res.locals.filter = {
47
+ Parent: req.user.id
48
+ }
49
+
50
+ res.locals.filter = Object.assign({}, res.app.modules['core-modules'].generateQueryFilter(subAccountFilters, req.query), res.locals.filter);
51
+
52
+ res.locals.fields = [
53
+ 'id',
54
+ 'LastUpdateDate',
55
+ 'Profile',
56
+ 'Enabled',
57
+ ];
58
+
59
+ return next();
60
+ }, router.FindDocuments('account', false, async (req, res) => {
61
+ // add summary
62
+ res.locals.data.summary = {};
63
+ res.locals.data.summary.enabled = await res.app.models['account'].countDocuments({ Parent: req.user.id, Enabled: true });
64
+ res.locals.data.summary.disabled = await res.app.models['account'].countDocuments({ Parent: req.user.id, Enabled: false });
65
+
66
+ res.locals.data.Filters = subAccountFilters;
67
+ }));
68
+
69
+ // create sub accousnt
70
+ router.post('/',
71
+ (req, res, next) => {
72
+ req.body.Parent = req.user.id;
73
+ req.body.Enabled = true;
74
+ // req.body.Permission = req.body.Permission || req.user.Permission;
75
+ // cannot change permission of sub account yet, just assign the same permission with the current account
76
+ req.body.Permission = req.user.Permission;
77
+ res.app.modules.account.utils.clearPermission(req.body.Permission);
78
+
79
+ // also same Org (but should check whether we have Org module??)
80
+ if (req.user.Org) req.body.Org = req.user.Org;
81
+
82
+ // TODO: should not set status here as we don't have this field yet (which was added in passport)
83
+ req.body.Status = '1';
84
+
85
+ // TODO: check permission, should not be bigger than the main account
86
+
87
+ // password
88
+ if (req.body.Password) {
89
+ const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
90
+ req.body.Password = res.app.modules.account.utils.encryptPwd(password, res.app.modules.account.config.pwdEncryptMethod || 'md5');
91
+ }
92
+ return next();
93
+ },
94
+ router.CreateDocument('account')
95
+ );
96
+
97
+ // specified sub account list
98
+ router.get('/:id', (req, res, next) => {
99
+ res.locals.filter = {
100
+ Parent: req.user.id,
101
+ id: req.params.id
102
+ }
103
+
104
+ res.locals.fields = [
105
+ 'id',
106
+ 'Profile',
107
+ 'Enabled',
108
+ 'PhoneNumber',
109
+ // 'Permission'
110
+ ];
111
+
112
+ return next();
113
+ }, router.FindDocuments('account', false, (req, res) => {
114
+ if (res.locals.data && res.locals.data.total) {
115
+ res.locals.data = res.locals.data.docs[0];
116
+ }
117
+ }));
118
+
119
+ // update sub account
120
+ router.put('/:id',
121
+ (req, res, next) => {
122
+ res.locals.filter = {
123
+ Parent: req.user.id,
124
+ id: req.params.id
125
+ }
126
+
127
+ res.locals.fields = [
128
+ 'Profile',
129
+ 'PhoneNumber',
130
+ 'Password',
131
+ 'Enabled'
132
+ ];
133
+
134
+ if (req.body.Password) {
135
+ const password = res.app.modules.account.utils.crypto.encoder.desDecode(req.body.Password, res.app.modules.account.config.desKey);
136
+ req.body.Password = res.app.modules.account.utils.encryptPwd(password, res.app.modules.account.config.pwdEncryptMethod || 'md5');
137
+ }
138
+
139
+ // TODO: check permission, should not be bigger than the main account
140
+
141
+ return next();
142
+ },
143
+ router.UpdateDocument('account')
144
+ );
145
+
146
+ // delete sub account
147
+ router.delete('/:id',
148
+ (req, res, next) => {
149
+ res.locals.filter = {
150
+ Parent: req.user.id,
151
+ id: req.params.id
152
+ }
153
+ return next();
154
+ },
155
+ router.DeleteDocument('account')
156
+ );
157
+
158
+ module.exports = router;
package/sms/index.js ADDED
@@ -0,0 +1,134 @@
1
+ const AliyunCore = require('@alicloud/pop-core');
2
+
3
+ function _generateMSG (f = '4n') {
4
+ if (typeof f !== 'string' || f.length < 2) {
5
+ f = '4n';
6
+ }
7
+
8
+ let mLength = 0,
9
+ mUseNumber = false,
10
+ mUseChar = false;
11
+
12
+ for (let i = 0; i < f.length; i += 1) {
13
+ const fi = f[i].toLowerCase();
14
+
15
+ switch (fi) {
16
+ case 'n':
17
+ mUseNumber = true;
18
+ break;
19
+ case 'c':
20
+ mUseChar = true;
21
+ break;
22
+ default:
23
+ mLength = mLength || Number(fi) || 0;
24
+ }
25
+ }
26
+
27
+ mLength = mLength || 4;
28
+ if (!mUseNumber && !mUseChar) {
29
+ mUseNumber = true;
30
+ }
31
+
32
+ let charList = '';
33
+ if (mUseNumber) charList = charList.concat('1234567890');
34
+ if (mUseChar) charList = charList.concat('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
35
+
36
+ let msgValue = '';
37
+ for (let i = 0; i < mLength; i += 1) {
38
+ msgValue += charList[Math.floor(Math.random() * charList.length)]
39
+ }
40
+
41
+ return msgValue;
42
+ }
43
+
44
+ const _sms_lib = {
45
+ aliyun: {
46
+ send: async function (k, p, v) {
47
+ if (!k || !k.accessKeyId || !k.secret || !k.signName || !k.templateCode || !k.templateParamName) {
48
+ throw new Error('SMS parameters not configured correctly for platform (Aliyun)');
49
+ }
50
+ var client = new AliyunCore({
51
+ accessKeyId: k.accessKeyId,
52
+ accessKeySecret: k.secret,
53
+ endpoint: 'https://dysmsapi.aliyuncs.com',
54
+ apiVersion: '2017-05-25'
55
+ });
56
+
57
+ var codeAndValue = {};
58
+ codeAndValue[k.templateParamName] = v;
59
+
60
+ var params = {
61
+ "PhoneNumbers": p,
62
+ "SignName": k.signName,
63
+ "TemplateCode": k.templateCode,
64
+ "TemplateParam": JSON.stringify(codeAndValue)
65
+ }
66
+
67
+ // params[k.templateParamName] = v;
68
+
69
+ var requestOption = {
70
+ method: 'POST'
71
+ };
72
+
73
+ const result = await client.request('SendSms', params, requestOption);
74
+
75
+ return result && result.Code && result.Code === 'OK';
76
+ }
77
+ },
78
+ eis: {
79
+ send: async function () {
80
+ return true;
81
+ }
82
+ }
83
+ }
84
+
85
+ module.exports = (app) => ({
86
+ send: async function (p, value, c = true, t = 'default') {
87
+ const keys = app.config.account.sms[t] || app.config.account.sms;
88
+ if (keys.platform) {
89
+ // if the cached code still there, we should not re-send!
90
+ if (await app.cache.get(p)) {
91
+ throw new Error('Cannot send too frequently!');
92
+ }
93
+ if (_sms_lib[keys.platform]) {
94
+ let sent = true;
95
+ const v = keys.fixedCode || value;
96
+
97
+ try {
98
+ sent = await _sms_lib[keys.platform].send(keys, p, v);
99
+ } catch (ex) {
100
+ app.logger.error(JSON.stringify(ex));
101
+ return false;
102
+ }
103
+ if (sent) {
104
+ if (c) {
105
+ const cTime = keys.cacheTime || (5 * 60 * 1000)
106
+ await app.cache.put(p, v, cTime);
107
+ }
108
+ return true;
109
+ } else {
110
+ return false;
111
+ }
112
+ } else {
113
+ throw new Error(`SMS platform ${keys.platform} is not supported yet!`);
114
+ }
115
+ } else {
116
+ throw new Error('SMS platform not configured!');
117
+ }
118
+ },
119
+ /**
120
+ *
121
+ * @param {String} p Phone number
122
+ * @param {String} f Random pattern, should be like '4nc', 4 means length, n means incude Numbers, c means include Chars.
123
+ * @param {Boolean} c Cache or not
124
+ * @param {String} t template, the key in the sms config
125
+ */
126
+ sendRandom: async function (p, f, c = true, t = 'default') {
127
+ let v = _generateMSG(f);
128
+ return await this.send(p, v, c, t);
129
+ },
130
+ verify: async function (p, v) {
131
+ const cached = await app.cache.get(p);
132
+ return cached === v;
133
+ }
134
+ })
package/test/index.js ADDED
@@ -0,0 +1 @@
1
+ // each module should have test script for itself.
package/utils.js ADDED
@@ -0,0 +1,209 @@
1
+ const crypto = require('./crypto');
2
+
3
+ const specialNames = [
4
+ 'Scope'
5
+ ];
6
+
7
+ const EncryptOptions = {
8
+ saltLength: 16,
9
+ sha1Iteration: 1024
10
+ }
11
+
12
+ /**
13
+ *
14
+ * Translate permission object into path list
15
+ *
16
+ * @param {*} perm
17
+ * @param {*} k
18
+ */
19
+ function getPermissionPathList(perm, n = '') {
20
+ const ret = [];
21
+
22
+ if (perm && typeof perm === 'object') {
23
+ if (n) ret.push(n);
24
+
25
+ Object.keys(perm).forEach(k => {
26
+ getPermissionPathList(perm[k], k).forEach(p => {
27
+ ret.push(`${n}/${p}`);
28
+ })
29
+ })
30
+ }
31
+
32
+ return ret;
33
+ }
34
+
35
+ function clearPermission(perm) {
36
+ // remove unused fields from permisson object
37
+ // invalid: all values are not object, and all names are in the specified list
38
+ function clearP(p) {
39
+
40
+ if (typeof p !== 'object') {
41
+ return false;
42
+ }
43
+
44
+ Object.keys(p).forEach(s => {
45
+ if (specialNames.indexOf(s) >= 0) return;
46
+
47
+ if (!clearP(p[s])) {
48
+ delete p[s];
49
+ }
50
+ });
51
+
52
+ // TODO: add a field if nothing here, otherwise the db will not save it??? should be fixed!!
53
+ if (Object.keys(p).length <= 0) {
54
+ p.has = true;
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ return clearP(perm);
61
+ }
62
+
63
+ function verifyPassword(pwd, storedPwd, method = 'md5') {
64
+ let verified = false;
65
+ let methods = [];
66
+
67
+ if (typeof method === 'string') {
68
+ methods.push(method);
69
+ } else if (Array.isArray(method)) {
70
+ methods = method;
71
+ }
72
+
73
+ for (let i = 0; i < methods.length; i += 1) {
74
+ const m = methods[i];
75
+ switch (m) {
76
+ case 'md5':
77
+ verified = verified || (crypto.MD5(pwd) === storedPwd);
78
+ break;
79
+ case 'sha1':
80
+ verified = verified || (crypto.sha1(pwd, storedPwd.substr(0, EncryptOptions.saltLength), EncryptOptions.sha1Iteration).toString() === storedPwd.substr(EncryptOptions.saltLength));
81
+ break;
82
+ case 'bcrypt':
83
+ verified = verified || crypto.bcryptVerify(pwd, storedPwd);
84
+ break;
85
+ default:
86
+ verified = verified || (pwd === storedPwd);
87
+ }
88
+
89
+ if (verified) return verified;
90
+ }
91
+
92
+ return verified;
93
+ }
94
+
95
+ function getClearPwd(pwd, key){
96
+ if(!pwd) return undefined;
97
+
98
+ return crypto.encoder.desDecode(pwd.substr(EncryptOptions.saltLength), key);
99
+ }
100
+
101
+ function encryptPwd(pwd, method) {
102
+ let theMethod = [];
103
+
104
+ if (typeof method === 'string') {
105
+ theMethod = method;
106
+ } else if (Array.isArray(method)) {
107
+ theMethod = method[0];
108
+ }
109
+
110
+ let salt;
111
+ if (theMethod === 'sha1') {
112
+ salt = crypto.generateSalt(EncryptOptions.saltLength / 2);
113
+ }
114
+
115
+ switch (theMethod) {
116
+ case 'md5':
117
+ return crypto.MD5(pwd);
118
+ case 'sha1':
119
+ return `${salt}${crypto.sha1(pwd, salt.toString(), EncryptOptions.sha1Iteration)}`;
120
+ case 'bcrypt':
121
+ return crypto.bcrypt(pwd);
122
+ default:
123
+ throw 'Unknown password encrypt method!'
124
+ }
125
+ }
126
+
127
+ async function saveServiceList (app, clean=false) {
128
+ // add app.serviceList into db if not yet
129
+ const checkService = async (perm, parent, pt) => {
130
+ if (!perm || typeof perm !== 'object') return;
131
+
132
+ for (let i = 0; i < Object.keys(perm).length; i += 1) {
133
+ const p = Object.keys(perm)[i];
134
+
135
+ // TODO: notify user if they are creating permission with these names
136
+ if (['title', 'description', 'scope', 'label'].indexOf(p.toLowerCase()) >= 0) continue;
137
+
138
+ let newCreated;
139
+ const existCount = await app.models['permission'].countDocuments({ Name: p, Path: `${pt}/${p}` });
140
+ if (existCount <= 0) {
141
+ const newDoc = {
142
+ Name: p,
143
+ Title: perm[p].title,
144
+ Description: typeof perm[p].description === 'string' ? perm[p].description : JSON.stringify(perm[p].description),
145
+ Index: i + 1,
146
+ Path: `${pt}/${p}`
147
+ };
148
+ if (parent) newDoc.Parent = parent;
149
+
150
+ newCreated = await app.models.permission.create(newDoc);
151
+ } else {
152
+ newCreated = (await app.models['permission'].findOne({ Name: p, Path: `${pt}/${p}`}));
153
+
154
+ // update
155
+ newCreated.Title = perm[p].title;
156
+ newCreated.Description = typeof perm[p].description === 'string' ? perm[p].description : JSON.stringify(perm[p].description);
157
+ if(parent) newCreated.Parent = parent;
158
+ newCreated.Index = i + 1;
159
+ await newCreated.save();
160
+ }
161
+
162
+ if (newCreated) {
163
+ await checkService(perm[p], newCreated.id, `${pt}/${p}`);
164
+ }
165
+ }
166
+ };
167
+
168
+ // clear all built-in permissions first
169
+ if(clean){
170
+ const userCreatedList = await app.models.permission.find({ BuiltIn: false });
171
+ if(userCreatedList && userCreatedList.length > 0){
172
+ const uclIds = [];
173
+ const addParentId = async (p) => {
174
+ if(!p) return;
175
+
176
+ const parent = await app.models.permission.findOne({id: p});
177
+
178
+ if(parent && uclIds.indexOf(parent.id) < 0){
179
+ uclIds.push(parent.id);
180
+ }
181
+
182
+ await addParentId(parent.Parent);
183
+ }
184
+ for (let i = 0; i < userCreatedList.length; i += 1) {
185
+ const ucli = userCreatedList[i];
186
+
187
+ uclIds.push(ucli.id);
188
+
189
+ await addParentId(ucli.Parent);
190
+ }
191
+
192
+ // remove all built-in
193
+ await app.models.permission.remove({id: {$nin: uclIds}});
194
+ }
195
+ }
196
+
197
+ const serviceList = app.ctx.serviceList();
198
+ await checkService(serviceList, undefined, '');
199
+ }
200
+
201
+ module.exports = {
202
+ clearPermission,
203
+ getPermissionPathList,
204
+ verifyPassword,
205
+ encryptPwd,
206
+ getClearPwd,
207
+ crypto,
208
+ saveServiceList,
209
+ }