@w3-commons/js-build-resources 0.0.1-security → 1.0.4
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.
Potentially problematic release.
This version of @w3-commons/js-build-resources might be problematic. Click here for more details.
- package/package.json +9 -3
- package/settings-service/.eslintrc.js +4 -0
- package/settings-service/.node-version +1 -0
- package/settings-service/.prettierrc +6 -0
- package/settings-service/.whitesource +15 -0
- package/settings-service/LICENSE +4 -0
- package/settings-service/README.md +50 -0
- package/settings-service/build.yml +56 -0
- package/settings-service/collectCodeCoverage.js +9 -0
- package/settings-service/db/cassandra/Dockerfile +3 -0
- package/settings-service/db/cassandra/createkeyspace.dev.cql +4 -0
- package/settings-service/db/cassandra/createkeyspace.dev.sh +3 -0
- package/settings-service/db/cassandra/schema_001.cql +15 -0
- package/settings-service/db/cassandra/schema_002.cql +8 -0
- package/settings-service/db/cassandra/schema_003.cql +10 -0
- package/settings-service/db/cassandra/schema_004.cql +1 -0
- package/settings-service/db/cassandra/schema_005.cql +39 -0
- package/settings-service/db/cassandra/schema_006.cql +255 -0
- package/settings-service/db/cassandra/schema_007.cql +40 -0
- package/settings-service/db/cassandra/schema_008.cql +2 -0
- package/settings-service/db/cassandra/schema_009.cql +143 -0
- package/settings-service/db/cassandra/schema_010.cql +143 -0
- package/settings-service/db/cassandra/schema_011.cql +2 -0
- package/settings-service/db/cassandra/schema_012.cql +8 -0
- package/settings-service/jest.config.fn.js +3 -0
- package/settings-service/jest.config.it.js +3 -0
- package/settings-service/jest.config.js +14 -0
- package/settings-service/jest.config.unit.js +19 -0
- package/settings-service/jest.setup.js +11 -0
- package/settings-service/package-lock.json +11772 -0
- package/settings-service/package.json +101 -0
- package/settings-service/scripts/run-fn-tests.sh +3 -0
- package/settings-service/sonar-project.properties +3 -0
- package/settings-service/src/__tests__/functional/controller/ApiKeyController.fn.ts +132 -0
- package/settings-service/src/__tests__/functional/middleware/AuthMiddlewareNextGenSSO.fn.ts +82 -0
- package/settings-service/src/__tests__/functional/repo/settingsRepo.fn.ts +302 -0
- package/settings-service/src/__tests__/functional/unified-profile/unified-profile.fn.ts +66 -0
- package/settings-service/src/__tests__/integration/repo/ApiKeyRepo.it.ts +43 -0
- package/settings-service/src/__tests__/integration/repo/settingsRepo.it.ts +142 -0
- package/settings-service/src/__tests__/integration/unified-profile/unified-profile.it.ts +31 -0
- package/settings-service/src/__tests__/unit/ErrResponse.ts +4 -0
- package/settings-service/src/__tests__/unit/JWTResponse.ts +18 -0
- package/settings-service/src/__tests__/unit/bluepagesResponse.ts +25 -0
- package/settings-service/src/__tests__/unit/controller/ApiKeyController.spec.ts +217 -0
- package/settings-service/src/__tests__/unit/controller/AppSettingsController.spec.ts +133 -0
- package/settings-service/src/__tests__/unit/controller/UserSettingsController.spec.ts +328 -0
- package/settings-service/src/__tests__/unit/controller/getAllSettings.spec.ts +83 -0
- package/settings-service/src/__tests__/unit/middleware/AuthMiddlewareNextGenSSO.spec.ts +282 -0
- package/settings-service/src/__tests__/unit/middleware/AuthenticationMiddleware.spec.ts +494 -0
- package/settings-service/src/__tests__/unit/repo/ApiKeyRepo.spec.ts +194 -0
- package/settings-service/src/__tests__/unit/repo/getAllSettings.spec.ts +100 -0
- package/settings-service/src/__tests__/unit/repo/getUserSettingsRepo.spec.ts +249 -0
- package/settings-service/src/__tests__/unit/repo/settingsRepo.spec.ts +614 -0
- package/settings-service/src/__tests__/unit/unified-profile/UnifiedProfileClient.spec.ts +31 -0
- package/settings-service/src/__tests__/unit/unified-profile/unifiedProfileUtils.spec.ts +36 -0
- package/settings-service/src/__tests__/utils/test-utils.ts +41 -0
- package/settings-service/src/config/config.ts +190 -0
- package/settings-service/src/controller/ApiKeyController.ts +114 -0
- package/settings-service/src/controller/AppSettingsController.ts +137 -0
- package/settings-service/src/controller/UserSettingsController.ts +202 -0
- package/settings-service/src/helpers/commons.ts +69 -0
- package/settings-service/src/logger/logger.ts +17 -0
- package/settings-service/src/middleware/AuthenticationMiddleware.ts +486 -0
- package/settings-service/src/middleware/AuthenticationMiddlewareFactory.ts +10 -0
- package/settings-service/src/repo/ApiKeyRepo.ts +135 -0
- package/settings-service/src/repo/ApiKeyRepoFactory.ts +10 -0
- package/settings-service/src/repo/CassandraClient.ts +33 -0
- package/settings-service/src/repo/CassandraClientFactory.ts +11 -0
- package/settings-service/src/repo/apiKeyQueries.ts +64 -0
- package/settings-service/src/repo/cassandraDBHelpers.ts +119 -0
- package/settings-service/src/repo/settingsRepo.ts +388 -0
- package/settings-service/src/repo/settingsRepoFactory.ts +10 -0
- package/settings-service/src/repo/settingsRepoQueries.ts +62 -0
- package/settings-service/src/routes/apiKeyRoutes.ts +27 -0
- package/settings-service/src/routes/appSettingsRoutes.ts +30 -0
- package/settings-service/src/routes/healthCheck.ts +10 -0
- package/settings-service/src/routes/swagger.ts +8 -0
- package/settings-service/src/routes/userSettingsRoutes.ts +30 -0
- package/settings-service/src/server.ts +77 -0
- package/settings-service/src/swagger.json +732 -0
- package/settings-service/src/types/ApiKey.ts +19 -0
- package/settings-service/src/types/IRequest.ts +9 -0
- package/settings-service/src/types/IRequestAuthorization.ts +5 -0
- package/settings-service/src/types/IRouteOptions.ts +5 -0
- package/settings-service/src/types/QueryResultsTypes.ts +6 -0
- package/settings-service/src/types/UserSettingsControllerTypes.ts +5 -0
- package/settings-service/src/types/W3IdUser.ts +36 -0
- package/settings-service/src/types/settingsRepoTypes.ts +61 -0
- package/settings-service/src/types/unifiedProfileTypes.ts +10 -0
- package/settings-service/src/unified-profile/UnifiedProfileClient.ts +29 -0
- package/settings-service/src/unified-profile/UnifiedProfileClientFactory.ts +10 -0
- package/settings-service/src/unified-profile/unifiedProfileUtils.ts +22 -0
- package/settings-service/src/util/downloadCassandra.ts +34 -0
- package/settings-service/src/util/isocodeMapper.ts +22 -0
- package/settings-service/src/util/languages.ts +1457 -0
- package/settings-service/test_resources/mockApiKeyDBResult.json +8 -0
- package/settings-service/tsconfig.json +40 -0
- package/README.md +0 -5
@@ -0,0 +1,328 @@
|
|
1
|
+
import { Request, Response } from 'restify';
|
2
|
+
import { settings } from '../../../config/config';
|
3
|
+
import {
|
4
|
+
deleteUserSettings,
|
5
|
+
getUserSettings,
|
6
|
+
postUserSettings,
|
7
|
+
} from '../../../controller/UserSettingsController';
|
8
|
+
import { cassandraDBHelpers } from '../../../repo/cassandraDBHelpers';
|
9
|
+
import { SettingsRepoCassandra } from '../../../repo/settingsRepo';
|
10
|
+
import { INVALID_APP_ID, TEST_APP_ID } from '../../utils/test-utils';
|
11
|
+
|
12
|
+
settings.settingsAppIds = ['w3notifications-test', 'general'];
|
13
|
+
|
14
|
+
jest.mock('../../../unified-profile/unifiedProfileUtils');
|
15
|
+
|
16
|
+
const USER_ID = 'AAABBBCCC';
|
17
|
+
const INVALID_USER_ID = 'BBBCCCDDD';
|
18
|
+
|
19
|
+
function test_send(code?: number | undefined): any {
|
20
|
+
return code;
|
21
|
+
}
|
22
|
+
|
23
|
+
function test_send_with_body(code?: number | undefined, body?: any): any {
|
24
|
+
return { code, body };
|
25
|
+
}
|
26
|
+
|
27
|
+
describe('get settings using userID and appID', () => {
|
28
|
+
const request: Request = {} as Request;
|
29
|
+
const response: Response = {} as Response;
|
30
|
+
|
31
|
+
request.params = { userID: USER_ID, appID: TEST_APP_ID };
|
32
|
+
request.query = {};
|
33
|
+
request.username = USER_ID;
|
34
|
+
|
35
|
+
it('should return 200 set while getting data', async () => {
|
36
|
+
const mockResultSet1: any = {
|
37
|
+
rows: [{ setting_name: 'theme', setting_default: null, setting_options: ['darkmode'] }],
|
38
|
+
};
|
39
|
+
const mockResultSet2: any = {
|
40
|
+
rows: [
|
41
|
+
{ setting_name: 'theme', setting_value: 'darkmode' },
|
42
|
+
{ setting_name: 'primary-language', setting_value: 'english' },
|
43
|
+
],
|
44
|
+
};
|
45
|
+
|
46
|
+
cassandraDBHelpers.execute = jest
|
47
|
+
.fn()
|
48
|
+
.mockReturnValueOnce(
|
49
|
+
new Promise((resolve) => {
|
50
|
+
resolve(mockResultSet1);
|
51
|
+
}),
|
52
|
+
)
|
53
|
+
.mockReturnValueOnce(
|
54
|
+
new Promise((resolve) => {
|
55
|
+
resolve(mockResultSet2);
|
56
|
+
}),
|
57
|
+
)
|
58
|
+
.mockReturnValueOnce(
|
59
|
+
new Promise((resolve) => {
|
60
|
+
resolve(mockResultSet1);
|
61
|
+
}),
|
62
|
+
)
|
63
|
+
.mockReturnValueOnce(
|
64
|
+
new Promise((resolve) => {
|
65
|
+
resolve(mockResultSet2);
|
66
|
+
}),
|
67
|
+
);
|
68
|
+
response.send = test_send;
|
69
|
+
expect(await getUserSettings(request, response)).toEqual(200);
|
70
|
+
|
71
|
+
cassandraDBHelpers.execute = jest
|
72
|
+
.fn()
|
73
|
+
.mockReturnValueOnce(
|
74
|
+
new Promise((resolve) => {
|
75
|
+
resolve(mockResultSet1);
|
76
|
+
}),
|
77
|
+
)
|
78
|
+
.mockReturnValue(
|
79
|
+
new Promise((resolve) => {
|
80
|
+
resolve(mockResultSet2);
|
81
|
+
}),
|
82
|
+
);
|
83
|
+
request.params.appID = 'general';
|
84
|
+
expect(await getUserSettings(request, response)).toEqual(200);
|
85
|
+
});
|
86
|
+
it('user requesting other user resource should result in 403 error', async () => {
|
87
|
+
request.username = INVALID_USER_ID;
|
88
|
+
response.send = test_send;
|
89
|
+
expect(await getUserSettings(request, response)).toEqual(403);
|
90
|
+
});
|
91
|
+
it('invalid appID should return 400 error', async () => {
|
92
|
+
request.params.appID = INVALID_APP_ID;
|
93
|
+
response.send = test_send;
|
94
|
+
expect(await getUserSettings(request, response)).toEqual(400);
|
95
|
+
});
|
96
|
+
it('internal server error should return 500 error', async () => {
|
97
|
+
request.params = undefined;
|
98
|
+
response.send = test_send;
|
99
|
+
expect(await getUserSettings(request, response)).toEqual(500);
|
100
|
+
});
|
101
|
+
});
|
102
|
+
|
103
|
+
describe('post settings', () => {
|
104
|
+
const request: Request = {} as Request;
|
105
|
+
const response: Response = {} as Response;
|
106
|
+
|
107
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
108
|
+
request.query = {};
|
109
|
+
request.username = USER_ID;
|
110
|
+
request.body = { theme: 'darkmode' };
|
111
|
+
|
112
|
+
it('should return 200 set while setting data', async () => {
|
113
|
+
const mockResultSet1: any = {
|
114
|
+
rows: [{ setting_name: 'theme', setting_options: ['darkmode'] }],
|
115
|
+
};
|
116
|
+
const mockResultSet2: any = {
|
117
|
+
rows: [],
|
118
|
+
};
|
119
|
+
cassandraDBHelpers.execute = jest.fn().mockReturnValueOnce(
|
120
|
+
new Promise((resolve) => {
|
121
|
+
resolve(mockResultSet1);
|
122
|
+
}),
|
123
|
+
);
|
124
|
+
cassandraDBHelpers.batch = jest.fn().mockResolvedValueOnce(mockResultSet2);
|
125
|
+
response.send = test_send;
|
126
|
+
expect(await postUserSettings(request, response)).toEqual(200);
|
127
|
+
});
|
128
|
+
it('invalid input should return 400 error', async () => {
|
129
|
+
request.params.appID = INVALID_APP_ID;
|
130
|
+
response.send = test_send;
|
131
|
+
expect(await postUserSettings(request, response)).toEqual(400);
|
132
|
+
request.params.appID = null;
|
133
|
+
expect(await postUserSettings(request, response)).toEqual(400);
|
134
|
+
});
|
135
|
+
it('internal server error should return 500 error', async () => {
|
136
|
+
request.params = undefined;
|
137
|
+
response.send = test_send;
|
138
|
+
expect(await postUserSettings(request, response)).toEqual(500);
|
139
|
+
});
|
140
|
+
it('should be able to post a setting that has an array value', async () => {
|
141
|
+
const mockAppResults: any = {
|
142
|
+
rows: [
|
143
|
+
{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'], setting_type: 'array' },
|
144
|
+
],
|
145
|
+
};
|
146
|
+
const mockUpdateSetting: any = {
|
147
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_value: 'sk,fr' }],
|
148
|
+
};
|
149
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
150
|
+
request.query = {};
|
151
|
+
request.username = USER_ID;
|
152
|
+
request.body = { secondaryLanguages: ['sk', 'en'] };
|
153
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
154
|
+
cassandraDBHelpers.batch = jest.fn().mockResolvedValueOnce(mockUpdateSetting);
|
155
|
+
response.send = test_send_with_body;
|
156
|
+
|
157
|
+
const resp: any = await postUserSettings(request, response);
|
158
|
+
|
159
|
+
expect(resp.code).toBe(200);
|
160
|
+
expect(resp.body).toEqual({ secondaryLanguages: ['sk', 'en'] });
|
161
|
+
});
|
162
|
+
|
163
|
+
it('should return an error if array value not provided', async () => {
|
164
|
+
const mockAppResults: any = {
|
165
|
+
rows: [
|
166
|
+
{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'], setting_type: 'array' },
|
167
|
+
],
|
168
|
+
};
|
169
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
170
|
+
request.query = {};
|
171
|
+
request.username = USER_ID;
|
172
|
+
request.body = { secondaryLanguages: 'sk' };
|
173
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
174
|
+
response.send = test_send_with_body;
|
175
|
+
|
176
|
+
const resp: any = await postUserSettings(request, response);
|
177
|
+
|
178
|
+
expect(resp.code).toBe(400);
|
179
|
+
expect(resp.body.message).toEqual('secondaryLanguages expects to receive an array value');
|
180
|
+
});
|
181
|
+
|
182
|
+
it('should return an error if array value provided for a non-array type setting', async () => {
|
183
|
+
const mockAppResults: any = {
|
184
|
+
rows: [{ setting_name: 'theme', setting_options: ['dark', 'light', 'auto'], setting_type: 'string' }],
|
185
|
+
};
|
186
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
187
|
+
request.query = {};
|
188
|
+
request.username = USER_ID;
|
189
|
+
request.body = { theme: ['dark', 'light'] };
|
190
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
191
|
+
response.send = test_send_with_body;
|
192
|
+
|
193
|
+
const resp: any = await postUserSettings(request, response);
|
194
|
+
expect(resp.code).toBe(400);
|
195
|
+
expect(resp.body.message).toEqual('theme should not be an array value');
|
196
|
+
});
|
197
|
+
|
198
|
+
it('should not update a setting that includes an invalid option', async () => {
|
199
|
+
const mockAppResults: any = {
|
200
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'] }],
|
201
|
+
};
|
202
|
+
const mockUpdateSetting: any = {
|
203
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_value: 'sk,fr' }],
|
204
|
+
};
|
205
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
206
|
+
request.query = {};
|
207
|
+
request.username = USER_ID;
|
208
|
+
request.body = { secondaryLanguages: ['en', 'br'] };
|
209
|
+
cassandraDBHelpers.execute = jest
|
210
|
+
.fn()
|
211
|
+
.mockResolvedValueOnce(mockAppResults)
|
212
|
+
.mockResolvedValueOnce(mockUpdateSetting);
|
213
|
+
response.send = test_send_with_body;
|
214
|
+
|
215
|
+
const resp: any = await postUserSettings(request, response);
|
216
|
+
|
217
|
+
expect(resp.code).toBe(400);
|
218
|
+
expect(resp.body.message).toEqual('Provided value includes an invalid option');
|
219
|
+
});
|
220
|
+
|
221
|
+
it('should not update a setting when an error is thrown from db', async () => {
|
222
|
+
const mockAppResults: any = {
|
223
|
+
rows: [
|
224
|
+
{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'], setting_type: 'array' },
|
225
|
+
],
|
226
|
+
};
|
227
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
228
|
+
request.query = {};
|
229
|
+
request.username = USER_ID;
|
230
|
+
request.body = { secondaryLanguages: ['en', 'fr'] };
|
231
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
232
|
+
cassandraDBHelpers.batch = jest.fn().mockImplementationOnce(() => {
|
233
|
+
throw new Error('Error while updating UserSettings');
|
234
|
+
});
|
235
|
+
response.send = test_send_with_body;
|
236
|
+
|
237
|
+
const resp: any = await postUserSettings(request, response);
|
238
|
+
expect(resp.code).toBe(500);
|
239
|
+
expect(resp.body.message).toEqual(
|
240
|
+
'Unable to update user settings: Error: Error while updating UserSettings ',
|
241
|
+
);
|
242
|
+
});
|
243
|
+
|
244
|
+
it('should update a setting with a string value', async () => {
|
245
|
+
const mockAppResults: any = {
|
246
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'] }],
|
247
|
+
};
|
248
|
+
const mockUpdateSetting: any = {
|
249
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_value: null }],
|
250
|
+
};
|
251
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
252
|
+
request.query = {};
|
253
|
+
request.username = USER_ID;
|
254
|
+
request.body = { secondaryLanguages: null };
|
255
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
256
|
+
cassandraDBHelpers.batch = jest.fn().mockResolvedValueOnce(mockUpdateSetting);
|
257
|
+
response.send = test_send_with_body;
|
258
|
+
|
259
|
+
const resp: any = await postUserSettings(request, response);
|
260
|
+
|
261
|
+
expect(resp.code).toBe(200);
|
262
|
+
expect(resp.body).toEqual({ secondaryLanguages: null });
|
263
|
+
});
|
264
|
+
|
265
|
+
it('should update multiple settings', async () => {
|
266
|
+
const mockAppResults: any = {
|
267
|
+
rows: [
|
268
|
+
{ setting_name: 'secondaryLanguages', setting_options: ['fr', 'en', 'sk'] },
|
269
|
+
{ setting_name: 'theme', setting_options: ['light', 'dark', 'auto'] },
|
270
|
+
],
|
271
|
+
};
|
272
|
+
const mockUpdateSetting1: any = {
|
273
|
+
rows: [{ setting_name: 'secondaryLanguages', setting_value: 'fr' }],
|
274
|
+
};
|
275
|
+
const mockUpdateSetting2: any = {
|
276
|
+
rows: [{ setting_name: 'theme', setting_value: 'dark' }],
|
277
|
+
};
|
278
|
+
request.params = { userID: USER_ID, appID: 'general' };
|
279
|
+
request.query = {};
|
280
|
+
request.username = USER_ID;
|
281
|
+
request.body = { secondaryLanguages: 'fr', theme: 'light' };
|
282
|
+
cassandraDBHelpers.execute = jest.fn().mockResolvedValueOnce(mockAppResults);
|
283
|
+
cassandraDBHelpers.batch = jest
|
284
|
+
.fn()
|
285
|
+
.mockResolvedValueOnce(mockUpdateSetting1)
|
286
|
+
.mockResolvedValueOnce(mockUpdateSetting2);
|
287
|
+
response.send = test_send_with_body;
|
288
|
+
|
289
|
+
const resp: any = await postUserSettings(request, response);
|
290
|
+
|
291
|
+
expect(resp.code).toBe(200);
|
292
|
+
expect(resp.body.secondaryLanguages).toEqual('fr');
|
293
|
+
expect(resp.body.theme).toBe('light');
|
294
|
+
});
|
295
|
+
});
|
296
|
+
|
297
|
+
describe('delete user settings', () => {
|
298
|
+
afterEach(() => {
|
299
|
+
jest.clearAllMocks();
|
300
|
+
});
|
301
|
+
const request: Request = {} as Request;
|
302
|
+
const response: Response = {} as Response;
|
303
|
+
response.send = test_send;
|
304
|
+
|
305
|
+
const deleteSpy = jest.spyOn(SettingsRepoCassandra.prototype, 'deleteUserSettings');
|
306
|
+
|
307
|
+
it('should return 200 after deleting data', async () => {
|
308
|
+
request.params = { userId: USER_ID };
|
309
|
+
deleteSpy.mockResolvedValueOnce(true);
|
310
|
+
expect(await deleteUserSettings(request, response)).toEqual(200);
|
311
|
+
expect(deleteSpy).toHaveBeenCalled();
|
312
|
+
});
|
313
|
+
it('should return 400 if no userId', async () => {
|
314
|
+
request.params = {};
|
315
|
+
response.send = test_send;
|
316
|
+
expect(await deleteUserSettings(request, response)).toEqual(400);
|
317
|
+
expect(deleteSpy).not.toHaveBeenCalled();
|
318
|
+
});
|
319
|
+
it('internal server error should return 500 error', async () => {
|
320
|
+
request.params = { userId: USER_ID };
|
321
|
+
deleteSpy.mockImplementationOnce(() => {
|
322
|
+
throw new Error('Error in delete settings tests');
|
323
|
+
});
|
324
|
+
response.send = test_send;
|
325
|
+
expect(await deleteUserSettings(request, response)).toEqual(500);
|
326
|
+
expect(deleteSpy).toHaveBeenCalled();
|
327
|
+
});
|
328
|
+
});
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import { Request, Response } from 'restify';
|
2
|
+
import { getAllSettings } from '../../../controller/UserSettingsController';
|
3
|
+
import { SettingsRepoCassandra } from '../../../repo/settingsRepo';
|
4
|
+
import { TEST_APP_ID } from '../../utils/test-utils';
|
5
|
+
|
6
|
+
const mockAllSettingsResponse = {
|
7
|
+
settings: [
|
8
|
+
{
|
9
|
+
app_id: 'general',
|
10
|
+
setting_name: 'tags',
|
11
|
+
user_id: 'ABCDEFGHI',
|
12
|
+
setting_value: 'AI,IBM Consulting',
|
13
|
+
},
|
14
|
+
],
|
15
|
+
pageState: null,
|
16
|
+
};
|
17
|
+
|
18
|
+
function test_send_with_body(code?: number | undefined, body?: any): any {
|
19
|
+
return { code, body };
|
20
|
+
}
|
21
|
+
|
22
|
+
afterEach(() => {
|
23
|
+
jest.resetAllMocks();
|
24
|
+
});
|
25
|
+
|
26
|
+
describe('getAllSettings controller tests', () => {
|
27
|
+
// tslint:disable-next-line
|
28
|
+
const request: Request = {} as Request;
|
29
|
+
// tslint:disable
|
30
|
+
const response: Response = {} as Response;
|
31
|
+
response.send = test_send_with_body;
|
32
|
+
|
33
|
+
it('should return 200 after sucessfully retrieving data', async () => {
|
34
|
+
request.query = {};
|
35
|
+
const getAllSettingsSpy = jest
|
36
|
+
.spyOn(SettingsRepoCassandra.prototype, 'getAllSettings')
|
37
|
+
.mockResolvedValueOnce(mockAllSettingsResponse);
|
38
|
+
const resp = await getAllSettings(request, response);
|
39
|
+
|
40
|
+
expect(resp.code).toEqual(200);
|
41
|
+
expect(getAllSettingsSpy).toHaveBeenCalledTimes(1);
|
42
|
+
});
|
43
|
+
it('should return 400 if settingName but no appId', async () => {
|
44
|
+
request.query = { settingName: 'theme' };
|
45
|
+
const getAllSettingsSpy = jest
|
46
|
+
.spyOn(SettingsRepoCassandra.prototype, 'getAllSettings')
|
47
|
+
.mockResolvedValueOnce(mockAllSettingsResponse);
|
48
|
+
const resp = await getAllSettings(request, response);
|
49
|
+
|
50
|
+
expect(resp.code).toEqual(400);
|
51
|
+
expect(getAllSettingsSpy).not.toHaveBeenCalled();
|
52
|
+
});
|
53
|
+
it('should return 500 if error thrown in settingsRepo method', async () => {
|
54
|
+
request.query = {};
|
55
|
+
const getAllSettingsSpy = jest
|
56
|
+
.spyOn(SettingsRepoCassandra.prototype, 'getAllSettings')
|
57
|
+
.mockImplementationOnce(() => {
|
58
|
+
throw new Error('error from db');
|
59
|
+
});
|
60
|
+
const resp = await getAllSettings(request, response);
|
61
|
+
|
62
|
+
expect(resp.code).toEqual(500);
|
63
|
+
expect(getAllSettingsSpy).toHaveBeenCalledTimes(1);
|
64
|
+
});
|
65
|
+
|
66
|
+
it('should call getAllSettings method with query params from request', async () => {
|
67
|
+
const queryParams = {
|
68
|
+
pageState: 'test page state',
|
69
|
+
appId: TEST_APP_ID,
|
70
|
+
settingName: 'theme',
|
71
|
+
fetchSize: 10,
|
72
|
+
};
|
73
|
+
request.query = queryParams;
|
74
|
+
const getAllSettingsSpy = jest
|
75
|
+
.spyOn(SettingsRepoCassandra.prototype, 'getAllSettings')
|
76
|
+
.mockResolvedValueOnce(mockAllSettingsResponse);
|
77
|
+
const resp = await getAllSettings(request, response);
|
78
|
+
|
79
|
+
expect(resp.code).toEqual(200);
|
80
|
+
expect(getAllSettingsSpy).toHaveBeenCalledTimes(1);
|
81
|
+
expect(getAllSettingsSpy).toHaveBeenCalledWith(queryParams);
|
82
|
+
});
|
83
|
+
});
|
@@ -0,0 +1,282 @@
|
|
1
|
+
import requestPromise from 'request-promise';
|
2
|
+
import { Next, Response } from 'restify';
|
3
|
+
import { AuthenticationMiddleware } from '../../../middleware/AuthenticationMiddleware';
|
4
|
+
import { TEST_APP_ID } from '../../utils/test-utils';
|
5
|
+
|
6
|
+
jest.mock('axios');
|
7
|
+
jest.mock('request-promise');
|
8
|
+
|
9
|
+
const jwtResponseNextGenSSO = {
|
10
|
+
lastName: 'Kemp',
|
11
|
+
uniqueSecurityName: '5j1218897',
|
12
|
+
token_type: 'Bearer',
|
13
|
+
uid: '5j1218897',
|
14
|
+
emailAddress: 'Ann-Marie.Kemp@IBM.com',
|
15
|
+
grant_type: 'authorization_code',
|
16
|
+
scope: 'openid',
|
17
|
+
exp: 1607015356,
|
18
|
+
iat: 1607008156,
|
19
|
+
active: true,
|
20
|
+
firstName: 'Ann-Marie',
|
21
|
+
grant_id: '1e104aca-5c78-4c9b-b55a-1427e39b20da',
|
22
|
+
userType: 'federated',
|
23
|
+
category: 'application',
|
24
|
+
};
|
25
|
+
|
26
|
+
const JWT_TOKEN = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZ
|
27
|
+
CI6Im55ZW51bXVsQHVzLmlibS5jb20iLCJjbiI6Ik5hcmFzaW1oYSBSZWRkeSBZZW51bXVsYSIsInVpZCI6IkMtTDNDVjg5NyIsImlzVzNIb21lc`;
|
28
|
+
|
29
|
+
const W3ID_TOKEN = `Bearer Q3ccWQ4TZxK3bm6vnuOk5kQ9JApv7PCr8aLFxtKf`;
|
30
|
+
|
31
|
+
const API_KEY = `apiKey bm90aWZpY2F0aW9uX3Rlc3Q6NlhLY0pmZVpHQ2prczBXRFFwRzNYRHN3R0VySzlhTVRHT0dvZmZKcVhQa1dDZ1RZ`;
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Below are the test cases for Authentication Middleware NextGen SSO (ISV)
|
35
|
+
*/
|
36
|
+
describe('requestHandler', () => {
|
37
|
+
const route = { path: 'settings', spec: { authRequired: true } };
|
38
|
+
const req = {
|
39
|
+
getRoute: jest.fn().mockReturnValue(route) as any,
|
40
|
+
} as any;
|
41
|
+
const res = {} as any;
|
42
|
+
let respObj = {} as any;
|
43
|
+
let statusCode = 0;
|
44
|
+
res.send = (code: number, obj: any) => {
|
45
|
+
statusCode = code;
|
46
|
+
respObj = obj;
|
47
|
+
return obj;
|
48
|
+
};
|
49
|
+
const next = jest.fn() as Next;
|
50
|
+
|
51
|
+
afterEach(() => {
|
52
|
+
process.env.APP_ENVIRONMENT = 'dev';
|
53
|
+
jest.clearAllMocks();
|
54
|
+
});
|
55
|
+
|
56
|
+
/**
|
57
|
+
* This test case checks for valid test token in the headers present in the
|
58
|
+
* incoming request.
|
59
|
+
*/
|
60
|
+
it('authRequired is enabled and should call ensureAuthenticated', async () => {
|
61
|
+
req.headers = { authorization: W3ID_TOKEN };
|
62
|
+
const spy = jest.spyOn(AuthenticationMiddleware, 'ensureAuthenticated');
|
63
|
+
|
64
|
+
await AuthenticationMiddleware.checkAuthentication(req, res, next);
|
65
|
+
expect(spy).toHaveBeenCalled();
|
66
|
+
});
|
67
|
+
|
68
|
+
/**
|
69
|
+
* This test case handles the missing token in header scenario.
|
70
|
+
*/
|
71
|
+
it('missing authorization token in header', async () => {
|
72
|
+
req.headers = { authorization: 'Bearer' };
|
73
|
+
|
74
|
+
await AuthenticationMiddleware.checkAuthentication(req, res, next);
|
75
|
+
expect(statusCode).toEqual(401);
|
76
|
+
expect(respObj.code).toEqual(401);
|
77
|
+
expect(respObj.message).toEqual('Missing required authentication token');
|
78
|
+
expect(next).toHaveBeenCalled();
|
79
|
+
});
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Test case which covers success behavior from JWT introspect.
|
83
|
+
*/
|
84
|
+
it('success response case from JWT introspect', async () => {
|
85
|
+
(requestPromise.post as jest.Mock).mockResolvedValue(JSON.stringify(jwtResponseNextGenSSO));
|
86
|
+
|
87
|
+
const response = await AuthenticationMiddleware.introspectISV(res, JWT_TOKEN);
|
88
|
+
expect(response).not.toBeNull();
|
89
|
+
expect(JSON.parse(response).uid).toEqual(jwtResponseNextGenSSO.uid);
|
90
|
+
});
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Test case which covers behavior when exception occurs while validating JWT token.
|
94
|
+
*/
|
95
|
+
it('calls next with false when exception occurs while validating JWT token in ensureAuthenticated()', async () => {
|
96
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}` };
|
97
|
+
AuthenticationMiddleware.introspectISV = jest.fn().mockRejectedValueOnce(new Error('Invalid JWT token'));
|
98
|
+
|
99
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
100
|
+
expect(next).toHaveBeenCalledWith(false);
|
101
|
+
});
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Test case which covers behavior userId not found in JWT response.
|
105
|
+
*/
|
106
|
+
it('Expired JWT token - ensureAuthenticated()', async () => {
|
107
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}` };
|
108
|
+
AuthenticationMiddleware.introspectISV = jest.fn().mockResolvedValueOnce('{ "active": false }');
|
109
|
+
|
110
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
111
|
+
expect(statusCode).toEqual(401);
|
112
|
+
expect(respObj.code).toEqual(401);
|
113
|
+
expect(respObj.message).toEqual('User token is expired or is invalid');
|
114
|
+
expect(next).toHaveBeenCalled();
|
115
|
+
});
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Test case for userId not found in JWT response.
|
119
|
+
*/
|
120
|
+
it('success case validation of JWT token- ensureAuthenticated()', async () => {
|
121
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}` };
|
122
|
+
AuthenticationMiddleware.introspectISV = jest
|
123
|
+
.fn()
|
124
|
+
.mockResolvedValueOnce(JSON.stringify(jwtResponseNextGenSSO));
|
125
|
+
|
126
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
127
|
+
expect(req.username).toEqual(jwtResponseNextGenSSO.uid);
|
128
|
+
expect(req.isAuthenticated).toEqual(true);
|
129
|
+
expect(next).toHaveBeenCalled();
|
130
|
+
});
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Test case which covers error thrown in from JWT introspect v1.
|
134
|
+
*/
|
135
|
+
it('Error thrown from introspect endpoint invocation', async () => {
|
136
|
+
(requestPromise.post as jest.Mock).mockRejectedValueOnce('Error from unit tests');
|
137
|
+
try {
|
138
|
+
await AuthenticationMiddleware.introspectISV(res, JWT_TOKEN);
|
139
|
+
} catch (err) {
|
140
|
+
expect(err).toBe('Unable to authenticate the user');
|
141
|
+
}
|
142
|
+
});
|
143
|
+
});
|
144
|
+
|
145
|
+
describe('requestHandler with header regression tests', () => {
|
146
|
+
const route = { path: 'settings', spec: { authRequired: true } };
|
147
|
+
const req = {
|
148
|
+
getRoute: jest.fn().mockReturnValue(route) as any,
|
149
|
+
} as any;
|
150
|
+
const res = {} as any;
|
151
|
+
let respObj = {} as any;
|
152
|
+
let statusCode = 0;
|
153
|
+
res.send = (code: number, obj: any) => {
|
154
|
+
statusCode = code;
|
155
|
+
respObj = obj;
|
156
|
+
return obj;
|
157
|
+
};
|
158
|
+
const next = jest.fn() as Next;
|
159
|
+
|
160
|
+
afterEach(() => {
|
161
|
+
process.env.APP_ENVIRONMENT = 'dev';
|
162
|
+
jest.clearAllMocks();
|
163
|
+
});
|
164
|
+
|
165
|
+
/**
|
166
|
+
* This test case checks for valid test token in the headers present in the
|
167
|
+
* incoming request.
|
168
|
+
*/
|
169
|
+
it('authRequired is enabled and should call ensureAuthenticated', async () => {
|
170
|
+
req.headers = { authorization: W3ID_TOKEN, nextgen_sso: true };
|
171
|
+
const spy = jest.spyOn(AuthenticationMiddleware, 'ensureAuthenticated');
|
172
|
+
|
173
|
+
await AuthenticationMiddleware.checkAuthentication(req, res, next);
|
174
|
+
expect(spy).toHaveBeenCalled();
|
175
|
+
});
|
176
|
+
|
177
|
+
/**
|
178
|
+
* This test case handles the missing token in header scenario.
|
179
|
+
*/
|
180
|
+
it('missing authorization token in header', async () => {
|
181
|
+
req.headers = { authorization: 'Bearer', nextgen_sso: true };
|
182
|
+
|
183
|
+
await AuthenticationMiddleware.checkAuthentication(req, res, next);
|
184
|
+
expect(statusCode).toEqual(401);
|
185
|
+
expect(respObj.code).toEqual(401);
|
186
|
+
expect(respObj.message).toEqual('Missing required authentication token');
|
187
|
+
expect(next).toHaveBeenCalled();
|
188
|
+
});
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Test case which covers behavior when exception occurs while validating JWT token.
|
192
|
+
*/
|
193
|
+
it('calls next with false when exception occurs while validating JWT token in ensureAuthenticated()', async () => {
|
194
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}`, nextgen_sso: true };
|
195
|
+
AuthenticationMiddleware.introspectISV = jest.fn().mockRejectedValueOnce(new Error('Invalid JWT token'));
|
196
|
+
|
197
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
198
|
+
expect(next).toHaveBeenCalledWith(false);
|
199
|
+
});
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Test case which covers behavior userId not found in JWT response.
|
203
|
+
*/
|
204
|
+
it('Expired JWT token - ensureAuthenticated()', async () => {
|
205
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}`, nextgen_sso: true };
|
206
|
+
AuthenticationMiddleware.introspectISV = jest.fn().mockResolvedValueOnce('{ "active": false }');
|
207
|
+
|
208
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
209
|
+
expect(statusCode).toEqual(401);
|
210
|
+
expect(respObj.code).toEqual(401);
|
211
|
+
expect(respObj.message).toEqual('User token is expired or is invalid');
|
212
|
+
expect(next).toHaveBeenCalled();
|
213
|
+
});
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Test case for userId not found in JWT response.
|
217
|
+
*/
|
218
|
+
it('success case validation of JWT token- ensureAuthenticated()', async () => {
|
219
|
+
req.headers = { authorization: `Bearer ${JWT_TOKEN}`, nextgen_sso: true };
|
220
|
+
AuthenticationMiddleware.introspectISV = jest
|
221
|
+
.fn()
|
222
|
+
.mockResolvedValueOnce(JSON.stringify(jwtResponseNextGenSSO));
|
223
|
+
|
224
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
225
|
+
expect(req.username).toEqual(jwtResponseNextGenSSO.uid);
|
226
|
+
expect(req.isAuthenticated).toEqual(true);
|
227
|
+
expect(next).toHaveBeenCalled();
|
228
|
+
});
|
229
|
+
});
|
230
|
+
|
231
|
+
describe('api key tests - regression tests', () => {
|
232
|
+
const req = {} as any;
|
233
|
+
const res: Response = {} as any;
|
234
|
+
let statusCode = 0;
|
235
|
+
res.send = (code: number, obj) => {
|
236
|
+
statusCode = code;
|
237
|
+
return obj;
|
238
|
+
};
|
239
|
+
const next = jest.fn() as Next;
|
240
|
+
req.params = { appID: TEST_APP_ID };
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Below test case checks for the successful api key verification from ensureAuthenticated method
|
244
|
+
*/
|
245
|
+
it('ensureAuthenticated with right API key', async () => {
|
246
|
+
req.headers = { authorization: API_KEY, nextgen_sso: true, appId: 'w3notifications-test' };
|
247
|
+
AuthenticationMiddleware.verifyApiKeyWithAppId = jest.fn().mockReturnValue(true);
|
248
|
+
|
249
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
250
|
+
expect(req.username).toBeUndefined();
|
251
|
+
expect(req.isAuthenticated).toEqual(true);
|
252
|
+
expect(req.auth_policy).toEqual(AuthenticationMiddleware.AUTH_POLICY_API_KEY);
|
253
|
+
expect(next).toHaveBeenCalled();
|
254
|
+
});
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Below test case checks for the invalid api key verification from ensureAuthenticated
|
258
|
+
*/
|
259
|
+
it('ensureAuthenticated with invalid API key', async () => {
|
260
|
+
req.headers = { authorization: API_KEY, nextgen_sso: true };
|
261
|
+
AuthenticationMiddleware.verifyApiKeyWithAppId = jest.fn().mockReturnValue(false);
|
262
|
+
|
263
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
264
|
+
expect(req.username).toBeUndefined();
|
265
|
+
expect(statusCode).toEqual(401);
|
266
|
+
expect(next).toHaveBeenCalled();
|
267
|
+
});
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Below test case checks for the case of invalid api key from ensureAuthenticated method
|
271
|
+
*/
|
272
|
+
it('failure case validation of API key', async () => {
|
273
|
+
req.headers = { authorization: API_KEY, nextgen_sso: true };
|
274
|
+
AuthenticationMiddleware.verifyApiKeyWithAppId = jest.fn().mockImplementation(() => {
|
275
|
+
throw new Error('Unable to verify API key');
|
276
|
+
});
|
277
|
+
|
278
|
+
await AuthenticationMiddleware.ensureAuthenticated(req, res, next);
|
279
|
+
expect(next).toHaveBeenCalledWith(false);
|
280
|
+
jest.clearAllMocks();
|
281
|
+
});
|
282
|
+
});
|