@azure/storage-blob
Version:
Microsoft Azure Storage SDK for JavaScript - Blob
132 lines • 6.32 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { createHmac } from "node:crypto";
import { HeaderConstants } from "../utils/constants.js";
import { getURLPath, getURLQueries } from "../utils/utils.common.js";
import { compareHeader } from "../utils/SharedKeyComparator.js";
/**
* The programmatic identifier of the storageSharedKeyCredentialPolicy.
*/
export const storageSharedKeyCredentialPolicyName = "storageSharedKeyCredentialPolicy";
/**
* storageSharedKeyCredentialPolicy handles signing requests using storage account keys.
*/
export function storageSharedKeyCredentialPolicy(options) {
function signRequest(request) {
request.headers.set(HeaderConstants.X_MS_DATE, new Date().toUTCString());
if (request.body &&
(typeof request.body === "string" || Buffer.isBuffer(request.body)) &&
request.body.length > 0) {
request.headers.set(HeaderConstants.CONTENT_LENGTH, Buffer.byteLength(request.body));
}
const stringToSign = [
request.method.toUpperCase(),
getHeaderValueToSign(request, HeaderConstants.CONTENT_LANGUAGE),
getHeaderValueToSign(request, HeaderConstants.CONTENT_ENCODING),
getHeaderValueToSign(request, HeaderConstants.CONTENT_LENGTH),
getHeaderValueToSign(request, HeaderConstants.CONTENT_MD5),
getHeaderValueToSign(request, HeaderConstants.CONTENT_TYPE),
getHeaderValueToSign(request, HeaderConstants.DATE),
getHeaderValueToSign(request, HeaderConstants.IF_MODIFIED_SINCE),
getHeaderValueToSign(request, HeaderConstants.IF_MATCH),
getHeaderValueToSign(request, HeaderConstants.IF_NONE_MATCH),
getHeaderValueToSign(request, HeaderConstants.IF_UNMODIFIED_SINCE),
getHeaderValueToSign(request, HeaderConstants.RANGE),
].join("\n") +
"\n" +
getCanonicalizedHeadersString(request) +
getCanonicalizedResourceString(request);
const signature = createHmac("sha256", options.accountKey)
.update(stringToSign, "utf8")
.digest("base64");
request.headers.set(HeaderConstants.AUTHORIZATION, `SharedKey ${options.accountName}:${signature}`);
// console.log(`[URL]:${request.url}`);
// console.log(`[HEADERS]:${request.headers.toString()}`);
// console.log(`[STRING TO SIGN]:${JSON.stringify(stringToSign)}`);
// console.log(`[KEY]: ${request.headers.get(HeaderConstants.AUTHORIZATION)}`);
}
/**
* Retrieve header value according to shared key sign rules.
* @see https://learn.microsoft.com/rest/api/storageservices/authenticate-with-shared-key
*/
function getHeaderValueToSign(request, headerName) {
const value = request.headers.get(headerName);
if (!value) {
return "";
}
// When using version 2015-02-21 or later, if Content-Length is zero, then
// set the Content-Length part of the StringToSign to an empty string.
// https://learn.microsoft.com/rest/api/storageservices/authenticate-with-shared-key
if (headerName === HeaderConstants.CONTENT_LENGTH && value === "0") {
return "";
}
return value;
}
/**
* To construct the CanonicalizedHeaders portion of the signature string, follow these steps:
* 1. Retrieve all headers for the resource that begin with x-ms-, including the x-ms-date header.
* 2. Convert each HTTP header name to lowercase.
* 3. Sort the headers lexicographically by header name, in ascending order.
* Each header may appear only once in the string.
* 4. Replace any linear whitespace in the header value with a single space.
* 5. Trim any whitespace around the colon in the header.
* 6. Finally, append a new-line character to each canonicalized header in the resulting list.
* Construct the CanonicalizedHeaders string by concatenating all headers in this list into a single string.
*
*/
function getCanonicalizedHeadersString(request) {
let headersArray = [];
for (const [name, value] of request.headers) {
if (name.toLowerCase().startsWith(HeaderConstants.PREFIX_FOR_STORAGE)) {
headersArray.push({ name, value });
}
}
headersArray.sort((a, b) => {
return compareHeader(a.name.toLowerCase(), b.name.toLowerCase());
});
// Remove duplicate headers
headersArray = headersArray.filter((value, index, array) => {
if (index > 0 && value.name.toLowerCase() === array[index - 1].name.toLowerCase()) {
return false;
}
return true;
});
let canonicalizedHeadersStringToSign = "";
headersArray.forEach((header) => {
canonicalizedHeadersStringToSign += `${header.name
.toLowerCase()
.trimRight()}:${header.value.trimLeft()}\n`;
});
return canonicalizedHeadersStringToSign;
}
function getCanonicalizedResourceString(request) {
const path = getURLPath(request.url) || "/";
let canonicalizedResourceString = "";
canonicalizedResourceString += `/${options.accountName}${path}`;
const queries = getURLQueries(request.url);
const lowercaseQueries = {};
if (queries) {
const queryKeys = [];
for (const key in queries) {
if (Object.prototype.hasOwnProperty.call(queries, key)) {
const lowercaseKey = key.toLowerCase();
lowercaseQueries[lowercaseKey] = queries[key];
queryKeys.push(lowercaseKey);
}
}
queryKeys.sort();
for (const key of queryKeys) {
canonicalizedResourceString += `\n${key}:${decodeURIComponent(lowercaseQueries[key])}`;
}
}
return canonicalizedResourceString;
}
return {
name: storageSharedKeyCredentialPolicyName,
async sendRequest(request, next) {
signRequest(request);
return next(request);
},
};
}
//# sourceMappingURL=StorageSharedKeyCredentialPolicyV2.js.map