azurite
Version:
An open source Azure Storage API compatible server
208 lines • 8.19 kB
JavaScript
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
;