UNPKG

azurite

Version:

An open source Azure Storage API compatible server

208 lines 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toBlobTags = exports.validateBlobTag = exports.getTagsFromString = exports.getBlobTagsCount = exports.getUserDelegationKeyValue = exports.validateContainerName = exports.removeQuotationFromListBlobEtag = exports.deserializePageBlobRangeHeader = exports.deserializeRangeHeader = exports.streamToLocalFile = exports.checkApiVersion = void 0; const tslib_1 = require("tslib"); const crypto_1 = require("crypto"); const fs_1 = require("fs"); const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory")); const constants_1 = require("./constants"); function checkApiVersion(inputApiVersion, validApiVersions, requestId) { if (!validApiVersions.includes(inputApiVersion)) { throw StorageErrorFactory_1.default.getInvalidAPIVersion(requestId, inputApiVersion); } } exports.checkApiVersion = checkApiVersion; async function streamToLocalFile(stream, path) { return new Promise((resolve, reject) => { const writeStream = (0, fs_1.createWriteStream)(path); stream .on("error", reject) // .on("end", resolve) .pipe(writeStream) .on("close", resolve) .on("error", reject); }); } exports.streamToLocalFile = streamToLocalFile; /** * Default range value [0, Infinite] will be returned if all parameters not provided. * * @export * @param {string} [rangeHeaderValue] * @param {string} [xMsRangeHeaderValue] * @returns {[number, number]} */ function deserializeRangeHeader(rangeHeaderValue, xMsRangeHeaderValue) { const range = xMsRangeHeaderValue || rangeHeaderValue; if (!range) { return [0, Infinity]; } let parts = range.split("="); if (parts === undefined || parts.length !== 2) { throw new RangeError(`deserializeRangeHeader: raw range value ${range} is wrong.`); } parts = parts[1].split("-"); if (parts === undefined || parts.length < 1 || parts.length > 2) { throw new RangeError(`deserializeRangeHeader: raw range value ${range} is wrong.`); } const startInclusive = parseInt(parts[0], 10); let endInclusive = Infinity; if (parts.length > 1 && parts[1] !== "") { endInclusive = parseInt(parts[1], 10); } if (startInclusive > endInclusive) { throw new RangeError(`deserializeRangeHeader: raw range value ${range} is wrong.`); } return [startInclusive, endInclusive]; } exports.deserializeRangeHeader = deserializeRangeHeader; /** * Deserialize range header into valid page ranges. * For example, "bytes=0-1023" will return [0, 1023]. * * Default range value [0, Infinite] will be returned if all parameters not provided. * * @private * @param {string} [rangeHeaderValue] * @param {string} [xMsRangeHeaderValue] * @returns {([number, number] | undefined)} */ function deserializePageBlobRangeHeader(rangeHeaderValue, xMsRangeHeaderValue, force512boundary = true) { const ranges = deserializeRangeHeader(rangeHeaderValue, xMsRangeHeaderValue); const startInclusive = ranges[0]; const endInclusive = ranges[1]; if (force512boundary && startInclusive % 512 !== 0) { throw new RangeError(`deserializePageBlobRangeHeader: range start value ${startInclusive} doesn't align with 512 boundary.`); } if (force512boundary && endInclusive !== Infinity && (endInclusive + 1) % 512 !== 0) { throw new RangeError(`deserializePageBlobRangeHeader: range end value ${endInclusive} doesn't align with 512 boundary.`); } return [startInclusive, endInclusive]; } exports.deserializePageBlobRangeHeader = deserializePageBlobRangeHeader; /** * Remove double Quotation mark from ListBlob returned Etag, to align with server * * @param {string} [inputEtag] * @returns {string} */ function removeQuotationFromListBlobEtag(inputEtag) { if (inputEtag === undefined) { return inputEtag; } if (inputEtag[0] === '"' && inputEtag[inputEtag.length - 1] === '"') { return inputEtag.substring(1, inputEtag.length - 1); } return inputEtag; } exports.removeQuotationFromListBlobEtag = removeQuotationFromListBlobEtag; function validateContainerName(requestID, containerName) { if (containerName !== "" && (containerName.length < 3 || containerName.length > 63)) { throw StorageErrorFactory_1.default.getOutOfRangeName(requestID); } const reg = new RegExp("^[a-z0-9](?!.*--)[a-z0-9-]{1,61}[a-z0-9]$"); if (!reg.test(containerName)) { throw StorageErrorFactory_1.default.getInvalidResourceName(requestID); } } exports.validateContainerName = validateContainerName; function getUserDelegationKeyValue(signedObjectid, signedTenantid, signedStartsOn, signedExpiresOn, signedVersion) { const stringToSign = [ signedObjectid, signedTenantid, signedStartsOn, signedExpiresOn, "b", signedVersion ].join("\n"); return (0, crypto_1.createHmac)("sha256", constants_1.USERDELEGATIONKEY_BASIC_KEY).update(stringToSign, "utf8").digest("base64"); } exports.getUserDelegationKeyValue = getUserDelegationKeyValue; function getBlobTagsCount(blobTags) { return (blobTags === undefined || blobTags?.blobTagSet.length === 0) ? undefined : blobTags?.blobTagSet.length; } exports.getBlobTagsCount = getBlobTagsCount; function getTagsFromString(blobTagsString, contextID) { if (blobTagsString === '' || blobTagsString === undefined) { return undefined; } let blobTags = []; const rawTags = blobTagsString.split("&"); rawTags.forEach((rawTag) => { const tagpair = rawTag.split("="); blobTags.push({ // When the Blob tag is input with header, it's encoded, sometimes space will be encoded to "+" ("+" will be encoded to "%2B") // But in decodeURIComponent(), "+" won't be decode to space, so we need first replace "+" to "%20", then decode the tag. key: decodeURIComponent(tagpair[0].replace(/\+/g, '%20')), value: decodeURIComponent(tagpair[1].replace(/\+/g, '%20')), }); }); validateBlobTag({ blobTagSet: blobTags, }, contextID); return { blobTagSet: blobTags, }; } exports.getTagsFromString = getTagsFromString; // validate as the limitation from https://learn.microsoft.com/en-us/rest/api/storageservices/set-blob-tags?tabs=azure-ad#request-body function validateBlobTag(tags, contextID) { if (tags.blobTagSet.length > 10) { throw StorageErrorFactory_1.default.getTagsTooLarge(contextID); } tags.blobTagSet.forEach((tag) => { if (tag.key.length == 0) { throw StorageErrorFactory_1.default.getEmptyTagName(contextID); } if (tag.key.length > 128) { throw StorageErrorFactory_1.default.getTagsTooLarge(contextID); } if (tag.value.length > 256) { throw StorageErrorFactory_1.default.getTagsTooLarge(contextID); } if (ContainsInvalidTagCharacter(tag.key)) { throw StorageErrorFactory_1.default.getInvalidTag(contextID); } if (ContainsInvalidTagCharacter(tag.value)) { throw StorageErrorFactory_1.default.getInvalidTag(contextID); } }); } exports.validateBlobTag = validateBlobTag; function ContainsInvalidTagCharacter(s) { for (let c of s) { if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == ' ' || c == '+' || c == '-' || c == '.' || c == '/' || c == ':' || c == '=' || c == '_')) { return true; } } return false; } function toBlobTags(input) { const tags = {}; input.forEach(element => { if (element.key !== '@container') { tags[element.key] = element.value; } }); return Object.entries(tags).map(([key, value]) => { return { key: key, value: value }; }); } exports.toBlobTags = toBlobTags; //# sourceMappingURL=utils.js.map