Azure Table Storage (Table Services REST API) SharedKeyLite not working - azure

After seeing through this link, I tried the same in my postman.
var storageAccount = "mystorage";
var accountKey = "<<primaryKey>>";
var date = new Date();
var UTCstring = date.toUTCString();
var data = UTCstring + "\n" + "/mystorage/Health"
var encodedData = unescape(encodeURIComponent(data));
var hash = CryptoJS.HmacSHA256(encodedData, CryptoJS.enc.Base64.parse(accountKey));
var signature = hash.toString(CryptoJS.enc.Base64);
var auth = "SharedKeyLite " + storageAccount + ":" + signature;
postman.setEnvironmentVariable("auth", auth);
postman.setEnvironmentVariable("date", UTCstring);
When I make the request to ATS, to the following url,
I get the auth denied!
Can someone please guide me what's going wrong here?!

I think you need to generate a bearer token and put it to the Authorization of Postman.
If you are using C#, you can use this to get the bearer token:
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
string accesstoken = azureServiceTokenProvider.GetAccessTokenAsync("https://storage.azure.com/").Result;
string bearertoken = "Bearer " + accesstoken;
Then Copy the bearer token:
After that, it should be ok.

Just realized that the url and the data that we are encoding should exactly match the url we are querying...
After changing
var data = UTCstring + "\n" + "/mystorage/Health"
to
var data = UTCstring + "\n" + "/mystorage/Health(PartitionKey='USA',RowKey='WA')"
things started working.
Update :
It just expects the right table query. The following works fine,
var data = UTCstring + "\n" + "/mystorage/Health()"
with all filter expressions in the url being invoked from postman.

Related

Bringing data from Rapidmail to Salesforce Marketing Cloud

We are trying to get the recepient activity data from Rapidmail to SFMC using the below code. It throws an error saying that >Script.Util.HttpResponse{"type":"http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html","title":"Not Acceptable","status":406,"detail":"Unable to resolve Accept header to a representation"}"
req.emptyContentHandling = 0;
req.retries = 2;
req.continueOnError = true;
var baseIt = Platform.Function.Base64Encode(config.username + ":" + config.password);
var auth = "Basic " + baseIt;
req.setHeader("Authorization" , auth);
req.setHeader("Accept-Language", "*");
req.method = "GET";
var res = req.send();
Write(res.content);```
Could you please let me know how to solve the issue.
Thanks,
Nikhila

How do I form an SAS token for Microsoft Azure API Management's REST API in Node.js?

I am using Microsoft Azure API Management service and want to use the REST API service. In creating my SAS token, which is needed otherwise the API call doesn't authorize, I'm having difficulty forming a proper token. Microsoft's webpage about this SAS token for API Management only shows an example in C#. I want to know how to form an SAS token in Node.js, which is not shown. Below is my code that was working last week, but is not now for some unknown reason. The error I get is: 401 Authorization error, token invalid
If someone can help me formulate this token, I would appreciate it.
This is Microsoft's webpage regarding this authentication token: https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
Here's my code:
const crypto = require('crypto');
const util = require('util');
const sign = () => {
const id = ${process.env.id}
const key = `${process.env.SASKey}`;
const date = new Date();
const newDate = new Date(date.setTime(date.getTime() + 8 * 86400000));
const expiry = `${newDate.getFullYear()}${
newDate.getMonth() < 10
? '' + newDate.getMonth() + 1
: newDate.getMonth() + 1
}${newDate.getDate()}${newDate.getHours()}${
newDate.getMinutes() < 10
? '0' + newDate.getMinutes()
: newDate.getMinutes()
}`;
const dataToSignString = '%s\n%s';
const dataToSign = util.format(dataToSignString, ${id}, expiry);
const hash = crypto
.createHmac('sha512', key)
.update(dataToSign)
.digest('base64');
const encodedToken = `SharedAccessSignature ${id}&${expiry}&${hash}`;
console.log(encodedToken);
return encodedToken;
};
Try the code:
protected getAPIManagementSAS(){
let utf8 = require("utf8")
let crypto= require("crypto")
let identifier = process.env.API_IDENTIFIER;
let key = process.env.API_KEY;
var now = new Date;
var utcDate = new Date(now.getUTCFullYear(),now.getUTCMonth(), now.getUTCDate() , now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
let expiry = addMinutes(utcDate,1,"yyyy-MM-ddThh:mm:ss") + '.0000000Z'
var dataToSign = identifier + "\n" + expiry;
var signatureUTF8 = utf8.encode(key);
var signature = crypto.createHmac('sha512', signatureUTF8).update(dataToSign).digest('base64');
var encodedToken = `SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}`;
return encodedToken
}
For more information, see here.
After a million tries, it seems like the only format acceptable right now is:
SharedAccessSignature uid=${identifier}&ex=${expiry}&sn=${signature}
If you are using the other format that has the "integration" parameter, that's a hit or a miss, mostly miss though. Set the uid as "integration" if that's your identifier and follow the above format as it works.

Problem in JSR223 script JSR223 Sampler, while connecting to Azure Cosmos DB from Jmeter

I am trying to push my JMeter result to Azure Cosmos DB through the rest API exposed from the azure portal.
To achieve so I am using JSR223 sampler (as my pre processer) to get the auth token to connect to cosmos db also using the stand script to generate the auth_token (refer:https://github.com/MicrosoftCSA/documentdb-postman-collection/issues).
But I am getting Problem in JSR223 script JSR223 Sampler, message: javax.script.ScriptException: ReferenceError: "request" is not defined.
Code Snippet:
var mastKey = "master_key_for_cosmos_db";
log.info("mastKey = " + mastKey);
var today = new Date();
var UTCstring = today.toUTCString();
var url = "uri_key_for_cosmos_db"
var strippedurl = url.replace(new RegExp('^https?://[^/]+/'), '/');
log.info("stripped Url = " + strippedurl);
var strippedparts = strippedurl.split("/");
var truestrippedcount = (strippedparts.length - 1);
var resourceId = "";
var resType = "";
if (truestrippedcount % 2) {
resType = strippedparts[truestrippedcount];
if (truestrippedcount > 1) {
var lastPart = strippedurl.lastIndexOf("/");
resourceId = strippedurl.substring(1, lastPart);
}
} else // its even (item request on resource)
{
resType = strippedparts[truestrippedcount - 1];
strippedurl = strippedurl.substring(1);
resourceId = strippedurl;
}
var verb = request.method.toLowerCase();
var date = UTCstring.toLowerCase();
var key = CryptoJS.enc.Base64.parse(mastKey);
var text = (verb || "").toLowerCase() + "\n" +
(resType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
(date || "").toLowerCase() + "\n" +
"" + "\n";
var signature = CryptoJS.HmacSHA256(text, key);
var base64Bits = CryptoJS.enc.Base64.stringify(signature);
var MasterToken = "master";
var TokenVersion = "1.0";
auth = encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + base64Bits);
vars.put("authToken", auth);
anything I am doing wrong or missed?
This request object belongs to Postman tool, you cannot use it in your JMeter scripts as it is not defined there.
You will also need to import this CryptoJS which might not be trivial.
Moreover the recommended language for scripting in JMeter is Groovy
So instead of trying to copy and paste someone's Postman code into JMeter which will not work I would rather recommend going into one of the following directions:
Use Azure Cosmos DB Java SDK for SQL API from JSR223 Test Elements using Groovy language
Or replicate the Postman's JavaScript in Groovy like it's described in How to Handle Dynamic AWS SigV4 in JMeter for API Testing article

DocumentDB REST API - Authorization Token Error

Problem
We are seeing this error returned from the DocumentDB REST API whenever we request a list or query, but not when we fetch objects by name/id:
The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used.
Background
We have been successfully using the node.js sdk with DocumentDB for over a year now, but as we want to migrate our back-end restful API code from a node.js App Service to Azure Functions we are seeing 10-30 second lag times come into play as the DocumentDB sdk loads slowly when the Function hasn't been called in a while. We know that the Function instance is hot, and this isn't a cold instance issue based on previous communication with the Azure Functions team.
To work around this we want to test the DocumentDB REST API which requires zero external libraries to run in a node.js Function and should execute as quickly as possible.
Code
This is the test harness running in local node.js. We'll move this to an Azure Function once it's working.
var express = require('express');
var router = express.Router();
var crypto = require("crypto");
var request = require('request');
router.get('/', function (req, res, next) {
var key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
var uri = "https://xxxxxx.documents.azure.com";
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
var url = `${uri}/${link}`;
var headers = getDefaultRequestHeaders();
// var body = `{"query":"SELECT * FROM c", "parameters": []}`;
var body = '';
headers['content-length'] = body.length;
headers['authorization'] = getAuthorizationTokenUsingMasterKey(verb, type, link, headers['x-ms-date'], key);
request[verb.toLowerCase()]({"url": url, "headers": headers, "body": body}, function (error, response, body) {
// console.log(`error is ${error}`);
// console.log(`response is ${JSON.stringify(response, null, 2)}`);
console.log(`body is ${body}`);
res.status(response.statusCode).json(body);
});
});
function getDefaultRequestHeaders(isQuery, date) {
var headers = {
"content-type": "application/json",
"x-ms-date": new Date().toUTCString(),
"x-ms-version": "2017-02-22",
"accept": "application/json",
"cache-control": "no-cache",
"user-agent": "xxxxxx/1.0"
};
if(isQuery) {
headers["x-ms-documentdb-isquery"] = true;
headers["content-type"] = "application/query+json";
}
if(date) {
headers["x-ms-date"] = date;
}
return headers;
}
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceLink || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
module.exports = router;
We are using the getAuthorizationTokenFromMasterKey function verbatim from the Access control in the DocumentDB API page.
The key, app name, and user-agent have been replaced with x's for privacy/security.
Test Results
List Databases
When I try the most basic call to list dbs the server returns the token error:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 12 aug 2017 12:28:41 gmt\n\n'\r\nActivityId: acbf19d9-6485-45c5-9c30-6aa21f14d5b3\"}"
Get Database
However, when I perform the get database request it works fine:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs/00001';
Response:
"{\"id\":\"00001\",\"_rid\":\"0eUiAA==\",\"_ts\":1441256154,\"_self\":\"dbs\/0eUiAA==\/\",\"_etag\":\"\\"00007d4a-0000-0000-0000-55e7d2da0000\\"\",\"_colls\":\"colls\/\",\"_users\":\"users\/\"}"
List Collections
Similarly, requesting the list of collections from this database returns a token error:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls';
Respose:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ncolls\ndbs/00001\nsat, 12 aug 2017 12:32:19 gmt\n\n'\r\nActivityId: 8a9d4ff8-24ef-4fd2-b400-f9f8aa743572\"}"
Get Collection
But when I call get collection I get a valid response:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls/00001';
Response:
"{\"id\":\"00001\",\"indexingPolicy\":{\"indexingMode\":\"consistent\",\"automatic\":true,\"includedPaths\":[{\"path\":\"\/*\",\"indexes\":[{\"kind\":\"Range\",\"dataType\":\"Number\",\"precision\":-1},{\"kind\":\"Range\",\"dataType\":\"String\",\"precision\":-1},{\"kind\":\"Spatial\",\"dataType\":\"Point\"}]}],\"excludedPaths\":[]},\"_rid\":\"0eUiAJMAdQA=\",\"_ts\":1454200014,\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/\",\"_etag\":\"\\"00000100-0000-0000-0000-56ad54ce0000\\"\",\"_docs\":\"docs\/\",\"_sprocs\":\"sprocs\/\",\"_triggers\":\"triggers\/\",\"_udfs\":\"udfs\/\",\"_conflicts\":\"conflicts\/\"}"
List Documents
Requesting list documents on that collection gives me this error:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:34:48 gmt\n\n'\r\nActivityId: 57097e95-c41b-4770-b91a-370418ef2cce\"}"
Get Document
Not surprisingly, fetching a single document works fine:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs/e7fe638d-2152-2097-f9c6-9801d7cf5cdd';
Response:
"{\"name\":\"test rest api\",\"id\":\"e7fe638d-2152-2097-f9c6-9801d7cf5cdd\",\"_rid\":\"0eUiAJMAdQCbHgAAAAAAAA==\",\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/docs\/0eUiAJMAdQCbHgAAAAAAAA==\/\",\"_etag\":\"\\"0d00d1ee-0000-0000-0000-598ef7d40000\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1502541779}"
Query Documents
Finally, sending a query also results in a token error:
var verb = 'POST';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
var body = `{"query":"SELECT * FROM c", "parameters": []}`;
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'post\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:35:42 gmt\n\n'\r\nActivityId: b8b95f8c-1339-423e-b0e7-0d15d3056180\"}"
I believe the documentation is incorrect. Where they say resourceLink, they should actually say resource id. If you look at the Node SDK code, this is how they are calculating the authorization header (notice the use of resourceId):
getAuthorizationTokenUsingMasterKey: function (verb, resourceId, resourceType, headers, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
(headers["x-ms-date"] || "").toLowerCase() + "\n" +
(headers["date"] || "").toLowerCase() + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return "type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature;
},
So if you want to list the databases, because there is no resource id you will need to use an empty string for your link variable. Similarly, if you want to list collections in the database, the link should actually be the id of the database (e.g. dbs/00001 and not dbs/00001/colls).
I was getting the same issue. For querying documents I was getting authorization token error. It was due to wrong ResourceId/ResourceLink
var verb = 'POST';
var type = 'docs';
var link = 'dbs/{db-id}/colls/{coll-id}/docs';
var url = `${uri}/${link}`;
var resourceLink = "dbs/{db-id}/colls/{coll-id}"
getAuthorizationTokenUsingMasterKey(verb, type, resourceLink, headers['x-ms-date'], key)
the only correction is required from the given question data is to change the appropriate resourceLink while generating AuthorizationToken. For querying Documents the resourceLink is <dbs/{db-id}/colls/{coll-id}> instead of <dbs/{db-id}/colls/{coll-id}/docs>
I want to provide another thing to consider in addressing this issue. In my case I had to add this header: x-ms-documentdb-query-enablecrosspartition: true, because I created my container with a partitionKey.
I also want to confirm how my main parameters were setup to calculate the authorization header for querying over Documents:
resourceType: docs
resourceLink: dbs/<databaseId>/colls/<containerId>
I had thought, up to this point, that the resourceLInk had to match with the request URL but this is showing me I was wrong. Similarly notice that the resourceType is not present in the resourceLink.
POST /dbs/<databaseId>/colls/<containerId>/docs HTTP/1.1
accept: application/json
x-ms-documentdb-isquery: true
x-ms-version: 2018-12-31
authorization: type%3Dmaster%26ver%3D1.0%26sig%***********************
x-ms-date: Sat, 03 Apr 2021 22:34:24 GMT
x-ms-documentdb-query-enablecrosspartition: true
x-correlation-id: be1b1fe1-94cc-11eb-a0a4-38f9d3924940
Host: <host>.documents.azure.com
User-Agent: AHC/1.0
Connection: keep-alive
Content-Type: application/query+json
Content-Length: 72
{
"query": "SELECT * FROM <containerId>",
"parameters": [
]
}
I got the same error while making an update to document DB but in my case I realized I was using the Read-Only keys. After changing connection string to use it to Read-Write Keys, I was able to update the records.

Amazon S3 Signature

How do you create the hex encoded signature in crypto in a nodejs environment?
Do you just do this like so?
const secret = 'mysecret';
const date = yyyymmdd;
const dateKey = crypto.createHmac('sha256', 'AWS4' + secret + ',' + date);
const dateRegionKey = crypto('sha256', dateKey + ',' + 'myregion')
const DateRegionServiceKey = crypto('sha256', dateRegionKey + ',' + 'someservice');
const signingKey = crypto('sha256', DateRegionServiceKey + ',' + 'aws4_request');
const signature = crypo('sha256', signingKey + base64Policy);
High level it looks ok. I hope you know that secret is not just a string, it is the secretKey of your IAM User accessKey/secretKey pair.
Any particular reason you want to do it yourself and not use the AWS SDK?
Also look at below for a sample signing implementation (in Java) which works for AWS ElasticSearch. Look at getSignatureKey and calculateSignature methods.
https://github.com/dy10/aws-elasticsearch-query-java/blob/master/src/main/java/dy/aws/es/AWSV4Auth.java
you should use this library https://github.com/mhart/aws4
you need to use result = aws4.sign({}, { cred from AWS })
and it returns result.headers that are the headers you need to send in your request

Resources