feat: ✨ Created a mini nodeJS server with NewMan for testing without PostMan GUI.
This will mimic a run in a CD/CI environment or docker container.
This commit is contained in:
303
node_modules/postman-runtime/lib/authorizer/aws4.js
generated
vendored
Normal file
303
node_modules/postman-runtime/lib/authorizer/aws4.js
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
var _ = require('lodash'),
|
||||
aws4 = require('aws4'),
|
||||
crypto = require('crypto'),
|
||||
sdk = require('postman-collection'),
|
||||
urlEncoder = require('postman-url-encoder'),
|
||||
bodyBuilder = require('../requester/core-body-builder'),
|
||||
|
||||
RequestBody = sdk.RequestBody,
|
||||
|
||||
X_AMZ_PREFIX = 'X-Amz-',
|
||||
BODY_HASH_HEADER = 'X-Amz-Content-Sha256',
|
||||
|
||||
/**
|
||||
* Calculates body hash with given algorithm and digestEncoding.
|
||||
*
|
||||
* @todo This function can also be used in Digest auth so that it works correctly for urlencoded and file body types
|
||||
*
|
||||
* @param {RequestBody} body
|
||||
* @param {String} algorithm
|
||||
* @param {String} digestEncoding
|
||||
* @param {Function} callback
|
||||
*/
|
||||
computeBodyHash = function (body, algorithm, digestEncoding, callback) {
|
||||
if (!(body && algorithm && digestEncoding) || body.isEmpty()) { return callback(); }
|
||||
|
||||
var hash = crypto.createHash(algorithm),
|
||||
originalReadStream,
|
||||
rawBody,
|
||||
urlencodedBody,
|
||||
graphqlBody;
|
||||
|
||||
if (body.mode === RequestBody.MODES.raw) {
|
||||
rawBody = bodyBuilder.raw(body.raw).body;
|
||||
hash.update(rawBody);
|
||||
|
||||
return callback(hash.digest(digestEncoding));
|
||||
}
|
||||
|
||||
if (body.mode === RequestBody.MODES.urlencoded) {
|
||||
urlencodedBody = bodyBuilder.urlencoded(body.urlencoded).form;
|
||||
urlencodedBody = urlEncoder.encodeQueryString(urlencodedBody);
|
||||
hash.update(urlencodedBody);
|
||||
|
||||
return callback(hash.digest(digestEncoding));
|
||||
}
|
||||
|
||||
if (body.mode === RequestBody.MODES.file) {
|
||||
originalReadStream = _.get(body, 'file.content');
|
||||
|
||||
if (!originalReadStream) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
return originalReadStream.cloneReadStream(function (err, clonedStream) {
|
||||
if (err) { return callback(); }
|
||||
|
||||
clonedStream.on('data', function (chunk) {
|
||||
hash.update(chunk);
|
||||
});
|
||||
|
||||
clonedStream.on('end', function () {
|
||||
callback(hash.digest(digestEncoding));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (body.mode === RequestBody.MODES.graphql) {
|
||||
graphqlBody = bodyBuilder.graphql(body.graphql).body;
|
||||
hash.update(graphqlBody);
|
||||
|
||||
return callback(hash.digest(digestEncoding));
|
||||
}
|
||||
|
||||
// @todo: formdata body type requires adding new data to form instead of setting headers for AWS auth.
|
||||
// Figure out how to do that. See below link:
|
||||
// AWS auth with formdata: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
|
||||
|
||||
// ensure that callback is called if body.mode doesn't match with any of the above modes
|
||||
return callback();
|
||||
};
|
||||
|
||||
/**
|
||||
* @implements {AuthHandlerInterface}
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* @property {AuthHandlerInterface~AuthManifest}
|
||||
*/
|
||||
manifest: {
|
||||
info: {
|
||||
name: 'awsv4',
|
||||
version: '1.0.0'
|
||||
},
|
||||
updates: [
|
||||
{
|
||||
property: 'Host',
|
||||
type: 'header'
|
||||
},
|
||||
{
|
||||
property: 'Authorization',
|
||||
type: 'header'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Date',
|
||||
type: 'header'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Security-Token',
|
||||
type: 'header'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Content-Sha256',
|
||||
type: 'header'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Security-Token',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Expires',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Date',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Algorithm',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Credential',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-SignedHeaders',
|
||||
type: 'url.param'
|
||||
},
|
||||
{
|
||||
property: 'X-Amz-Signature',
|
||||
type: 'url.param'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a item (fetches all required parameters, etc) before the actual authorization step.
|
||||
*
|
||||
* @param {AuthInterface} auth
|
||||
* @param {Response} response
|
||||
* @param {AuthHandlerInterface~authInitHookCallback} done
|
||||
*/
|
||||
init: function (auth, response, done) {
|
||||
done(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks the item, and fetches any parameters that are not already provided.
|
||||
*
|
||||
* @param {AuthInterface} auth
|
||||
* @param {AuthHandlerInterface~authPreHookCallback} done
|
||||
*/
|
||||
pre: function (auth, done) {
|
||||
done(null, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verifies whether the request was successful after being sent.
|
||||
*
|
||||
* @param {AuthInterface} auth
|
||||
* @param {Requester} response
|
||||
* @param {AuthHandlerInterface~authPostHookCallback} done
|
||||
*/
|
||||
post: function (auth, response, done) {
|
||||
done(null, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates the signature and adds auth data to the request as additional headers/query params.
|
||||
* AWS v4 auth mandates that a content type header be present in each request.
|
||||
*
|
||||
* @param {Request} request request to add auth data
|
||||
* @param {Object} params data required for auth
|
||||
* @param {Object} params.credentials Should contain the AWS credentials, "accessKeyId" and "secretAccessKey"
|
||||
* @param {String} params.host Contains the host name for the request
|
||||
* @param {String} params.path Contains the complete path, with query string as well, e.g: /something/kane?hi=ho
|
||||
* @param {String} params.service The name of the AWS service
|
||||
* @param {String} params.region AWS region
|
||||
* @param {String} params.method Request method
|
||||
* @param {String} params.body Stringified request body
|
||||
* @param {Object} params.headers Each key should be a header key, and the value should be a header value
|
||||
* @param {Boolean} params.signQuery Add auth data to query params if true, otherwise add it to headers
|
||||
*/
|
||||
addAuthDataToRequest: function (request, params) {
|
||||
var signedData = aws4.sign(params, params.credentials);
|
||||
|
||||
if (params.signQuery) {
|
||||
_.forEach(sdk.Url.parse(signedData.path).query, function (param) {
|
||||
// only add additional AWS specific params to request
|
||||
if (_.startsWith(param.key, X_AMZ_PREFIX) && !request.url.query.has(param.key)) {
|
||||
param.system = true;
|
||||
request.url.query.add(param);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_.forEach(signedData.headers, function (value, key) {
|
||||
request.upsertHeader({
|
||||
key: key,
|
||||
value: value,
|
||||
system: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Signs a request.
|
||||
*
|
||||
* @param {AuthInterface} auth
|
||||
* @param {Request} request
|
||||
* @param {AuthHandlerInterface~authSignHookCallback} done
|
||||
*/
|
||||
sign: function (auth, request, done) {
|
||||
var self = this,
|
||||
params = auth.get([
|
||||
'accessKey',
|
||||
'secretKey',
|
||||
'sessionToken',
|
||||
'service',
|
||||
'region',
|
||||
'addAuthDataToQuery'
|
||||
]),
|
||||
url = urlEncoder.toNodeUrl(request.url),
|
||||
dataToSign;
|
||||
|
||||
// Clean up the request (if needed)
|
||||
request.removeHeader('Authorization', {ignoreCase: true});
|
||||
request.removeHeader('X-Amz-Date', {ignoreCase: true});
|
||||
request.removeHeader('X-Amz-Security-Token', {ignoreCase: true});
|
||||
request.removeHeader('X-Amz-Content-Sha256', {ignoreCase: true});
|
||||
|
||||
// Not removing `X-Amz-Expires` from params here allowing user to override
|
||||
// default value
|
||||
request.removeQueryParams([
|
||||
'X-Amz-Security-Token',
|
||||
'X-Amz-Date',
|
||||
'X-Amz-Algorithm',
|
||||
'X-Amz-Credential',
|
||||
'X-Amz-SignedHeaders',
|
||||
'X-Amz-Signature'
|
||||
]);
|
||||
|
||||
dataToSign = {
|
||||
credentials: {
|
||||
accessKeyId: params.accessKey,
|
||||
secretAccessKey: params.secretKey,
|
||||
sessionToken: params.sessionToken || undefined
|
||||
},
|
||||
host: url.host,
|
||||
path: url.path, // path = pathname + query
|
||||
service: params.service || 'execute-api', // AWS API Gateway is the default service.
|
||||
region: params.region || 'us-east-1',
|
||||
method: request.method,
|
||||
body: undefined, // no need to give body since we are setting 'X-Amz-Content-Sha256' header
|
||||
headers: _.transform(request.getHeaders({enabled: true}), function (accumulator, value, key) {
|
||||
accumulator[key] = value;
|
||||
}, {}),
|
||||
signQuery: params.addAuthDataToQuery
|
||||
};
|
||||
|
||||
// Removed the code which was adding content-type header if it is not there in the request. Because
|
||||
// aws4 does not require content-type header. It is only mandatory to include content-type header in signature
|
||||
// calculation if it is there in the request.
|
||||
// Refer: https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request
|
||||
|
||||
// body hash is not required when adding auth data to qury params
|
||||
// @see: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
|
||||
if (params.addAuthDataToQuery) {
|
||||
self.addAuthDataToRequest(request, dataToSign);
|
||||
|
||||
return done();
|
||||
}
|
||||
|
||||
// aws4 module can't calculate body hash for body with ReadStream.
|
||||
// So we calculate it our self and set 'X-Amz-Content-Sha256' header which will be used by aws4 module
|
||||
// to calculate the signature.
|
||||
computeBodyHash(request.body, 'sha256', 'hex', function (bodyHash) {
|
||||
if (bodyHash) {
|
||||
request.upsertHeader({
|
||||
key: BODY_HASH_HEADER,
|
||||
value: bodyHash,
|
||||
system: true
|
||||
});
|
||||
|
||||
dataToSign.headers[BODY_HASH_HEADER] = bodyHash;
|
||||
}
|
||||
|
||||
self.addAuthDataToRequest(request, dataToSign);
|
||||
|
||||
return done();
|
||||
});
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user