@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,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
|
+
}
|