@drax/identity-back 0.46.0 → 0.49.0
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/dist/config/IdentityConfig.js +1 -0
- package/dist/middleware/apiKeyMiddleware.js +18 -1
- package/dist/middleware/rbacMiddleware.js +1 -1
- package/dist/repository/sqlite/UserApiKeySqliteRepository.js +11 -4
- package/package.json +7 -7
- package/src/config/IdentityConfig.ts +1 -2
- package/src/middleware/apiKeyMiddleware.ts +56 -36
- package/src/middleware/rbacMiddleware.ts +1 -1
- package/src/repository/sqlite/UserApiKeySqliteRepository.ts +18 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/types/config/IdentityConfig.d.ts +2 -1
- package/types/config/IdentityConfig.d.ts.map +1 -1
- package/types/middleware/apiKeyMiddleware.d.ts.map +1 -1
- package/types/repository/sqlite/UserApiKeySqliteRepository.d.ts.map +1 -1
|
@@ -8,6 +8,7 @@ var IdentityConfig;
|
|
|
8
8
|
IdentityConfig["RbacCacheTTL"] = "DRAX_RBAC_CACHE_TTL";
|
|
9
9
|
IdentityConfig["AvatarDir"] = "DRAX_AVATAR_DIR";
|
|
10
10
|
IdentityConfig["defaultRole"] = "DRAX_DEFAULT_ROLE";
|
|
11
|
+
IdentityConfig["VerifyIP"] = "DRAX_VERIFY_IP";
|
|
11
12
|
})(IdentityConfig || (IdentityConfig = {}));
|
|
12
13
|
export default IdentityConfig;
|
|
13
14
|
export { IdentityConfig };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DraxCache, DraxConfig } from "@drax/common-back";
|
|
2
2
|
import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
3
3
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
4
|
-
const
|
|
4
|
+
const verifyIp = DraxConfig.getOrLoad(IdentityConfig.VerifyIP, 'boolean', true);
|
|
5
|
+
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL, 'number', 10000);
|
|
5
6
|
const draxCache = new DraxCache(cacheTTL);
|
|
6
7
|
async function userApiKeyLoader(k) {
|
|
7
8
|
const userApiKeyService = UserApiKeyServiceFactory();
|
|
@@ -27,6 +28,22 @@ async function apiKeyMiddleware(request, reply) {
|
|
|
27
28
|
}
|
|
28
29
|
if (apiKey) {
|
|
29
30
|
const userApiKey = await draxCache.getOrLoad(apiKey, userApiKeyLoader);
|
|
31
|
+
if (verifyIp) {
|
|
32
|
+
if (userApiKey.ipv4 && userApiKey.ipv4.length > 0) {
|
|
33
|
+
const clientIp = request.ip;
|
|
34
|
+
if (!userApiKey.ipv4.includes(clientIp)) {
|
|
35
|
+
reply.code(401).send({ "statusCode": "401", error: 'IP not allowed', i18nMessage: 'error.ipNotAllowed', clientIp: clientIp, message: `Remote IP ${clientIp} is not allowed` });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (userApiKey.ipv6 && userApiKey.ipv6.length > 0) {
|
|
40
|
+
const clientIp = request.ip;
|
|
41
|
+
if (!userApiKey.ipv6.includes(clientIp)) {
|
|
42
|
+
reply.code(401).send({ "statusCode": "401", error: 'IP not allowed', i18nMessage: 'error.ipNotAllowed', clientIp: clientIp, message: `Remote IP ${clientIp} is not allowed` });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
30
47
|
if (userApiKey && userApiKey.user) {
|
|
31
48
|
const authUser = {
|
|
32
49
|
id: userApiKey.user._id.toString(),
|
|
@@ -2,7 +2,7 @@ import { DraxCache, DraxConfig } from "@drax/common-back";
|
|
|
2
2
|
import RoleServiceFactory from "../factory/RoleServiceFactory.js";
|
|
3
3
|
import Rbac from "../rbac/Rbac.js";
|
|
4
4
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
5
|
-
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL
|
|
5
|
+
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL, 'number', 10000);
|
|
6
6
|
const draxCache = new DraxCache(cacheTTL);
|
|
7
7
|
async function roleLoader(k) {
|
|
8
8
|
const roleService = RoleServiceFactory();
|
|
@@ -32,12 +32,13 @@ class UserApiKeySqliteRepository extends AbstractSqliteRepository {
|
|
|
32
32
|
if (item && item.createdBy) {
|
|
33
33
|
item.createdBy = await this.findUserById(item.createdBy);
|
|
34
34
|
}
|
|
35
|
-
if (item && item.ipv4) {
|
|
36
|
-
item.ipv4 = item.ipv4
|
|
35
|
+
if (item && item.hasOwnProperty('ipv4')) {
|
|
36
|
+
item.ipv4 = item.ipv4 ? item.ipv4.split(',') : [];
|
|
37
37
|
}
|
|
38
|
-
if (item && item.ipv6) {
|
|
39
|
-
item.ipv6 = item.ipv6
|
|
38
|
+
if (item && item.hasOwnProperty('ipv6')) {
|
|
39
|
+
item.ipv6 = item.ipv6 ? item.ipv6.split(',') : [];
|
|
40
40
|
}
|
|
41
|
+
return item;
|
|
41
42
|
}
|
|
42
43
|
async prepareData(userApiKeyData) {
|
|
43
44
|
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
@@ -52,6 +53,12 @@ class UserApiKeySqliteRepository extends AbstractSqliteRepository {
|
|
|
52
53
|
else {
|
|
53
54
|
userApiKeyData.ipv6 = "";
|
|
54
55
|
}
|
|
56
|
+
if (userApiKeyData.user && userApiKeyData.user.hasOwnProperty('_id')) {
|
|
57
|
+
userApiKeyData.user = userApiKeyData.user._id;
|
|
58
|
+
}
|
|
59
|
+
if (userApiKeyData.createdBy && userApiKeyData.createdBy.hasOwnProperty('_id')) {
|
|
60
|
+
userApiKeyData.createdBy = userApiKeyData.createdBy._id;
|
|
61
|
+
}
|
|
55
62
|
}
|
|
56
63
|
async findBySecret(secret) {
|
|
57
64
|
const userApiKey = this.db.prepare('SELECT * FROM user_api_keys WHERE secret = ?').get(secret);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.49.0",
|
|
7
7
|
"description": "Identity module for user management, authentication and authorization.",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "types/index.d.ts",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"author": "Cristian Incarnato & Drax Team",
|
|
29
29
|
"license": "ISC",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@drax/common-back": "^0.
|
|
32
|
-
"@drax/crud-back": "^0.
|
|
33
|
-
"@drax/crud-share": "^0.
|
|
34
|
-
"@drax/email-back": "^0.
|
|
35
|
-
"@drax/identity-share": "^0.
|
|
31
|
+
"@drax/common-back": "^0.49.0",
|
|
32
|
+
"@drax/crud-back": "^0.49.0",
|
|
33
|
+
"@drax/crud-share": "^0.49.0",
|
|
34
|
+
"@drax/email-back": "^0.49.0",
|
|
35
|
+
"@drax/identity-share": "^0.49.0",
|
|
36
36
|
"bcryptjs": "^2.4.3",
|
|
37
37
|
"graphql": "^16.8.2",
|
|
38
38
|
"jsonwebtoken": "^9.0.2"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"debug": "0"
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "ea99f6fd79d282e6efe02fca660d5733c13bb86d"
|
|
67
67
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
enum IdentityConfig {
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
3
|
JwtSecret = "DRAX_JWT_SECRET",
|
|
6
4
|
JwtExpiration = "DRAX_JWT_EXPIRATION",
|
|
7
5
|
JwtIssuer = "DRAX_JWT_ISSUER",
|
|
@@ -15,6 +13,7 @@ enum IdentityConfig {
|
|
|
15
13
|
|
|
16
14
|
defaultRole = "DRAX_DEFAULT_ROLE",
|
|
17
15
|
|
|
16
|
+
VerifyIP = "DRAX_VERIFY_IP",
|
|
18
17
|
|
|
19
18
|
}
|
|
20
19
|
|
|
@@ -3,58 +3,78 @@ import {DraxCache, DraxConfig} from "@drax/common-back";
|
|
|
3
3
|
import UserApiKeyServiceFactory from "../factory/UserApiKeyServiceFactory.js";
|
|
4
4
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const verifyIp = DraxConfig.getOrLoad(IdentityConfig.VerifyIP, 'boolean', true);
|
|
7
|
+
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.ApiKeyCacheTTL, 'number',10000)
|
|
8
|
+
|
|
7
9
|
const draxCache = new DraxCache<IUserApiKey>(cacheTTL);
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
async function userApiKeyLoader(k):Promise<IUserApiKey | null> {
|
|
12
|
+
async function userApiKeyLoader(k): Promise<IUserApiKey | null> {
|
|
11
13
|
const userApiKeyService = UserApiKeyServiceFactory()
|
|
12
14
|
const userApiKey: IUserApiKey = await userApiKeyService.findBySecret(k)
|
|
13
15
|
return userApiKey
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
async function apiKeyMiddleware
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
async function apiKeyMiddleware(request, reply) {
|
|
19
|
+
try {
|
|
20
|
+
let apiKey: string
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
//Por header 'x-api-key'
|
|
23
|
+
if (request.headers['x-api-key']) {
|
|
24
|
+
apiKey = request.headers['x-api-key']
|
|
25
|
+
}
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
//Por authorization 'ApiKey <uuid-key>'
|
|
28
|
+
const apiKeyRegExp = /^ApiKey (.*)$/i;
|
|
29
|
+
if (request.headers['authorization'] && apiKeyRegExp.test(request.headers['authorization'])) {
|
|
30
|
+
apiKey = request.headers?.authorization?.replace(/ApiKey /i, "")
|
|
31
|
+
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
//Por authorization '<uuid-key>'
|
|
34
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
35
|
+
if (request.headers['authorization'] && uuidRegex.test(request.headers['authorization'])) {
|
|
36
|
+
apiKey = request.headers['authorization']
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
if (apiKey) {
|
|
40
|
+
const userApiKey: IUserApiKey = await draxCache.getOrLoad(apiKey, userApiKeyLoader)
|
|
41
|
+
|
|
42
|
+
if (verifyIp) {
|
|
43
|
+
if (userApiKey.ipv4 && userApiKey.ipv4.length > 0) {
|
|
44
|
+
const clientIp = request.ip
|
|
45
|
+
if (!userApiKey.ipv4.includes(clientIp)) {
|
|
46
|
+
reply.code(401).send({"statusCode": "401", error: 'IP not allowed', i18nMessage: 'error.ipNotAllowed', clientIp: clientIp, message: `Remote IP ${clientIp} is not allowed`});
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
} else if (userApiKey.ipv6 && userApiKey.ipv6.length > 0) {
|
|
50
|
+
const clientIp = request.ip
|
|
51
|
+
if (!userApiKey.ipv6.includes(clientIp)) {
|
|
52
|
+
reply.code(401).send({"statusCode": "401", error: 'IP not allowed', i18nMessage: 'error.ipNotAllowed', clientIp: clientIp, message: `Remote IP ${clientIp} is not allowed`});
|
|
53
|
+
return
|
|
49
54
|
}
|
|
50
|
-
request.authUser = authUser
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
if (userApiKey && userApiKey.user) {
|
|
60
|
+
const authUser: IAuthUser = {
|
|
61
|
+
id: userApiKey.user._id.toString(),
|
|
62
|
+
username: userApiKey.user.username,
|
|
63
|
+
roleId: userApiKey.user.role?._id?.toString(),
|
|
64
|
+
roleName: userApiKey.user.role?.name,
|
|
65
|
+
tenantId: userApiKey.user?.tenant?._id?.toString(),
|
|
66
|
+
tenantName: userApiKey.user?.tenant?.name,
|
|
67
|
+
apiKeyId: userApiKey?._id?.toString(),
|
|
68
|
+
apiKeyName: userApiKey?.name
|
|
69
|
+
}
|
|
70
|
+
request.authUser = authUser
|
|
71
|
+
}
|
|
57
72
|
}
|
|
73
|
+
return
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(e)
|
|
76
|
+
reply.code(500).send({error: 'APIKEY ERROR'});
|
|
77
|
+
}
|
|
58
78
|
}
|
|
59
79
|
|
|
60
80
|
export default apiKeyMiddleware;
|
|
@@ -4,7 +4,7 @@ import RoleServiceFactory from "../factory/RoleServiceFactory.js";
|
|
|
4
4
|
import Rbac from "../rbac/Rbac.js";
|
|
5
5
|
import IdentityConfig from "../config/IdentityConfig.js";
|
|
6
6
|
|
|
7
|
-
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL
|
|
7
|
+
const cacheTTL = DraxConfig.getOrLoad(IdentityConfig.RbacCacheTTL, 'number',10000) ;
|
|
8
8
|
const draxCache = new DraxCache<IRole>(cacheTTL);
|
|
9
9
|
|
|
10
10
|
|
|
@@ -40,6 +40,8 @@ class UserApiKeySqliteRepository extends AbstractSqliteRepository<IUserApiKey, I
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
async prepareItem(item: any): Promise<any> {
|
|
43
|
+
|
|
44
|
+
|
|
43
45
|
if (item && item.user) {
|
|
44
46
|
item.user = await this.findUserById(item.user)
|
|
45
47
|
}
|
|
@@ -48,16 +50,19 @@ class UserApiKeySqliteRepository extends AbstractSqliteRepository<IUserApiKey, I
|
|
|
48
50
|
item.createdBy = await this.findUserById(item.createdBy)
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
if (item && item.ipv4) {
|
|
52
|
-
item.ipv4 = item.ipv4
|
|
53
|
+
if (item && item.hasOwnProperty('ipv4')) {
|
|
54
|
+
item.ipv4 = item.ipv4 ? item.ipv4.split(',') : []
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
if (item && item.ipv6) {
|
|
56
|
-
item.ipv6 = item.ipv6
|
|
57
|
+
if (item && item.hasOwnProperty('ipv6')) {
|
|
58
|
+
item.ipv6 = item.ipv6 ? item.ipv6.split(',') : []
|
|
57
59
|
}
|
|
60
|
+
|
|
61
|
+
return item;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
async prepareData(userApiKeyData) {
|
|
65
|
+
|
|
61
66
|
if (userApiKeyData.ipv4 && Array.isArray(userApiKeyData.ipv4) && userApiKeyData.ipv4.length > 0) {
|
|
62
67
|
userApiKeyData.ipv4 = userApiKeyData.ipv4.join(',')
|
|
63
68
|
} else {
|
|
@@ -69,6 +74,15 @@ class UserApiKeySqliteRepository extends AbstractSqliteRepository<IUserApiKey, I
|
|
|
69
74
|
} else {
|
|
70
75
|
userApiKeyData.ipv6 = ""
|
|
71
76
|
}
|
|
77
|
+
|
|
78
|
+
if(userApiKeyData.user && userApiKeyData.user.hasOwnProperty('_id')) {
|
|
79
|
+
userApiKeyData.user = userApiKeyData.user._id
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if(userApiKeyData.createdBy && userApiKeyData.createdBy.hasOwnProperty('_id')) {
|
|
83
|
+
userApiKeyData.createdBy = userApiKeyData.createdBy._id
|
|
84
|
+
}
|
|
85
|
+
|
|
72
86
|
}
|
|
73
87
|
|
|
74
88
|
|