@itentialopensource/adapter-utils
Advanced tools
Comparing version
## 5.1.5 [08-29-2023] | ||
* fix awsroleauth and add authdata to generic | ||
See merge request itentialopensource/adapter-utils!272 | ||
--- | ||
## 5.1.4 [08-28-2023] | ||
@@ -3,0 +11,0 @@ |
@@ -11,3 +11,2 @@ /* @copyright Itential, LLC 2023 */ | ||
const AWS = require('aws-sdk'); | ||
const http = require('http'); | ||
const querystring = require('querystring'); | ||
@@ -65,77 +64,136 @@ | ||
getAWSAuthorization(method, requestObj, uriPath, service, STSParams, roleName, callback) { | ||
const origin = `${this.id}-adapter-getAuthorization`; | ||
const meth = 'authenticationHandler-getAWSAuthorization'; | ||
const origin = `${this.myid}-${meth}`; | ||
log.trace(origin); | ||
// Form path | ||
let uriPathTranslated = ''; | ||
if (requestObj.uriQuery && uriPath.indexOf('?') === -1) { | ||
const query = requestObj.uriQuery; | ||
uriPathTranslated = `${uriPath}?${querystring.stringify(query)}`; | ||
} else if (requestObj.uriQuery && uriPath.endsWith('?')) { | ||
const query = requestObj.uriQuery; | ||
uriPathTranslated = `${uriPath}${querystring.stringify(query)}`; | ||
} else { | ||
uriPathTranslated = uriPath; | ||
} | ||
try { | ||
// Form path | ||
let uriPathTranslated = ''; | ||
if (requestObj.uriQuery && uriPath.indexOf('?') === -1) { | ||
const query = requestObj.uriQuery; | ||
uriPathTranslated = `${uriPath}?${querystring.stringify(query)}`; | ||
} else if (requestObj.uriQuery && uriPath.endsWith('?')) { | ||
const query = requestObj.uriQuery; | ||
uriPathTranslated = `${uriPath}${querystring.stringify(query)}`; | ||
} else { | ||
uriPathTranslated = uriPath; | ||
} | ||
// set up the options for the AWS signature | ||
const options = { | ||
host: this.allProps.host, | ||
method, | ||
path: uriPathTranslated.replace(/\/\/+/g, '/'), | ||
service, | ||
region: this.allProps.region | ||
}; | ||
// set up the options for the AWS signature | ||
const options = { | ||
host: this.allProps.host, | ||
method, | ||
path: uriPathTranslated.replace(/\/\/+/g, '/'), | ||
service, | ||
region: this.allProps.region | ||
}; | ||
// add any provided headers for the call | ||
if (requestObj.addlHeaders) { | ||
options.headers = requestObj.addlHeaders; | ||
} | ||
// remove ? if there is no query and the last character is ? | ||
if (options.path.endsWith('?')) { | ||
options.path = options.path.substring(0, options.path.length - 1); | ||
} | ||
// If there is a body (POST, PATCH, PUT) add the body to the call | ||
if (method !== 'GET' && method !== 'DELETE') { | ||
if (typeof requestObj.payload === 'string') { | ||
options.body = requestObj.payload; | ||
} else { | ||
options.body = JSON.stringify(requestObj.payload); | ||
// add any provided headers for the call | ||
if (requestObj.addlHeaders) { | ||
options.headers = requestObj.addlHeaders; | ||
} | ||
} | ||
// override anything set in callProperties | ||
if (requestObj.callProperties) { | ||
if (requestObj.callProperties.host) { | ||
options.host = requestObj.callProperties.host; | ||
// remove ? if there is no query and the last character is ? | ||
if (options.path.endsWith('?')) { | ||
options.path = options.path.substring(0, options.path.length - 1); | ||
} | ||
if (requestObj.callProperties.region) { | ||
options.region = requestObj.callProperties.region; | ||
// If there is a body (POST, PATCH, PUT) add the body to the call | ||
if (method !== 'GET' && method !== 'DELETE') { | ||
if (typeof requestObj.payload === 'string') { | ||
options.body = requestObj.payload; | ||
} else { | ||
options.body = JSON.stringify(requestObj.payload); | ||
} | ||
} | ||
} | ||
log.debug(`SIG OPTIONS: ${JSON.stringify(options)}`); | ||
// override anything set in callProperties | ||
if (requestObj.callProperties) { | ||
if (requestObj.callProperties.host) { | ||
options.host = requestObj.callProperties.host; | ||
} | ||
if (requestObj.callProperties.region) { | ||
options.region = requestObj.callProperties.region; | ||
} | ||
} | ||
log.debug(`SIG OPTIONS: ${JSON.stringify(options)}`); | ||
/* STS AUTHENTICATION */ | ||
if (STSParams) { | ||
log.info('Using STS for AWS Authentication'); | ||
/* STS AUTHENTICATION */ | ||
if (STSParams) { | ||
log.info('Using STS for AWS Authentication'); | ||
// set the original AWS access information (from properties) | ||
AWS.config.update({ | ||
sessionToken: this.allProps.authentication.aws_session_token, | ||
accessKeyId: this.allProps.authentication.aws_access_key, | ||
secretAccessKey: this.allProps.authentication.aws_secret_key, | ||
region: this.allProps.region | ||
}); | ||
// set the original AWS access information (from properties) | ||
AWS.config.update({ | ||
sessionToken: this.allProps.authentication.aws_session_token, | ||
accessKeyId: this.allProps.authentication.aws_access_key, | ||
secretAccessKey: this.allProps.authentication.aws_secret_key, | ||
region: this.allProps.region | ||
}); | ||
// use STS to get the AWS access information for the user defined in STWS Params | ||
// use STS to get the AWS access information for the user defined in STWS Params | ||
const sts = new AWS.STS(); | ||
const stsData = { | ||
RoleArn: STSParams.RoleArn, | ||
RoleSessionName: STSParams.RoleSessionName, | ||
DurationSeconds: 3600 | ||
}; | ||
return sts.assumeRole(stsData, (err, data) => { | ||
if (err) { | ||
const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, `AWS Assume Role Error ${err}`, null, null, null, null); | ||
log.error(`${origin}: ${errorObj.IAPerror.displayString}`); | ||
return callback(null, errorObj); | ||
} | ||
// extract the user specific info from the response | ||
const accessKeyId = data.Credentials.AccessKeyId; | ||
const secretAccessKey = data.Credentials.SecretAccessKey; | ||
const sessionToken = data.Credentials.SessionToken; | ||
// call the signature with the user specific information | ||
const authOpts = aws4.sign(options, { accessKeyId, secretAccessKey, sessionToken }); | ||
if (sessionToken) { | ||
authOpts.headers['X-Amz-Security-Token'] = sessionToken; | ||
} | ||
// return the headers | ||
return callback(authOpts.headers); | ||
}); | ||
} | ||
/* ADAPTER PROPERTIES AUTHENTICATION */ | ||
if (!roleName && !this.allProps.authentication.aws_iam_role) { | ||
log.info('Using Adapter PROPERTIES for AWS Authentication'); | ||
// call the signature with the property information | ||
const authOpts = aws4.sign(options, { accessKeyId: this.allProps.authentication.aws_access_key, secretAccessKey: this.allProps.authentication.aws_secret_key, sessionToken: this.allProps.authentication.aws_session_token }); | ||
if (this.allProps.authentication.aws_session_token) { | ||
authOpts.headers['X-Amz-Security-Token'] = this.allProps.authentication.aws_session_token; | ||
} | ||
// return the headers | ||
return callback(authOpts.headers); | ||
} | ||
/* ROLE NAME AUTHENTICATION */ | ||
log.info('Using roleName for AWS Authentication'); | ||
// determine where to get roleName (task is higher priority) | ||
let myRole = roleName; | ||
if (!roleName) { | ||
myRole = this.allProps.authentication.aws_iam_role; | ||
} | ||
const myDate = new Date().getTime(); | ||
const mySess = `${this.myid}-${myDate}`; | ||
const sts = new AWS.STS(); | ||
const stsData = { | ||
RoleArn: STSParams.RoleArn, | ||
RoleSessionName: STSParams.RoleSessionName, | ||
RoleArn: myRole, | ||
RoleSessionName: mySess, | ||
DurationSeconds: 3600 | ||
}; | ||
// change role to the role name provided | ||
return sts.assumeRole(stsData, (err, data) => { | ||
if (err) { | ||
throw err; | ||
const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, `AWS Assume Role Error ${err}`, null, null, null, null); | ||
log.error(`${origin}: ${errorObj.IAPerror.displayString}`); | ||
return callback(null, errorObj); | ||
} | ||
// extract the user specific info from the response | ||
// get role keys from response so we can sign the request | ||
const accessKeyId = data.Credentials.AccessKeyId; | ||
@@ -145,3 +203,3 @@ const secretAccessKey = data.Credentials.SecretAccessKey; | ||
// call the signature with the user specific information | ||
// sign the request | ||
const authOpts = aws4.sign(options, { accessKeyId, secretAccessKey, sessionToken }); | ||
@@ -155,110 +213,7 @@ if (sessionToken) { | ||
}); | ||
} catch (e) { | ||
const errorObj = this.requestHandlerInst.formatErrorObject(this.myid, meth, 'Caught Exception', null, null, null, e); | ||
log.error(`${origin}: ${errorObj.IAPerror.displayString}`); | ||
return callback(null, errorObj); | ||
} | ||
/* ADAPTER PROPERTIES AUTHENTICATION */ | ||
if (!roleName && !this.allProps.authentication.aws_iam_role) { | ||
log.info('Using Adapter PROPERTIES for AWS Authentication'); | ||
// call the signature with the property information | ||
const authOpts = aws4.sign(options, { accessKeyId: this.allProps.authentication.aws_access_key, secretAccessKey: this.allProps.authentication.aws_secret_key, sessionToken: this.allProps.authentication.aws_session_token }); | ||
if (this.allProps.authentication.aws_session_token) { | ||
authOpts.headers['X-Amz-Security-Token'] = this.allProps.authentication.aws_session_token; | ||
} | ||
// return the headers | ||
return callback(authOpts.headers); | ||
} | ||
/* ROLE NAME AUTHENTICATION */ | ||
log.info('Using roleName for AWS Authentication'); | ||
// set up information for token - this is always ec2! | ||
const getTokenOptions = { | ||
method: 'PUT', | ||
url: 'http://169.254.169.254/latest/api/token', | ||
headers: { | ||
'X-aws-ec2-metadata-token-ttl-seconds': 21600 | ||
} | ||
}; | ||
// get token from AWS internal server | ||
const req1 = http.request(getTokenOptions, (res1) => { | ||
let response = ''; | ||
// HERE??? - can we check status here or does this need to be in end??? | ||
if (res1.statusCode < 200 || res1.statusCode >= 300) { | ||
return callback(null, new Error(`Invalid status code recieved: ${res1.statusCode}`)); | ||
} | ||
// add data to the response | ||
res1.on('data', (resp) => { | ||
response += resp; | ||
}); | ||
// process everything when the call finishes | ||
res1.on('end', () => { | ||
if (!response) { | ||
return callback(null, 'No authentication token'); | ||
} | ||
// get token and set up to make IAM role call - this is always ec2! | ||
const tokenKey = response; | ||
let myRole = roleName; | ||
if (!roleName) { | ||
myRole = this.allProps.authentication.aws_iam_role; | ||
} | ||
const toptions = { | ||
method: 'GET', | ||
hostname: '169.254.169.254', | ||
path: `/latest/meta-data/iam/security-credentials/${myRole}`, | ||
headers: { | ||
'X-aws-ec2-metadata-token': tokenKey | ||
} | ||
}; | ||
// IAM Role call | ||
const req2 = http.request(toptions, (res2) => { | ||
let response2 = ''; | ||
// HERE??? - can we check status here or does this need to be in end??? | ||
if (res2.statusCode < 200 || res2.statusCode >= 300) { | ||
return callback(null, new Error(`Invalid status code recieved: ${res2.statusCode}`)); | ||
} | ||
// add data to the response | ||
res2.on('data', (chunk) => { | ||
response2 += chunk; | ||
}); | ||
// process everything when the call finishes | ||
res2.on('end', () => { | ||
if (!response2) { | ||
return callback(null, 'No authentication token'); | ||
} | ||
try { | ||
// get role keys from response so we can sign the request | ||
const awsResponse = JSON.parse(response2); | ||
const accessKeyId = awsResponse.AccessKeyId; | ||
const secretAccessKey = awsResponse.SecretAccessKey; | ||
const sessionToken = awsResponse.Token; | ||
// sign the request | ||
const authOpts = aws4.sign(options, { accessKeyId, secretAccessKey, sessionToken }); | ||
if (sessionToken) { | ||
authOpts.headers['X-Amz-Security-Token'] = sessionToken; | ||
} | ||
// return the headers | ||
return callback(authOpts.headers); | ||
} catch (e) { | ||
return callback(null, e); | ||
} | ||
}); | ||
}); | ||
req2.on('error', (err2) => callback(null, err2)); | ||
req2.end(); | ||
}); | ||
}); | ||
req1.on('error', (err1) => callback(null, err1)); | ||
req1.end(); | ||
} | ||
@@ -265,0 +220,0 @@ } |
@@ -114,2 +114,5 @@ /* @copyright Itential, LLC 2023 */ | ||
} | ||
if (metadata && metadata.authData) { | ||
reqObj.authData = metadata.authData; | ||
} | ||
@@ -116,0 +119,0 @@ // determine the call and return flag |
{ | ||
"name": "@itentialopensource/adapter-utils", | ||
"version": "5.1.4", | ||
"version": "5.1.5", | ||
"description": "Itential Adapter Utility Libraries", | ||
@@ -5,0 +5,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
5
-16.67%791926
-0.06%15863
-0.22%