azurite
Version:
An open source Azure Storage API compatible server
152 lines • 8.63 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const utils_1 = require("../../common/utils/utils");
const TableStorageContext_1 = tslib_1.__importDefault(require("../context/TableStorageContext"));
const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory"));
const constants_1 = require("../utils/constants");
class TableSharedKeyLiteAuthenticator {
constructor(dataStore, logger) {
this.dataStore = dataStore;
this.logger = logger;
}
async validate(req, context) {
const tableContext = new TableStorageContext_1.default(context);
const account = tableContext.account;
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Start validation against account shared key authentication.`, tableContext.contextID);
const authHeaderValue = req.getHeader(constants_1.HeaderConstants.AUTHORIZATION);
if (authHeaderValue === undefined ||
!authHeaderValue.startsWith("SharedKeyLite")) {
this.logger.info(
// tslint:disable-next-line:max-line-length
`TableSharedKeyLiteAuthenticator:validate() Request doesn't include valid authentication header. Skip SharedKeyLite authentication.`, tableContext.contextID);
return;
}
// TODO: Make following async
const accountProperties = this.dataStore.getAccount(account);
if (accountProperties === undefined) {
this.logger.error(`TableSharedKeyLiteAuthenticator:validate() Invalid storage account ${account}.`, tableContext.contextID);
throw StorageErrorFactory_1.default.ResourceNotFound(context);
}
const stringToSign = [
this.getHeaderValueToSign(req, constants_1.HeaderConstants.DATE) ||
this.getHeaderValueToSign(req, constants_1.HeaderConstants.X_MS_DATE)
].join("\n") +
"\n" +
this.getCanonicalizedResourceString(req, account, tableContext.authenticationPath);
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() [STRING TO SIGN]:${JSON.stringify(stringToSign)}`, tableContext.contextID);
const signature1 = (0, utils_1.computeHMACSHA256)(stringToSign, accountProperties.key1);
const authValue1 = `SharedKeyLite ${account}:${signature1}`;
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Calculated authentication header based on key1: ${authValue1}`, tableContext.contextID);
if (authHeaderValue === authValue1) {
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Signature 1 matched.`, tableContext.contextID);
return true;
}
if (accountProperties.key2) {
const signature2 = (0, utils_1.computeHMACSHA256)(stringToSign, accountProperties.key2);
const authValue2 = `SharedKeyLite ${account}:${signature2}`;
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Calculated authentication header based on key2: ${authValue2}`, tableContext.contextID);
if (authHeaderValue === authValue2) {
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Signature 2 matched.`, tableContext.contextID);
return true;
}
}
if (context.context.isSecondary && tableContext.authenticationPath?.indexOf(account) === 1) {
// JS/.net Track2 SDK will generate stringToSign from IP style URI with "-secondary" in authenticationPath, so will also compare signature with this kind stringToSignconst stringToSign: string =
const stringToSign_secondary = [
this.getHeaderValueToSign(req, constants_1.HeaderConstants.DATE) ||
this.getHeaderValueToSign(req, constants_1.HeaderConstants.X_MS_DATE)
].join("\n") +
"\n" +
this.getCanonicalizedResourceString(req, account,
// The authenticationPath looks like "/devstoreaccount1/table", add "-secondary" after account name to "/devstoreaccount1-secondary/table"
tableContext.authenticationPath?.replace(account, account + "-secondary"));
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() [STRING TO SIGN_secondary]:${JSON.stringify(stringToSign_secondary)}`, tableContext.contextID);
const signature1_secondary = (0, utils_1.computeHMACSHA256)(stringToSign_secondary, accountProperties.key1);
const authValue1_secondary = `SharedKeyLite ${account}:${signature1_secondary}`;
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Calculated authentication header based on key1: ${authValue1_secondary}`, tableContext.contextID);
if (authHeaderValue === authValue1_secondary) {
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Signature 1_secondary matched.`, tableContext.contextID);
return true;
}
if (accountProperties.key2) {
const signature2_secondary = (0, utils_1.computeHMACSHA256)(stringToSign_secondary, accountProperties.key2);
const authValue2_secondary = `SharedKeyLite ${account}:${signature2_secondary}`;
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Calculated authentication header based on key2: ${authValue2_secondary}`, tableContext.contextID);
if (authHeaderValue === authValue2_secondary) {
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Signature 2_secondary matched.`, tableContext.contextID);
return true;
}
}
}
// this.logger.info(`[URL]:${req.getUrl()}`);
// this.logger.info(`[HEADERS]:${req.getHeaders().toString()}`);
// this.logger.info(`[KEY]: ${request.headers.get(HeaderConstants.AUTHORIZATION)}`);
this.logger.info(`TableSharedKeyLiteAuthenticator:validate() Validation failed.`, tableContext.contextID);
return false;
}
/**
* Retrieve header value according to shared key sign rules.
* @see https://docs.microsoft.com/en-us/rest/api/storageservices/authenticate-with-shared-key
*
* @private
* @param {WebResource} request
* @param {string} headerName
* @returns {string}
* @memberof SharedKeyCredentialPolicy
*/
getHeaderValueToSign(request, headerName) {
const value = request.getHeader(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://docs.microsoft.com/en-us/rest/api/storageservices/authenticate-with-shared-key
if (headerName === constants_1.HeaderConstants.CONTENT_LENGTH && value === "0") {
return "";
}
return value;
}
/**
* Retrieves canonicalized resource string.
*
* @private
* @param {IRequest} request
* @returns {string}
* @memberof SharedKeyCredentialPolicy
*/
getCanonicalizedResourceString(request, account, authenticationPath) {
let path = request.getPath() || "/";
// For secondary account, we use account name (without "-secondary") for the path
if (authenticationPath !== undefined) {
path = authenticationPath;
}
let canonicalizedResourceString = "";
canonicalizedResourceString += `/${account}${path}`;
const queries = (0, utils_1.getURLQueries)(request.getUrl());
const lowercaseQueries = {};
if (queries) {
const queryKeys = [];
for (const key in queries) {
if (queries.hasOwnProperty(key)) {
const lowercaseKey = key.toLowerCase();
lowercaseQueries[lowercaseKey] = queries[key];
queryKeys.push(lowercaseKey);
}
}
if (queryKeys.includes("comp")) {
canonicalizedResourceString += "?comp=" + lowercaseQueries.comp;
}
// queryKeys.sort();
// for (const key of queryKeys) {
// canonicalizedResourceString += `\n${key}:${decodeURIComponent(
// lowercaseQueries[key]
// )}`;
// }
}
return canonicalizedResourceString;
}
}
exports.default = TableSharedKeyLiteAuthenticator;
//# sourceMappingURL=TableSharedKeyLiteAuthenticator.js.map
;