@itentialopensource/adapter-utils
Advanced tools
Comparing version 5.1.4 to 5.1.5
## 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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
6
791926
15863