UNPKG

azurite

Version:

An open source Azure Storage API compatible server

159 lines 9.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory")); const operation_1 = tslib_1.__importDefault(require("../generated/artifacts/operation")); const IAccountSASSignatureValues_1 = require("../../common/authentication/IAccountSASSignatureValues"); const OperationAccountSASPermission_1 = tslib_1.__importDefault(require("./OperationAccountSASPermission")); class AccountSASAuthenticator { constructor(accountDataStore, logger) { this.accountDataStore = accountDataStore; this.logger = logger; } async validate(req, context) { this.logger.info(`AccountSASAuthenticator:validate() Start validation against account Shared Access Signature pattern.`, context.contextID); this.logger.debug("AccountSASAuthenticator:validate() Getting account properties...", context.contextID); const account = context.context.account; const queueName = context.context.queue; this.logger.debug( // tslint:disable-next-line:max-line-length `AccountSASAuthenticator:validate() Retrieved account name from context: ${account}, queue: ${queueName}`, context.contextID); // TODO: Make following async const accountProperties = this.accountDataStore.getAccount(account); if (accountProperties === undefined) { throw StorageErrorFactory_1.default.ResourceNotFound(context.contextID); } this.logger.debug("AccountSASAuthenticator:validate() Got account properties successfully.", context.contextID); const signature = this.decodeIfExist(req.getQuery("sig")); this.logger.debug(`AccountSASAuthenticator:validate() Retrieved signature from URL parameter sig: ${signature}`, context.contextID); const values = this.getAccountSASSignatureValuesFromRequest(req); if (values === undefined) { this.logger.info(`AccountSASAuthenticator:validate() Failed to get valid account SAS values from request.`, context.contextID); return false; } this.logger.debug(`AccountSASAuthenticator:validate() Successfully got valid account SAS values from request. ${JSON.stringify(values)}`, context.contextID); this.logger.info(`AccountSASAuthenticator:validate() Validate signature based account key1.`, context.contextID); const [sig1, stringToSign1] = (0, IAccountSASSignatureValues_1.generateAccountSASSignature)(values, account, accountProperties.key1); this.logger.debug(`AccountSASAuthenticator:validate() String to sign is: ${JSON.stringify(stringToSign1)}`, context.contextID); this.logger.debug(`AccountSASAuthenticator:validate() Calculated signature is: ${sig1}`, context.contextID); const sig1Pass = sig1 === signature; this.logger.info(`AccountSASAuthenticator:validate() Signature based on key1 validation ${sig1Pass ? "passed" : "failed"}.`, context.contextID); if (!sig1Pass) { if (accountProperties.key2 === undefined) { return false; } this.logger.info(`AccountSASAuthenticator:validate() Account key2 is not empty, validate signature based account key2.`, context.contextID); const [sig2, stringToSign2] = (0, IAccountSASSignatureValues_1.generateAccountSASSignature)(values, account, accountProperties.key2); this.logger.debug(`AccountSASAuthenticator:validate() String to sign is: ${JSON.stringify(stringToSign2)}`, context.contextID); this.logger.debug(`AccountSASAuthenticator:validate() Calculated signature is: ${sig2}`, context.contextID); const sig2Pass = sig2 === signature; this.logger.info(`AccountSASAuthenticator:validate() Signature based on key2 validation ${sig2Pass ? "passed" : "failed"}.`, context.contextID); if (!sig2Pass) { this.logger.info(`AccountSASAuthenticator:validate() Validate signature based account key1 and key2 failed.`, context.contextID); return false; } } // When signature validation passes, we enforce account SAS validation // Any validation errors will stop this request immediately this.logger.info(`AccountSASAuthenticator:validate() Validate start and expiry time.`, context.contextID); if (!this.validateTime(values.expiryTime, values.startTime)) { this.logger.info(`AccountSASAuthenticator:validate() Validate start and expiry failed.`, context.contextID); throw StorageErrorFactory_1.default.getAuthorizationFailure(context.contextID); } this.logger.info(`AccountSASAuthenticator:validate() Validate IP range.`, context.contextID); if (!this.validateIPRange()) { this.logger.info(`AccountSASAuthenticator:validate() Validate IP range failed.`, context.contextID); throw StorageErrorFactory_1.default.getAuthorizationSourceIPMismatch(context.contextID); } this.logger.info(`AccountSASAuthenticator:validate() Validate request protocol.`, context.contextID); if (!this.validateProtocol(values.protocol, req.getProtocol())) { this.logger.info(`AccountSASAuthenticator:validate() Validate protocol failed.`, context.contextID); throw StorageErrorFactory_1.default.getAuthorizationProtocolMismatch(context.contextID); } const operation = context.operation; if (operation === undefined) { throw new Error( // tslint:disable-next-line:max-line-length `AccountSASAuthenticator:validate() operation shouldn't be undefined. Please make sure DispatchMiddleware is hooked before authentication related middleware.`); } const accountSASPermission = OperationAccountSASPermission_1.default.get(operation); this.logger.debug(`AccountSASAuthenticator:validate() Got permission requirements for operation ${operation_1.default[operation]} - ${JSON.stringify(accountSASPermission)}`, context.contextID); if (accountSASPermission === undefined) { throw new Error( // tslint:disable-next-line:max-line-length `AccountSASAuthenticator:validate() OPERATION_ACCOUNT_SAS_PERMISSIONS doesn't have configuration for operation ${operation_1.default[operation]}'s account SAS permission.`); } if (!accountSASPermission.validateServices(values.services)) { throw StorageErrorFactory_1.default.getAuthorizationServiceMismatch(context.contextID); } if (!accountSASPermission.validateResourceTypes(values.resourceTypes)) { throw StorageErrorFactory_1.default.getAuthorizationResourceTypeMismatch(context.contextID); } if (!accountSASPermission.validatePermissions(values.permissions)) { throw StorageErrorFactory_1.default.getAuthorizationPermissionMismatch(context.contextID); } this.logger.info(`AccountSASAuthenticator:validate() Account SAS validation successfully.`, context.contextID); return true; } getAccountSASSignatureValuesFromRequest(req) { const version = this.decodeIfExist(req.getQuery("sv")); const services = this.decodeIfExist(req.getQuery("ss")); const resourceTypes = this.decodeIfExist(req.getQuery("srt")); const protocol = this.decodeIfExist(req.getQuery("spr")); const startTime = this.decodeIfExist(req.getQuery("st")); const expiryTime = this.decodeIfExist(req.getQuery("se")); const ipRange = this.decodeIfExist(req.getQuery("sip")); const permissions = this.decodeIfExist(req.getQuery("sp")); const signature = this.decodeIfExist(req.getQuery("sig")); if (version === undefined || expiryTime === undefined || permissions === undefined || services === undefined || resourceTypes === undefined || signature === undefined) { return undefined; } const accountSASValues = { version, protocol, startTime, expiryTime, permissions, ipRange, services, resourceTypes }; return accountSASValues; } validateTime(expiry, start) { const expiryTime = new Date(expiry); const now = new Date(); if (now > expiryTime) { return false; } if (start !== undefined) { const startTime = new Date(start); if (now < startTime) { return false; } } return true; } validateIPRange() { // TODO: Emulator doesn't validate IP Address return true; } validateProtocol(sasProtocol = "https,http", requestProtocol) { if (sasProtocol.includes(",")) { return true; } else { return sasProtocol.toLowerCase() === requestProtocol; } } decodeIfExist(value) { return value === undefined ? value : decodeURIComponent(value); } } exports.default = AccountSASAuthenticator; //# sourceMappingURL=AccountSASAuthenticator.js.map