I am trying to fetch a jpg image from AWS S3 Bucket using a HTTP GET request in React-Native.
So far I was following these 2 documentations from Amazon :
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
Here is the logic I implemented so far.
When my component render, I am doing the following :
import { Auth } from 'aws-amplify';
import Moment from 'moment';
import sha256 from 'crypto-js/sha256';
var CryptoJS = require("crypto-js");
...
Auth.currentCredentials()
.then(async credentials => {
let awsHost = 'https://myAwsHost.amazonaws.com'
let awsPath = '/public/pathToMyImage.jpg'
let accessKeyId = credentials.accessKeyId
let secretAccessKey = credentials.secretAccessKey
let sessionToken = credentials.sessionToken
let awsDate = Moment().utc().format("YYYYMMDD")
let awsRequestDateTime = Moment().utc().format("YYYYMMDD" + "T" + "HHmmss") + "Z"
let awsRegion = "myRegion"
let awsService = "s3"
let awsRequest = "aws4_request"
let awsAlgorithm = "AWS4-HMAC-SHA256"
let awsCredentialScope = awsDate + "/" + awsRegion + "/" + awsService + "/" + awsRequest
let awsHTTPRequestMethod = "GET"
let awsSignedHeaders = "host;x-amz-content-sha256;x-amz-date;x-amz-security-token"
let awsCanonicalURI = awsHost + awsPath
let awsCanonicalQueryString = ""
// Step 1
let canonicalRequest =
awsHTTPRequestMethod + '\n' +
awsCanonicalURI + '\n' +
awsCanonicalQueryString + '\n' +
"X-Amz-Content-Sha256".toLowerCase() + ":" + sha256("") + "\n" +
"X-Amz-Date".toLowerCase() + ":" + awsRequestDateTime.trim() + "\n" +
"X-Amz-Security-Token".toLowerCase() + ":" + sessionToken + "\n" +
awsSignedHeaders + '\n' +
sha256("")
// Step 2 :
let stringToSign =
awsAlgorithm + "\n" +
awsRequestDateTime + "\n" +
awsCredentialScope + "\n" +
sha256(canonicalRequest)
// Step 3 :
let kSecret = secretAccessKey
let kDate = CryptoJS.HmacSHA256("AWS4" + kSecret, awsDate)
let kRegion = CryptoJS.HmacSHA256(kDate, awsRegion)
let kService = CryptoJS.HmacSHA256(kRegion, awsService)
let kSigning = CryptoJS.HmacSHA256(kService, awsRequest).toString(CryptoJS.enc.Hex)
let awsSignature = CryptoJS.HmacSHA256(awsDerivedSignInKey, stringToSign).toString(CryptoJS.enc.Hex)
let awsAuthorization = awsAlgorithm + " Credential=" + accessKeyId + "/" + awsCredentialScope + ", SignedHeaders=" + awsSignedHeaders + ", Signature=" + awsSignature
// Fetching the image with an HTTP GET request to AWS S3 Buckets
try {
await fetch(
awsCanonicalURI,
{
headers: {
'X-Amz-Security-Token': sessionToken,
'X-Amz-Content-Sha256': sha256("").toString(),
'X-Amz-Date': awsRequestDateTime,
'Authorization': awsAuthorization
},
}
).then(res => {
console.log("HTML status : " + res.status) // Status return : Error 403
});
} catch (error) {
console.error(error);
}
})
}
When I am trying to execute the same GET request with postman it work's and it retrieve the image from AWS S3 Buckets.
The only difference between my request and the one generated by postman is the Signature.
I know that I can fetch images from S3Image component from aws-amplify-react-native but it is not what I am trying to achieve.
Main goal
Finally, I am looking to execute those HTTP GET Request's in order to use them in FastImage from the module react-native-fast-image and use it in order to cache easely images in my react-native application.
If anyone has an answer to my problem or a better alternative to what I am trying to achieve, be my guest!!
If it works in postman, You can get the Axios code sample for that using postman. Checkout the gif below.
Related
I'm using postman for an api request. My question is; I'm getting an error when I made one of the variables random.
For example when I sent request this link;
https://testrest.xxxxxx.com/voucher/purchase/testAUD000/49/1/30000017/1 it is working. But when I change with this;
https://testrest.xxxxxx.com/voucher/purchase/testAUD000/49/1/30000017/{{transactionId}}
I'm getting this error; "You are not authorized to access this resource"
In my collection pre-request script;
var apiKey = pm.collectionVariables.get("API-KEY");
var apiSecret = pm.collectionVariables.get("API-SECRET");
var nonce = _.random(1, 50000000);
var signature = pm.request.method + "\n" + pm.request.url.getPath() + "\n" + nonce + "\n";
var hash = CryptoJS.HmacSHA256(signature, apiSecret).toString();
pm.request.headers.add("AUTHENTICATION: HMAC " + apiKey + ":" + hash + ":" + nonce);
I added this in collection pre-request script or request in pre-request script I'm getting error;
var random = Math.floor(Math.random() * 10);
pm.variables.set('transactionId',random)
I have s3 bucket as below,
myBucket
a/
b/
c/
where myBucket is a s3 bucket and a, b, c are the key folders inside that bucket.
I will upload images into a/. The s3 event notification will trigger SQS which will then trigger lambda function which does the process of removing image background and uploads into b/ folder.
The problem here is for example, if I upload a folder which has around 26 images into s3 only 23 or 22 images are getting triggered by lambda and only those images are getting are processed.
For some reason s3 is not triggering all the images or is that something I should configure in my lambda function?
Here is my function code
exports.handler = async(event, context, callback) => {
try {
console.log(event.Records[0]);
var json = JSON.parse(event.Records[0]['body']);
console.log('json: '+json);
json = JSON.parse(json['Message']);
json = json['Records'][0]['s3'];
console.log(json);
var srcBucket = json['bucket']['name'];
console.log('srcBucket: ' + srcBucket);
var srcKey = decodeURIComponent(json['object']['key'].replace(/\+/g, ' '));
console.log('srcKey: ' + srcKey);
var str = (srcKey.split('/').pop()).split('.')[0];
console.log('str: ' + str);
if (str != '') {
var folderPath = srcKey.substr(srcKey.indexOf('/') + 1).split('.')[0];
folderPath = folderPath.substring(0, folderPath.lastIndexOf('/'));
console.log('folderPath: ' + folderPath);
const params1 = { Bucket: srcBucket, Key: srcKey };
var origimage = await s3.getObject(params1).promise();
var destObject = await origimage.Body;
var destKey = 'removebg/' + folderPath + '/' + str + '.jpg';
var options = {
'method': 'POST',
'url': 'https://api.remove.bg/v1.0/removebg',
'headers': {
'X-Api-Key': 'xxxxxxxxxxx'
},
formData: {
'image_file': destObject,
'size': 'auto'
},
encoding: null
};
request(options, function(error, response, body) {
if (error) {
console.log(error);
sendmessage(error, 'Error removing image background', arn, srcBucket + '/' + srcKey, destBucket + destKey);
}
var params = { Bucket: destBucket, Key: destKey, Body: body };
s3.upload(params, function(err, data) {
if (err) {
console.log('Error uploading data: ', err);
sendmessage(err, 'Error uploading transparent image to s3', arn, srcBucket + '/' + srcKey, destBucket + destKey);
}
else { console.log('Successfully uploaded data to ' + destBucket); }
});
});
}
}
catch (e) {
console.log(e);
}
callback(null, 'All done!');
};
Please let me know. Thanks in advance.
I think your problem lies on these lines:
console.log(event.Records[0]);
var json = JSON.parse(event.Records[0]['body']);
console.log('json: '+json);
json = JSON.parse(json['Message']);
json = json['Records'][0]['s3'];
Your function is only looking at the first record that is provided to the function. Multiple events can be given the the Lambda function, so your function should loop through the Records entries and process all of the events that are provided.
It should do something like:
for record in event.Records:
console.log(record);
var json = JSON.parse(record['body']);
console.log('json: '+json);
json = JSON.parse(json['Message']);
json = record['s3'];
I'm trying to create the final step of this example in Flutter and I can't get it right for some reason:
https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
Using their Signing Key + String to sign they get this resulting signature:
5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
Using the exact same key / string to sign I end up with:
fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
Here is my code:
var testSigninKey =
'c4afb1cc5771d871763a393e44b703571b55cc28424d1a5e86da6ed3c154a4b9';
var testStringToSign = 'AWS4-HMAC-SHA256' + '\n' +
'20150830T123600Z' + '\n' +
'20150830/us-east-1/iam/aws4_request' + '\n' +
'f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59';
var hmac1 = Hmac(sha256, utf8.encode(testSigninKey));
var digest1 = hmac1.convert(utf8.encode(testStringToSign));
print(digest1);
print(hex.encode(digestBytes.bytes));
//both print: fe52b221b5173b501c9863cec59554224072ca34c1c827ec5fb8a257f97637b1
The hmac1 calls expects you to use the signing key as is, not to use an encoded version of the hex string representation of it.
You should be able to properly construct the Hmac object with this:
var hmac1 = Hmac(sha256, hex.decode(testSigninKey));
Here is a complete example of my flutter code, referring to the s3 signature creation example:
https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
I know it's not perfect but it took me extremely long to figure it out. Maybe it helps someone.
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:http/http.dart' as http;
class AWS {
var _algorithm = ["X-Amz-Algorithm", "AWS4-HMAC-SHA256"];
final String _awsService = "s3";
final String _aws4Request = "aws4_request";
var _expires = ["X-Amz-Expires", "86400"];
var _signedHeaders = ["X-Amz-SignedHeaders", "host"];
var testbucket = "https://s3.amazonaws.com/examplebucket";
var testfilePath = "/test.txt";
var _testDate = ["X-Amz-Date", "20130524T000000Z"];
var _testDateymd = 20130524;
var _testRegion = "us-east-1";
var _testCredential = [
"X-Amz-Credential",
"AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request"
];
var testSecretkey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
testString() {
var httpMethod = "GET";
var canUri = testfilePath;
var canQueryStr = Uri.encodeQueryComponent(_algorithm[0]) +
"=" +
Uri.encodeQueryComponent(_algorithm[1]) +
"&" +
Uri.encodeQueryComponent(_testCredential[0]) +
"=" +
Uri.encodeQueryComponent(_testCredential[1]) +
"&" +
Uri.encodeQueryComponent(_testDate[0]) +
"=" +
Uri.encodeQueryComponent(_testDate[1]) +
"&" +
Uri.encodeQueryComponent(_expires[0]) +
"=" +
Uri.encodeQueryComponent(_expires[1]) +
"&" +
Uri.encodeQueryComponent(_signedHeaders[0]) +
"=" +
Uri.encodeQueryComponent(_signedHeaders[1]);
// <CanonicalQueryString>\n
// <CanonicalHeaders>\n
// <SignedHeaders>\n
// <HashedPayload>
var _canonicalRequest = httpMethod +
"\n" +
canUri +
"\n" +
canQueryStr +
"\n" +
"host:examplebucket.s3.amazonaws.com" +
"\n" +
"\n" +
"host" +
"\n" +
"UNSIGNED-PAYLOAD";
var bytes = utf8.encode(_canonicalRequest);
var _stringToSign = "AWS4-HMAC-SHA256" +
"\n" +
"20130524T000000Z" +
"\n" +
"20130524/us-east-1/s3/aws4_request" +
"\n" +
sha256.convert(bytes).toString();
print("String to sign: $_stringToSign");
// HEX und HMAC signature
List<int> _dateKey = utf8.encode("AWS4" + testSecretkey);
List<int> _dateMsg = utf8.encode(_testDateymd.toString());
Hmac dateHmac = Hmac(sha256, _dateKey);
Digest dateDigest = dateHmac.convert(_dateMsg);
String _dateRegionKey = dateDigest.toString();
List<int> _dateRegionMsg = utf8.encode(_testRegion.toString());
Hmac dateRegionHmac = Hmac(sha256, hex.decode(_dateRegionKey.toString()));
Digest dateRegionDigest = dateRegionHmac.convert(_dateRegionMsg);
String _dateRegionServiceKey = dateRegionDigest.toString();
List<int> _dateRegionServiceMsg = utf8.encode(_awsService.toString());
Hmac dateRegionServiceHmac =
Hmac(sha256, hex.decode(_dateRegionServiceKey.toString()));
Digest dateRegionServiceDigest =
dateRegionServiceHmac.convert(_dateRegionServiceMsg);
String _signingKey = dateRegionServiceDigest.toString();
List<int> _signingKeyMsg = utf8.encode(_aws4Request.toString());
Hmac _signingKeyHmac = Hmac(sha256, hex.decode(_signingKey.toString()));
Digest _signingKeyDigest = _signingKeyHmac.convert(_signingKeyMsg);
print("signing key: $_signingKeyDigest");
var _signatureKey = _signingKeyDigest.toString();
List<int> _signatureKeyMsg = utf8.encode(_stringToSign);
Hmac _signatureHmac = Hmac(sha256, hex.decode(_signatureKey));
var _signature = _signatureHmac.convert(_signatureKeyMsg);
print("Signature: $_signature");
}
}
Can anyone help me figure out what is happening?
I am trying to load test an application where a user can upload and download files.
Journey:
Step-1:When a user choose a file from disk (POST request) it creates a fileID and path with uuid.
Response looks like:
{"id":"FILE-VX-1234","path":"uuid/filename.jpg","uri":["s3://{location}/{uuid}/{filename}?endpoint=s3.dualstack.eu-west-1.amazonaws.com"],"state":"OPEN","size":-1,"timestamp":"2020-02-13T10:59:43.146+0000","refreshFlag":1,"storage":"STORAGEID","metadata":{}
Step-2:Using these (POST request) which responds with a s3 uri with assesskeyID, secretaccesskey and sessionToken.
Response looks like:
{"uri":["s3://{accesskeyID}:{secretaccesskey}#{storage location}/{uuid}/{filename}?endpoint=s3.dualstack.eu-west-1.amazonaws.com&sessionToken={security token}"]}
Step-3:Using these and added temporary parameters (date), a PUT request uploads a file in s3 bucket.
Header looks like:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Authorization: AWS4-HMAC-SHA256 Credential=${accesskeyID}/${currentDate}/{region}/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=${secretaccesskey}
Connection: keep-alive
Content-Length: 145541
Content-Type: image/jpeg
Host: <the host address>
Origin: https://{url}
Referer: https://{url}/upload/
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
X-Amz-Content-Sha256: UNSIGNED-PAYLOAD
X-Amz-Date:${currentDateInUTC}
x-amz-security-token: ${sessionToken}
X-Amz-User-Agent: aws-sdk-js/2.409.0 callback
Error:
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
I have verified that the signature(secretaccesskey), accesskeyID and sessionToken which are passed in PUT request are correct.
Note: Additional parameters date and "{region}/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent" in Authorisation error are hardcoded.
It is impossible to provide any assistance without seeing how do you generate the signature for the request, to wit this Authorization header
As per Signing and Authenticating REST Requests article
The Amazon S3 REST API uses a custom HTTP scheme based on a keyed-HMAC (Hash Message Authentication Code) for authentication. To authenticate a request, you first concatenate selected elements of the request to form a string. You then use your AWS secret access key to calculate the HMAC of that string. Informally, we call this process "signing the request," and we call the output of the HMAC algorithm the signature, because it simulates the security properties of a real signature. Finally, you add this signature as a parameter of the request by using the syntax described in this section.
Here is a pseudo-code demonstrating how the header needs to be generated:
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
Signature = Base64( HMAC-SHA1( YourSecretAccessKey, UTF-8-Encoding-Of( StringToSign ) ) );
StringToSign = HTTP-Verb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
CanonicalizedResource = [ "/" + Bucket ] +
<HTTP-Request-URI, from the protocol name up to the query string> +
[ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
CanonicalizedAmzHeaders = <described below>
You can check How to Handle Dynamic AWS SigV4 in JMeter for API Testing article for example implementation.
The AWS is now supporting the Signature version 4 which requires to be generated while uploading file to S3 bucket.
There are two type of credential you can use while upload:
Service credential which are combination of: accessKey, secretKey, serviceName and region
Temporary credential which are combination of: accessKey, secretKey, serviceName, sessionToken and region
In both of the cases you need to add JSR223 Preprocessor under the HTTP Request Sampler.
Add the below code to JSR223 Preprocessor and provide the required details:
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.security.InvalidKeyException
import java.security.MessageDigest
import groovy.json.JsonSlurper
import java.text.SimpleDateFormat
//Defined in User Defined Variables
def access_key = <access key>
def secret_key = <secret key>
def host = <host>
def service = "s3"
def region = "us-east-1"
def token = <session token>
def localfile = <absolute path for local file>
def fileData = readBinaryFile(localfile)
//Create SHA256 of file data
def contentSha256 = org.apache.commons.codec.digest.DigestUtils.sha256Hex(fileData)
vars.put("aws_sha256", contentSha256)
vars.put("aws_content_length", fileData.length + "")
log.info("access key: " + access_key)
log.info("secret_key: " + secret_key)
log.info("host: " + host)
log.info("service: " + service)
log.info("region: " + region)
log.info("file: " + props.get("file_1.jpg"))
log.info("file sha256: " + contentSha256)
log.info("token: " + token)
log.info("content-length: " + fileData.length)
//Obtain data form the Http Request Sampler
def method = sampler.getMethod()
def url = sampler.getUrl()
def req_path = url.getPath()
def req_query_string = orderQuery(url)
def request_parameters = '';
sampler.getArguments().each {arg ->
request_parameters = arg.getStringValue().substring(1)
}
//Create the variable x-amz-date
def now = new Date()
def amzFormat = new SimpleDateFormat( "yyyyMMdd'T'HHmmss'Z'" )
def stampFormat = new SimpleDateFormat( "yyyyMMdd" )
amzFormat.setTimeZone(TimeZone.getTimeZone("UTC")); //server timezone
def amzDate = amzFormat.format(now)
def dateStamp = stampFormat.format(now)
vars.put("x_amz_date", amzDate)
//Create a Canonical Request
def canonical_uri = req_path
def canonical_querystring = req_query_string
def canonical_headers = "host:" + host + "\n" + "x-amz-date:" + amzDate + "\n"
canonical_headers = "host:" + host + "\n" + "x-amz-content-sha256:" + contentSha256 + "\n" + "x-amz-date:" + amzDate + "\n" + "x-amz-security-token:" + token + "\n"
//def signed_headers = "host;x-amz-date"
def signed_headers = "host;x-amz-content-sha256;x-amz-date;x-amz-security-token"
def payload_hash = getHexDigest(request_parameters)
def canonical_request = method + "\n" + canonical_uri + "\n" + canonical_querystring + "\n" + canonical_headers + "\n" + signed_headers + "\n" + contentSha256
log.info("---------canonical_request-------: " + canonical_request)
//Create the String to Sign
def algorithm = "AWS4-HMAC-SHA256"
def credential_scope = dateStamp + "/" + region + "/" + service + "/" + "aws4_request"
def hash_canonical_request = getHexDigest(canonical_request)
def string_to_sign = algorithm + "\n" + amzDate + "\n" + credential_scope + "\n" + hash_canonical_request
log.info("------string to sign-----: " + string_to_sign)
//Calculate the String to Sign
log.info("------datestamp to sign----: "+ dateStamp)
def signing_key = getSignatureKey(secret_key, dateStamp, region, service)
log.info("------key to sign----: "+ org.apache.commons.codec.digest.DigestUtils.sha256Hex(signing_key))
def signature = hmac_sha256Hex(signing_key, string_to_sign)
//Add Signing information to Variable
def authorization_header = algorithm + " " + "Credential=" + access_key + "/" + credential_scope + ", " + "SignedHeaders=" + signed_headers + ", " + "Signature=" + signature
vars.put("aws_authorization", authorization_header)
def hmac_sha256(secretKey, data) {
Mac mac = Mac.getInstance("HmacSHA256")
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256")
mac.init(secretKeySpec)
byte[] digest = mac.doFinal(data.getBytes())
return digest
}
def hmac_sha256Hex(secretKey, data) {
def result = hmac_sha256(secretKey, data)
return result.encodeHex()
}
def getSignatureKey(key, dateStamp, regionName, serviceName) {
def kDate = hmac_sha256(("AWS4" + key).getBytes(), dateStamp)
def kRegion = hmac_sha256(kDate, regionName)
def kService = hmac_sha256(kRegion, serviceName)
def kSigning = hmac_sha256(kService, "aws4_request")
return kSigning
}
def getHexDigest(text) {
def md = MessageDigest.getInstance("SHA-256")
md.update(text.getBytes())
return md.digest().encodeHex()
}
public static String orderQuery(URL url) throws UnsupportedEncodingException {
def orderQueryString = "";
Map<String, String> queryPairs = new LinkedHashMap<>();
String queryParams = url.getQuery();
if (queryParams != null) {
String[] pairs = queryParams.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
queryPairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
def orderQueryArray = new TreeMap<String, String>(queryPairs);
orderQueryString = urlEncodeUTF8(orderQueryArray)
}
return orderQueryString;
}
public static String urlEncodeUTF8(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new UnsupportedOperationException(e);
}
}
public static String urlEncodeUTF8(Map<?,?> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<?,?> entry : map.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(String.format("%s=%s",
urlEncodeUTF8(entry.getKey().toString()),
urlEncodeUTF8(entry.getValue().toString())
));
}
return sb.toString();
}
public static byte[] readBinaryFile(String filePath) {
File file = new File(filePath)
byte[] binaryContent = file.bytes
return binaryContent
}
Provide the below parameters into the preprocessor:
def access_key = <access key>
def secret_key = <secret key>
def host = <host>
def service = "s3" //is any specific name given for service else it will be s3 by default
def region = "us-east-1" //the required region
def token = <session token>
def localfile = <absolute path for local file>
Then add the HTTP Header Manager into the HTTP Request Sampler where add the below headers:
Header Name: Value
X-Amz-Date: ${x_amz_date}
X-Amz-Security-Token: ${sessionToken}
X-Amz-Content-Sha256: ${aws_sha256}
Authorization: ${aws_authorization}
Content-Length: ${aws_content_length}
Content-Type: <file content type>
Host: ${host}
Here, below header's value calculated dynamically when file comes to upload by the preprocessor:
X-Amz-Date
X-Amz-Content-Sha256
Authorization
Content-Length
Now, In HTTP Request sampler set the request method (normally PUT), protocol (e.g. https), server (...s3.service.com), port (e.g. 443), path (path of the file including bucket name).
Click on File Upload tab in HTTP Request Sampler, profile the absolute path of local file which need to be uploaded and provide the mime type.
Note: Do not provide parameter name (it will be blank as file will go in raw binary format) and Use multipart/form data checkbox should be unchecked as file is going in raw format.
Just to give a little more help, you can use JSR223 Sampler for making HTTP request with different file upload / download approach. E.g. Uploading dynamically calculated data as file. Refer the link: How to pass data from JSR223 sampler to Http Request sampler in JMeter
Hope it will help you.
Perhaps I'm going a bridge to far here but heres what I got:
An AWS API Gateway Method that has AWS_IAM set for Authorization.
A Policy that allows access to that Method.
An EC2 Role that has that policy attached to it.
An EC2 Launched with that Role.
I would like to have my NodeJS program (or any language for that matter) on that EC2 to be able to call that API without hardcoding an AccessKey and SecretKey in the code.
I have used this approach to use the aws-sdk to put/get records on S3, and do other AWS functionality (like all the steps I mentioned above), However, invoking an API Gateway seems to be outside the aws-sdk scope.
Calling the API with Wreck (the NPM I use from my HTTP calls in my app) and no headers results in:
{
"message": "Missing Authentication Token"
}
Not a big shock there.
Anything obvious I am missing?
So it appears you need to access your EC2 at http://169.254.169.254/latest/meta-data/iam/security-credentials/Role_Name
As explained here.
Here is my final code including Signing AWS Requests with Signature Version 4:
var Moment = require('moment');
var Wreck = require('wreck');
var Crypto = require('crypto');
function getSignatureKey(key, dateStamp, regionName, serviceName) {
var kDate = Crypto.createHmac('sha256', 'AWS4' + key).update(dateStamp,'utf8').digest();
var kRegion = Crypto.createHmac('sha256', kDate).update(regionName, 'utf8').digest();
var kService = Crypto.createHmac('sha256', kRegion).update(serviceName, 'utf8').digest();
var kSigning = Crypto.createHmac('sha256', kService).update('aws4_request', 'utf8').digest();
return kSigning;
}
var assumed_role = 'MY_ROLE';
Wreck.get('http://169.254.169.254/latest/meta-data/iam/security-credentials/' + assumed_role, function(err, res, payload) {
var payload_obj = JSON.parse(payload.toString());
var access_key = payload_obj.AccessKeyId;
var secret_key = payload_obj.SecretAccessKey;
var token = payload_obj.Token;
var payload = {}
payload.email = 'devin.stewart#example.com';
payload.first_name = 'Devin';
payload.last_name = 'Stewart';
payload.full_name = 'Devin Stewart';
var request_parameters = JSON.stringify(payload);
var method = 'POST';
var api_id = 'MY_API_ID'
var service = 'execute-api';
var region = 'us-east-1';
var api_path = '/production/people';
var host = api_id + '.' + service + '.' + region + '.amazonaws.com';
var endpoint = 'https://' + host + api_path;
var content_type = 'application/json';
var t = Moment.utc()
var amz_date = t.format('YYYYMMDD[T]HHmmss[Z]');
var date_stamp = t.format('YYYYMMDD'); // Date w/o time, used in credential scope
var canonical_querystring = '';
var canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-security-token:' + token + '\n';
var signed_headers = 'content-type;host;x-amz-date;x-amz-security-token';
var payload_hash = Crypto.createHash('sha256').update(request_parameters).digest('hex');
var canonical_request = method + '\n' + api_path + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
var algorithm = 'AWS4-HMAC-SHA256';
var credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request';
var string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + Crypto.createHash('sha256').update(canonical_request).digest('hex');
var signing_key = getSignatureKey(secret_key, date_stamp, region, service);
var signature = Crypto.createHmac('sha256', signing_key).update(string_to_sign, 'utf8').digest('hex');
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
var headers = {
'Content-Type':content_type,
'X-Amz-Date':amz_date,
'X-Amz-Security-Token':token,
'Authorization':authorization_header
};
var options = {headers: headers, payload: request_parameters};
Wreck.post(endpoint, options, function (err, res, payload) {
if (err) {
console.log(err.data.payload.toString());
} else {
console.log(payload.toString());
}
});
});