@itentialopensource/adapter-utils 4.48.6 → 4.48.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/MFA.md +43 -0
- package/lib/connectorRest.js +1092 -757
- package/lib/propertyUtil.js +1 -1
- package/package.json +4 -3
- package/schemas/propertiesSchema.json +2 -1
package/lib/connectorRest.js
CHANGED
|
@@ -47,6 +47,7 @@ let basepath = null;
|
|
|
47
47
|
let version = null;
|
|
48
48
|
let choosepath = null;
|
|
49
49
|
let authMethod = null;
|
|
50
|
+
let multiStepAuthCalls = null;
|
|
50
51
|
let authField = null;
|
|
51
52
|
let authFormat = null;
|
|
52
53
|
let authLogging = false;
|
|
@@ -117,6 +118,8 @@ let cacheHHead = null;
|
|
|
117
118
|
let cacheHSchema = null;
|
|
118
119
|
let cacheHPay = null;
|
|
119
120
|
|
|
121
|
+
const mfaStepsResults = []; // keeps requested result for each step
|
|
122
|
+
|
|
120
123
|
/* CONNECTOR ENGINE INTERNAL FUNCTIONS */
|
|
121
124
|
/** Wait for adapter-mongo to be available.
|
|
122
125
|
* @summary adapter may load before adapter-mongo but it requires UPDATE: test if dbUtil object can connect.
|
|
@@ -1084,7 +1087,6 @@ function findExpireInResult(result) {
|
|
|
1084
1087
|
const origin = `${id}-connectorRest-findExpireInResult`;
|
|
1085
1088
|
log.trace(origin);
|
|
1086
1089
|
let expire = null;
|
|
1087
|
-
|
|
1088
1090
|
if (!result) {
|
|
1089
1091
|
return expire;
|
|
1090
1092
|
}
|
|
@@ -1117,887 +1119,932 @@ function findExpireInResult(result) {
|
|
|
1117
1119
|
* INTERNAL FUNCTION: makes the request and processes the response
|
|
1118
1120
|
* for the request to get the token
|
|
1119
1121
|
*/
|
|
1120
|
-
function getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback) {
|
|
1122
|
+
async function getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback) {
|
|
1121
1123
|
const origin = `${id}-connectorRest-getToken`;
|
|
1122
1124
|
log.trace(origin);
|
|
1123
1125
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
// set stub if that is the mode we are in
|
|
1133
|
-
let useStub = false;
|
|
1134
|
-
if (callProperties && Object.hasOwnProperty.call(callProperties, 'stub')) {
|
|
1135
|
-
useStub = callProperties.stub;
|
|
1136
|
-
} else if (stub) {
|
|
1137
|
-
useStub = stub;
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
// if there is a mock result, return that
|
|
1141
|
-
if (useStub && tokenSchema) {
|
|
1142
|
-
// get the data from the mock data file
|
|
1143
|
-
const tokenResp = returnStub(request, tokenSchema, callProperties);
|
|
1144
|
-
|
|
1145
|
-
// if the request failed, return the error
|
|
1146
|
-
if (tokenResp.code < 200 || tokenResp.code > 299) {
|
|
1147
|
-
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', tokenResp.code], null, null, null);
|
|
1148
|
-
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1149
|
-
return callback(null, errorObj);
|
|
1150
|
-
}
|
|
1126
|
+
const p = new Promise((resolve, reject) => {
|
|
1127
|
+
try {
|
|
1128
|
+
// no need for orig path since we handled the stub case
|
|
1129
|
+
const request = {
|
|
1130
|
+
header: options,
|
|
1131
|
+
body: bodyString,
|
|
1132
|
+
origPath: tokenSchema.entitypath
|
|
1133
|
+
};
|
|
1151
1134
|
|
|
1152
|
-
if
|
|
1153
|
-
|
|
1135
|
+
// set stub if that is the mode we are in
|
|
1136
|
+
let useStub = false;
|
|
1137
|
+
if (callProperties && Object.hasOwnProperty.call(callProperties, 'stub')) {
|
|
1138
|
+
useStub = callProperties.stub;
|
|
1139
|
+
} else if (stub) {
|
|
1140
|
+
useStub = stub;
|
|
1154
1141
|
}
|
|
1155
|
-
log.debug(`${origin}: ${JSON.stringify(tokenResp.response)}`);
|
|
1156
1142
|
|
|
1157
|
-
//
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
} else {
|
|
1162
|
-
translated = tokenResp.response;
|
|
1163
|
-
}
|
|
1143
|
+
// if there is a mock result, return that
|
|
1144
|
+
if (useStub && tokenSchema) {
|
|
1145
|
+
// get the data from the mock data file
|
|
1146
|
+
const tokenResp = returnStub(request, tokenSchema, callProperties);
|
|
1164
1147
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1148
|
+
// if the request failed, return the error
|
|
1149
|
+
if (tokenResp.code < 200 || tokenResp.code > 299) {
|
|
1150
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', tokenResp.code], null, null, null);
|
|
1151
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1152
|
+
return reject(errorObj);
|
|
1153
|
+
}
|
|
1172
1154
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
translated[0].end = tokenSchema.responseSchema.properties.token.end;
|
|
1178
|
-
return callback(translated[0]);
|
|
1179
|
-
}
|
|
1155
|
+
if (!tokenSchema.responseDatatype || tokenSchema.responseDatatype === 'JSON') {
|
|
1156
|
+
tokenResp.response = JSON.parse(tokenResp.response);
|
|
1157
|
+
}
|
|
1158
|
+
log.debug(`${origin}: ${JSON.stringify(tokenResp.response)}`);
|
|
1180
1159
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1160
|
+
// return the token from the token schema
|
|
1161
|
+
let translated = null;
|
|
1162
|
+
if (typeof tokenResp.response !== 'string') {
|
|
1163
|
+
translated = transUtilInst.mapFromOutboundEntity(tokenResp.response, tokenSchema.responseSchema);
|
|
1164
|
+
} else {
|
|
1165
|
+
translated = tokenResp.response;
|
|
1166
|
+
}
|
|
1186
1167
|
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1168
|
+
// since this is a stub, make sure we have what we need
|
|
1169
|
+
if (!translated.token) {
|
|
1170
|
+
translated.token = 'garbagetoken';
|
|
1171
|
+
}
|
|
1172
|
+
if (!translated.tokenp2) {
|
|
1173
|
+
translated.tokenp2 = 'garbagetoken';
|
|
1174
|
+
}
|
|
1193
1175
|
|
|
1194
|
-
|
|
1176
|
+
// if what we got back is an array, just return the first element
|
|
1177
|
+
// should only have one token!!!
|
|
1178
|
+
if (translated && Array.isArray(translated)) {
|
|
1179
|
+
translated[0].front = tokenSchema.responseSchema.properties.token.front;
|
|
1180
|
+
translated[0].end = tokenSchema.responseSchema.properties.token.end;
|
|
1181
|
+
return resolve(translated[0]);
|
|
1182
|
+
}
|
|
1195
1183
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
return
|
|
1184
|
+
// return the token that we find in the translated object
|
|
1185
|
+
translated.front = tokenSchema.responseSchema.properties.token.front;
|
|
1186
|
+
translated.end = tokenSchema.responseSchema.properties.token.end;
|
|
1187
|
+
return resolve(translated);
|
|
1200
1188
|
}
|
|
1201
1189
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1190
|
+
if (useStub || reqPath === tokenPath || (tokenSchema && reqPath === tokenSchema.entitypath)) {
|
|
1191
|
+
// do not make the call to return a token if the request is actually
|
|
1192
|
+
// to get a token. Getting a token to get a token -- that should go
|
|
1193
|
+
// direct to make request
|
|
1194
|
+
return resolve({ token: 'faketoken', tokenp2: 'faketoken' });
|
|
1207
1195
|
}
|
|
1208
1196
|
|
|
1209
|
-
|
|
1210
|
-
if ((result.code < 200 || result.code > 299) && !handleTokenRedirect) {
|
|
1211
|
-
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', result.code], null, null, null);
|
|
1212
|
-
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1213
|
-
return callback(null, errorObj);
|
|
1214
|
-
}
|
|
1197
|
+
log.debug(`${origin}: OPTIONS: ${JSON.stringify(options)}`);
|
|
1215
1198
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
}
|
|
1199
|
+
// request the token
|
|
1200
|
+
return makeRequest(request, tokenSchema, callProperties, null, 0, (result, merror) => {
|
|
1201
|
+
if (merror) {
|
|
1202
|
+
return reject(merror);
|
|
1203
|
+
}
|
|
1222
1204
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
};
|
|
1205
|
+
// if the request is a redirect, try to pull token but log a warning
|
|
1206
|
+
let handleTokenRedirect = false;
|
|
1207
|
+
if (result.code >= 300 && result.code <= 308 && numRedirects === 0) {
|
|
1208
|
+
log.warn(`${origin}: Going to attempt to get token from redirect message!`);
|
|
1209
|
+
handleTokenRedirect = true;
|
|
1210
|
+
}
|
|
1230
1211
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1212
|
+
// if the request failed, return the error
|
|
1213
|
+
if ((result.code < 200 || result.code > 299) && !handleTokenRedirect) {
|
|
1214
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', result.code], null, null, null);
|
|
1215
|
+
if (callProperties && callProperties.mfa && callProperties.mfa.successfullResponseCode) { // MFA call
|
|
1216
|
+
if (callProperties.mfa.successfullResponseCode !== result.code) {
|
|
1217
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1218
|
+
return reject(errorObj);
|
|
1219
|
+
}
|
|
1220
|
+
} else { // non-MFA call
|
|
1221
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1222
|
+
return reject(errorObj);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (!tokenSchema) {
|
|
1227
|
+
// if no token schema, can not determine what to return
|
|
1228
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Authenticate', ['Token', result.code], null, null, null);
|
|
1236
1229
|
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1237
|
-
return
|
|
1230
|
+
return reject(errorObj);
|
|
1238
1231
|
}
|
|
1239
1232
|
|
|
1240
|
-
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1233
|
+
// parse the token out of the result
|
|
1234
|
+
const currResult = {
|
|
1235
|
+
token: null,
|
|
1236
|
+
tokenp2: null,
|
|
1237
|
+
front: tokenSchema.responseSchema.properties.token.front,
|
|
1238
|
+
end: tokenSchema.responseSchema.properties.token.end
|
|
1239
|
+
};
|
|
1244
1240
|
|
|
1245
|
-
//
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1241
|
+
// process primary token from header
|
|
1242
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1243
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'HEADER') {
|
|
1244
|
+
if (!tokenSchema.responseSchema.properties.token.external_name) {
|
|
1245
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Primary Token', ['Primary Token', result.code], null, null, null);
|
|
1246
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1247
|
+
return reject(errorObj);
|
|
1250
1248
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1249
|
+
|
|
1250
|
+
const exName = tokenSchema.responseSchema.properties.token.external_name.toLowerCase();
|
|
1251
|
+
const headKeys = Object.keys(result.headers);
|
|
1252
|
+
let fullToken = null;
|
|
1253
|
+
let setCookie = null;
|
|
1254
|
+
|
|
1255
|
+
// go through and find the token
|
|
1256
|
+
for (let h = 0; h < headKeys.length; h += 1) {
|
|
1257
|
+
if (headKeys[h].toLowerCase() === exName) {
|
|
1258
|
+
fullToken = result.headers[headKeys[h]];
|
|
1259
|
+
currResult.token = result.headers[headKeys[h]];
|
|
1260
|
+
}
|
|
1261
|
+
if (headKeys[h].toLowerCase() === 'set-cookie') {
|
|
1262
|
+
setCookie = result.headers[headKeys[h]];
|
|
1263
|
+
}
|
|
1253
1264
|
}
|
|
1254
|
-
}
|
|
1255
1265
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1266
|
+
// if the token is in the requestToken
|
|
1267
|
+
if (exName === 'requestcookie' && result.requestCookie) {
|
|
1268
|
+
currResult.token = result.requestCookie;
|
|
1269
|
+
}
|
|
1260
1270
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1271
|
+
// if the exName field is an array
|
|
1272
|
+
if (exName === 'set-cookie' && fullToken && Array.isArray(fullToken)) {
|
|
1273
|
+
currResult.token = fullToken[0];
|
|
1274
|
+
for (let ex = 1; ex < fullToken.length; ex += 1) {
|
|
1275
|
+
currResult.token += `; ${fullToken[ex]}`;
|
|
1276
|
+
}
|
|
1266
1277
|
}
|
|
1267
|
-
}
|
|
1268
1278
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1279
|
+
// if the token has been returned in the cookie
|
|
1280
|
+
if (exName.substring(0, 11) === 'set-cookie.') {
|
|
1281
|
+
const fname = exName.substring(11).toLowerCase();
|
|
1282
|
+
let thisCook = null;
|
|
1283
|
+
|
|
1284
|
+
// if the cookie is an array - usual case
|
|
1285
|
+
if (setCookie && Array.isArray(setCookie)) {
|
|
1286
|
+
// go through the array looking for the defined token field
|
|
1287
|
+
for (let sc = 0; sc < setCookie.length; sc += 1) {
|
|
1288
|
+
// parses the cookie into an object
|
|
1289
|
+
thisCook = cookieHandler.parse(setCookie[sc]);
|
|
1290
|
+
const cookKeys = Object.keys(thisCook);
|
|
1291
|
+
let set = false;
|
|
1292
|
+
|
|
1293
|
+
// go through the cookie to find the token
|
|
1294
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1295
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1296
|
+
currResult.token = thisCook[cookKeys[h]];
|
|
1297
|
+
set = true;
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (set) {
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
} else if (setCookie) {
|
|
1306
|
+
// if the cookie is just one string, parse into an object
|
|
1307
|
+
thisCook = cookieHandler.parse(setCookie);
|
|
1280
1308
|
const cookKeys = Object.keys(thisCook);
|
|
1281
|
-
let set = false;
|
|
1282
1309
|
|
|
1283
1310
|
// go through the cookie to find the token
|
|
1284
1311
|
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1285
1312
|
if (cookKeys[h].toLowerCase() === fname) {
|
|
1286
1313
|
currResult.token = thisCook[cookKeys[h]];
|
|
1287
|
-
set = true;
|
|
1288
1314
|
break;
|
|
1289
1315
|
}
|
|
1290
1316
|
}
|
|
1291
|
-
if (set) {
|
|
1292
|
-
break;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
} else if (setCookie) {
|
|
1296
|
-
// if the cookie is just one string, parse into an object
|
|
1297
|
-
thisCook = cookieHandler.parse(setCookie);
|
|
1298
|
-
const cookKeys = Object.keys(thisCook);
|
|
1299
|
-
|
|
1300
|
-
// go through the cookie to find the token
|
|
1301
|
-
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1302
|
-
if (cookKeys[h].toLowerCase() === fname) {
|
|
1303
|
-
currResult.token = thisCook[cookKeys[h]];
|
|
1304
|
-
break;
|
|
1305
|
-
}
|
|
1306
1317
|
}
|
|
1307
1318
|
}
|
|
1308
1319
|
}
|
|
1309
|
-
}
|
|
1310
1320
|
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1321
|
+
// process second token from header
|
|
1322
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1323
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'HEADER') {
|
|
1324
|
+
if (!tokenSchema.responseSchema.properties.tokenp2.external_name) {
|
|
1325
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Secondary Token', ['Secondary Token', result.code], null, null, null);
|
|
1326
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1327
|
+
return reject(errorObj);
|
|
1328
|
+
}
|
|
1319
1329
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1330
|
+
const exName = tokenSchema.responseSchema.properties.tokenp2.external_name.toLowerCase();
|
|
1331
|
+
const headKeys = Object.keys(result.headers);
|
|
1332
|
+
let fullToken = null;
|
|
1333
|
+
let setCookie = null;
|
|
1324
1334
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1335
|
+
// go through and find the token
|
|
1336
|
+
for (let h = 0; h < headKeys.length; h += 1) {
|
|
1337
|
+
if (headKeys[h].toLowerCase() === exName) {
|
|
1338
|
+
fullToken = result.headers[headKeys[h]];
|
|
1339
|
+
currResult.tokenp2 = result.headers[headKeys[h]];
|
|
1340
|
+
}
|
|
1341
|
+
if (headKeys[h].toLowerCase() === 'set-cookie') {
|
|
1342
|
+
setCookie = result.headers[headKeys[h]];
|
|
1343
|
+
}
|
|
1333
1344
|
}
|
|
1334
|
-
}
|
|
1335
1345
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1346
|
+
// if the token is in the requestToken
|
|
1347
|
+
if (exName === 'requestcookie' && result.requestCookie) {
|
|
1348
|
+
currResult.token = result.requestCookie;
|
|
1349
|
+
}
|
|
1340
1350
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1351
|
+
// if the exName field is an array
|
|
1352
|
+
if (exName === 'set-cookie' && fullToken && Array.isArray(fullToken)) {
|
|
1353
|
+
currResult.tokenp2 = fullToken[0];
|
|
1354
|
+
for (let ex = 1; ex < fullToken.length; ex += 1) {
|
|
1355
|
+
currResult.tokenp2 += `; ${fullToken[ex]}`;
|
|
1356
|
+
}
|
|
1346
1357
|
}
|
|
1347
|
-
}
|
|
1348
1358
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1359
|
+
// if the token has been returned in the cookie
|
|
1360
|
+
if (exName.substring(0, 11) === 'set-cookie.') {
|
|
1361
|
+
const fname = exName.substring(11).toLowerCase();
|
|
1362
|
+
let thisCook = null;
|
|
1363
|
+
|
|
1364
|
+
// if the cookie is an array - usual case
|
|
1365
|
+
if (setCookie && Array.isArray(setCookie)) {
|
|
1366
|
+
// go through the array looking for the defined token field
|
|
1367
|
+
for (let sc = 0; sc < setCookie.length; sc += 1) {
|
|
1368
|
+
// parses the cookie into an object
|
|
1369
|
+
thisCook = cookieHandler.parse(setCookie[sc]);
|
|
1370
|
+
const cookKeys = Object.keys(thisCook);
|
|
1371
|
+
let set = false;
|
|
1372
|
+
|
|
1373
|
+
// go through the cookie to find the token
|
|
1374
|
+
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1375
|
+
if (cookKeys[h].toLowerCase() === fname) {
|
|
1376
|
+
currResult.tokenp2 = thisCook[cookKeys[h]];
|
|
1377
|
+
set = true;
|
|
1378
|
+
break;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (set) {
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
} else if (setCookie) {
|
|
1386
|
+
// if the cookie is just one string, parse into an object
|
|
1387
|
+
thisCook = cookieHandler.parse(setCookie);
|
|
1360
1388
|
const cookKeys = Object.keys(thisCook);
|
|
1361
|
-
let set = false;
|
|
1362
1389
|
|
|
1363
1390
|
// go through the cookie to find the token
|
|
1364
1391
|
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1365
1392
|
if (cookKeys[h].toLowerCase() === fname) {
|
|
1366
1393
|
currResult.tokenp2 = thisCook[cookKeys[h]];
|
|
1367
|
-
set = true;
|
|
1368
1394
|
break;
|
|
1369
1395
|
}
|
|
1370
1396
|
}
|
|
1371
|
-
if (set) {
|
|
1372
|
-
break;
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
} else if (setCookie) {
|
|
1376
|
-
// if the cookie is just one string, parse into an object
|
|
1377
|
-
thisCook = cookieHandler.parse(setCookie);
|
|
1378
|
-
const cookKeys = Object.keys(thisCook);
|
|
1379
|
-
|
|
1380
|
-
// go through the cookie to find the token
|
|
1381
|
-
for (let h = 0; h < cookKeys.length; h += 1) {
|
|
1382
|
-
if (cookKeys[h].toLowerCase() === fname) {
|
|
1383
|
-
currResult.tokenp2 = thisCook[cookKeys[h]];
|
|
1384
|
-
break;
|
|
1385
|
-
}
|
|
1386
1397
|
}
|
|
1387
1398
|
}
|
|
1388
1399
|
}
|
|
1389
|
-
}
|
|
1390
1400
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1401
|
+
// process the body
|
|
1402
|
+
// if response is just a string
|
|
1403
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1404
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1405
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1406
|
+
currResult.token = result.response;
|
|
1397
1407
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1408
|
+
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
1409
|
+
if (currResult.token.substring(0, 1) === '"' && currResult.token.substring(-1, 1) === '"') {
|
|
1410
|
+
currResult.token = currResult.token.substring(1, currResult.token.length - 1);
|
|
1411
|
+
}
|
|
1401
1412
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
currResult.tokenp2 = result.response;
|
|
1413
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1414
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1415
|
+
currResult.tokenp2 = result.response;
|
|
1406
1416
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1417
|
+
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
1418
|
+
if (currResult.token.substring(0, 1) === '"' && currResult.token.substring(-1, 1) === '"') {
|
|
1419
|
+
currResult.token = currResult.token.substring(1, currResult.token.length - 1);
|
|
1420
|
+
}
|
|
1410
1421
|
}
|
|
1411
|
-
}
|
|
1412
1422
|
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'XML') {
|
|
1417
|
-
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1418
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1419
|
-
currResult.token = result.response;
|
|
1420
|
-
}
|
|
1421
|
-
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1422
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1423
|
-
currResult.tokenp2 = result.response;
|
|
1423
|
+
// return the string as there is no other processing needed
|
|
1424
|
+
return resolve(currResult);
|
|
1424
1425
|
}
|
|
1426
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'XML') {
|
|
1427
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1428
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1429
|
+
currResult.token = result.response;
|
|
1430
|
+
}
|
|
1431
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1432
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1433
|
+
currResult.tokenp2 = result.response;
|
|
1434
|
+
}
|
|
1425
1435
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1436
|
+
// return the xml as there is no other processing needed
|
|
1437
|
+
return resolve(currResult);
|
|
1438
|
+
}
|
|
1429
1439
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
+
// if response can be put into a JSON object
|
|
1441
|
+
let tempResult = null;
|
|
1442
|
+
if (result.response) {
|
|
1443
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'XML2JSON') {
|
|
1444
|
+
try {
|
|
1445
|
+
const parser = new xml2js.Parser({ explicitArray: false, attrkey: '_attr' });
|
|
1446
|
+
parser.parseString(result.response, (error, presult) => {
|
|
1447
|
+
if (error) {
|
|
1448
|
+
log.warn(`${origin}: Unable to parse xml to json ${error}`);
|
|
1449
|
+
return resolve(parser.toJson(presult));
|
|
1450
|
+
}
|
|
1451
|
+
tempResult = presult;
|
|
1452
|
+
});
|
|
1453
|
+
} catch (ex) {
|
|
1454
|
+
log.warn(`${origin}: Unable to get json from xml ${ex}`);
|
|
1455
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1456
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1457
|
+
currResult.token = result.response;
|
|
1458
|
+
}
|
|
1459
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1460
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1461
|
+
currResult.tokenp2 = result.response;
|
|
1440
1462
|
}
|
|
1441
|
-
tempResult = presult;
|
|
1442
|
-
});
|
|
1443
|
-
} catch (ex) {
|
|
1444
|
-
log.warn(`${origin}: Unable to get json from xml ${ex}`);
|
|
1445
|
-
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1446
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1447
|
-
currResult.token = result.response;
|
|
1448
1463
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1464
|
+
}
|
|
1465
|
+
if (Object.hasOwnProperty.call(tokenSchema, 'responseDatatype') && tokenSchema.responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1466
|
+
tempResult = querystring.parse(result.response.trim());
|
|
1467
|
+
} else {
|
|
1468
|
+
try {
|
|
1469
|
+
tempResult = JSON.parse(result.response.trim());
|
|
1470
|
+
} catch (exc) {
|
|
1471
|
+
log.warn(exc);
|
|
1452
1472
|
}
|
|
1453
1473
|
}
|
|
1454
1474
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
tempResult = JSON.parse(result.response.trim());
|
|
1460
|
-
} catch (exc) {
|
|
1461
|
-
log.warn(exc);
|
|
1462
|
-
}
|
|
1475
|
+
|
|
1476
|
+
// at this point if there is nothing in tempResult nothing further to do
|
|
1477
|
+
if (!tempResult) {
|
|
1478
|
+
return resolve(currResult);
|
|
1463
1479
|
}
|
|
1464
|
-
}
|
|
1465
1480
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
}
|
|
1481
|
+
// need to see if there is a response key
|
|
1482
|
+
if (tokenSchema.responseObjects) {
|
|
1483
|
+
let tKey = null;
|
|
1470
1484
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1485
|
+
// it should be an array - then take the first element of array
|
|
1486
|
+
if (Array.isArray(tokenSchema.responseObjects)) {
|
|
1487
|
+
if (tokenSchema.responseObjects[0].key) {
|
|
1488
|
+
tKey = tokenSchema.responseObjects[0].key;
|
|
1489
|
+
}
|
|
1490
|
+
} else if (tokenSchema.responseObjects.key) {
|
|
1491
|
+
tKey = tokenSchema.responseObjects.key;
|
|
1492
|
+
}
|
|
1474
1493
|
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
tKey = tokenSchema.responseObjects[0].key;
|
|
1494
|
+
// if we found a key, use it
|
|
1495
|
+
if (tKey) {
|
|
1496
|
+
tempResult = jsonQuery(tKey, { data: tempResult }).value;
|
|
1479
1497
|
}
|
|
1480
|
-
} else if (tokenSchema.responseObjects.key) {
|
|
1481
|
-
tKey = tokenSchema.responseObjects.key;
|
|
1482
1498
|
}
|
|
1483
1499
|
|
|
1484
|
-
//
|
|
1485
|
-
if (
|
|
1486
|
-
tempResult =
|
|
1500
|
+
// the token should not be an array so if it is, return the 0 item.
|
|
1501
|
+
if (Array.isArray(tempResult)) {
|
|
1502
|
+
tempResult = tempResult[0];
|
|
1487
1503
|
}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
// the token should not be an array so if it is, return the 0 item.
|
|
1491
|
-
if (Array.isArray(tempResult)) {
|
|
1492
|
-
tempResult = tempResult[0];
|
|
1493
|
-
}
|
|
1494
1504
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1505
|
+
// return the token from the token schema
|
|
1506
|
+
let translated = transUtilInst.mapFromOutboundEntity(tempResult, tokenSchema.responseSchema);
|
|
1497
1507
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1508
|
+
// if what we got back is an array, just return the first element
|
|
1509
|
+
// should only have one token!!!
|
|
1510
|
+
if (translated && Array.isArray(translated)) {
|
|
1511
|
+
translated = translated[0];
|
|
1512
|
+
}
|
|
1503
1513
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1514
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.token
|
|
1515
|
+
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1516
|
+
currResult.token = translated.token;
|
|
1517
|
+
}
|
|
1518
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.tokenp2
|
|
1519
|
+
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1520
|
+
currResult.tokenp2 = translated.tokenp2;
|
|
1521
|
+
}
|
|
1522
|
+
if (tokenSchema.responseSchema && tokenSchema.responseSchema.properties && tokenSchema.responseSchema.properties.expires
|
|
1523
|
+
&& tokenSchema.responseSchema.properties.expires.placement && tokenSchema.responseSchema.properties.expires.placement.toUpperCase() === 'BODY') {
|
|
1524
|
+
currResult.expires = translated.expires;
|
|
1525
|
+
}
|
|
1526
|
+
// return the token that we find in the translated object
|
|
1527
|
+
return resolve(currResult);
|
|
1528
|
+
});
|
|
1529
|
+
} catch (e) {
|
|
1530
|
+
// handle any exception
|
|
1531
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue retrieving a token');
|
|
1532
|
+
return reject(errorObj);
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
1512
1535
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
});
|
|
1516
|
-
} catch (e) {
|
|
1517
|
-
// handle any exception
|
|
1518
|
-
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue retrieving a token');
|
|
1519
|
-
return callback(null, errorObj);
|
|
1536
|
+
if (typeof callback === 'function') {
|
|
1537
|
+
return p.then((result) => callback(result)).catch((error) => callback(null, error));
|
|
1520
1538
|
}
|
|
1539
|
+
return p;
|
|
1521
1540
|
}
|
|
1522
1541
|
|
|
1523
1542
|
/*
|
|
1524
1543
|
* INTERNAL FUNCTION: prepares a request to get a token from the system
|
|
1525
1544
|
*/
|
|
1526
|
-
function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
1545
|
+
async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
1527
1546
|
const origin = `${id}-connectorRest-buildTokenRequest`;
|
|
1528
1547
|
log.trace(origin);
|
|
1529
1548
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
if (
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1549
|
+
const promise = new Promise((resolve, reject) => {
|
|
1550
|
+
try {
|
|
1551
|
+
let entity = 'getToken';
|
|
1552
|
+
if (callProperties && callProperties.mfa) {
|
|
1553
|
+
entity = callProperties.mfa.stepAtionName;
|
|
1554
|
+
}
|
|
1555
|
+
// Get the entity schema from the file system
|
|
1556
|
+
return propUtilInst.getEntitySchema('.system', entity, choosepath, this.dbUtil, async (tokenSchema, healthError) => {
|
|
1557
|
+
if (healthError || !tokenSchema || Object.keys(tokenSchema).length === 0) {
|
|
1558
|
+
log.debug(`${origin}: Using adapter properties for token information`);
|
|
1559
|
+
tokenSchema = null;
|
|
1560
|
+
} else {
|
|
1561
|
+
log.debug(`${origin}: Using action and schema for token information`);
|
|
1562
|
+
}
|
|
1542
1563
|
|
|
1543
|
-
|
|
1544
|
-
thisAHdata =
|
|
1545
|
-
}
|
|
1546
|
-
if (globalRequest) {
|
|
1547
|
-
thisAHdata = transUtilInst.mergeObjects(thisAHdata, globalRequest.addlHeaders);
|
|
1548
|
-
}
|
|
1564
|
+
// prepare the additional headers we received
|
|
1565
|
+
let thisAHdata = null;
|
|
1549
1566
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1567
|
+
if (tokenSchema) {
|
|
1568
|
+
thisAHdata = transUtilInst.mergeObjects(thisAHdata, tokenSchema.headers);
|
|
1569
|
+
}
|
|
1570
|
+
if (globalRequest) {
|
|
1571
|
+
thisAHdata = transUtilInst.mergeObjects(thisAHdata, globalRequest.addlHeaders);
|
|
1572
|
+
}
|
|
1553
1573
|
|
|
1554
|
-
|
|
1555
|
-
useUser =
|
|
1556
|
-
|
|
1557
|
-
if (callProperties && callProperties.authentication && callProperties.authentication.password) {
|
|
1558
|
-
usePass = callProperties.authentication.password;
|
|
1559
|
-
}
|
|
1574
|
+
// set up the right credentials - passed in overrides default
|
|
1575
|
+
let useUser = username;
|
|
1576
|
+
let usePass = password;
|
|
1560
1577
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
for (let h = 0; h < hKeys.length; h += 1) {
|
|
1568
|
-
let tempStr = thisAHdata[hKeys[h]];
|
|
1578
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.username) {
|
|
1579
|
+
useUser = callProperties.authentication.username;
|
|
1580
|
+
}
|
|
1581
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.password) {
|
|
1582
|
+
usePass = callProperties.authentication.password;
|
|
1583
|
+
}
|
|
1569
1584
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
if (tempStr.indexOf('{password}') >= 0) {
|
|
1576
|
-
tempStr = tempStr.replace('{password}', usePass);
|
|
1577
|
-
}
|
|
1578
|
-
thisAHdata[hKeys[h]] = tempStr;
|
|
1585
|
+
// if no header data passed in create empty - will add data below
|
|
1586
|
+
if (!thisAHdata) {
|
|
1587
|
+
thisAHdata = {};
|
|
1588
|
+
} else {
|
|
1589
|
+
const hKeys = Object.keys(thisAHdata);
|
|
1579
1590
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
// get the range to be encoded
|
|
1583
|
-
const sIndex = thisAHdata[hKeys[h]].indexOf('{b64}');
|
|
1584
|
-
const eIndex = thisAHdata[hKeys[h]].indexOf('{/b64}');
|
|
1591
|
+
for (let h = 0; h < hKeys.length; h += 1) {
|
|
1592
|
+
let tempStr = thisAHdata[hKeys[h]];
|
|
1585
1593
|
|
|
1586
|
-
//
|
|
1587
|
-
if (
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1594
|
+
// replace username variable
|
|
1595
|
+
if (tempStr.indexOf('{username}') >= 0) {
|
|
1596
|
+
tempStr = tempStr.replace('{username}', useUser);
|
|
1597
|
+
}
|
|
1598
|
+
// replace password variable
|
|
1599
|
+
if (tempStr.indexOf('{password}') >= 0) {
|
|
1600
|
+
tempStr = tempStr.replace('{password}', usePass);
|
|
1591
1601
|
}
|
|
1602
|
+
thisAHdata[hKeys[h]] = tempStr;
|
|
1603
|
+
|
|
1604
|
+
// handle any base64 encoding required on the authStr
|
|
1605
|
+
if (thisAHdata[hKeys[h]].indexOf('{b64}') >= 0) {
|
|
1606
|
+
// get the range to be encoded
|
|
1607
|
+
const sIndex = thisAHdata[hKeys[h]].indexOf('{b64}');
|
|
1608
|
+
const eIndex = thisAHdata[hKeys[h]].indexOf('{/b64}');
|
|
1609
|
+
|
|
1610
|
+
// if start but no end - return an error
|
|
1611
|
+
if (sIndex >= 0 && eIndex < sIndex + 5) {
|
|
1612
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Encode', [thisAHdata[hKeys[h]]], null, null, null);
|
|
1613
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1614
|
+
return reject(errorObj);
|
|
1615
|
+
}
|
|
1592
1616
|
|
|
1593
|
-
|
|
1594
|
-
|
|
1617
|
+
// get the string to be encoded
|
|
1618
|
+
const bufString = thisAHdata[hKeys[h]].substring(sIndex + 5, eIndex);
|
|
1595
1619
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1620
|
+
// encode the string
|
|
1621
|
+
const encString = Buffer.from(bufString).toString('base64');
|
|
1622
|
+
let tempAuthStr = '';
|
|
1599
1623
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1624
|
+
// build the new auth field with the encoded string
|
|
1625
|
+
if (sIndex > 0) {
|
|
1626
|
+
// add the start of the string that did not need encoding
|
|
1627
|
+
tempAuthStr = thisAHdata[hKeys[h]].substring(0, sIndex);
|
|
1628
|
+
}
|
|
1629
|
+
// add the encoded string
|
|
1630
|
+
tempAuthStr += encString;
|
|
1631
|
+
if (eIndex + 5 < thisAHdata[hKeys[h]].length) {
|
|
1632
|
+
// add the end of the string that did not need encoding
|
|
1633
|
+
tempAuthStr += thisAHdata[hKeys[h]].substring(eIndex + 6);
|
|
1634
|
+
}
|
|
1611
1635
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1636
|
+
// put the temp string into the auth string we will add to the request
|
|
1637
|
+
thisAHdata[hKeys[h]] = tempAuthStr;
|
|
1638
|
+
}
|
|
1614
1639
|
}
|
|
1615
1640
|
}
|
|
1616
|
-
}
|
|
1617
1641
|
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1642
|
+
// set the Content Type headers based on the type of request data for the call
|
|
1643
|
+
if (thisAHdata['Content-Type'] === undefined || thisAHdata['Content-Type'] === null) {
|
|
1644
|
+
if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'PLAIN') {
|
|
1645
|
+
// add the Plain headers if they were not set already
|
|
1646
|
+
thisAHdata['Content-Type'] = 'text/plain';
|
|
1647
|
+
} else if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'XML') {
|
|
1648
|
+
// add the XML headers if they were not set already
|
|
1649
|
+
thisAHdata['Content-Type'] = 'application/xml';
|
|
1650
|
+
} else if (tokenSchema && tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
1651
|
+
// add the URLENCODE headers if they were not set already
|
|
1652
|
+
thisAHdata['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1653
|
+
} else {
|
|
1654
|
+
// add the JSON headers if they were not set already
|
|
1655
|
+
thisAHdata['Content-Type'] = 'application/json';
|
|
1656
|
+
}
|
|
1632
1657
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1658
|
+
// set the Accept headers based on the type of response data for the call
|
|
1659
|
+
if (thisAHdata.Accept === undefined || thisAHdata.Accept === null) {
|
|
1660
|
+
if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1661
|
+
// add the Plain headers if they were not set already
|
|
1662
|
+
thisAHdata.Accept = 'text/plain';
|
|
1663
|
+
} else if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'XML') {
|
|
1664
|
+
// add the XML headers if they were not set already
|
|
1665
|
+
thisAHdata.Accept = 'application/xml';
|
|
1666
|
+
} else if (tokenSchema && tokenSchema.responseDatatype && tokenSchema.responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1667
|
+
// add the URLENCODE headers if they were not set already
|
|
1668
|
+
thisAHdata.Accept = 'application/x-www-form-urlencoded';
|
|
1669
|
+
} else {
|
|
1670
|
+
// add the JSON headers if they were not set already
|
|
1671
|
+
thisAHdata.Accept = 'application/json';
|
|
1672
|
+
}
|
|
1648
1673
|
}
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
if (thisAHdata.Accept === '') {
|
|
1652
|
-
delete thisAHdata.Accept;
|
|
1653
|
-
}
|
|
1654
|
-
if (thisAHdata['Content-Type'] === '') {
|
|
1655
|
-
delete thisAHdata['Content-Type'];
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
// set up the options for the call to get incidents - default is all
|
|
1659
|
-
const options = {
|
|
1660
|
-
hostname: host,
|
|
1661
|
-
port,
|
|
1662
|
-
path: tokenPath,
|
|
1663
|
-
method: 'POST',
|
|
1664
|
-
headers: thisAHdata
|
|
1665
|
-
};
|
|
1666
|
-
|
|
1667
|
-
// passed in properties override defaults
|
|
1668
|
-
if (callProperties && callProperties.host) {
|
|
1669
|
-
options.hostname = callProperties.host;
|
|
1670
|
-
}
|
|
1671
|
-
if (callProperties && callProperties.port) {
|
|
1672
|
-
options.port = callProperties.port;
|
|
1673
|
-
}
|
|
1674
1674
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
options.port = sso.port;
|
|
1681
|
-
}
|
|
1682
|
-
if (sso && sso.protocol) {
|
|
1683
|
-
// need to put protocol in token schema
|
|
1684
|
-
if (!tokenSchema) {
|
|
1685
|
-
tokenSchema = {
|
|
1686
|
-
sso: {
|
|
1687
|
-
protocol: sso.protocol
|
|
1688
|
-
}
|
|
1689
|
-
};
|
|
1690
|
-
} else if (tokenSchema && !tokenSchema.sso) {
|
|
1691
|
-
tokenSchema.sso = {
|
|
1692
|
-
protocol: sso.protocol
|
|
1693
|
-
};
|
|
1694
|
-
} else if (tokenSchema && tokenSchema.sso && !tokenSchema.sso.protocol) {
|
|
1695
|
-
tokenSchema.sso.protocol = sso.protocol;
|
|
1675
|
+
if (thisAHdata.Accept === '') {
|
|
1676
|
+
delete thisAHdata.Accept;
|
|
1677
|
+
}
|
|
1678
|
+
if (thisAHdata['Content-Type'] === '') {
|
|
1679
|
+
delete thisAHdata['Content-Type'];
|
|
1696
1680
|
}
|
|
1697
|
-
}
|
|
1698
|
-
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host) {
|
|
1699
|
-
options.hostname = tokenSchema.sso.host;
|
|
1700
|
-
}
|
|
1701
|
-
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port) {
|
|
1702
|
-
options.port = tokenSchema.sso.port;
|
|
1703
|
-
}
|
|
1704
1681
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1682
|
+
// set up the options for the call to get incidents - default is all
|
|
1683
|
+
const options = {
|
|
1684
|
+
hostname: host,
|
|
1685
|
+
port,
|
|
1686
|
+
path: tokenPath,
|
|
1687
|
+
method: 'POST',
|
|
1688
|
+
headers: thisAHdata
|
|
1689
|
+
};
|
|
1710
1690
|
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
if (
|
|
1716
|
-
|
|
1691
|
+
// passed in properties override defaults
|
|
1692
|
+
if (callProperties && callProperties.host) {
|
|
1693
|
+
options.hostname = callProperties.host;
|
|
1694
|
+
}
|
|
1695
|
+
if (callProperties && callProperties.port) {
|
|
1696
|
+
options.port = callProperties.port;
|
|
1717
1697
|
}
|
|
1718
1698
|
|
|
1719
|
-
//
|
|
1720
|
-
if (
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1699
|
+
// specific token properties override everything (Single Sign On System)
|
|
1700
|
+
if (sso && sso.host) {
|
|
1701
|
+
options.hostname = sso.host;
|
|
1702
|
+
}
|
|
1703
|
+
if (sso && sso.port) {
|
|
1704
|
+
options.port = sso.port;
|
|
1705
|
+
}
|
|
1706
|
+
if (sso && sso.protocol) {
|
|
1707
|
+
// need to put protocol in token schema
|
|
1708
|
+
if (!tokenSchema) {
|
|
1709
|
+
tokenSchema = {
|
|
1710
|
+
sso: {
|
|
1711
|
+
protocol: sso.protocol
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
} else if (tokenSchema && !tokenSchema.sso) {
|
|
1715
|
+
tokenSchema.sso = {
|
|
1716
|
+
protocol: sso.protocol
|
|
1717
|
+
};
|
|
1718
|
+
} else if (tokenSchema && tokenSchema.sso && !tokenSchema.sso.protocol) {
|
|
1719
|
+
tokenSchema.sso.protocol = sso.protocol;
|
|
1733
1720
|
}
|
|
1734
|
-
} else {
|
|
1735
|
-
options.path = options.path.replace(bpathStr, '');
|
|
1736
1721
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
// be able to support this if the version has a slash before it or not
|
|
1743
|
-
if (options.path.indexOf('/{version}') >= 0) {
|
|
1744
|
-
versStr = '/{version}';
|
|
1722
|
+
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.host) {
|
|
1723
|
+
options.hostname = tokenSchema.sso.host;
|
|
1724
|
+
}
|
|
1725
|
+
if (tokenSchema && tokenSchema.sso && tokenSchema.sso.port) {
|
|
1726
|
+
options.port = tokenSchema.sso.port;
|
|
1745
1727
|
}
|
|
1746
1728
|
|
|
1747
|
-
//
|
|
1748
|
-
if (
|
|
1749
|
-
options.path =
|
|
1750
|
-
|
|
1751
|
-
options.path = options.path.replace(versStr, `/${encodeURIComponent(version)}`);
|
|
1752
|
-
} else {
|
|
1753
|
-
options.path = options.path.replace(versStr, '');
|
|
1729
|
+
// If there is a token schema, need to take the data from there
|
|
1730
|
+
if (tokenSchema) {
|
|
1731
|
+
options.path = tokenSchema.entitypath;
|
|
1732
|
+
options.method = tokenSchema.method;
|
|
1754
1733
|
}
|
|
1755
|
-
}
|
|
1756
1734
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
//
|
|
1766
|
-
if (
|
|
1767
|
-
//
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1735
|
+
// if the path has a base path parameter in it, need to replace it
|
|
1736
|
+
let bpathStr = '{base_path}';
|
|
1737
|
+
if (options.path.indexOf(bpathStr) >= 0) {
|
|
1738
|
+
// be able to support this if the base path has a slash before it or not
|
|
1739
|
+
if (options.path.indexOf('/{base_path}') >= 0) {
|
|
1740
|
+
bpathStr = '/{base_path}';
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
// replace with base path if we have one, otherwise remove base path
|
|
1744
|
+
if (callProperties && callProperties.base_path) {
|
|
1745
|
+
// if no leading /, insert one
|
|
1746
|
+
if (callProperties.base_path.indexOf('/') !== 0) {
|
|
1747
|
+
options.path = options.path.replace(bpathStr, `/${callProperties.base_path}`);
|
|
1748
|
+
} else {
|
|
1749
|
+
options.path = options.path.replace(bpathStr, callProperties.base_path);
|
|
1750
|
+
}
|
|
1751
|
+
} else if (basepath) {
|
|
1752
|
+
// if no leading /, insert one
|
|
1753
|
+
if (basepath.indexOf('/') !== 0) {
|
|
1754
|
+
options.path = options.path.replace(bpathStr, `/${basepath}`);
|
|
1775
1755
|
} else {
|
|
1776
|
-
|
|
1777
|
-
idString = '/';
|
|
1778
|
-
idString += encodeURIComponent(reqBody.uriPathVars[p]);
|
|
1756
|
+
options.path = options.path.replace(bpathStr, basepath);
|
|
1779
1757
|
}
|
|
1758
|
+
} else {
|
|
1759
|
+
options.path = options.path.replace(bpathStr, '');
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// if the path has a version parameter in it, need to replace it
|
|
1764
|
+
let versStr = '{version}';
|
|
1765
|
+
if (options.path.indexOf(versStr) >= 0) {
|
|
1766
|
+
// be able to support this if the version has a slash before it or not
|
|
1767
|
+
if (options.path.indexOf('/{version}') >= 0) {
|
|
1768
|
+
versStr = '/{version}';
|
|
1769
|
+
}
|
|
1780
1770
|
|
|
1781
|
-
|
|
1782
|
-
|
|
1771
|
+
// replace with version if we have one, otherwise remove version
|
|
1772
|
+
if (callProperties && callProperties.version) {
|
|
1773
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(callProperties.version)}`);
|
|
1774
|
+
} else if (version) {
|
|
1775
|
+
options.path = options.path.replace(versStr, `/${encodeURIComponent(version)}`);
|
|
1776
|
+
} else {
|
|
1777
|
+
options.path = options.path.replace(versStr, '');
|
|
1783
1778
|
}
|
|
1784
1779
|
}
|
|
1785
|
-
}
|
|
1786
1780
|
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1781
|
+
// if there are URI path variables that have been provided, need to add
|
|
1782
|
+
// them to the path
|
|
1783
|
+
if (reqBody && reqBody.uriPathVars && reqBody.uriPathVars.length > 0) {
|
|
1784
|
+
for (let p = 0; p < reqBody.uriPathVars.length; p += 1) {
|
|
1785
|
+
const vnum = p + 1;
|
|
1786
|
+
const holder = `pathv${vnum.toString()}`;
|
|
1787
|
+
const hindex = options.path.indexOf(holder);
|
|
1788
|
+
|
|
1789
|
+
// if path variable is in the url, replace it!!!
|
|
1790
|
+
if (hindex >= 0 && reqBody.uriPathVars[p] !== null && reqBody.uriPathVars[p] !== '') {
|
|
1791
|
+
// with the provided id
|
|
1792
|
+
let idString = '';
|
|
1793
|
+
|
|
1794
|
+
// check if the current URI path ends with a slash (may require
|
|
1795
|
+
// slash at end)
|
|
1796
|
+
if (options.path[hindex - 2] === '/' || options.path[hindex - 2] === ':') {
|
|
1797
|
+
// ends with a slash need to add slash to end
|
|
1798
|
+
idString = encodeURIComponent(reqBody.uriPathVars[p]);
|
|
1799
|
+
} else {
|
|
1800
|
+
// otherwise add / to start
|
|
1801
|
+
idString = '/';
|
|
1802
|
+
idString += encodeURIComponent(reqBody.uriPathVars[p]);
|
|
1803
|
+
}
|
|
1791
1804
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1805
|
+
// replace the id in url with the id string
|
|
1806
|
+
options.path = options.path.replace(`{${holder}}`, idString);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1795
1809
|
}
|
|
1796
1810
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
let
|
|
1811
|
+
// need to remove all of the remaining path holders from the URI
|
|
1812
|
+
while (options.path.indexOf('{pathv') >= 0) {
|
|
1813
|
+
let sIndex = options.path.indexOf('{pathv');
|
|
1814
|
+
const eIndex = options.path.indexOf('}', sIndex);
|
|
1800
1815
|
|
|
1801
|
-
if
|
|
1802
|
-
|
|
1803
|
-
|
|
1816
|
+
// if there is a / before the {pathv} need to remove it
|
|
1817
|
+
if (options.path[sIndex - 1] === '/' || options.path[sIndex - 1] === ':') {
|
|
1818
|
+
sIndex -= 1;
|
|
1804
1819
|
}
|
|
1805
1820
|
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1821
|
+
if (sIndex > 0) {
|
|
1822
|
+
// add the start of the path
|
|
1823
|
+
let tempStr = options.path.substring(0, sIndex);
|
|
1824
|
+
|
|
1825
|
+
if (eIndex < options.path.length) {
|
|
1826
|
+
// add the end of the path
|
|
1827
|
+
tempStr += options.path.substring(eIndex + 1);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
options.path = tempStr;
|
|
1831
|
+
} else if (eIndex > 0 && eIndex < options.path.length) {
|
|
1832
|
+
// add the end of the path
|
|
1833
|
+
options.path = options.path.substring(eIndex + 1);
|
|
1834
|
+
} else {
|
|
1835
|
+
// should not get here - there is some issue in the uripath - missing
|
|
1836
|
+
// an end or the path is just {pathv#}
|
|
1837
|
+
// add the specific pieces of the error object
|
|
1838
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Invalid Action File', ['missing entity path', '.system/getToken'], null, null, null);
|
|
1839
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1840
|
+
return reject(errorObj);
|
|
1841
|
+
}
|
|
1817
1842
|
}
|
|
1818
|
-
}
|
|
1819
1843
|
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1844
|
+
// only add global options if there are global options to add
|
|
1845
|
+
if (globalRequest && globalRequest.uriOptions
|
|
1846
|
+
&& Object.keys(globalRequest.uriOptions).length > 0) {
|
|
1847
|
+
const optionString = querystring.stringify(globalRequest.uriOptions);
|
|
1824
1848
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1849
|
+
// if no query paramters yet - start with ?
|
|
1850
|
+
if (options.path.indexOf('?') < 0) {
|
|
1851
|
+
options.path += `?${optionString}`;
|
|
1852
|
+
} else {
|
|
1853
|
+
// if already have query parameters, add on to end
|
|
1854
|
+
options.path += `&${optionString}`;
|
|
1855
|
+
}
|
|
1831
1856
|
}
|
|
1832
|
-
}
|
|
1833
1857
|
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1858
|
+
// remove the path vars from the reqBody
|
|
1859
|
+
const actReqBody = Object.assign({}, reqBody);
|
|
1860
|
+
if (actReqBody && actReqBody.uriPathVars) {
|
|
1861
|
+
delete actReqBody.uriPathVars;
|
|
1862
|
+
}
|
|
1839
1863
|
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1864
|
+
// if ssl enabled add the options for ssl
|
|
1865
|
+
if (callProperties && callProperties.ssl && Object.hasOwnProperty.call(callProperties.ssl, 'enabled')) {
|
|
1866
|
+
if (callProperties.ssl.enabled) {
|
|
1867
|
+
if (callProperties.ssl.accept_invalid_cert) {
|
|
1868
|
+
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
1869
|
+
options.rejectUnauthorized = false;
|
|
1870
|
+
} else {
|
|
1871
|
+
// if we are not accepting invalid certs, need the ca file in the options
|
|
1872
|
+
try {
|
|
1873
|
+
options.rejectUnauthorized = true;
|
|
1874
|
+
options.ca = [fs.readFileSync(callProperties.ssl.ca_file)];
|
|
1875
|
+
} catch (e) {
|
|
1876
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [callProperties.ssl.ca_file], null, null, null);
|
|
1877
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1878
|
+
return reject(errorObj);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (callProperties.ssl.ciphers) {
|
|
1883
|
+
options.ciphers = callProperties.ssl.ciphers;
|
|
1884
|
+
}
|
|
1885
|
+
if (callProperties.ssl.secure_protocol) {
|
|
1886
|
+
options.secureProtocol = callProperties.ssl.secure_protocol;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
1890
|
+
}
|
|
1891
|
+
} else if (sslEnabled) {
|
|
1892
|
+
if (sslAcceptInvalid) {
|
|
1844
1893
|
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
1845
1894
|
options.rejectUnauthorized = false;
|
|
1846
1895
|
} else {
|
|
1847
1896
|
// if we are not accepting invalid certs, need the ca file in the options
|
|
1848
1897
|
try {
|
|
1849
1898
|
options.rejectUnauthorized = true;
|
|
1850
|
-
options.ca = [fs.readFileSync(
|
|
1899
|
+
options.ca = [fs.readFileSync(sslCAFile)];
|
|
1851
1900
|
} catch (e) {
|
|
1852
|
-
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [
|
|
1901
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCAFile], null, null, null);
|
|
1853
1902
|
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1854
|
-
return
|
|
1903
|
+
return reject(errorObj);
|
|
1904
|
+
}
|
|
1905
|
+
// if there is a cert file, try to read in a cert file in the options
|
|
1906
|
+
if (sslCertFile) {
|
|
1907
|
+
try {
|
|
1908
|
+
options.cert = [fs.readFileSync(sslCertFile)];
|
|
1909
|
+
} catch (e) {
|
|
1910
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCertFile], null, null, null);
|
|
1911
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1912
|
+
return reject(errorObj);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
// if there is a key file, try to read in a key file in the options
|
|
1916
|
+
if (sslKeyFile) {
|
|
1917
|
+
try {
|
|
1918
|
+
options.key = [fs.readFileSync(sslKeyFile)];
|
|
1919
|
+
} catch (e) {
|
|
1920
|
+
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslKeyFile], null, null, null);
|
|
1921
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1922
|
+
return reject(errorObj);
|
|
1923
|
+
}
|
|
1855
1924
|
}
|
|
1856
1925
|
}
|
|
1857
1926
|
|
|
1858
|
-
if (
|
|
1859
|
-
options.ciphers =
|
|
1860
|
-
}
|
|
1861
|
-
if (callProperties.ssl.secure_protocol) {
|
|
1862
|
-
options.secureProtocol = callProperties.ssl.secure_protocol;
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
log.info(`${origin}: Connector SSL connections enabled`);
|
|
1866
|
-
}
|
|
1867
|
-
} else if (sslEnabled) {
|
|
1868
|
-
if (sslAcceptInvalid) {
|
|
1869
|
-
// if we are accepting invalid certificates (ok for lab not so much production)
|
|
1870
|
-
options.rejectUnauthorized = false;
|
|
1871
|
-
} else {
|
|
1872
|
-
// if we are not accepting invalid certs, need the ca file in the options
|
|
1873
|
-
try {
|
|
1874
|
-
options.rejectUnauthorized = true;
|
|
1875
|
-
options.ca = [fs.readFileSync(sslCAFile)];
|
|
1876
|
-
} catch (e) {
|
|
1877
|
-
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCAFile], null, null, null);
|
|
1878
|
-
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1879
|
-
return callback(null, errorObj);
|
|
1927
|
+
if (sslCiphers) {
|
|
1928
|
+
options.ciphers = sslCiphers;
|
|
1880
1929
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
try {
|
|
1884
|
-
options.cert = [fs.readFileSync(sslCertFile)];
|
|
1885
|
-
} catch (e) {
|
|
1886
|
-
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslCertFile], null, null, null);
|
|
1887
|
-
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1888
|
-
return callback(null, errorObj);
|
|
1889
|
-
}
|
|
1930
|
+
if (sslPassphrase) {
|
|
1931
|
+
options.passphrase = sslPassphrase;
|
|
1890
1932
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
try {
|
|
1894
|
-
options.key = [fs.readFileSync(sslKeyFile)];
|
|
1895
|
-
} catch (e) {
|
|
1896
|
-
const errorObj = this.transUtil.formatErrorObject(origin, 'Missing File', [sslKeyFile], null, null, null);
|
|
1897
|
-
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
1898
|
-
return callback(null, errorObj);
|
|
1899
|
-
}
|
|
1933
|
+
if (secureProtocol) {
|
|
1934
|
+
options.secureProtocol = secureProtocol;
|
|
1900
1935
|
}
|
|
1901
|
-
}
|
|
1902
1936
|
|
|
1903
|
-
|
|
1904
|
-
options.ciphers = sslCiphers;
|
|
1905
|
-
}
|
|
1906
|
-
if (sslPassphrase) {
|
|
1907
|
-
options.passphrase = sslPassphrase;
|
|
1908
|
-
}
|
|
1909
|
-
if (secureProtocol) {
|
|
1910
|
-
options.secureProtocol = secureProtocol;
|
|
1937
|
+
log.info(`${origin}: Connector SSL connections enabled`);
|
|
1911
1938
|
}
|
|
1912
1939
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1940
|
+
const reqData = {};
|
|
1941
|
+
let bodyString = null;
|
|
1942
|
+
|
|
1943
|
+
// if we have been passed a schema and the Authorization is in a header
|
|
1944
|
+
// COMMENTED OUT AND HANDLED IN IF BELOW - take stuff out of body on line 1930
|
|
1945
|
+
// if (tokenSchema && tokenSchema.headers && tokenSchema.headers.Authorization) {
|
|
1946
|
+
// // request the token
|
|
1947
|
+
// options.headers['Content-length'] = 0;
|
|
1948
|
+
// return getToken(reqPath, options, tokenSchema, '', callProperties, callback);
|
|
1949
|
+
// }
|
|
1950
|
+
if (tokenSchema) {
|
|
1951
|
+
// if this is a get, need to put the username on the url
|
|
1952
|
+
if (options.path.indexOf('/{username}') >= 0) {
|
|
1953
|
+
options.path = options.path.replace('/{username}', useUser);
|
|
1954
|
+
} else {
|
|
1955
|
+
options.path = options.path.replace('{username}', useUser);
|
|
1956
|
+
}
|
|
1915
1957
|
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
// // request the token
|
|
1923
|
-
// options.headers['Content-length'] = 0;
|
|
1924
|
-
// return getToken(reqPath, options, tokenSchema, '', callProperties, callback);
|
|
1925
|
-
// }
|
|
1926
|
-
if (tokenSchema) {
|
|
1927
|
-
// if this is a get, need to put the username on the url
|
|
1928
|
-
if (options.path.indexOf('/{username}') >= 0) {
|
|
1929
|
-
options.path = options.path.replace('/{username}', useUser);
|
|
1930
|
-
} else {
|
|
1931
|
-
options.path = options.path.replace('{username}', useUser);
|
|
1932
|
-
}
|
|
1958
|
+
// if this is a get, need to put the password on the url
|
|
1959
|
+
if (options.path.indexOf('/{password}') >= 0) {
|
|
1960
|
+
options.path = options.path.replace('/{password}', usePass);
|
|
1961
|
+
} else {
|
|
1962
|
+
options.path = options.path.replace('{password}', usePass);
|
|
1963
|
+
}
|
|
1933
1964
|
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
options.
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1965
|
+
// if this is not a get, need to add the info to the request
|
|
1966
|
+
let creds = {};
|
|
1967
|
+
if (options.method !== 'GET') {
|
|
1968
|
+
if (authMethod === 'multi_step_authentication') {
|
|
1969
|
+
const { stepBody, stepHeaders } = callProperties.mfa;
|
|
1970
|
+
Object.assign(creds, stepBody);
|
|
1971
|
+
Object.assign(options.headers, stepHeaders);
|
|
1972
|
+
} else {
|
|
1973
|
+
creds = {
|
|
1974
|
+
username: useUser,
|
|
1975
|
+
password: usePass
|
|
1976
|
+
};
|
|
1977
|
+
if (clientId) {
|
|
1978
|
+
creds.client_id = clientId;
|
|
1979
|
+
}
|
|
1980
|
+
if (clientSecret) {
|
|
1981
|
+
creds.client_secret = clientSecret;
|
|
1982
|
+
}
|
|
1983
|
+
if (grantType) {
|
|
1984
|
+
creds.grant_type = grantType;
|
|
1985
|
+
}
|
|
1986
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.client_id) {
|
|
1987
|
+
creds.client_id = callProperties.authentication.client_id;
|
|
1988
|
+
}
|
|
1989
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.client_secret) {
|
|
1990
|
+
creds.client_secret = callProperties.authentication.client_secret;
|
|
1991
|
+
}
|
|
1992
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.grant_type) {
|
|
1993
|
+
creds.grant_type = callProperties.authentication.grant_type;
|
|
1994
|
+
}
|
|
1940
1995
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
let creds = {
|
|
1944
|
-
username: useUser,
|
|
1945
|
-
password: usePass
|
|
1946
|
-
};
|
|
1947
|
-
if (clientId) {
|
|
1948
|
-
creds.client_id = clientId;
|
|
1949
|
-
}
|
|
1950
|
-
if (clientSecret) {
|
|
1951
|
-
creds.client_secret = clientSecret;
|
|
1952
|
-
}
|
|
1953
|
-
if (grantType) {
|
|
1954
|
-
creds.grant_type = grantType;
|
|
1955
|
-
}
|
|
1956
|
-
if (callProperties && callProperties.authentication && callProperties.authentication.client_id) {
|
|
1957
|
-
creds.client_id = callProperties.authentication.client_id;
|
|
1958
|
-
}
|
|
1959
|
-
if (callProperties && callProperties.authentication && callProperties.authentication.client_secret) {
|
|
1960
|
-
creds.client_secret = callProperties.authentication.client_secret;
|
|
1961
|
-
}
|
|
1962
|
-
if (callProperties && callProperties.authentication && callProperties.authentication.grant_type) {
|
|
1963
|
-
creds.grant_type = callProperties.authentication.grant_type;
|
|
1964
|
-
}
|
|
1996
|
+
// if there is body data to add to the token request body
|
|
1997
|
+
creds = transUtilInst.mergeObjects(actReqBody, creds);
|
|
1965
1998
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1999
|
+
if (globalRequest) {
|
|
2000
|
+
creds = transUtilInst.mergeObjects(creds, globalRequest.authData);
|
|
2001
|
+
}
|
|
1968
2002
|
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2003
|
+
// if there is body data to add to the token request body
|
|
2004
|
+
if (actReqBody) {
|
|
2005
|
+
const bodyKey = Object.keys(actReqBody);
|
|
1972
2006
|
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2007
|
+
for (let k = 0; k < bodyKey.length; k += 1) {
|
|
2008
|
+
creds[bodyKey[k]] = actReqBody[bodyKey[k]];
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1976
2011
|
|
|
1977
|
-
|
|
1978
|
-
|
|
2012
|
+
if (tokenSchema.headers && tokenSchema.headers.Authorization) {
|
|
2013
|
+
// remove the username and password from the creds
|
|
2014
|
+
delete creds.username;
|
|
2015
|
+
delete creds.password;
|
|
2016
|
+
}
|
|
1979
2017
|
}
|
|
1980
|
-
}
|
|
1981
2018
|
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
delete creds.password;
|
|
1986
|
-
}
|
|
2019
|
+
// map the data we received to an Entity - will get back the defaults
|
|
2020
|
+
const tokenEntity = transUtilInst.mapToOutboundEntity(creds, tokenSchema.requestSchema);
|
|
2021
|
+
bodyString = tokenEntity;
|
|
1987
2022
|
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2023
|
+
// if it is JSON or URLENCODE need to put body into right format
|
|
2024
|
+
if (!tokenSchema.requestDatatype || tokenSchema.requestDatatype.toUpperCase() === 'JSON' || tokenSchema.requestDatatype.toUpperCase() === 'FORM') {
|
|
2025
|
+
bodyString = JSON.stringify(tokenEntity);
|
|
2026
|
+
} else if (tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
2027
|
+
bodyString = querystring.stringify(tokenEntity);
|
|
2028
|
+
} else if (tokenSchema.requestDatatype && tokenSchema.requestDatatype.toUpperCase() === 'URLQUERY') {
|
|
2029
|
+
// if the datatype is URLQUERY need to put into the query on the request
|
|
2030
|
+
if (authQueryEncode != null) {
|
|
2031
|
+
if (authQueryEncode === true) {
|
|
2032
|
+
bodyString = querystring.stringify(tokenEntity);
|
|
2033
|
+
} else {
|
|
2034
|
+
bodyString = '';
|
|
2035
|
+
// if not encoding we need to build
|
|
2036
|
+
const qkeys = Object.keys(tokenEntity);
|
|
2037
|
+
// add each query parameter and its value
|
|
2038
|
+
for (let k = 0; k < qkeys.length; k += 1) {
|
|
2039
|
+
// need to add separator for everything after the first one
|
|
2040
|
+
if (k > 0) {
|
|
2041
|
+
bodyString += '&';
|
|
2042
|
+
}
|
|
2043
|
+
// adds key=value
|
|
2044
|
+
bodyString += `${qkeys[k]}=${tokenEntity[qkeys[k]]}`;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
} else if (encodeUri === true) {
|
|
2001
2048
|
bodyString = querystring.stringify(tokenEntity);
|
|
2002
2049
|
} else {
|
|
2003
2050
|
bodyString = '';
|
|
@@ -2013,60 +2060,58 @@ function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
|
2013
2060
|
bodyString += `${qkeys[k]}=${tokenEntity[qkeys[k]]}`;
|
|
2014
2061
|
}
|
|
2015
2062
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
// add each query parameter and its value
|
|
2023
|
-
for (let k = 0; k < qkeys.length; k += 1) {
|
|
2024
|
-
// need to add separator for everything after the first one
|
|
2025
|
-
if (k > 0) {
|
|
2026
|
-
bodyString += '&';
|
|
2027
|
-
}
|
|
2028
|
-
// adds key=value
|
|
2029
|
-
bodyString += `${qkeys[k]}=${tokenEntity[qkeys[k]]}`;
|
|
2063
|
+
|
|
2064
|
+
// append to the path
|
|
2065
|
+
if (options.path.indexOf('?') < 0) {
|
|
2066
|
+
options.path = `${options.path}?${bodyString}`;
|
|
2067
|
+
} else {
|
|
2068
|
+
options.path = `${options.path}&${bodyString}`;
|
|
2030
2069
|
}
|
|
2070
|
+
bodyString = '';
|
|
2031
2071
|
}
|
|
2032
|
-
|
|
2033
|
-
//
|
|
2034
|
-
if (
|
|
2035
|
-
options.
|
|
2036
|
-
} else {
|
|
2037
|
-
options.path = `${options.path}&${bodyString}`;
|
|
2072
|
+
// if there is a body, set the content length of the body and add it to
|
|
2073
|
+
// the header
|
|
2074
|
+
if (Object.keys(tokenEntity).length > 0 || tokenSchema.sendEmpty) {
|
|
2075
|
+
options.headers['Content-length'] = Buffer.byteLength(bodyString);
|
|
2038
2076
|
}
|
|
2039
|
-
bodyString = '';
|
|
2040
|
-
}
|
|
2041
2077
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2078
|
+
// request the token
|
|
2079
|
+
await getToken(reqPath, options, tokenSchema, bodyString, callProperties).then((result) => {
|
|
2080
|
+
resolve(result);
|
|
2081
|
+
}).catch((error) => {
|
|
2082
|
+
reject(error);
|
|
2083
|
+
});
|
|
2084
|
+
return;
|
|
2046
2085
|
}
|
|
2047
|
-
|
|
2048
|
-
//
|
|
2049
|
-
|
|
2086
|
+
} else {
|
|
2087
|
+
// set the user and password for the token request
|
|
2088
|
+
reqData[tokenUserField] = useUser;
|
|
2089
|
+
reqData[tokenPwdField] = usePass;
|
|
2090
|
+
bodyString = reqData;
|
|
2091
|
+
|
|
2092
|
+
// since not a get call, convert reqData to a string and add length
|
|
2093
|
+
bodyString = JSON.stringify(reqData);
|
|
2094
|
+
options.headers['Content-length'] = Buffer.byteLength(bodyString);
|
|
2050
2095
|
}
|
|
2051
|
-
} else {
|
|
2052
|
-
// set the user and password for the token request
|
|
2053
|
-
reqData[tokenUserField] = useUser;
|
|
2054
|
-
reqData[tokenPwdField] = usePass;
|
|
2055
|
-
bodyString = reqData;
|
|
2056
2096
|
|
|
2057
|
-
//
|
|
2058
|
-
bodyString
|
|
2059
|
-
|
|
2060
|
-
|
|
2097
|
+
// request the token
|
|
2098
|
+
await getToken(reqPath, options, tokenSchema, bodyString, callProperties).then((result) => {
|
|
2099
|
+
resolve(result);
|
|
2100
|
+
}).catch((error) => {
|
|
2101
|
+
reject(error);
|
|
2102
|
+
});
|
|
2103
|
+
});
|
|
2104
|
+
} catch (e) {
|
|
2105
|
+
// handle any exception
|
|
2106
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue requesting token');
|
|
2107
|
+
return reject(errorObj);
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2061
2110
|
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
});
|
|
2065
|
-
} catch (e) {
|
|
2066
|
-
// handle any exception
|
|
2067
|
-
const errorObj = transUtilInst.checkAndReturn(e, origin, 'Issue requesting token');
|
|
2068
|
-
return callback(null, errorObj);
|
|
2111
|
+
if (typeof callback === 'function') {
|
|
2112
|
+
return promise.then((result) => callback(result)).catch((error) => callback(null, error));
|
|
2069
2113
|
}
|
|
2114
|
+
return promise;
|
|
2070
2115
|
}
|
|
2071
2116
|
|
|
2072
2117
|
/*
|
|
@@ -2413,6 +2458,264 @@ function addAuthToRequest(request, authStrs, callProperties, callback) {
|
|
|
2413
2458
|
}
|
|
2414
2459
|
}
|
|
2415
2460
|
|
|
2461
|
+
/* */
|
|
2462
|
+
function buildAuthDataMfa(finalTokens, callProperties) {
|
|
2463
|
+
const authStrs = [];
|
|
2464
|
+
if (callProperties && callProperties.authentication && callProperties.authentication.auth_field_format) {
|
|
2465
|
+
if (Array.isArray(callProperties.authentication.auth_field_format)) {
|
|
2466
|
+
for (let a = 0; a < callProperties.authentication.auth_field_format.length; a += 1) {
|
|
2467
|
+
let authStr = callProperties.authentication.auth_field_format[a].replace('{token}', finalTokens.token);
|
|
2468
|
+
authStr = authStr.replace('{tokenp2}', finalTokens.tokenp2);
|
|
2469
|
+
authStrs.push(authStr);
|
|
2470
|
+
}
|
|
2471
|
+
} else {
|
|
2472
|
+
let authStr = callProperties.authentication.auth_field_format.replace('{token}', finalTokens.token);
|
|
2473
|
+
authStr = authStr.replace('{tokenp2}', finalTokens.tokenp2);
|
|
2474
|
+
authStrs.push(authStr);
|
|
2475
|
+
}
|
|
2476
|
+
} else {
|
|
2477
|
+
for (let a = 0; a < authFormat.length; a += 1) {
|
|
2478
|
+
let authStr = authFormat[a].replace('{token}', finalTokens.token);
|
|
2479
|
+
authStr = authStr.replace('{tokenp2}', finalTokens.tokenp2);
|
|
2480
|
+
// if authFormat needs referenced value from previous steps (e.g., Bearer {getSession.responseFields.session})
|
|
2481
|
+
if (!authFormat[a].includes('{token}') && !authFormat[a].includes('{tokenp2}')) {
|
|
2482
|
+
const matching = searchTextInCurlyBraces(authStr);
|
|
2483
|
+
let propertyValue = authFormat[a];
|
|
2484
|
+
if (matching) {
|
|
2485
|
+
const matchedText = matching[0];
|
|
2486
|
+
propertyValue = matching[1];
|
|
2487
|
+
const resolvedValue = getReferencedMfaValue(propertyValue);
|
|
2488
|
+
authStr = authStr.replace(matchedText, resolvedValue);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
authStrs.push(authStr);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return authStrs;
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
/* */
|
|
2498
|
+
const searchTextInCurlyBraces = (text) => {
|
|
2499
|
+
const rxp = /{([^}]+)}/g;
|
|
2500
|
+
const matching = rxp.exec(text);
|
|
2501
|
+
return matching;
|
|
2502
|
+
};
|
|
2503
|
+
|
|
2504
|
+
/* */
|
|
2505
|
+
function buildMfaHeader(prop, mfaStepConfiguration, callProperties) {
|
|
2506
|
+
const origin = `${id}-connectorRest-buildMfaHeader`;
|
|
2507
|
+
log.trace(origin);
|
|
2508
|
+
const fullPropertyValue = mfaStepConfiguration.requestFields[prop];
|
|
2509
|
+
// Check if fullPropertyValue has reference in curly braces (e.g., Bearer {getSession.responseFields.session})
|
|
2510
|
+
let propertyValue = fullPropertyValue;
|
|
2511
|
+
const matching = searchTextInCurlyBraces(fullPropertyValue);
|
|
2512
|
+
const headerName = prop.split('.')[1];
|
|
2513
|
+
if (matching) {
|
|
2514
|
+
const matchedText = matching[0];
|
|
2515
|
+
propertyValue = matching[1];
|
|
2516
|
+
const resolvedValue = getReferencedMfaValue(propertyValue);
|
|
2517
|
+
callProperties.mfa.stepHeaders[headerName] = fullPropertyValue.replace(matchedText, resolvedValue);
|
|
2518
|
+
} else {
|
|
2519
|
+
callProperties.mfa.stepHeaders[headerName] = mfaStepConfiguration.requestFields[prop];
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
/* */
|
|
2524
|
+
function buildMfaBody(prop, mfaStepConfiguration, callProperties) {
|
|
2525
|
+
const origin = `${id}-connectorRest-buildMfaBody`;
|
|
2526
|
+
log.trace(origin);
|
|
2527
|
+
const fullPropertyValue = mfaStepConfiguration.requestFields[prop];
|
|
2528
|
+
// Check if fullPropertyValue has reference in curly braces (e.g., {getSubToken.responseFields.token})
|
|
2529
|
+
let propertyValue = fullPropertyValue;
|
|
2530
|
+
const matching = searchTextInCurlyBraces(fullPropertyValue);
|
|
2531
|
+
if (matching) {
|
|
2532
|
+
const matchedText = matching[0];
|
|
2533
|
+
propertyValue = matching[1];
|
|
2534
|
+
const resolvedValue = getReferencedMfaValue(propertyValue);
|
|
2535
|
+
callProperties.mfa.stepBody[prop] = fullPropertyValue.replace(matchedText, resolvedValue);
|
|
2536
|
+
} else {
|
|
2537
|
+
callProperties.mfa.stepBody[prop] = mfaStepConfiguration.requestFields[prop];
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
/* */
|
|
2541
|
+
function getReferencedMfaValue(propertyValue) {
|
|
2542
|
+
const origin = `${id}-connectorRest-getReferencedMfaValue`;
|
|
2543
|
+
log.trace(origin);
|
|
2544
|
+
let stepRequestField = null;
|
|
2545
|
+
// e.g. prop value is: 'getSession.responseFields.session'
|
|
2546
|
+
const referencedStepName = propertyValue.split('.')[0]; // getSession
|
|
2547
|
+
const referencedPropertyName = propertyValue.split('.')[2]; // session
|
|
2548
|
+
const referencedMfaCallConfig = multiStepAuthCalls.find((item) => item.name === referencedStepName);
|
|
2549
|
+
const referencedMfaCallResult = mfaStepsResults.find((item) => item.name === referencedStepName);
|
|
2550
|
+
if (referencedMfaCallConfig.responseFields[referencedPropertyName]) {
|
|
2551
|
+
stepRequestField = referencedMfaCallResult.result[referencedPropertyName];
|
|
2552
|
+
}
|
|
2553
|
+
return stepRequestField;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
/* */
|
|
2557
|
+
function buildMfaStepData(step, callProperties) {
|
|
2558
|
+
const origin = `${id}-connectorRest-buildMfaStepData`;
|
|
2559
|
+
log.trace(origin);
|
|
2560
|
+
const mfaStepConfiguration = multiStepAuthCalls[step];
|
|
2561
|
+
callProperties.mfa.stepBody = {};
|
|
2562
|
+
callProperties.mfa.stepHeaders = {};
|
|
2563
|
+
if (mfaStepConfiguration.requestFields) {
|
|
2564
|
+
Object.keys(mfaStepConfiguration.requestFields).forEach((prop) => {
|
|
2565
|
+
if (prop.startsWith('header')) {
|
|
2566
|
+
buildMfaHeader(prop, mfaStepConfiguration, callProperties);
|
|
2567
|
+
} else {
|
|
2568
|
+
buildMfaBody(prop, mfaStepConfiguration, callProperties);
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
/* */
|
|
2575
|
+
async function translateMfaStepResult(step, stepResult, callProperties) {
|
|
2576
|
+
const origin = `${id}-connectorRest-translateMfaStepResult`;
|
|
2577
|
+
const mfaStepConfiguration = multiStepAuthCalls[step];
|
|
2578
|
+
const entity = callProperties.mfa.stepAtionName;
|
|
2579
|
+
// define the fields that are not mapped to its external name but keep its name
|
|
2580
|
+
// needed as 'expires' handling is hardcoded in findExpireInResult()
|
|
2581
|
+
const immutableFields = ['expires'];
|
|
2582
|
+
return new Promise((resolve, reject) => {
|
|
2583
|
+
// Get the entity schema from the file system
|
|
2584
|
+
propUtilInst.getEntitySchema('.system', entity, choosepath, this.dbUtil, async (tokenSchema, healthError) => {
|
|
2585
|
+
if (healthError) return reject(healthError);
|
|
2586
|
+
const allProperties = Object.keys(tokenSchema.responseSchema.properties);
|
|
2587
|
+
allProperties.forEach((prop) => {
|
|
2588
|
+
if (Object.prototype.hasOwnProperty.call(stepResult, prop)) {
|
|
2589
|
+
const externalName = tokenSchema.responseSchema.properties[prop].external_name;
|
|
2590
|
+
if (!stepResult[prop]) {
|
|
2591
|
+
log.error(`No step-${step + 1} result for responseSchema (prop=>external_name): (${prop}=>${externalName}) found in step response`);
|
|
2592
|
+
return reject(new Error(`Response schema for step-${step + 1} misconfiguration`));
|
|
2593
|
+
}
|
|
2594
|
+
// map response schema attribute name to MFA configured response field name
|
|
2595
|
+
let responseFieldName = Object.keys(mfaStepConfiguration.responseFields).find((key) => mfaStepConfiguration.responseFields[key] === externalName);
|
|
2596
|
+
if (!responseFieldName) {
|
|
2597
|
+
log.warn(`${origin}-Unable to map property: '${prop}' to external name: '${externalName}'. Check your mfa step ${step} configuration`);
|
|
2598
|
+
responseFieldName = prop;
|
|
2599
|
+
}
|
|
2600
|
+
if (!responseFieldName) {
|
|
2601
|
+
log.warn(`${origin}-Unable to map property: '${prop}'. Check your mfa step ${step} configuration`);
|
|
2602
|
+
}
|
|
2603
|
+
stepResult[responseFieldName] = stepResult[prop];
|
|
2604
|
+
// remove original property from response after mapping
|
|
2605
|
+
if (prop !== externalName && responseFieldName !== prop && immutableFields.indexOf(prop) === -1) delete stepResult[prop];
|
|
2606
|
+
}
|
|
2607
|
+
});
|
|
2608
|
+
return resolve(stepResult);
|
|
2609
|
+
});
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
/* Promisification of called callback-based function */
|
|
2614
|
+
function getCachedMfaToken(cachedTokenIdentifier, invalidToken) {
|
|
2615
|
+
const origin = `${id}-connectorRest-getCachedMfaToken`;
|
|
2616
|
+
log.trace(origin);
|
|
2617
|
+
|
|
2618
|
+
return new Promise((resolve, reject) => {
|
|
2619
|
+
validToken(id, cachedTokenIdentifier, invalidToken, (retToken, verror) => {
|
|
2620
|
+
if (verror) {
|
|
2621
|
+
reject(verror);
|
|
2622
|
+
} else {
|
|
2623
|
+
resolve(retToken);
|
|
2624
|
+
}
|
|
2625
|
+
});
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
/* Promisification of called callback-based function */
|
|
2630
|
+
function cacheMfaToken(cachedTokenIdentifier, token) {
|
|
2631
|
+
const origin = `${id}-connectorRest-cacheMfaToken`;
|
|
2632
|
+
log.trace(origin);
|
|
2633
|
+
|
|
2634
|
+
let timeout = tokenTimeout;
|
|
2635
|
+
// if we should use the timeout from the token request
|
|
2636
|
+
if (timeout === 0) {
|
|
2637
|
+
timeout = findExpireInResult(token);
|
|
2638
|
+
} else {
|
|
2639
|
+
// otherwise add the timeout to the current time
|
|
2640
|
+
timeout += new Date().getTime();
|
|
2641
|
+
}
|
|
2642
|
+
return new Promise((resolve, reject) => {
|
|
2643
|
+
addTokenItem(id, cachedTokenIdentifier, token, timeout, (addedtoken, error) => {
|
|
2644
|
+
if (error) {
|
|
2645
|
+
reject(error);
|
|
2646
|
+
} else {
|
|
2647
|
+
resolve(addedtoken);
|
|
2648
|
+
}
|
|
2649
|
+
});
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
/* */
|
|
2654
|
+
async function getMfaFinalTokens(request, callProperties, invalidToken) {
|
|
2655
|
+
const origin = `${id}-connectorRest-getMfaFinalTokens`;
|
|
2656
|
+
let finalTokens = null; // final authentication token of the last step
|
|
2657
|
+
let stepToken = null; // result of given step request
|
|
2658
|
+
// token locked function
|
|
2659
|
+
const f = async () => {
|
|
2660
|
+
// retrieve token from cache and return it if still valid
|
|
2661
|
+
// adapter's Id and 1st item of MFA config is used as token identifier in cache
|
|
2662
|
+
const cachedTokenIdentifier = multiStepAuthCalls[0];
|
|
2663
|
+
const cachedToken = await getCachedMfaToken(cachedTokenIdentifier, invalidToken)
|
|
2664
|
+
.catch((error) => {
|
|
2665
|
+
log.error(error);
|
|
2666
|
+
throw error;
|
|
2667
|
+
});
|
|
2668
|
+
if (cachedToken) {
|
|
2669
|
+
log.debug(`${origin}-returning cached MFA token`);
|
|
2670
|
+
finalTokens = cachedToken;
|
|
2671
|
+
return;
|
|
2672
|
+
} mfaStepsResults.length = 0;
|
|
2673
|
+
// no cached token found, trigger auth steps to obtain new token
|
|
2674
|
+
for (let step = 0; step < multiStepAuthCalls.length; step += 1) {
|
|
2675
|
+
const stepAtionName = `MFA_Step_${step + 1}`;
|
|
2676
|
+
log.debug(`${origin}-Executing MFA step-${step + 1}, action: ${stepAtionName}`);
|
|
2677
|
+
if (!callProperties) {
|
|
2678
|
+
callProperties = {};
|
|
2679
|
+
}
|
|
2680
|
+
callProperties.mfa = { stepAtionName };
|
|
2681
|
+
if (multiStepAuthCalls[step].successfullResponseCode) {
|
|
2682
|
+
callProperties.mfa.successfullResponseCode = multiStepAuthCalls[step].successfullResponseCode;
|
|
2683
|
+
}
|
|
2684
|
+
buildMfaStepData(step, callProperties);
|
|
2685
|
+
stepToken = await buildTokenRequest(request.header.path, request.authData, callProperties).catch((error) => { // eslint-disable-line no-await-in-loop
|
|
2686
|
+
log.error(error);
|
|
2687
|
+
throw error;
|
|
2688
|
+
});
|
|
2689
|
+
log.debug(`${origin}-MFA result for step-${step + 1}, stepToken: ${JSON.stringify(stepToken)}`);
|
|
2690
|
+
// one of steps failing results in whole authentication chain failure
|
|
2691
|
+
if (!stepToken) {
|
|
2692
|
+
finalTokens = null;
|
|
2693
|
+
break;
|
|
2694
|
+
}
|
|
2695
|
+
const translatedStepResult = await translateMfaStepResult(step, stepToken, callProperties) // eslint-disable-line no-await-in-loop
|
|
2696
|
+
.catch((error) => {
|
|
2697
|
+
log.error(error);
|
|
2698
|
+
throw error;
|
|
2699
|
+
});
|
|
2700
|
+
mfaStepsResults.push({
|
|
2701
|
+
name: multiStepAuthCalls[step].name,
|
|
2702
|
+
result: translatedStepResult
|
|
2703
|
+
});
|
|
2704
|
+
// result of last MFA step request shall contain the final token used to authenticate subsequent requests
|
|
2705
|
+
finalTokens = stepToken;
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
// save final MFA token to cache for reuse in subsequent requests
|
|
2709
|
+
if (finalTokens) {
|
|
2710
|
+
await cacheMfaToken(cachedTokenIdentifier, finalTokens).catch((error) => log.error(error));
|
|
2711
|
+
}
|
|
2712
|
+
};
|
|
2713
|
+
// Aquire token lock across all MFA steps,
|
|
2714
|
+
// release it when final token is obtained or one of the steps fails
|
|
2715
|
+
await tlock.acquire(tokenlock, f);
|
|
2716
|
+
|
|
2717
|
+
return finalTokens;
|
|
2718
|
+
}
|
|
2416
2719
|
/*
|
|
2417
2720
|
* INTERNAL FUNCTION: requestAuthenticate determines the authentication for System,
|
|
2418
2721
|
* and takes appropriate action to authenticate and then makes the request
|
|
@@ -2433,6 +2736,34 @@ function requestAuthenticate(request, entitySchema, invalidToken, callProperties
|
|
|
2433
2736
|
usePass = callProperties.authentication.password;
|
|
2434
2737
|
}
|
|
2435
2738
|
|
|
2739
|
+
if (authMethod === 'multi_step_authentication') {
|
|
2740
|
+
// set MFA configuration out of adapter's configuration
|
|
2741
|
+
multiStepAuthCalls = props.authentication.multiStepAuthCalls;
|
|
2742
|
+
return getMfaFinalTokens(request, callProperties, invalidToken).then((finalTokens) => {
|
|
2743
|
+
if (!finalTokens) {
|
|
2744
|
+
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Token', [useUser], null, null, null);
|
|
2745
|
+
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
2746
|
+
return callback(null, errorObj);
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
const authStrs = buildAuthDataMfa(finalTokens, callProperties);
|
|
2750
|
+
|
|
2751
|
+
return addAuthToRequest(request, authStrs, callProperties, (authReq, aerror) => {
|
|
2752
|
+
if (aerror) {
|
|
2753
|
+
return callback(aerror);
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
request.tokenUsed = authReq.token;
|
|
2757
|
+
|
|
2758
|
+
// actually make the request now that the authentication has been added
|
|
2759
|
+
return makeRequest(request, entitySchema, callProperties, null, 0, callback);
|
|
2760
|
+
});
|
|
2761
|
+
}).catch((error) => {
|
|
2762
|
+
log.error(`${origin}-${JSON.stringify(error)}`);
|
|
2763
|
+
return callback(null, error);
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2436
2767
|
if (authMethod === 'request_token') {
|
|
2437
2768
|
// are we working with reusing tokens until they expire?
|
|
2438
2769
|
if (tokenTimeout >= 0) {
|
|
@@ -3407,6 +3738,10 @@ class ConnectorRest {
|
|
|
3407
3738
|
authMethod = props.authentication.auth_method;
|
|
3408
3739
|
}
|
|
3409
3740
|
|
|
3741
|
+
if (Array.isArray(props.authentication.multiStepAuthCalls)) {
|
|
3742
|
+
multiStepAuthCalls = props.authentication.multiStepAuthCalls;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3410
3745
|
// set the username (required - default is null)
|
|
3411
3746
|
if (typeof props.authentication.username === 'string') {
|
|
3412
3747
|
username = props.authentication.username;
|