lesgo 0.7.6 → 0.7.8
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 +1 -1
- package/package.json +23 -23
- package/src/middlewares/__tests__/basicAuthMiddleware.spec.js +225 -0
- package/src/middlewares/__tests__/clientAuthMiddleware.spec.js +137 -0
- package/src/middlewares/__tests__/errorHttpResponseMiddleware.spec.js +30 -0
- package/src/middlewares/__tests__/httpNoOutputMiddleware.spec.js +199 -0
- package/src/middlewares/__tests__/normalizeSQSMessageMiddleware.spec.js +89 -1
- package/src/middlewares/__tests__/serverAuthMiddleware.spec.js +170 -0
- package/src/middlewares/__tests__/successHttpResponseMiddleware.spec.js +32 -0
- package/src/middlewares/__tests__/verifyJwtMiddleware.spec.js +49 -1
- package/src/middlewares/basicAuthMiddleware.js +145 -0
- package/src/middlewares/clientAuthMiddleware.js +82 -0
- package/src/middlewares/errorHttpResponseMiddleware.js +9 -2
- package/src/middlewares/httpNoOutputMiddleware.js +87 -0
- package/src/middlewares/normalizeHttpRequestMiddleware.js +2 -1
- package/src/middlewares/normalizeSQSMessageMiddleware.js +30 -1
- package/src/middlewares/serverAuthMiddleware.js +29 -0
- package/src/middlewares/successHttpResponseMiddleware.js +12 -2
- package/src/middlewares/verifyJwtMiddleware.js +10 -4
- package/src/services/AuroraDbRDSProxyService.js +2 -3
- package/src/services/ElastiCacheService.js +3 -3
- package/src/services/__tests__/ElasticsearchService.spec.js +33 -0
- package/src/utils/__tests__/cache.spec.js +88 -44
- package/src/utils/__tests__/db.spec.js +0 -1
- package/src/utils/__tests__/validateFields.spec.js +223 -32
- package/src/utils/cache.js +157 -51
- package/src/utils/validateFields.js +85 -44
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
normalizeHandler,
|
|
3
|
+
disconnectConnections,
|
|
4
|
+
} from '../normalizeSQSMessageMiddleware';
|
|
2
5
|
|
|
3
6
|
describe('MiddlewareGroup: test normalizeRecords middleware', () => {
|
|
4
7
|
it('test without parameters', () => {
|
|
@@ -30,3 +33,88 @@ describe('MiddlewareGroup: test normalizeRecords middleware', () => {
|
|
|
30
33
|
]);
|
|
31
34
|
});
|
|
32
35
|
});
|
|
36
|
+
|
|
37
|
+
describe('MiddlewareGroup: test disconnectConnections db middleware', () => {
|
|
38
|
+
it('should not call db.end() whenever a db options is not present', async () => {
|
|
39
|
+
const end = jest.fn().mockResolvedValue();
|
|
40
|
+
await disconnectConnections();
|
|
41
|
+
|
|
42
|
+
expect(end).toHaveBeenCalledTimes(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should not call db.end() whenever a db options is not set', async () => {
|
|
46
|
+
const end = jest.fn().mockResolvedValue();
|
|
47
|
+
await disconnectConnections({ db: {} });
|
|
48
|
+
|
|
49
|
+
expect(end).toHaveBeenCalledTimes(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should call db.end() whenever a db options is set', async () => {
|
|
53
|
+
const end = jest.fn().mockResolvedValue();
|
|
54
|
+
await disconnectConnections({
|
|
55
|
+
db: {
|
|
56
|
+
end,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(end).toHaveBeenCalledTimes(1);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return exception if failure is detected', async () => {
|
|
64
|
+
const end = jest.fn().mockImplementationOnce(() => {
|
|
65
|
+
throw new Error('Test Error');
|
|
66
|
+
});
|
|
67
|
+
await disconnectConnections({
|
|
68
|
+
db: {
|
|
69
|
+
end,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('MiddlewareGroup: test disconnectConnections dbRead middleware', () => {
|
|
76
|
+
it('should not call dbRead.end() whenever a dbRead options is not set', async () => {
|
|
77
|
+
const end = jest.fn().mockResolvedValue();
|
|
78
|
+
await disconnectConnections({ dbRead: {} });
|
|
79
|
+
|
|
80
|
+
expect(end).toHaveBeenCalledTimes(0);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should not call anything whenever no options is passed', async () => {
|
|
84
|
+
const end = jest.fn().mockResolvedValue();
|
|
85
|
+
await disconnectConnections();
|
|
86
|
+
|
|
87
|
+
expect(end).toHaveBeenCalledTimes(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should call dbRead.end() whenever a dbRead options is set', async () => {
|
|
91
|
+
const end = jest.fn().mockResolvedValue();
|
|
92
|
+
await disconnectConnections({
|
|
93
|
+
dbRead: {
|
|
94
|
+
end,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(end).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('MiddlewareGroup: test disconnectConnections cache middleware', () => {
|
|
103
|
+
it('should not call cache.end() whenever a cache options is not set', async () => {
|
|
104
|
+
const cache = jest.fn().mockResolvedValue();
|
|
105
|
+
await disconnectConnections({ cache: {} });
|
|
106
|
+
|
|
107
|
+
expect(cache).toHaveBeenCalledTimes(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should call cache.end() whenever a cache options is set', async () => {
|
|
111
|
+
const end = jest.fn().mockResolvedValue();
|
|
112
|
+
await disconnectConnections({
|
|
113
|
+
cache: {
|
|
114
|
+
end,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(end).toHaveBeenCalledTimes(1);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import serverAuthMiddleware, {
|
|
2
|
+
serverAuthBeforeHandler,
|
|
3
|
+
} from '../serverAuthMiddleware';
|
|
4
|
+
import { generateBasicAuthorizationHash } from '../basicAuthMiddleware';
|
|
5
|
+
import client from '../../../tests/__mocks__/config/client';
|
|
6
|
+
|
|
7
|
+
describe('test serverAuthMiddleware middleware', () => {
|
|
8
|
+
test.each`
|
|
9
|
+
clientObj
|
|
10
|
+
${undefined}
|
|
11
|
+
${{}}
|
|
12
|
+
${{
|
|
13
|
+
default: {
|
|
14
|
+
key: '1111-1111-1111-1111',
|
|
15
|
+
secret: '1111-1111-1111-1111',
|
|
16
|
+
},
|
|
17
|
+
}}
|
|
18
|
+
`('should return object', ({ clientObj }) => {
|
|
19
|
+
const result = serverAuthMiddleware({
|
|
20
|
+
client: clientObj,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(result).toHaveProperty('before');
|
|
24
|
+
expect(result).toHaveProperty('onError');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line
|
|
29
|
+
const next = () => {};
|
|
30
|
+
|
|
31
|
+
describe('test serverAuthBeforeHandler with valid credentials', () => {
|
|
32
|
+
const validBasicAuth = Buffer.from(
|
|
33
|
+
generateBasicAuthorizationHash(
|
|
34
|
+
client.platform_2.key,
|
|
35
|
+
client.platform_2.secret
|
|
36
|
+
)
|
|
37
|
+
).toString('base64');
|
|
38
|
+
|
|
39
|
+
test.each`
|
|
40
|
+
clientObj
|
|
41
|
+
${undefined}
|
|
42
|
+
${{}}
|
|
43
|
+
${{
|
|
44
|
+
platform_2: {
|
|
45
|
+
key: '2222-2222-2222-2222',
|
|
46
|
+
secret: '2222-2222-2222-2222',
|
|
47
|
+
},
|
|
48
|
+
}}
|
|
49
|
+
`('should return undefined when successful', ({ clientObj }) => {
|
|
50
|
+
const handler = {
|
|
51
|
+
event: {
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: `basic ${validBasicAuth}`,
|
|
54
|
+
},
|
|
55
|
+
site: {
|
|
56
|
+
id: 'platform_2',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
let hasError = false;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
serverAuthBeforeHandler(handler, next, {
|
|
65
|
+
client: clientObj,
|
|
66
|
+
});
|
|
67
|
+
} catch (e) {
|
|
68
|
+
hasError = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
expect(hasError).toBeFalsy();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test.each`
|
|
75
|
+
Authorization | blacklistMode
|
|
76
|
+
${undefined} | ${false}
|
|
77
|
+
${`basic ${validBasicAuth}`} | ${false}
|
|
78
|
+
${`Basic ${validBasicAuth}`} | ${false}
|
|
79
|
+
${`basic ${validBasicAuth}`} | ${true}
|
|
80
|
+
${`Basic ${validBasicAuth}`} | ${true}
|
|
81
|
+
`(
|
|
82
|
+
'test Exception with valid credentials',
|
|
83
|
+
({ Authorization, blacklistMode }) => {
|
|
84
|
+
const handler = {
|
|
85
|
+
event: {
|
|
86
|
+
headers: {
|
|
87
|
+
Authorization,
|
|
88
|
+
},
|
|
89
|
+
site: {
|
|
90
|
+
id: 'platform_2',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let hasError = false;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
serverAuthBeforeHandler(handler, next, {
|
|
99
|
+
blacklistMode,
|
|
100
|
+
});
|
|
101
|
+
} catch (e) {
|
|
102
|
+
hasError = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expect(hasError).toBeFalsy();
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('test serverAuthBeforeHandler error handling', () => {
|
|
111
|
+
const invalidClientKey = Buffer.from('client_key:secret_key').toString(
|
|
112
|
+
'base64'
|
|
113
|
+
);
|
|
114
|
+
const invalidSecretKey = Buffer.from(
|
|
115
|
+
`${client.platform_2.key}:secret_key`
|
|
116
|
+
).toString('base64');
|
|
117
|
+
|
|
118
|
+
test.each`
|
|
119
|
+
headers | errorName | errorMessage | errorStatusCode | errorCode | blacklistMode
|
|
120
|
+
${{}} | ${'LesgoException'} | ${'Authorization Header is required!'} | ${403} | ${'JWT_MISSING_AUTHORIZATION_HEADER'} | ${undefined}
|
|
121
|
+
${{ Authorization: 'auth' }} | ${'LesgoException'} | ${'Missing Bearer token!'} | ${403} | ${'JWT_MISSING_BEARER_TOKEN'} | ${undefined}
|
|
122
|
+
${{ Authorization: 'basic ' }} | ${'LesgoException'} | ${'Empty basic authentication hash provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_EMPTY_BASIC_HASH'} | ${undefined}
|
|
123
|
+
${{ Authorization: `basic ${invalidClientKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${undefined}
|
|
124
|
+
${{ Authorization: `basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${undefined}
|
|
125
|
+
${{ Authorization: `Basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${undefined}
|
|
126
|
+
${{}} | ${'LesgoException'} | ${'Authorization Header is required!'} | ${403} | ${'JWT_MISSING_AUTHORIZATION_HEADER'} | ${true}
|
|
127
|
+
${{ Authorization: 'auth' }} | ${'LesgoException'} | ${'Missing Bearer token!'} | ${403} | ${'JWT_MISSING_BEARER_TOKEN'} | ${true}
|
|
128
|
+
${{ Authorization: 'basic ' }} | ${'LesgoException'} | ${'Empty basic authentication hash provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_EMPTY_BASIC_HASH'} | ${true}
|
|
129
|
+
${{ Authorization: `basic ${invalidClientKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${true}
|
|
130
|
+
${{ Authorization: `basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${true}
|
|
131
|
+
${{ Authorization: `Basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${true}
|
|
132
|
+
${{ Authorization: 'auth' }} | ${'LesgoException'} | ${'Missing Bearer token!'} | ${403} | ${'JWT_MISSING_BEARER_TOKEN'} | ${false}
|
|
133
|
+
${{ Authorization: 'basic ' }} | ${'LesgoException'} | ${'Empty basic authentication hash provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_EMPTY_BASIC_HASH'} | ${false}
|
|
134
|
+
${{ Authorization: `basic ${invalidClientKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${false}
|
|
135
|
+
${{ Authorization: `basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${false}
|
|
136
|
+
${{ Authorization: `Basic ${invalidSecretKey}` }} | ${'LesgoException'} | ${'Invalid client key or secret provided'} | ${403} | ${'Middlewares/basicAuthMiddleware::AUTH_INVALID_CLIENT_OR_SECRET_KEY'} | ${false}
|
|
137
|
+
`(
|
|
138
|
+
'should throw $errorMessage when authorization header is $headers',
|
|
139
|
+
async ({
|
|
140
|
+
headers,
|
|
141
|
+
errorName,
|
|
142
|
+
errorMessage,
|
|
143
|
+
errorStatusCode,
|
|
144
|
+
errorCode,
|
|
145
|
+
blacklistMode,
|
|
146
|
+
}) => {
|
|
147
|
+
const handler = {
|
|
148
|
+
event: {
|
|
149
|
+
headers,
|
|
150
|
+
site: {
|
|
151
|
+
id: 'platform_1',
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
expect(
|
|
158
|
+
serverAuthBeforeHandler(handler, next, {
|
|
159
|
+
blacklistMode,
|
|
160
|
+
})
|
|
161
|
+
).toThrow();
|
|
162
|
+
} catch (error) {
|
|
163
|
+
expect(error.name).toBe(errorName);
|
|
164
|
+
expect(error.message).toBe(errorMessage);
|
|
165
|
+
expect(error.statusCode).toBe(errorStatusCode);
|
|
166
|
+
expect(error.code).toBe(errorCode);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
});
|
|
@@ -112,6 +112,38 @@ describe('MiddlewareGroup: test successHttpResponseHandler middleware', () => {
|
|
|
112
112
|
|
|
113
113
|
expect(end).toHaveBeenCalledTimes(1);
|
|
114
114
|
});
|
|
115
|
+
|
|
116
|
+
it('should call dbRead.end() whenever a dbRead options is set', async () => {
|
|
117
|
+
const end = jest.fn().mockResolvedValue();
|
|
118
|
+
await successHttpResponseHandler({
|
|
119
|
+
response: 'Some message',
|
|
120
|
+
headers: {
|
|
121
|
+
'Access-Control-Allow-Credentials': false,
|
|
122
|
+
'X-Token-Id': 'token',
|
|
123
|
+
},
|
|
124
|
+
dbRead: {
|
|
125
|
+
end,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(end).toHaveBeenCalledTimes(1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should call cache.end() whenever a cache options is set', async () => {
|
|
133
|
+
const end = jest.fn().mockResolvedValue();
|
|
134
|
+
await successHttpResponseHandler({
|
|
135
|
+
response: 'Some message',
|
|
136
|
+
headers: {
|
|
137
|
+
'Access-Control-Allow-Credentials': false,
|
|
138
|
+
'X-Token-Id': 'token',
|
|
139
|
+
},
|
|
140
|
+
cache: {
|
|
141
|
+
end,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
expect(end).toHaveBeenCalledTimes(1);
|
|
146
|
+
});
|
|
115
147
|
});
|
|
116
148
|
|
|
117
149
|
describe('MiddlewareGroup: test successHttpResponseAfterHandler', () => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import config from 'Config/jwt'; // eslint-disable-line import/no-unresolved
|
|
2
|
-
import {
|
|
2
|
+
import verifyJwtMiddleware, {
|
|
3
|
+
verifyJwtMiddlewareBeforeHandler,
|
|
4
|
+
} from '../verifyJwtMiddleware';
|
|
3
5
|
import LesgoException from '../../exceptions/LesgoException';
|
|
4
6
|
|
|
5
7
|
describe('MiddlewareGroup: test verifyJwtMiddleware middleware', () => {
|
|
@@ -11,6 +13,21 @@ describe('MiddlewareGroup: test verifyJwtMiddleware middleware', () => {
|
|
|
11
13
|
},
|
|
12
14
|
};
|
|
13
15
|
|
|
16
|
+
it('should return before object', () => {
|
|
17
|
+
const newHandler = {
|
|
18
|
+
event: {
|
|
19
|
+
...handler.event,
|
|
20
|
+
headers: {
|
|
21
|
+
Authorization:
|
|
22
|
+
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJkb21haW4uY29tIiwiZGVwYXJ0bWVudF9pZCI6MX0.pa2TBRqdVSFUhmiglB8SD8ImthqhqZBn0stAdNRcJ3w',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const result = verifyJwtMiddleware(newHandler, () => {});
|
|
27
|
+
|
|
28
|
+
expect(result).toHaveProperty('before');
|
|
29
|
+
});
|
|
30
|
+
|
|
14
31
|
it('test without authorization header', () => {
|
|
15
32
|
expect(() => verifyJwtMiddlewareBeforeHandler(handler, () => {})).toThrow(
|
|
16
33
|
new LesgoException(
|
|
@@ -171,4 +188,35 @@ describe('MiddlewareGroup: test verifyJwtMiddleware middleware', () => {
|
|
|
171
188
|
iss: config.iss.data[0],
|
|
172
189
|
});
|
|
173
190
|
});
|
|
191
|
+
|
|
192
|
+
it('test with custom config', () => {
|
|
193
|
+
const newHandler = {
|
|
194
|
+
event: {
|
|
195
|
+
...handler.event,
|
|
196
|
+
headers: {
|
|
197
|
+
Authorization:
|
|
198
|
+
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJpc3MiOiJkb21haW4uY29tIiwiZGVwYXJ0bWVudF9pZCI6MX0.pa2TBRqdVSFUhmiglB8SD8ImthqhqZBn0stAdNRcJ3w',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
verifyJwtMiddlewareBeforeHandler(newHandler, () => {}, {
|
|
204
|
+
jwtConfig: {
|
|
205
|
+
secret:
|
|
206
|
+
'c4156b94c80b7f163feabd4ff268c99eb11ce8995df370a4fd872afb4377b273',
|
|
207
|
+
iss: {
|
|
208
|
+
validate: true,
|
|
209
|
+
data: ['domain.com'],
|
|
210
|
+
},
|
|
211
|
+
customClaims: {
|
|
212
|
+
validate: true,
|
|
213
|
+
data: ['department_id'],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
expect(newHandler.event.decodedJwt).toMatchObject({
|
|
218
|
+
sub: '1234567890',
|
|
219
|
+
iss: config.iss.data[0],
|
|
220
|
+
});
|
|
221
|
+
});
|
|
174
222
|
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import client from 'Config/client'; // eslint-disable-line import/no-unresolved
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import LesgoException from '../exceptions/LesgoException';
|
|
4
|
+
import { errorHttpResponseAfterHandler } from './errorHttpResponseMiddleware';
|
|
5
|
+
|
|
6
|
+
const FILE = 'Middlewares/basicAuthMiddleware';
|
|
7
|
+
|
|
8
|
+
const blacklistMode = opts => {
|
|
9
|
+
if (opts && typeof opts.blacklistMode !== 'undefined') {
|
|
10
|
+
return !!opts.blacklistMode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return true;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const generateBasicAuthorizationHash = (key, secret) => {
|
|
17
|
+
return crypto
|
|
18
|
+
.createHash('sha1')
|
|
19
|
+
.update(`${key}:${secret}`)
|
|
20
|
+
.digest('hex');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getSiteId = event => {
|
|
24
|
+
let siteId;
|
|
25
|
+
|
|
26
|
+
if (event.site && event.site.id) {
|
|
27
|
+
siteId = event.site.id;
|
|
28
|
+
} else if (
|
|
29
|
+
event.requestContext &&
|
|
30
|
+
event.requestContext.site &&
|
|
31
|
+
event.requestContext.site.id
|
|
32
|
+
) {
|
|
33
|
+
siteId = event.requestContext.site.id;
|
|
34
|
+
} else if (event.platform) {
|
|
35
|
+
siteId = event.platform;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof siteId === 'undefined') {
|
|
39
|
+
throw new LesgoException(
|
|
40
|
+
'Site ID could not be found',
|
|
41
|
+
`${FILE}::SITE_ID_NOT_FOUND`,
|
|
42
|
+
403,
|
|
43
|
+
'Ensure that clientAuthMiddleware() is called before this Middleware'
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return siteId;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const getClient = opts => {
|
|
51
|
+
if (opts && opts.client && Object.keys(opts.client).length > 0) {
|
|
52
|
+
return opts.client;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return client;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const getHashFromHeaders = (headers, opts) => {
|
|
59
|
+
const basicAuth = headers.Authorization || headers.authorization;
|
|
60
|
+
|
|
61
|
+
if (typeof basicAuth === 'undefined') {
|
|
62
|
+
if (blacklistMode(opts)) {
|
|
63
|
+
throw new LesgoException(
|
|
64
|
+
'Authorization header not found',
|
|
65
|
+
`${FILE}::AUTHORIZATION_HEADER_NOT_FOUND`,
|
|
66
|
+
403,
|
|
67
|
+
'Ensure you are have provided the basic authentication code using Authorization header'
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (
|
|
75
|
+
typeof basicAuth !== 'undefined' &&
|
|
76
|
+
!basicAuth.startsWith('basic ') &&
|
|
77
|
+
!basicAuth.startsWith('Basic ')
|
|
78
|
+
) {
|
|
79
|
+
throw new LesgoException(
|
|
80
|
+
'Invalid authorization type provided',
|
|
81
|
+
`${FILE}::AUTH_INVALID_AUTHORIZATION_TYPE`,
|
|
82
|
+
403,
|
|
83
|
+
'Use the basic authorization method'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const authEncoded = basicAuth.startsWith('basic ')
|
|
88
|
+
? basicAuth.replace('basic ', '')
|
|
89
|
+
: basicAuth.replace('Basic ', '');
|
|
90
|
+
|
|
91
|
+
if (authEncoded.length <= 0) {
|
|
92
|
+
throw new LesgoException(
|
|
93
|
+
'Empty basic authentication hash provided',
|
|
94
|
+
`${FILE}::AUTH_EMPTY_BASIC_HASH`,
|
|
95
|
+
403,
|
|
96
|
+
'Ensure basic authentication has is provided along with the keyword "Basic"'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const buff = Buffer.from(authEncoded, 'base64');
|
|
101
|
+
|
|
102
|
+
return buff.toString('utf-8');
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const validateBasicAuth = (hash, siteId, clientObject, opts) => {
|
|
106
|
+
const site = Object.keys(clientObject).find(clientCode => {
|
|
107
|
+
const hashIsEquals =
|
|
108
|
+
generateBasicAuthorizationHash(
|
|
109
|
+
clientObject[clientCode].key,
|
|
110
|
+
clientObject[clientCode].secret
|
|
111
|
+
) === hash;
|
|
112
|
+
|
|
113
|
+
return siteId === clientCode && hashIsEquals;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (!site && (hash.length > 0 || (hash.length <= 0 && blacklistMode(opts)))) {
|
|
117
|
+
throw new LesgoException(
|
|
118
|
+
'Invalid client key or secret provided',
|
|
119
|
+
`${FILE}::AUTH_INVALID_CLIENT_OR_SECRET_KEY`,
|
|
120
|
+
403,
|
|
121
|
+
'Ensure you are using the correct client key or secret key provided'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const verifyBasicAuthBeforeHandler = (handler, next, opts) => {
|
|
127
|
+
const siteId = getSiteId(handler.event);
|
|
128
|
+
const finalClient = getClient(opts);
|
|
129
|
+
const hashFromHeader = getHashFromHeaders(handler.event.headers, opts);
|
|
130
|
+
|
|
131
|
+
validateBasicAuth(hashFromHeader, siteId, finalClient, opts);
|
|
132
|
+
|
|
133
|
+
next();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/* istanbul ignore next */
|
|
137
|
+
const basicAuthMiddleware = opts => {
|
|
138
|
+
return {
|
|
139
|
+
before: (handler, next) =>
|
|
140
|
+
verifyBasicAuthBeforeHandler(handler, next, opts),
|
|
141
|
+
onError: (handler, next) => errorHttpResponseAfterHandler(handler, next),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default basicAuthMiddleware;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import client from 'Config/client'; // eslint-disable-line import/no-unresolved
|
|
2
|
+
import { errorHttpResponseAfterHandler } from './errorHttpResponseMiddleware';
|
|
3
|
+
import validateFields from '../utils/validateFields';
|
|
4
|
+
import { LesgoException } from '../exceptions';
|
|
5
|
+
|
|
6
|
+
const FILE = 'Middlewares/clientAuthMiddleware';
|
|
7
|
+
|
|
8
|
+
const validateParams = params => {
|
|
9
|
+
const validFields = [
|
|
10
|
+
{ key: 'x-client-id', type: 'string', required: true },
|
|
11
|
+
{ key: 'client', type: 'object', required: true },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
return validateFields(params, validFields);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw new LesgoException(error.message, `${FILE}::INVALID_AUTH_DATA`, 403, {
|
|
18
|
+
error,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getClientKey = event => {
|
|
24
|
+
if (typeof event.headers['x-client-id'] === 'string') {
|
|
25
|
+
return event.headers['x-client-id'];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (event.input && typeof event.input.clientid === 'string') {
|
|
29
|
+
return event.input.clientid;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return undefined;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const clientAuthMiddlewareBeforeHandler = (
|
|
36
|
+
handler,
|
|
37
|
+
next,
|
|
38
|
+
opt = undefined
|
|
39
|
+
) => {
|
|
40
|
+
const validated = validateParams({
|
|
41
|
+
'x-client-id': getClientKey(handler.event),
|
|
42
|
+
client,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const clientKey = validated['x-client-id'];
|
|
46
|
+
|
|
47
|
+
const platform = Object.keys(validated.client).filter(clientPlatform => {
|
|
48
|
+
return validated.client[clientPlatform].key === clientKey;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (platform.length === 0) {
|
|
52
|
+
throw new LesgoException(
|
|
53
|
+
'Invalid ClientId provided',
|
|
54
|
+
`${FILE}::INVALID_CLIENT_ID`,
|
|
55
|
+
403,
|
|
56
|
+
'Ensure you are using the correct Client Id provided'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line no-param-reassign,prefer-destructuring
|
|
61
|
+
handler.event.platform = platform[0];
|
|
62
|
+
|
|
63
|
+
if (typeof opt === 'function') {
|
|
64
|
+
opt(handler);
|
|
65
|
+
} else if (typeof opt === 'object') {
|
|
66
|
+
const { callback } = opt;
|
|
67
|
+
if (typeof callback === 'function') callback(handler);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
next();
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/* istanbul ignore next */
|
|
74
|
+
const clientAuthMiddleware = opt => {
|
|
75
|
+
return {
|
|
76
|
+
before: (handler, next) =>
|
|
77
|
+
clientAuthMiddlewareBeforeHandler(handler, next, opt),
|
|
78
|
+
onError: (handler, next) => errorHttpResponseAfterHandler(handler, next),
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default clientAuthMiddleware;
|
|
@@ -51,9 +51,16 @@ export const errorHttpResponseHandler = async opts => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
try {
|
|
54
|
-
|
|
54
|
+
const disconnect = [];
|
|
55
|
+
if (!isEmpty(opts.cache)) disconnect.push(opts.cache.end());
|
|
56
|
+
if (!isEmpty(opts.db)) disconnect.push(opts.db.end());
|
|
57
|
+
if (!isEmpty(opts.dbRead)) disconnect.push(opts.dbRead.end());
|
|
58
|
+
|
|
59
|
+
if (disconnect.length > 0) {
|
|
60
|
+
await Promise.all(disconnect);
|
|
61
|
+
}
|
|
55
62
|
} catch (err) {
|
|
56
|
-
logger.error(`${FILE}::Failed to end
|
|
63
|
+
logger.error(`${FILE}::Failed to end connection`, err);
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
return {
|