beech-api 3.8.0 → 3.9.0-beta.8-rc
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/README.md +498 -130
- package/package.json +12 -12
- package/packages/cli/bin/beech-app.js +3 -3
- package/packages/cli/bin/beech-service.js +61 -25
- package/packages/cli/core/auth/Credentials.js +92 -87
- package/packages/cli/core/auth/Passport.js +111 -39
- package/packages/cli/core/configure/passport.config.js +1 -1
- package/packages/cli/core/databases/mysql.js +1 -1
- package/packages/cli/core/databases/sequelize.js +15 -6
- package/packages/cli/core/databases/test.js +124 -38
- package/packages/cli/core/generator/_models +3 -26
- package/packages/cli/core/generator/_models_basic +0 -9
- package/packages/cli/core/generator/_package +2 -2
- package/packages/cli/core/generator/index.js +197 -23
- package/packages/cli/core/helpers/2fa.js +22 -1
- package/packages/cli/core/helpers/math.js +14 -2
- package/packages/cli/core/helpers/poolEntity.js +70 -26
- package/packages/cli/core/index.js +8 -3
- package/packages/cli/core/middleware/express/jwtCheckAllow.js +52 -34
- package/packages/cli/core/middleware/origin/guard/advance.js +5 -4
- package/packages/cli/core/services/http.express.js +0 -2
- package/packages/lib/src/endpoint.js +306 -103
- package/packages/lib/src/schema.js +4 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
1
2
|
const { FindOne } = require("../../../lib/src/user");
|
|
2
3
|
const { findPassportPk } = require("./poolEntity");
|
|
4
|
+
const passport_config_file = appRoot + "/passport.config.js";
|
|
5
|
+
const md5 = require("md5");
|
|
6
|
+
const secret = require("../../../lib/src/salt").salt;
|
|
3
7
|
|
|
4
8
|
function TwoFactor(user, reqBody, guard_field, cb) {
|
|
5
9
|
try {
|
|
@@ -8,6 +12,22 @@ function TwoFactor(user, reqBody, guard_field, cb) {
|
|
|
8
12
|
cb(true, err);
|
|
9
13
|
} else {
|
|
10
14
|
if(userId.length) {
|
|
15
|
+
if (fs.existsSync(passport_config_file)) {
|
|
16
|
+
passport_config = require(passport_config_file);
|
|
17
|
+
} else {
|
|
18
|
+
cb(true, {
|
|
19
|
+
code: 500,
|
|
20
|
+
status: "INTERNAL_SERVER_ERR",
|
|
21
|
+
error: {
|
|
22
|
+
code: 404,
|
|
23
|
+
status: "ERROR_FILE_NOT_EXISTS",
|
|
24
|
+
message: "The file passport.config.js not exists!",
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let usrField = passport_config.model.username_field || "username";
|
|
30
|
+
let pwdField = passport_config.model.password_field || "password"
|
|
11
31
|
// filter without base user, pass
|
|
12
32
|
let without_base = Object.keys(reqBody).map((k) => {
|
|
13
33
|
return guard_field.filter((e) => e == k)[0];
|
|
@@ -19,7 +39,8 @@ function TwoFactor(user, reqBody, guard_field, cb) {
|
|
|
19
39
|
// check length match ?
|
|
20
40
|
if(x.length == guard_field.length) {
|
|
21
41
|
let z = {};
|
|
22
|
-
z[
|
|
42
|
+
z[usrField] = reqBody[usrField];
|
|
43
|
+
z[pwdField] = md5(reqBody[pwdField] + secret);
|
|
23
44
|
x.map((guard) => {
|
|
24
45
|
z[guard] = reqBody[guard];
|
|
25
46
|
});
|
|
@@ -34,7 +34,19 @@ function getAppKey(cb) {
|
|
|
34
34
|
|
|
35
35
|
function HashIt(txt, app_key, iteration = 10000, len = 10, cb) {
|
|
36
36
|
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "base64url", pbkdf2Iterations: iteration, saltLength: len, });
|
|
37
|
-
|
|
37
|
+
let result = "";
|
|
38
|
+
let loop = 0;
|
|
39
|
+
const maxLoop = 10;
|
|
40
|
+
do {
|
|
41
|
+
const payload = txt.concat(md5(secret).toString().slice(0, len + 1));
|
|
42
|
+
result = crypIt.encrypt(payload);
|
|
43
|
+
loop++;
|
|
44
|
+
if (loop >= maxLoop) {
|
|
45
|
+
console.log("\n[101m FAIL [0m Hash loop limit exceeded, Try again.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
} while (/[+-]/.test(result)); // reject -, +
|
|
49
|
+
cb(result);
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
function DeHashIt(txtHashed, iteration = 10000, len = 10, cb) {
|
|
@@ -43,7 +55,7 @@ function DeHashIt(txtHashed, iteration = 10000, len = 10, cb) {
|
|
|
43
55
|
if(err) {
|
|
44
56
|
cb(err, null);
|
|
45
57
|
} else {
|
|
46
|
-
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "
|
|
58
|
+
const crypIt = new Cryptr(secret.toString().concat(app_key.toString()), { encoding: "base64", pbkdf2Iterations: iteration, saltLength: len, });
|
|
47
59
|
let decryped = crypIt.decrypt(txtHashed);
|
|
48
60
|
cb(false, decryped.concat(md5(secret).toString()));
|
|
49
61
|
}
|
|
@@ -1,34 +1,60 @@
|
|
|
1
|
-
function findPassportPk(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
1
|
+
async function findPassportPk(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
2
2
|
try {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
let finalPassportField = [];
|
|
4
|
+
// Push and Replace it
|
|
5
|
+
const pushOrReplace = (arr, value) => {
|
|
6
|
+
arr = arr.filter(v => !value.includes(v));
|
|
7
|
+
arr = [...value, ...arr];
|
|
8
|
+
return arr;
|
|
9
|
+
}
|
|
10
|
+
// Check pool engine
|
|
11
|
+
if(pool_base == "basic") {
|
|
12
|
+
// pool base is Basic
|
|
13
|
+
pool.query("SHOW KEYS FROM " + passportTable + " WHERE Key_name = 'PRIMARY'", (err, pk) => {
|
|
14
|
+
if(err) {
|
|
15
|
+
throw "Authentication table: " + err;
|
|
16
|
+
} else {
|
|
17
|
+
if(passportConfigField.length) {
|
|
18
|
+
finalPassportField = pushOrReplace(passportConfigField, [pk[0].Column_name]);
|
|
19
|
+
cb(null, finalPassportField);
|
|
11
20
|
} else {
|
|
12
21
|
cb(null, [pk[0].Column_name]);
|
|
13
22
|
}
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} else if (pool_base == "sequelize") {
|
|
26
|
+
// pool base is Sequelize
|
|
27
|
+
// Find table primaryKey
|
|
28
|
+
try {
|
|
29
|
+
const tableInfo = await pool.getQueryInterface().describeTable(String(passportTable));
|
|
30
|
+
const primaryKeys = Object.entries(tableInfo).filter(([columnName, columnInfo]) => columnInfo.primaryKey).map(([columnName]) => columnName);
|
|
31
|
+
if(primaryKeys.length) {
|
|
32
|
+
if(passportConfigField.length) {
|
|
33
|
+
finalPassportField = pushOrReplace(passportConfigField, primaryKeys);
|
|
34
|
+
cb(null, finalPassportField);
|
|
35
|
+
} else {
|
|
36
|
+
cb(null, primaryKeys);
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if(passportConfigField.length) {
|
|
40
|
+
finalPassportField = pushOrReplace(passportConfigField, [Object.keys(tableInfo)[0]]);
|
|
41
|
+
cb(null, finalPassportField);
|
|
42
|
+
} else {
|
|
43
|
+
cb(null, [Object.keys(tableInfo)[0]]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw `Query Interface ${error}`;
|
|
24
48
|
}
|
|
49
|
+
} else {
|
|
50
|
+
throw "The Base pool error. UNKNOWN pool_base = '"+ pool_base +"'";
|
|
25
51
|
}
|
|
26
52
|
} catch (error) {
|
|
27
53
|
cb(error, null);
|
|
28
54
|
}
|
|
29
55
|
}
|
|
30
56
|
|
|
31
|
-
function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
57
|
+
async function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb) {
|
|
32
58
|
try {
|
|
33
59
|
if(passportConfigField.length) {
|
|
34
60
|
if(pool_base == "basic") {
|
|
@@ -42,18 +68,36 @@ function checkAuthFields(pool_base, pool, passportTable, passportConfigField, cb
|
|
|
42
68
|
});
|
|
43
69
|
} else if (pool_base == "sequelize") {
|
|
44
70
|
// pool base is Sequelize
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
71
|
+
// Check assing fields exists
|
|
72
|
+
const checkColumnsExist = async (tableName, fields = []) => {
|
|
73
|
+
const queryInterface = pool.getQueryInterface();
|
|
74
|
+
try {
|
|
75
|
+
const tableDescription = await queryInterface.describeTable(tableName);
|
|
76
|
+
const result = {};
|
|
77
|
+
for (const column of fields) {
|
|
78
|
+
result[column] = tableDescription.hasOwnProperty(column);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw `Query Interface ${error}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const openFields = await checkColumnsExist(passportTable, passportConfigField);
|
|
86
|
+
const assignFieldsIsWhitelist = Object.values(openFields).includes(false) ? false : true;
|
|
87
|
+
if(assignFieldsIsWhitelist) {
|
|
88
|
+
cb(null, openFields);
|
|
89
|
+
} else {
|
|
90
|
+
cb(`Authentication table fields error: '${passportTable}' => [${passportConfigField}]`, null);
|
|
91
|
+
}
|
|
50
92
|
} else {
|
|
51
93
|
cb("The Base pool error. UNKNOWN pool_base = '"+ pool_base +"'", null);
|
|
52
94
|
}
|
|
95
|
+
} else {
|
|
96
|
+
cb(null, []);
|
|
53
97
|
}
|
|
54
98
|
} catch (error) {
|
|
55
99
|
cb(error, null);
|
|
56
100
|
}
|
|
57
101
|
}
|
|
58
102
|
|
|
59
|
-
module.exports = { findPassportPk, checkAuthFields }
|
|
103
|
+
module.exports = { findPassportPk, checkAuthFields };
|
|
@@ -164,9 +164,11 @@ init = async (jsfiles) => {
|
|
|
164
164
|
Promise.all([testConnectToDB]).then(async (x) => {
|
|
165
165
|
if (x[0]) {
|
|
166
166
|
await (pool_base == "basic" ? new Promise((resolve) => resolve(mySqlDbConnect.connect())) : new Promise((resolve) => resolve(SequelizeDbConnect.connect())));
|
|
167
|
-
await authPassport.init().then(async (
|
|
168
|
-
if (
|
|
169
|
-
|
|
167
|
+
await authPassport.init().then(async (p) => {
|
|
168
|
+
if (p[0]) {
|
|
169
|
+
console.log("\n[101m Init failed [0m", p[0]);
|
|
170
|
+
return;
|
|
171
|
+
//throw p[0];
|
|
170
172
|
} else {
|
|
171
173
|
await new Promise((resolve) => resolve(fileWalk.fileWalk(jsfiles)));
|
|
172
174
|
await (pool_base == "basic" ? new Promise((resolve) => resolve()) : new Promise((resolve) => resolve(Base())));
|
|
@@ -176,6 +178,9 @@ init = async (jsfiles) => {
|
|
|
176
178
|
});
|
|
177
179
|
});
|
|
178
180
|
}
|
|
181
|
+
}).catch((err) => {
|
|
182
|
+
console.log("[101m Catch init failed [0m", err);
|
|
183
|
+
throw err;
|
|
179
184
|
});
|
|
180
185
|
}
|
|
181
186
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const passport = require("passport");
|
|
2
2
|
|
|
3
3
|
const checkRoleMiddleware = (options) => {
|
|
4
|
-
return
|
|
4
|
+
return (req, res, next) => {
|
|
5
5
|
if(!Array.isArray(options)) {
|
|
6
|
+
// Perfectly with options is not type Array
|
|
6
7
|
return next();
|
|
7
8
|
} else {
|
|
8
9
|
passport.authenticate("jwt", {
|
|
@@ -10,7 +11,7 @@ const checkRoleMiddleware = (options) => {
|
|
|
10
11
|
}, (err, user, info) => {
|
|
11
12
|
// error check
|
|
12
13
|
if (err) {
|
|
13
|
-
console.log(err, info);
|
|
14
|
+
//console.log(err, info);
|
|
14
15
|
return res.status(403).json({
|
|
15
16
|
code: 403,
|
|
16
17
|
status: "FORBIDDEN",
|
|
@@ -20,49 +21,66 @@ const checkRoleMiddleware = (options) => {
|
|
|
20
21
|
},
|
|
21
22
|
});
|
|
22
23
|
} else {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
if(isPerfectly) {
|
|
37
|
-
res.status(403).json({
|
|
38
|
-
code: 403,
|
|
39
|
-
status: "FORBIDDEN",
|
|
40
|
-
message: "Forbidden: Insufficient role",
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
isPerfectly = false;
|
|
44
|
-
}
|
|
24
|
+
if(!options.length) {
|
|
25
|
+
// Perfectly with no options
|
|
26
|
+
return next();
|
|
27
|
+
} else {
|
|
28
|
+
const allowed = options.some(rule => {
|
|
29
|
+
Object.entries(rule).every(([key, condition]) => {
|
|
30
|
+
return matchCondition(user?.[key], condition, user);
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
if (allowed) {
|
|
34
|
+
return next();
|
|
45
35
|
} else {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
36
|
+
res.status(403).json({
|
|
37
|
+
code: 403,
|
|
38
|
+
status: "FORBIDDEN",
|
|
39
|
+
message: "Forbidden: Insufficient role",
|
|
40
|
+
info: {
|
|
41
|
+
status: "ROLE_NOT_ALLOWED",
|
|
42
|
+
error: "Insufficient role or User token does not have sufficient role.",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
54
45
|
}
|
|
55
|
-
}
|
|
46
|
+
}
|
|
56
47
|
}
|
|
57
48
|
})(req, res, next);
|
|
58
49
|
}
|
|
59
50
|
}
|
|
60
51
|
}
|
|
61
52
|
|
|
53
|
+
const operators = {
|
|
54
|
+
$eq: (userValue, expected) => userValue === expected,
|
|
55
|
+
$ne: (userValue, expected) => userValue !== expected,
|
|
56
|
+
$in: (userValue, expected) => Array.isArray(expected) && expected.includes(userValue),
|
|
57
|
+
$not: (userValue, expected) => Array.isArray(expected) && !expected.includes(userValue),
|
|
58
|
+
$regex: (userValue, expected) =>
|
|
59
|
+
typeof userValue === 'string' && expected instanceof RegExp
|
|
60
|
+
? expected.test(userValue)
|
|
61
|
+
: false,
|
|
62
|
+
$fn: (userValue, fn, user) => typeof fn === 'function' && fn(userValue, user),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const matchCondition = (userValue, condition, user) => {
|
|
66
|
+
// primitive -> eq
|
|
67
|
+
if (typeof condition !== 'object' || condition instanceof RegExp || Array.isArray(condition)) {
|
|
68
|
+
return Array.isArray(condition)
|
|
69
|
+
? operators.$in(userValue, condition)
|
|
70
|
+
: operators.$eq(userValue, condition);
|
|
71
|
+
}
|
|
72
|
+
// operator object
|
|
73
|
+
return Object.entries(condition).every(([op, expected]) => {
|
|
74
|
+
const handler = operators[op];
|
|
75
|
+
if (!handler) return false;
|
|
76
|
+
return handler(userValue, expected, user);
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
62
80
|
const checkRoleMiddlewareWithDefaultProject = (options) => {
|
|
63
81
|
return function (req, res, next) {
|
|
64
82
|
return checkRoleMiddleware(options)(req, res, next);
|
|
65
83
|
}
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
module.exports = { checkRoleMiddleware, checkRoleMiddlewareWithDefaultProject }
|
|
86
|
+
module.exports = { checkRoleMiddleware, checkRoleMiddlewareWithDefaultProject };
|
|
@@ -14,12 +14,13 @@ function avg(req, res, next) {
|
|
|
14
14
|
});
|
|
15
15
|
// promise all
|
|
16
16
|
Promise.all([checkPassport]).then((final) => {
|
|
17
|
-
let item = final[0];
|
|
18
|
-
let passport_config = item[1];
|
|
19
17
|
/**
|
|
20
18
|
* item[0] : Boolean = passport file found.
|
|
21
|
-
* item[1] : Object = passport object.
|
|
19
|
+
* item[1] : Object = passport object. (for check on/off)
|
|
22
20
|
*/
|
|
21
|
+
let item = final[0];
|
|
22
|
+
let passport_config = item[1];
|
|
23
|
+
// check passport file exists ?, when not exists go to next
|
|
23
24
|
if(item[0]) {
|
|
24
25
|
if ((passport_config.model.guard.advanced_guard) ? passport_config.model.guard.advanced_guard.allow : false) {
|
|
25
26
|
let advanced_guard_entity = req.headers[passport_config.model.guard.advanced_guard.entity || "timing"];
|
|
@@ -47,7 +48,7 @@ function avg(req, res, next) {
|
|
|
47
48
|
message: "Bad request.",
|
|
48
49
|
info: {
|
|
49
50
|
status: "BAD_VALUE",
|
|
50
|
-
message: "Bad with wrong Advance guard."
|
|
51
|
+
message: "Bad with wrong Advance guard key."
|
|
51
52
|
},
|
|
52
53
|
});
|
|
53
54
|
}
|
|
@@ -3,8 +3,6 @@ const fs = require("fs");
|
|
|
3
3
|
const passport_config_file = "/passport.config.js";
|
|
4
4
|
const auth = require("../auth/Credentials");
|
|
5
5
|
const { TwoFactor } = require("../helpers/2fa");
|
|
6
|
-
const { avgDeHashIt } = require(__dirname + "/../helpers/math");
|
|
7
|
-
const moment = require("moment");
|
|
8
6
|
|
|
9
7
|
module.exports = {
|
|
10
8
|
expressStart() {
|