@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.

Files changed (98) hide show
  1. package/package.json +9 -3
  2. package/settings-service/.eslintrc.js +4 -0
  3. package/settings-service/.node-version +1 -0
  4. package/settings-service/.prettierrc +6 -0
  5. package/settings-service/.whitesource +15 -0
  6. package/settings-service/LICENSE +4 -0
  7. package/settings-service/README.md +50 -0
  8. package/settings-service/build.yml +56 -0
  9. package/settings-service/collectCodeCoverage.js +9 -0
  10. package/settings-service/db/cassandra/Dockerfile +3 -0
  11. package/settings-service/db/cassandra/createkeyspace.dev.cql +4 -0
  12. package/settings-service/db/cassandra/createkeyspace.dev.sh +3 -0
  13. package/settings-service/db/cassandra/schema_001.cql +15 -0
  14. package/settings-service/db/cassandra/schema_002.cql +8 -0
  15. package/settings-service/db/cassandra/schema_003.cql +10 -0
  16. package/settings-service/db/cassandra/schema_004.cql +1 -0
  17. package/settings-service/db/cassandra/schema_005.cql +39 -0
  18. package/settings-service/db/cassandra/schema_006.cql +255 -0
  19. package/settings-service/db/cassandra/schema_007.cql +40 -0
  20. package/settings-service/db/cassandra/schema_008.cql +2 -0
  21. package/settings-service/db/cassandra/schema_009.cql +143 -0
  22. package/settings-service/db/cassandra/schema_010.cql +143 -0
  23. package/settings-service/db/cassandra/schema_011.cql +2 -0
  24. package/settings-service/db/cassandra/schema_012.cql +8 -0
  25. package/settings-service/jest.config.fn.js +3 -0
  26. package/settings-service/jest.config.it.js +3 -0
  27. package/settings-service/jest.config.js +14 -0
  28. package/settings-service/jest.config.unit.js +19 -0
  29. package/settings-service/jest.setup.js +11 -0
  30. package/settings-service/package-lock.json +11772 -0
  31. package/settings-service/package.json +101 -0
  32. package/settings-service/scripts/run-fn-tests.sh +3 -0
  33. package/settings-service/sonar-project.properties +3 -0
  34. package/settings-service/src/__tests__/functional/controller/ApiKeyController.fn.ts +132 -0
  35. package/settings-service/src/__tests__/functional/middleware/AuthMiddlewareNextGenSSO.fn.ts +82 -0
  36. package/settings-service/src/__tests__/functional/repo/settingsRepo.fn.ts +302 -0
  37. package/settings-service/src/__tests__/functional/unified-profile/unified-profile.fn.ts +66 -0
  38. package/settings-service/src/__tests__/integration/repo/ApiKeyRepo.it.ts +43 -0
  39. package/settings-service/src/__tests__/integration/repo/settingsRepo.it.ts +142 -0
  40. package/settings-service/src/__tests__/integration/unified-profile/unified-profile.it.ts +31 -0
  41. package/settings-service/src/__tests__/unit/ErrResponse.ts +4 -0
  42. package/settings-service/src/__tests__/unit/JWTResponse.ts +18 -0
  43. package/settings-service/src/__tests__/unit/bluepagesResponse.ts +25 -0
  44. package/settings-service/src/__tests__/unit/controller/ApiKeyController.spec.ts +217 -0
  45. package/settings-service/src/__tests__/unit/controller/AppSettingsController.spec.ts +133 -0
  46. package/settings-service/src/__tests__/unit/controller/UserSettingsController.spec.ts +328 -0
  47. package/settings-service/src/__tests__/unit/controller/getAllSettings.spec.ts +83 -0
  48. package/settings-service/src/__tests__/unit/middleware/AuthMiddlewareNextGenSSO.spec.ts +282 -0
  49. package/settings-service/src/__tests__/unit/middleware/AuthenticationMiddleware.spec.ts +494 -0
  50. package/settings-service/src/__tests__/unit/repo/ApiKeyRepo.spec.ts +194 -0
  51. package/settings-service/src/__tests__/unit/repo/getAllSettings.spec.ts +100 -0
  52. package/settings-service/src/__tests__/unit/repo/getUserSettingsRepo.spec.ts +249 -0
  53. package/settings-service/src/__tests__/unit/repo/settingsRepo.spec.ts +614 -0
  54. package/settings-service/src/__tests__/unit/unified-profile/UnifiedProfileClient.spec.ts +31 -0
  55. package/settings-service/src/__tests__/unit/unified-profile/unifiedProfileUtils.spec.ts +36 -0
  56. package/settings-service/src/__tests__/utils/test-utils.ts +41 -0
  57. package/settings-service/src/config/config.ts +190 -0
  58. package/settings-service/src/controller/ApiKeyController.ts +114 -0
  59. package/settings-service/src/controller/AppSettingsController.ts +137 -0
  60. package/settings-service/src/controller/UserSettingsController.ts +202 -0
  61. package/settings-service/src/helpers/commons.ts +69 -0
  62. package/settings-service/src/logger/logger.ts +17 -0
  63. package/settings-service/src/middleware/AuthenticationMiddleware.ts +486 -0
  64. package/settings-service/src/middleware/AuthenticationMiddlewareFactory.ts +10 -0
  65. package/settings-service/src/repo/ApiKeyRepo.ts +135 -0
  66. package/settings-service/src/repo/ApiKeyRepoFactory.ts +10 -0
  67. package/settings-service/src/repo/CassandraClient.ts +33 -0
  68. package/settings-service/src/repo/CassandraClientFactory.ts +11 -0
  69. package/settings-service/src/repo/apiKeyQueries.ts +64 -0
  70. package/settings-service/src/repo/cassandraDBHelpers.ts +119 -0
  71. package/settings-service/src/repo/settingsRepo.ts +388 -0
  72. package/settings-service/src/repo/settingsRepoFactory.ts +10 -0
  73. package/settings-service/src/repo/settingsRepoQueries.ts +62 -0
  74. package/settings-service/src/routes/apiKeyRoutes.ts +27 -0
  75. package/settings-service/src/routes/appSettingsRoutes.ts +30 -0
  76. package/settings-service/src/routes/healthCheck.ts +10 -0
  77. package/settings-service/src/routes/swagger.ts +8 -0
  78. package/settings-service/src/routes/userSettingsRoutes.ts +30 -0
  79. package/settings-service/src/server.ts +77 -0
  80. package/settings-service/src/swagger.json +732 -0
  81. package/settings-service/src/types/ApiKey.ts +19 -0
  82. package/settings-service/src/types/IRequest.ts +9 -0
  83. package/settings-service/src/types/IRequestAuthorization.ts +5 -0
  84. package/settings-service/src/types/IRouteOptions.ts +5 -0
  85. package/settings-service/src/types/QueryResultsTypes.ts +6 -0
  86. package/settings-service/src/types/UserSettingsControllerTypes.ts +5 -0
  87. package/settings-service/src/types/W3IdUser.ts +36 -0
  88. package/settings-service/src/types/settingsRepoTypes.ts +61 -0
  89. package/settings-service/src/types/unifiedProfileTypes.ts +10 -0
  90. package/settings-service/src/unified-profile/UnifiedProfileClient.ts +29 -0
  91. package/settings-service/src/unified-profile/UnifiedProfileClientFactory.ts +10 -0
  92. package/settings-service/src/unified-profile/unifiedProfileUtils.ts +22 -0
  93. package/settings-service/src/util/downloadCassandra.ts +34 -0
  94. package/settings-service/src/util/isocodeMapper.ts +22 -0
  95. package/settings-service/src/util/languages.ts +1457 -0
  96. package/settings-service/test_resources/mockApiKeyDBResult.json +8 -0
  97. package/settings-service/tsconfig.json +40 -0
  98. package/README.md +0 -5
@@ -0,0 +1,41 @@
1
+ import { logger } from '../../logger/logger';
2
+ import { ApiKey } from '../../types/ApiKey';
3
+
4
+ export interface FakeUserInfo {
5
+ id: string;
6
+ email: string;
7
+ }
8
+ const CLASS_NAME = 'fn-tests-utils';
9
+
10
+ export const sampleAuthorization: FakeUserInfo = {
11
+ email: 'ann-marie.kemp@us.ibm.com',
12
+ id: '5J1218897',
13
+ };
14
+
15
+ export const TEST_USER_ID = sampleAuthorization.id;
16
+ export const TEST_API_KEY = 'soLTYIBqsR1Jb9foASn4paDUymBkGnvT4dtWjvL7PajCLmc8';
17
+ export const mockApiKey: ApiKey = {
18
+ appId: 'w3notifications-test',
19
+ key: TEST_API_KEY,
20
+ createdBy: 'unit-test',
21
+ createdDate: new Date('2020-10-15T23:41:15.314Z'),
22
+ updatedDate: new Date(),
23
+ };
24
+
25
+ export const INVALID_APP_ID = 'invalidApp';
26
+ export const TEST_APP_ID = 'w3notifications-test';
27
+
28
+ export async function delay(ms: number): Promise<void> {
29
+ await new Promise<void>((resolve: (value?: void) => void): void => {
30
+ setTimeout(() => resolve(), ms);
31
+ });
32
+ }
33
+
34
+ export function getRestURL(path: string): string {
35
+ const url: string =
36
+ process.env.INGRESS_HOSTNAME !== undefined
37
+ ? `https://${process.env.INGRESS_HOSTNAME}/${path}`
38
+ : `http://localhost:8000/${path}`;
39
+ logger.info(`${CLASS_NAME} | ${getRestURL.name}() | Creating test REST url: ${url}`);
40
+ return url;
41
+ }
@@ -0,0 +1,190 @@
1
+ import * as dotenvconfig from 'dotenv';
2
+ import * as fs from 'fs';
3
+
4
+ const packageMetaData = JSON.parse(fs.readFileSync('package.json', 'utf8'));
5
+
6
+ /**
7
+ * The configuration variables understood by the service.
8
+ *
9
+ * @export
10
+ * @interface Config
11
+ */
12
+ export interface Config {
13
+ /**
14
+ * The port the rest service listens on.
15
+ *
16
+ * @type {number}
17
+ * @memberOf Config
18
+ */
19
+ apiPort: number;
20
+
21
+ /**
22
+ * The api version used by the process.
23
+ *
24
+ * @type {string}
25
+ * @memberOf Config
26
+ */
27
+ apiVersion: string;
28
+
29
+ /**
30
+ * Client id used by the authentication service to authenticate User token using IBM Security Verify.
31
+ *
32
+ * @type {string}
33
+ * @memberOf Config
34
+ */
35
+ isv_clientId: string;
36
+
37
+ /**
38
+ * Client secret used by the authentication service to authenticate User token using IBM Security Verify.
39
+ *
40
+ * @type {string}
41
+ * @memberOf Config
42
+ */
43
+ isv_clientSecret: string;
44
+
45
+ /**
46
+ * The w3id introspect URL used by the authentication service to authenticate User credential (token).
47
+ *
48
+ * @type {string}
49
+ * @memberOf Config
50
+ */
51
+ isv_introspectionUrl: string;
52
+
53
+ /**
54
+ * The logging level used by the process.
55
+ *
56
+ * @type {string}
57
+ * @memberOf Config
58
+ */
59
+ logLevel: string;
60
+
61
+ /**
62
+ * Comma separated User ids authorized to request api key
63
+ *
64
+ * @type {string}
65
+ * @memberOf Config
66
+ */
67
+ apiKeyAuthorizedUserIds: string;
68
+
69
+ /**
70
+ * Secret key for AES encryption of api key
71
+ *
72
+ * @type {string}
73
+ * @memberOf Config
74
+ */
75
+ aesEncryptionKey: string;
76
+
77
+ /**
78
+ * list of whitelisted appIds for restricted actions
79
+ *
80
+ * @type {string}
81
+ * @memberOf Config
82
+ */
83
+ restrictedAppIdWhitelist: string[];
84
+
85
+ /**
86
+ * list of valid onboarded appIds
87
+ *
88
+ * @type {string}
89
+ * @memberOf Config
90
+ */
91
+ settingsAppIds: string[];
92
+
93
+ /**
94
+ * cassandra username
95
+ *
96
+ * @type {string}
97
+ * @memberOf Config
98
+ */
99
+ cassandraUsername: string;
100
+
101
+ /**
102
+ * cassandra password
103
+ *
104
+ * @type {string}
105
+ * @memberOf Config
106
+ */
107
+ cassandraPassword: string;
108
+
109
+ /**
110
+ * cassandra certs path
111
+ *
112
+ * @type {string}
113
+ * @memberOf Config
114
+ */
115
+ cassandraCertsURL: string;
116
+
117
+ /**
118
+ * cassandra keyspace
119
+ *
120
+ * @type {string}
121
+ * @memberOf Config
122
+ */
123
+ cassandraKeyspace: string;
124
+
125
+ /**
126
+ * local file path to store cassandra zip file
127
+ *
128
+ * @type {string}
129
+ * @memberOf Config
130
+ */
131
+ cassandraCertsFilePath: string;
132
+
133
+ /**
134
+ * local host for cassandra database
135
+ *
136
+ * @type {string}
137
+ * @memberOf Config
138
+ */
139
+ cassandraLocalHost: string;
140
+
141
+ /**
142
+ * keyspace name for local cassandra database
143
+ *
144
+ * @type {string}
145
+ * @memberOf Config
146
+ */
147
+ cassandraLocalKeyspace: string;
148
+ }
149
+
150
+ dotenvconfig.config();
151
+ /**
152
+ * Export the configuration.
153
+ */
154
+ export const settings: Config = {
155
+ apiPort: process.env.APP_REST_PORT ? Number(process.env.APP_REST_PORT) : 8000,
156
+ apiVersion: process.env.APP_VERSION ? process.env.APP_VERSION : packageMetaData.version,
157
+ isv_clientId:
158
+ process.env.NODE_ENV === 'production'
159
+ ? process.env.ISV_CLIENT_ID_PRODUCTION || ''
160
+ : process.env.ISV_CLIENT_ID_STAGE || '',
161
+ isv_clientSecret:
162
+ process.env.NODE_ENV === 'production'
163
+ ? process.env.ISV_CLIENT_SECRET_PRODUCTION || ''
164
+ : process.env.ISV_CLIENT_SECRET_STAGE || '',
165
+ isv_introspectionUrl: process.env.ISV_INTROSPECTION_URL || '',
166
+ logLevel: process.env.APP_LOG_LEVEL || 'info',
167
+ apiKeyAuthorizedUserIds: process.env.API_KEY_AUTHORIZED_IDS || '',
168
+ aesEncryptionKey: process.env.AES_ENCRYPTION_KEY || '',
169
+ restrictedAppIdWhitelist: process.env.RESTRICTED_APP_ID_WHITELIST?.split(',') || [],
170
+ settingsAppIds: process.env.SETTINGS_APP_IDS?.split(',') || [],
171
+ cassandraUsername:
172
+ process.env.NODE_ENV === 'production'
173
+ ? process.env.CASSANDRA_USERNAME_PROD || ''
174
+ : process.env.CASSANDRA_USERNAME_PREPROD || '',
175
+ cassandraPassword:
176
+ process.env.NODE_ENV === 'production'
177
+ ? process.env.CASSANDRA_PASSWORD_PROD || ''
178
+ : process.env.CASSANDRA_PASSWORD_PREPROD || '',
179
+ cassandraCertsURL:
180
+ process.env.NODE_ENV === 'production'
181
+ ? process.env.CASSANDRA_CERTS_URL_PROD || ''
182
+ : process.env.CASSANDRA_CERTS_URL_PREPROD || '',
183
+ cassandraKeyspace:
184
+ process.env.NODE_ENV === 'production'
185
+ ? process.env.CASSANDRA_KEYSPACE_PROD || 'w3_settings_prod'
186
+ : process.env.CASSANDRA_KEYSPACE_PREPROD || 'w3_settings_preprod',
187
+ cassandraCertsFilePath: process.env.CASSANDRA_CERTS_FILE_PATH || '',
188
+ cassandraLocalHost: process.env.CASSANDRA_HOST || 'localhost:9042',
189
+ cassandraLocalKeyspace: process.env.CASSANDRA_LOCAL_KEYSPACE || 'w3_settings',
190
+ };
@@ -0,0 +1,114 @@
1
+ import { Request, Response } from 'restify';
2
+ import { ApiKeyRepoFactory } from '../repo/ApiKeyRepoFactory';
3
+ import { settings } from '../config/config';
4
+ import { logger } from '../logger/logger';
5
+ import { ApiKeyRepo } from '../repo/ApiKeyRepo';
6
+
7
+ const CLASS_NAME = 'ApiKeyController';
8
+ const apiKeyRepo: ApiKeyRepo = ApiKeyRepoFactory.accessOrCreateSingleton();
9
+
10
+ enum OperationType {
11
+ GET = 'GET',
12
+ POST = 'POST',
13
+ PUT = 'PUT',
14
+ }
15
+
16
+ /**
17
+ * This method validates required mandatory paramters.
18
+ *
19
+ * @param request
20
+ * @param type
21
+ */
22
+ function validateApiKeyRequestParams(request: Request, type: OperationType): string | undefined {
23
+ const appId: string = request.params.appID;
24
+ const createdBy: string | undefined = request.username;
25
+ const updatedBy: string | undefined = request.username;
26
+ if (!appId) {
27
+ return 'Missing required query params.';
28
+ }
29
+ if (type === OperationType.POST && !createdBy) {
30
+ return 'Missing request username';
31
+ }
32
+ if (type === OperationType.PUT && !updatedBy) {
33
+ return 'Missing request username';
34
+ }
35
+ if (!settings.settingsAppIds.includes(appId)) {
36
+ logger.debug(`${CLASS_NAME} | ${validateApiKeyRequestParams.name}() | received appId: ${appId}}`);
37
+ return 'Invalid appID';
38
+ }
39
+ return undefined;
40
+ }
41
+
42
+ /**
43
+ * This is the controller method to get an api key
44
+ *
45
+ * @param request
46
+ * @param response
47
+ * @param next
48
+ */
49
+ export async function fetchApiKey(request: Request, response: Response): Promise<Response> {
50
+ try {
51
+ const status: string | undefined = validateApiKeyRequestParams(request, OperationType.GET);
52
+ if (status === undefined) {
53
+ const appId: string = request.params.appID;
54
+
55
+ logger.debug(`${CLASS_NAME} | ${fetchApiKey.name}() | Getting Api Key...`);
56
+
57
+ const result = await apiKeyRepo.getApiKey(appId);
58
+ return response.send(200, result);
59
+ }
60
+ return response.send(400, { code: 400, message: status });
61
+ } catch (err) {
62
+ return response.send(500, { code: 500, message: 'Unable to fetch Api Key' });
63
+ }
64
+ }
65
+
66
+ /**
67
+ * This the controller method to create an api key
68
+ * @param request
69
+ * @param response
70
+ * @param next
71
+ */
72
+ export async function postApiKey(request: Request, response: Response): Promise<Response> {
73
+ try {
74
+ const status: string | undefined = validateApiKeyRequestParams(request, OperationType.POST);
75
+ if (status === undefined && request.username) {
76
+ const appId: string = request.params.appID;
77
+ const { username } = request;
78
+
79
+ logger.debug(`${CLASS_NAME} | ${postApiKey.name}() | create Api Key...`);
80
+
81
+ const result = await apiKeyRepo.createApiKey(appId, username);
82
+
83
+ return response.send(200, { key: result });
84
+ }
85
+ return response.send(400, { code: 400, message: status });
86
+ } catch (err) {
87
+ return response.send(500, { code: 500, message: 'Unable to create api key' });
88
+ }
89
+ }
90
+
91
+ /**
92
+ * This the controller method to create an api key
93
+ * @param request
94
+ * @param response
95
+ * @param next
96
+ */
97
+ export async function putApiKey(request: Request, response: Response): Promise<Response> {
98
+ try {
99
+ const status: string | undefined = validateApiKeyRequestParams(request, OperationType.PUT);
100
+ if (status === undefined && request.username) {
101
+ const appId: string = request.params.appID;
102
+ const { username } = request;
103
+
104
+ logger.debug(`${CLASS_NAME} | ${putApiKey.name}() | Updating Api Key...`);
105
+
106
+ const result = await apiKeyRepo.updateApiKey(appId, username);
107
+
108
+ return response.send(200, { key: result });
109
+ }
110
+ return response.send(400, { code: 400, message: status });
111
+ } catch (err) {
112
+ return response.send(500, { code: 500, message: 'Unable to update api key' });
113
+ }
114
+ }
@@ -0,0 +1,137 @@
1
+ import { Request, Response } from 'restify';
2
+ import { AppSettings } from '../types/settingsRepoTypes';
3
+ import { settings } from '../config/config';
4
+ import { logger } from '../logger/logger';
5
+ import { SettingsRepo } from '../repo/settingsRepo';
6
+ import { SettingsRepoFactory } from '../repo/settingsRepoFactory';
7
+
8
+ const CLASS_NAME = 'AppSettingsController';
9
+ const settingsRepo: SettingsRepo = SettingsRepoFactory.accessOrCreateSingleton();
10
+
11
+ enum OperationType {
12
+ GET = 'GET',
13
+ POST = 'POST',
14
+ DELETE = 'DELETE',
15
+ }
16
+
17
+ /**
18
+ * This method validates required mandatory parameters.
19
+ *
20
+ * @param req
21
+ * @param res
22
+ */
23
+ function validateRequestParams(request: Request, type: OperationType): string | undefined {
24
+ const appId: string = request.params.appID;
25
+ const { settingName } = request.params;
26
+ if (!appId || (type !== OperationType.GET && !settingName)) {
27
+ return 'Missing required query params.';
28
+ }
29
+ if (!settings.settingsAppIds.includes(appId)) {
30
+ return 'Invalid appID';
31
+ }
32
+ return undefined;
33
+ }
34
+
35
+ /**
36
+ * This is the controller method to get app settings.
37
+ *
38
+ * @param request
39
+ * @param response
40
+ * @param next
41
+ */
42
+ export async function getAppSettings(request: Request, response: Response): Promise<Response> {
43
+ try {
44
+ const status = validateRequestParams(request, OperationType.GET);
45
+ if (status === undefined) {
46
+ const appId: string = request.params.appID;
47
+ const { settingName } = request.params;
48
+
49
+ logger.debug(`${CLASS_NAME} | ${getAppSettings.name}() | Getting App Settings`);
50
+
51
+ const result = await settingsRepo.getAppSettings(appId, settingName);
52
+ return response.send(200, result);
53
+ }
54
+ return response.send(400, { code: 400, message: status });
55
+ } catch (err) {
56
+ return response.send(500, { code: 500, message: 'Unable to fetch app settings' });
57
+ }
58
+ }
59
+
60
+ /**
61
+ * This is the controller method to update app settings.
62
+ *
63
+ * @param request
64
+ * @param response
65
+ * @param next
66
+ */
67
+ export async function postAppSettings(request: Request, response: Response): Promise<Response> {
68
+ try {
69
+ const status = validateRequestParams(request, OperationType.POST);
70
+ if (status === undefined) {
71
+ const appId: string = request.params.appID;
72
+ const { settingName } = request.params;
73
+
74
+ const settingsPayload: AppSettings = request.body;
75
+ const {
76
+ default: settingDefault,
77
+ options: settingOptionsList,
78
+ deprecated_values: deprecatedValuesFromRequest,
79
+ } = settingsPayload;
80
+
81
+ if (settingOptionsList == null) {
82
+ return response.send(500, { code: 500, message: 'Invalid JSON body' });
83
+ }
84
+ if (settingDefault && !settingOptionsList.includes(settingDefault)) {
85
+ return response.send(500, { code: 500, message: 'Invalid Default Value' });
86
+ }
87
+ const settingType = settingsPayload.setting_type;
88
+ if (![null, 'array', 'string'].includes(settingType)) {
89
+ return response.send(400, { code: 400, message: 'Invalid setting_type' });
90
+ }
91
+ if (deprecatedValuesFromRequest && typeof deprecatedValuesFromRequest !== 'object') {
92
+ return response.send(400, { code: 400, message: 'deprecated values must be an object' });
93
+ }
94
+ const deprecatedValuesToUpdate = JSON.stringify(deprecatedValuesFromRequest);
95
+
96
+ logger.debug(`${CLASS_NAME} | ${postAppSettings.name}() | Updating App Settings`);
97
+ await settingsRepo.updateAppSettings({
98
+ appId,
99
+ settingName,
100
+ settingDefault,
101
+ settingType,
102
+ deprecatedValues: deprecatedValuesToUpdate,
103
+ settingOptions: settingOptionsList,
104
+ });
105
+ return response.send(200, 'Success');
106
+ }
107
+ return response.send(400, { code: 400, message: status });
108
+ } catch (err) {
109
+ return response.send(500, { code: 500, message: 'Unable to update app settings' });
110
+ }
111
+ }
112
+
113
+ /**
114
+ * This is the controller method to delete app settings.
115
+ *
116
+ * @param request
117
+ * @param response
118
+ * @param next
119
+ */
120
+ export async function deleteAppSettings(request: Request, response: Response): Promise<Response> {
121
+ try {
122
+ const status = validateRequestParams(request, OperationType.DELETE);
123
+ if (status === undefined) {
124
+ const appId: string = request.params.appID;
125
+ const { settingName } = request.params;
126
+
127
+ logger.debug(`${CLASS_NAME} | ${deleteAppSettings.name}() | Updating App Settings`);
128
+
129
+ await settingsRepo.deleteAppSettings(appId, settingName);
130
+
131
+ return response.send(200, 'Success');
132
+ }
133
+ return response.send(400, { code: 400, message: status });
134
+ } catch (err) {
135
+ return response.send(500, { code: 500, message: 'Unable to delete app settings' });
136
+ }
137
+ }
@@ -0,0 +1,202 @@
1
+ import _ from 'lodash';
2
+ import { Request, Response } from 'restify';
3
+ import { UserSettingsResult } from 'types/UserSettingsControllerTypes';
4
+ import { SettingsRepoFactory } from '../repo/settingsRepoFactory';
5
+ import { settings } from '../config/config';
6
+ import { logger } from '../logger/logger';
7
+ import { SettingsRepo } from '../repo/settingsRepo';
8
+
9
+ const CLASS_NAME = 'UserSettingsController';
10
+ const settingsRepo: SettingsRepo = SettingsRepoFactory.accessOrCreateSingleton();
11
+
12
+ /**
13
+ * This method validates required mandatory parameters.
14
+ *
15
+ * @param req
16
+ * @param res
17
+ */
18
+ function validateRequestParams(request: Request): string | undefined {
19
+ const appId: string = request.params.appID;
20
+ const userId: string = request.params.userID;
21
+ if (!appId || !userId) {
22
+ return 'Missing required query params.';
23
+ }
24
+ if (!settings.settingsAppIds.includes(appId)) {
25
+ return 'Invalid appID';
26
+ }
27
+ return undefined;
28
+ }
29
+
30
+ /**
31
+ * This is the controller method to get user settings.
32
+ *
33
+ * @param request
34
+ * @param response
35
+ * @param next
36
+ */
37
+ export async function getUserSettings(request: Request, response: Response): Promise<Response> {
38
+ try {
39
+ const status = validateRequestParams(request);
40
+ if (status === undefined) {
41
+ const userId: string = request.params.userID;
42
+ const appId: string = request.params.appID;
43
+
44
+ logger.debug(`${CLASS_NAME} | ${getUserSettings.name}() | Getting User Settings`);
45
+ const authenticatedUser: string | undefined = _.toUpper(request.username);
46
+
47
+ if (authenticatedUser && userId && authenticatedUser !== _.toUpper(userId)) {
48
+ logger.warn(
49
+ `${CLASS_NAME} | ${getUserSettings.name}() | Authenticated user is trying to read other person details.`,
50
+ );
51
+ return response.send(403, {
52
+ code: 403,
53
+ message: `Forbidden: You are not allowed to read settings details of others.`,
54
+ });
55
+ }
56
+
57
+ const result: UserSettingsResult = {};
58
+ result.general = await settingsRepo.getUserSettings(userId, 'general');
59
+ if (appId !== 'general') {
60
+ result[appId] = await settingsRepo.getUserSettings(userId, appId);
61
+ }
62
+ return response.send(200, result);
63
+ }
64
+
65
+ return response.send(400, { code: 400, message: status });
66
+ } catch (err) {
67
+ return response.send(500, { code: 500, message: 'Unable to fetch user settings' });
68
+ }
69
+ }
70
+
71
+ /**
72
+ * This is the controller method to update user settings.
73
+ *
74
+ * @param request
75
+ * @param response
76
+ * @param next
77
+ */
78
+ export async function postUserSettings(request: Request, response: Response): Promise<Response> {
79
+ try {
80
+ const status = validateRequestParams(request);
81
+ if (status === undefined) {
82
+ const userId: string = request.params.userID;
83
+ const appId: string = request.params.appID;
84
+
85
+ const result = {};
86
+
87
+ logger.debug(`${CLASS_NAME} | ${postUserSettings.name}() | Updating User Settings`);
88
+ const authenticatedUser: string | undefined = _.toUpper(request.username);
89
+
90
+ if (authenticatedUser && userId && authenticatedUser !== _.toUpper(userId)) {
91
+ logger.warn(
92
+ `${CLASS_NAME} | ${postUserSettings.name}() | Authenticated user is trying to update other person details.`,
93
+ );
94
+ return response.send(403, {
95
+ code: 403,
96
+ message: `Forbidden: You are not allowed to update settings details of others.`,
97
+ });
98
+ }
99
+
100
+ const filter = await settingsRepo.getAppSettings(appId, undefined);
101
+ // const filter = { general: { theme: null, 'primary-language': null, gdprConsent: null }, w3homepage: { gdprConsentDisplay: null, usBetaSearch: null } };
102
+ const updateSettingsPromises: Promise<boolean>[] = [];
103
+
104
+ for (const setting in request.body) {
105
+ if (Object.prototype.hasOwnProperty.call(request.body, setting)) {
106
+ let updatedValue = request.body[setting];
107
+ if (
108
+ setting in filter &&
109
+ (updatedValue === null ||
110
+ settingsRepo.checkValueIsValidOption(updatedValue, filter[setting].options))
111
+ ) {
112
+ if (filter[setting].setting_type === 'array') {
113
+ if (Array.isArray(updatedValue)) {
114
+ updatedValue = updatedValue.join(',');
115
+ } else if (updatedValue != null) {
116
+ return response.send(400, {
117
+ code: 400,
118
+ message: `${setting} expects to receive an array value`,
119
+ });
120
+ }
121
+ }
122
+ if (Array.isArray(updatedValue)) {
123
+ return response.send(400, { code: 400, message: `${setting} should not be an array value` });
124
+ }
125
+ updateSettingsPromises.push(
126
+ settingsRepo.updateUserSettings(_.toUpper(userId), appId, setting, updatedValue),
127
+ );
128
+ result[setting] = updatedValue === null ? null : request.body[setting];
129
+ } else {
130
+ return response.send(400, { code: 400, message: 'Provided value includes an invalid option' });
131
+ }
132
+ }
133
+ }
134
+ try {
135
+ await Promise.all(updateSettingsPromises);
136
+ return response.send(200, result);
137
+ } catch (error) {
138
+ return response.send(500, { code: 500, message: `Unable to update user settings: ${error} ` });
139
+ }
140
+ }
141
+ return response.send(400, { code: 400, message: status });
142
+ } catch (err) {
143
+ return response.send(500, { code: 500, message: 'Unable to update user settings' });
144
+ }
145
+ }
146
+
147
+ /**
148
+ * This is the controller method to get all user settings from database.
149
+ *
150
+ * @param request
151
+ * @param response
152
+ * @param next
153
+ */
154
+ export async function getAllSettings(request: Request, response: Response): Promise<Response> {
155
+ try {
156
+ const { appId, settingName, pageState, fetchSize } = request.query;
157
+
158
+ if (settingName && !appId) {
159
+ return response.send(400, {
160
+ code: 400,
161
+ message: 'Must provide appId query parameter if filtering by settingName',
162
+ });
163
+ }
164
+
165
+ if (fetchSize > 1000) {
166
+ return response.send(400, {
167
+ code: 400,
168
+ message: 'Please provide fetch size value of 1000 or less',
169
+ });
170
+ }
171
+
172
+ logger.debug(`${CLASS_NAME} | ${getAllSettings.name}() | Getting All Settings`);
173
+
174
+ const result = await settingsRepo.getAllSettings({ pageState, appId, settingName, fetchSize });
175
+ return response.send(200, result);
176
+ } catch (err) {
177
+ return response.send(500, { code: 500, message: `Unable to fetch all settings: ${err}` });
178
+ }
179
+ }
180
+
181
+ /**
182
+ * This is the controller method to delete user settings.
183
+ *
184
+ * @param request
185
+ * @param response
186
+ * @param next
187
+ */
188
+ export async function deleteUserSettings(request: Request, response: Response): Promise<Response> {
189
+ if (!request.params.userId)
190
+ return response.send(400, { code: 400, message: 'userId is required to delete user settings' });
191
+
192
+ try {
193
+ const { userId } = request.params;
194
+ logger.debug(`${CLASS_NAME} | ${deleteUserSettings.name}() | Deleting User Settings`);
195
+
196
+ await settingsRepo.deleteUserSettings(userId);
197
+
198
+ return response.send(200, 'success');
199
+ } catch (err) {
200
+ return response.send(500, { code: 500, message: 'Unable to delete user settings' });
201
+ }
202
+ }