@itentialopensource/adapter-utils 5.10.28 → 5.11.0
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/.eslintrc.js +1 -0
- package/lib/connectorRest.js +114 -37
- package/package.json +1 -1
- package/schemas/propertiesSchema.json +38 -0
package/.eslintrc.js
CHANGED
package/lib/connectorRest.js
CHANGED
|
@@ -36,6 +36,18 @@ const validator = require('validator');
|
|
|
36
36
|
|
|
37
37
|
const ThrottleCl = require(path.join(__dirname, '/throttle.js'));
|
|
38
38
|
|
|
39
|
+
// Load properties schema to extract valid enum values
|
|
40
|
+
const propertiesSchema = require(path.join(__dirname, '../schemas/propertiesSchema.json'));
|
|
41
|
+
|
|
42
|
+
// Token placement constants
|
|
43
|
+
const TokenPlacement = Object.freeze({
|
|
44
|
+
HEADER: 'HEADER',
|
|
45
|
+
BODY: 'BODY',
|
|
46
|
+
XML2JSON: 'XML2JSON',
|
|
47
|
+
QUERY: 'QUERY',
|
|
48
|
+
PATH: 'PATH'
|
|
49
|
+
});
|
|
50
|
+
|
|
39
51
|
const allowFailover = 'AD.300';
|
|
40
52
|
const noFailover = 'AD.500';
|
|
41
53
|
let transUtilInst = null;
|
|
@@ -113,6 +125,32 @@ let refTokenReq = null;
|
|
|
113
125
|
let refTokenTimeout = -1;
|
|
114
126
|
let runRefreshToken = false;
|
|
115
127
|
let addSensitiveItems = [];
|
|
128
|
+
let authRequestDatatype = null;
|
|
129
|
+
let authResponseDatatype = null;
|
|
130
|
+
let tokenResponsePlacement = null;
|
|
131
|
+
// Extract valid values from schema (filter out empty string which is just for default)
|
|
132
|
+
const VALID_AUTH_REQUEST_DATATYPES = propertiesSchema.definitions.authentication.properties.auth_request_datatype.enum.filter((v) => v !== '');
|
|
133
|
+
const VALID_AUTH_RESPONSE_DATATYPES = propertiesSchema.definitions.authentication.properties.auth_response_datatype.enum.filter((v) => v !== '');
|
|
134
|
+
const VALID_TOKEN_RESPONSE_PLACEMENTS = propertiesSchema.definitions.authentication.properties.token_response_placement.enum.filter((v) => v !== '');
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Validates an authentication property value against a list of valid values
|
|
138
|
+
* @param {*} value - The value to validate
|
|
139
|
+
* @param {Array} validValues - Array of valid values
|
|
140
|
+
* @param {string} propertyName - Name of the property for error messaging
|
|
141
|
+
* @returns {string|undefined} - Returns the validated value or undefined if invalid/empty
|
|
142
|
+
*/
|
|
143
|
+
function validateAuthProperty(value, validValues, propertyName) {
|
|
144
|
+
if (typeof value !== 'string' || value === '') {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!validValues.includes(value)) {
|
|
149
|
+
throw new Error(`Invalid ${propertyName}: '${value}'. Must be one of: ${validValues.join(', ')}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
116
154
|
|
|
117
155
|
// Other global variables
|
|
118
156
|
let id = null;
|
|
@@ -1100,6 +1138,20 @@ function findExpireInResult(result) {
|
|
|
1100
1138
|
return expire;
|
|
1101
1139
|
}
|
|
1102
1140
|
|
|
1141
|
+
/*
|
|
1142
|
+
* INTERNAL FUNCTION: gets the effective token placement
|
|
1143
|
+
* (adapter property overrides schema)
|
|
1144
|
+
*/
|
|
1145
|
+
function getTokenPlacement(tokenSchema, tokenProperty) {
|
|
1146
|
+
if (tokenResponsePlacement) {
|
|
1147
|
+
return tokenResponsePlacement.toUpperCase();
|
|
1148
|
+
}
|
|
1149
|
+
if (tokenSchema?.responseSchema?.properties?.[tokenProperty]?.placement) {
|
|
1150
|
+
return tokenSchema.responseSchema.properties[tokenProperty].placement.toUpperCase();
|
|
1151
|
+
}
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1103
1155
|
/*
|
|
1104
1156
|
* INTERNAL FUNCTION: makes the request and processes the response
|
|
1105
1157
|
* for the request to get the token
|
|
@@ -1107,6 +1159,7 @@ function findExpireInResult(result) {
|
|
|
1107
1159
|
async function getToken(reqPath, options, tokenSchema, bodyString, callProperties, callback) {
|
|
1108
1160
|
const origin = `${id}-connectorRest-getToken`;
|
|
1109
1161
|
log.trace(origin);
|
|
1162
|
+
const responseDatatype = authResponseDatatype || (tokenSchema && tokenSchema.responseDatatype);
|
|
1110
1163
|
|
|
1111
1164
|
const p = new Promise((resolve, reject) => {
|
|
1112
1165
|
try {
|
|
@@ -1225,8 +1278,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1225
1278
|
};
|
|
1226
1279
|
|
|
1227
1280
|
// process primary token from header
|
|
1228
|
-
if (tokenSchema
|
|
1229
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'HEADER') {
|
|
1281
|
+
if (getTokenPlacement(tokenSchema, 'token') === TokenPlacement.HEADER) {
|
|
1230
1282
|
if (!tokenSchema.responseSchema.properties.token.external_name) {
|
|
1231
1283
|
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Primary Token', ['Primary Token', result.code], null, null, null);
|
|
1232
1284
|
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
@@ -1306,8 +1358,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1306
1358
|
}
|
|
1307
1359
|
|
|
1308
1360
|
// process second token from header
|
|
1309
|
-
if (tokenSchema
|
|
1310
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'HEADER') {
|
|
1361
|
+
if (getTokenPlacement(tokenSchema, 'tokenp2') === TokenPlacement.HEADER) {
|
|
1311
1362
|
if (!tokenSchema.responseSchema.properties.tokenp2.external_name) {
|
|
1312
1363
|
const errorObj = transUtilInst.formatErrorObject(origin, 'Unable To Get Secondary Token', ['Secondary Token', result.code], null, null, null);
|
|
1313
1364
|
log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
|
|
@@ -1388,11 +1439,10 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1388
1439
|
|
|
1389
1440
|
// process the body
|
|
1390
1441
|
// if response is just a string
|
|
1391
|
-
if (
|
|
1442
|
+
if (responseDatatype && responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1392
1443
|
log.debug('Attempting to get tokens from text body repsonse');
|
|
1393
1444
|
|
|
1394
|
-
if (tokenSchema
|
|
1395
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1445
|
+
if (getTokenPlacement(tokenSchema, 'token') === TokenPlacement.BODY) {
|
|
1396
1446
|
currResult.token = result.response;
|
|
1397
1447
|
|
|
1398
1448
|
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
@@ -1400,8 +1450,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1400
1450
|
currResult.token = currResult.token.substring(1, currResult.token.length - 1);
|
|
1401
1451
|
}
|
|
1402
1452
|
}
|
|
1403
|
-
if (tokenSchema
|
|
1404
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1453
|
+
if (getTokenPlacement(tokenSchema, 'tokenp2') === TokenPlacement.BODY) {
|
|
1405
1454
|
currResult.tokenp2 = result.response;
|
|
1406
1455
|
|
|
1407
1456
|
// if we got a stringified string - we can remove the double quotes wrapping it
|
|
@@ -1413,15 +1462,14 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1413
1462
|
// return the string as there is no other processing needed
|
|
1414
1463
|
return resolve(currResult);
|
|
1415
1464
|
}
|
|
1416
|
-
|
|
1465
|
+
|
|
1466
|
+
if (responseDatatype && responseDatatype.toUpperCase() === 'XML') {
|
|
1417
1467
|
log.debug('Attempting to get tokens from XML body repsonse');
|
|
1418
1468
|
|
|
1419
|
-
if (tokenSchema
|
|
1420
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1469
|
+
if (getTokenPlacement(tokenSchema, 'token') === TokenPlacement.BODY) {
|
|
1421
1470
|
currResult.token = result.response;
|
|
1422
1471
|
}
|
|
1423
|
-
if (tokenSchema
|
|
1424
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1472
|
+
if (getTokenPlacement(tokenSchema, 'tokenp2') === TokenPlacement.BODY) {
|
|
1425
1473
|
currResult.tokenp2 = result.response;
|
|
1426
1474
|
}
|
|
1427
1475
|
|
|
@@ -1432,7 +1480,7 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1432
1480
|
// if response can be put into a JSON object
|
|
1433
1481
|
let tempResult = null;
|
|
1434
1482
|
if (result.response) {
|
|
1435
|
-
if (
|
|
1483
|
+
if (responseDatatype && responseDatatype.toUpperCase() === TokenPlacement.XML2JSON) {
|
|
1436
1484
|
log.debug('Attempting to get tokens from XML2JSON repsonse');
|
|
1437
1485
|
|
|
1438
1486
|
try {
|
|
@@ -1446,17 +1494,15 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1446
1494
|
});
|
|
1447
1495
|
} catch (ex) {
|
|
1448
1496
|
log.warn(`${origin}: Unable to get json from xml ${ex}`);
|
|
1449
|
-
if (tokenSchema
|
|
1450
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1497
|
+
if (getTokenPlacement(tokenSchema, 'token') === TokenPlacement.BODY) {
|
|
1451
1498
|
currResult.token = result.response;
|
|
1452
1499
|
}
|
|
1453
|
-
if (tokenSchema
|
|
1454
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1500
|
+
if (getTokenPlacement(tokenSchema, 'tokenp2') === TokenPlacement.BODY) {
|
|
1455
1501
|
currResult.tokenp2 = result.response;
|
|
1456
1502
|
}
|
|
1457
1503
|
}
|
|
1458
1504
|
}
|
|
1459
|
-
if (
|
|
1505
|
+
if (responseDatatype && responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1460
1506
|
log.debug('Attempting to get tokens from URLENCODE repsonse');
|
|
1461
1507
|
tempResult = querystring.parse(result.response.trim());
|
|
1462
1508
|
} else {
|
|
@@ -1509,20 +1555,16 @@ async function getToken(reqPath, options, tokenSchema, bodyString, callPropertie
|
|
|
1509
1555
|
translated = translated[0];
|
|
1510
1556
|
}
|
|
1511
1557
|
|
|
1512
|
-
if (tokenSchema
|
|
1513
|
-
&& tokenSchema.responseSchema.properties.token.placement && tokenSchema.responseSchema.properties.token.placement.toUpperCase() === 'BODY') {
|
|
1558
|
+
if (getTokenPlacement(tokenSchema, 'token') === TokenPlacement.BODY) {
|
|
1514
1559
|
currResult.token = translated.token;
|
|
1515
1560
|
}
|
|
1516
|
-
if (tokenSchema
|
|
1517
|
-
&& tokenSchema.responseSchema.properties.tokenp2.placement && tokenSchema.responseSchema.properties.tokenp2.placement.toUpperCase() === 'BODY') {
|
|
1561
|
+
if (getTokenPlacement(tokenSchema, 'tokenp2') === TokenPlacement.BODY) {
|
|
1518
1562
|
currResult.tokenp2 = translated.tokenp2;
|
|
1519
1563
|
}
|
|
1520
|
-
if (tokenSchema
|
|
1521
|
-
&& tokenSchema.responseSchema.properties.expires.placement && tokenSchema.responseSchema.properties.expires.placement.toUpperCase() === 'BODY') {
|
|
1564
|
+
if (getTokenPlacement(tokenSchema, 'expires') === TokenPlacement.BODY) {
|
|
1522
1565
|
currResult.expires = translated.expires;
|
|
1523
1566
|
}
|
|
1524
|
-
if (tokenSchema
|
|
1525
|
-
&& tokenSchema.responseSchema.properties.refreshToken.placement && tokenSchema.responseSchema.properties.refreshToken.placement.toUpperCase() === 'BODY') {
|
|
1567
|
+
if (getTokenPlacement(tokenSchema, 'refreshToken') === TokenPlacement.BODY) {
|
|
1526
1568
|
currResult.refreshToken = translated.refreshToken;
|
|
1527
1569
|
}
|
|
1528
1570
|
// return the token that we find in the translated object
|
|
@@ -1669,13 +1711,15 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
|
1669
1711
|
|
|
1670
1712
|
// set the Content Type headers based on the type of request data for the call
|
|
1671
1713
|
if (thisAHdata['Content-Type'] === undefined || thisAHdata['Content-Type'] === null) {
|
|
1672
|
-
|
|
1714
|
+
// Check adapter property override first, then fall back to tokenSchema
|
|
1715
|
+
const requestDatatype = authRequestDatatype || (tokenSchema && tokenSchema.requestDatatype);
|
|
1716
|
+
if (requestDatatype && requestDatatype.toUpperCase() === 'PLAIN') {
|
|
1673
1717
|
// add the Plain headers if they were not set already
|
|
1674
1718
|
thisAHdata['Content-Type'] = 'text/plain';
|
|
1675
|
-
} else if (
|
|
1719
|
+
} else if (requestDatatype && requestDatatype.toUpperCase() === 'XML') {
|
|
1676
1720
|
// add the XML headers if they were not set already
|
|
1677
1721
|
thisAHdata['Content-Type'] = 'application/xml';
|
|
1678
|
-
} else if (
|
|
1722
|
+
} else if (requestDatatype && requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
1679
1723
|
// add the URLENCODE headers if they were not set already
|
|
1680
1724
|
thisAHdata['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
1681
1725
|
} else {
|
|
@@ -1685,13 +1729,14 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
|
1685
1729
|
}
|
|
1686
1730
|
// set the Accept headers based on the type of response data for the call
|
|
1687
1731
|
if (thisAHdata.Accept === undefined || thisAHdata.Accept === null) {
|
|
1688
|
-
|
|
1732
|
+
const responseDatatype = authResponseDatatype || (tokenSchema && tokenSchema.responseDatatype);
|
|
1733
|
+
if (responseDatatype && responseDatatype.toUpperCase() === 'PLAIN') {
|
|
1689
1734
|
// add the Plain headers if they were not set already
|
|
1690
1735
|
thisAHdata.Accept = 'text/plain';
|
|
1691
|
-
} else if (
|
|
1736
|
+
} else if (responseDatatype && (responseDatatype.toUpperCase() === 'XML' || responseDatatype.toUpperCase() === TokenPlacement.XML2JSON)) {
|
|
1692
1737
|
// add the XML headers if they were not set already
|
|
1693
1738
|
thisAHdata.Accept = 'application/xml';
|
|
1694
|
-
} else if (
|
|
1739
|
+
} else if (responseDatatype && responseDatatype.toUpperCase() === 'URLENCODE') {
|
|
1695
1740
|
// add the URLENCODE headers if they were not set already
|
|
1696
1741
|
thisAHdata.Accept = 'application/x-www-form-urlencoded';
|
|
1697
1742
|
} else {
|
|
@@ -2159,11 +2204,13 @@ async function buildTokenRequest(reqPath, reqBody, callProperties, callback) {
|
|
|
2159
2204
|
bodyString = tokenEntity;
|
|
2160
2205
|
|
|
2161
2206
|
// if it is JSON or URLENCODE need to put body into right format
|
|
2162
|
-
|
|
2207
|
+
// Check adapter property override first, then fall back to tokenSchema
|
|
2208
|
+
const requestDatatype = authRequestDatatype || (tokenSchema && tokenSchema.requestDatatype);
|
|
2209
|
+
if (!requestDatatype || requestDatatype.toUpperCase() === 'JSON' || requestDatatype.toUpperCase() === 'FORM') {
|
|
2163
2210
|
bodyString = JSON.stringify(tokenEntity);
|
|
2164
|
-
} else if (
|
|
2211
|
+
} else if (requestDatatype && requestDatatype.toUpperCase() === 'URLENCODE') {
|
|
2165
2212
|
bodyString = querystring.stringify(tokenEntity);
|
|
2166
|
-
} else if (
|
|
2213
|
+
} else if (requestDatatype && requestDatatype.toUpperCase() === 'URLQUERY') {
|
|
2167
2214
|
// if the datatype is URLQUERY need to put into the query on the request
|
|
2168
2215
|
if (authQueryEncode != null) {
|
|
2169
2216
|
if (authQueryEncode === true) {
|
|
@@ -4275,6 +4322,36 @@ class ConnectorRest {
|
|
|
4275
4322
|
if (props.authentication.sensitive) {
|
|
4276
4323
|
addSensitiveItems = props.authentication.sensitive;
|
|
4277
4324
|
}
|
|
4325
|
+
|
|
4326
|
+
// set the auth request datatype override (optional - default is null)
|
|
4327
|
+
const validatedAuthRequestDatatype = validateAuthProperty(
|
|
4328
|
+
props.authentication.auth_request_datatype,
|
|
4329
|
+
VALID_AUTH_REQUEST_DATATYPES,
|
|
4330
|
+
'auth_request_datatype'
|
|
4331
|
+
);
|
|
4332
|
+
if (validatedAuthRequestDatatype !== undefined) {
|
|
4333
|
+
authRequestDatatype = validatedAuthRequestDatatype;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
// set the auth response datatype override (optional - default is null)
|
|
4337
|
+
const validatedAuthResponseDatatype = validateAuthProperty(
|
|
4338
|
+
props.authentication.auth_response_datatype,
|
|
4339
|
+
VALID_AUTH_RESPONSE_DATATYPES,
|
|
4340
|
+
'auth_response_datatype'
|
|
4341
|
+
);
|
|
4342
|
+
if (validatedAuthResponseDatatype !== undefined) {
|
|
4343
|
+
authResponseDatatype = validatedAuthResponseDatatype;
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4346
|
+
// set the token response placement override (optional - default is null)
|
|
4347
|
+
const validatedTokenResponsePlacement = validateAuthProperty(
|
|
4348
|
+
props.authentication.token_response_placement,
|
|
4349
|
+
VALID_TOKEN_RESPONSE_PLACEMENTS,
|
|
4350
|
+
'token_response_placement'
|
|
4351
|
+
);
|
|
4352
|
+
if (validatedTokenResponsePlacement !== undefined) {
|
|
4353
|
+
tokenResponsePlacement = validatedTokenResponsePlacement;
|
|
4354
|
+
}
|
|
4278
4355
|
}
|
|
4279
4356
|
|
|
4280
4357
|
// set the stub mode (optional - default is false)
|
package/package.json
CHANGED
|
@@ -272,6 +272,44 @@
|
|
|
272
272
|
"description": "This property turns on logging of Authentication Information and should only be true when debugging authentication and connectivity",
|
|
273
273
|
"default": false
|
|
274
274
|
},
|
|
275
|
+
"auth_request_datatype": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"description": "Override the request data type for token authentication requests. When set, this overrides the schema's requestDatatype",
|
|
278
|
+
"default": "",
|
|
279
|
+
"enum": [
|
|
280
|
+
"",
|
|
281
|
+
"JSON",
|
|
282
|
+
"JSON2XML",
|
|
283
|
+
"PLAIN",
|
|
284
|
+
"XML",
|
|
285
|
+
"URLENCODE",
|
|
286
|
+
"URLQUERY",
|
|
287
|
+
"FORM"
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
"auth_response_datatype": {
|
|
291
|
+
"type": "string",
|
|
292
|
+
"description": "Override the response data type for token authentication requests. When set, this overrides the schema's responseDatatype",
|
|
293
|
+
"default": "",
|
|
294
|
+
"enum": [
|
|
295
|
+
"",
|
|
296
|
+
"JSON",
|
|
297
|
+
"XML2JSON",
|
|
298
|
+
"PLAIN",
|
|
299
|
+
"XML",
|
|
300
|
+
"URLENCODE"
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
"token_response_placement": {
|
|
304
|
+
"type": "string",
|
|
305
|
+
"description": "Override where to extract the token from the authentication response (HEADER or BODY). When set, this overrides the schema's token placement setting",
|
|
306
|
+
"default": "",
|
|
307
|
+
"enum": [
|
|
308
|
+
"",
|
|
309
|
+
"HEADER",
|
|
310
|
+
"BODY"
|
|
311
|
+
]
|
|
312
|
+
},
|
|
275
313
|
"client_id": {
|
|
276
314
|
"type": "string",
|
|
277
315
|
"description": "The client id for OAuth requests - can also use username depending on schema",
|