UNPKG

azurite

Version:

An open source Azure Storage API compatible server

152 lines 8.63 kB
"use strict"; 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