UNPKG

azurite

Version:

An open source Azure Storage API compatible server

163 lines 7.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const jsonwebtoken_1 = require("jsonwebtoken"); const models_1 = require("../../common/models"); const constants_1 = require("../../common/utils/constants"); const QueueStorageContext_1 = tslib_1.__importDefault(require("../context/QueueStorageContext")); const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory")); const operation_1 = require("../generated/artifacts/operation"); const constants_2 = require("../utils/constants"); class QueueTokenAuthenticator { constructor(dataStore, oauth, logger) { this.dataStore = dataStore; this.oauth = oauth; this.logger = logger; } async validate(req, context) { const queueContext = new QueueStorageContext_1.default(context); const account = queueContext.account; this.logger.info(`QueueTokenAuthenticator:validate() Start validation against token authentication.`, queueContext.contextID); // TODO: Make following async const accountProperties = this.dataStore.getAccount(account); if (accountProperties === undefined) { this.logger.error(`QueueTokenAuthenticator:validate() Invalid storage account ${account}.`, queueContext.contextID); throw StorageErrorFactory_1.default.ResourceNotFound(context.contextID); } const authHeaderValue = req.getHeader(constants_2.HeaderConstants.AUTHORIZATION); if (authHeaderValue === undefined) { this.logger.info(`QueueTokenAuthenticator:validate() Request doesn't include valid authentication header. Skip token authentication.`, queueContext.contextID); return; } if (queueContext.operation === operation_1.Operation.Queue_GetAccessPolicy || queueContext.operation === operation_1.Operation.Queue_SetAccessPolicy) { this.logger.info(`QueueTokenAuthenticator:validate() Operation is not available with OAuth. Skip token authentication.`, queueContext.contextID); return; } if (!authHeaderValue.startsWith(constants_1.BEARER_TOKEN_PREFIX)) { throw StorageErrorFactory_1.default.getInvalidAuthenticationInfo(context.contextID); } if (req.getProtocol().toLowerCase() !== constants_1.HTTPS) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not allowed with HTTP."); } // TODO: Check API Version and enable bearer challenge after 2019-12-12 const token = authHeaderValue.substr(constants_1.BEARER_TOKEN_PREFIX.length + 1); switch (this.oauth) { case models_1.OAuthLevel.BASIC: return this.authenticateBasic(token, context); default: this.logger.warn(`QueueTokenAuthenticator:validate() Unknown OAuth level ${this.oauth}. Skip token authentication.`, queueContext.contextID); return; } } async authenticateBasic(token, context) { // tslint:disable: max-line-length /** * Example OAuth Bearer Token: * { * "aud": "https://storage.azure.com", * "iss": "https://sts.windows-ppe.net/ab1f708d-50f6-404c-a006-d71b2ac7a606/", * "iat": 1511859603, * "nbf": 1511859603, * "exp": 1511863503, * "_claim_names": { * "groups": "src1" * }, * "_claim_sources": { * "src1": { * "endpoint": "https://graph.ppe.windows.net/ab1f708d-50f6-404c-a006-d71b2ac7a606/users/11059fcc-5514-4800-b5f8-49808b9cfab6/getMemberObjects" * } * }, * "acr": "1", * "aio": "ASQA2/8JAAAAE9BLp+qWOO0iEg3n6mjPNqaoHSCIqhQQ8OCinJcub7U=", * "amr": [ * "pwd" * ], * "appid": "f997392c-e15a-4ad8-af9e-cd6966caba7f", * "appidacr": "0", * "e_exp": 262800, * "ipaddr": "167.220.255.51", * "name": "test", * "oid": "11059fcc-5514-4800-b5f8-49808b9cfab6", * "puid": "10033FFFA6976143", * "scp": "user_impersonation", * "sub": "JeH2dBcxZp-_pKoskdxOilGj234LhlM_0GBM4JvSrJw", * "tid": "ab1f708d-50f6-404c-a006-d71b2ac7a606", * "unique_name": "test@oauthtest102.ccsctp.net", * "upn": "test@oauthtest102.ccsctp.net", * "uti": "dd77i7fCB0yn8aQYUqsJAA", * "ver": "1.0" * } */ // Validate JWT token format let decoded; try { decoded = (0, jsonwebtoken_1.decode)(token); } catch { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported."); } if (!decoded) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported."); } // Validate signature, skip in basic check // Validate nbf & exp if (decoded.nbf === undefined || decoded.exp === undefined || decoded.iat === undefined) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported."); } const now = context.startTime.getTime(); const nbf = decoded.nbf * 1000; const exp = decoded.exp * 1000; if (now < nbf) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Lifetime validation failed."); } if (now > exp) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Lifetime validation failed. The token is expired."); } const iss = decoded.iss; if (!iss) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported."); } let issMatch = false; for (const validIssuePrefix of constants_1.VALID_ISSUE_PREFIXES) { if (iss.startsWith(validIssuePrefix)) { issMatch = true; break; } } if (!issMatch) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Invalid token issuer."); } const aud = decoded.aud; if (!aud) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Authentication scheme Bearer is not supported."); } const queueContext = context; let audMatch = false; let m; for (const regex of constants_2.VALID_QUEUE_AUDIENCES) { m = regex.exec(aud); if (m !== null) { if (m[0] === aud) { if (m[1] !== undefined && m[1] !== queueContext.account) { // If account name doesn't match for fine-grained audience break; } audMatch = true; break; } } } if (!audMatch) { throw StorageErrorFactory_1.default.getAuthenticationFailed(context.contextID, "Invalid token audience."); } // Skip validate scope // Currently scope maybe null or user_impersonation this.logger.info(`QueueTokenAuthenticator:authenticateBasic() Validation against token authentication successfully.`, queueContext.contextID); return true; } } exports.default = QueueTokenAuthenticator; //# sourceMappingURL=QueueTokenAuthenticator.js.map