azurite
Version:
An open source Azure Storage API compatible server
171 lines • 9.61 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateWriteConditions = exports.validateSequenceNumberWriteConditions = void 0;
const tslib_1 = require("tslib");
const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory"));
const QueryInterpreter_1 = require("../persistence/QueryInterpreter/QueryInterpreter");
const ConditionalHeadersAdapter_1 = tslib_1.__importDefault(require("./ConditionalHeadersAdapter"));
const ConditionResourceAdapter_1 = tslib_1.__importDefault(require("./ConditionResourceAdapter"));
function validateSequenceNumberWriteConditions(context, conditionalHeaders, model) {
if (!conditionalHeaders || !model) {
return;
}
if (!model.properties || model.properties.blobSequenceNumber === undefined) {
throw Error(`validateSequenceNumberWriteConditions() Invalid blob model, blobSequenceNumber is not specified.`);
}
if (conditionalHeaders.ifSequenceNumberLessThanOrEqualTo !== undefined &&
conditionalHeaders.ifSequenceNumberLessThanOrEqualTo <
model.properties.blobSequenceNumber) {
throw StorageErrorFactory_1.default.getSequenceNumberConditionNotMet(context.contextId);
}
if (conditionalHeaders.ifSequenceNumberLessThan !== undefined &&
conditionalHeaders.ifSequenceNumberLessThan <=
model.properties.blobSequenceNumber) {
throw StorageErrorFactory_1.default.getSequenceNumberConditionNotMet(context.contextId);
}
if (conditionalHeaders.ifSequenceNumberEqualTo !== undefined &&
conditionalHeaders.ifSequenceNumberEqualTo !==
model.properties.blobSequenceNumber) {
throw StorageErrorFactory_1.default.getSequenceNumberConditionNotMet(context.contextId);
}
}
exports.validateSequenceNumberWriteConditions = validateSequenceNumberWriteConditions;
function validateWriteConditions(context, conditionalHeaders, model) {
new WriteConditionalHeadersValidator().validate(context, new ConditionalHeadersAdapter_1.default(context, conditionalHeaders), new ConditionResourceAdapter_1.default(model));
}
exports.validateWriteConditions = validateWriteConditions;
// tslint:disable: max-line-length
class WriteConditionalHeadersValidator {
/**
* Validate conditional Headers for Read Operations in Versions Prior to 2013-08-15,
* and for Write Operations (All Versions).
* @link https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-conditional-headers-for-blob-service-operations#specifying-conditional-headers-for-read-operations-in-versions-prior-to-2013-08-15-and-for-write-operations-all-versions
*
* @param context
* @param conditionalHeaders
* @param resource
*/
validate(context, conditionalHeaders, resource) {
this.validateCombinations(context, conditionalHeaders);
if (!resource.exist) {
if (conditionalHeaders.ifNoneMatch &&
conditionalHeaders.ifNoneMatch.length > 0) {
// If a request specifies both the If-None-Match and If-Modified-Since headers,
// the request is evaluated based on the criteria specified in If-None-Match.
// Skip for non exist blob
return;
}
if (conditionalHeaders.ifMatch && conditionalHeaders.ifMatch.length > 0) {
// If a request specifies both the If-Match and If-Unmodified-Since headers,
// the request is evaluated based on the criteria specified in If-Match.
if (conditionalHeaders.ifMatch[0] !== "*") {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
return;
}
if (conditionalHeaders.ifModifiedSince) {
// Skip for non exist blob
return;
}
if (conditionalHeaders.ifUnmodifiedSince) {
// Skip for non exist blob
return;
}
}
else {
if (conditionalHeaders.ifNoneMatch &&
conditionalHeaders.ifNoneMatch.length > 0) {
if (conditionalHeaders.ifNoneMatch[0] === "*") {
// According to restful doc, specify the wildcard character (*) to perform the operation
// only if the resource does not exist, and fail the operation if it does exist.
// However, Azure Storage Set Blob Properties Operation for an existing blob doesn't return 412 with *
// TODO: Check accurate behavior for different write operations
// Put Blob, Commit Block List has special logic for ifNoneMatch equals *, will return 409 conflict for existing blob, will handled in createBlob metadata store.
// throw StorageErrorFactory.getConditionNotMet(context.contextId!);
return;
}
if (conditionalHeaders.ifNoneMatch[0] === resource.etag) {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
// Stop processing
// If a request specifies both the If-None-Match and If-Modified-Since headers,
// the request is evaluated based on the criteria specified in If-None-Match.
return;
}
if (conditionalHeaders.ifMatch && conditionalHeaders.ifMatch.length > 0) {
if (conditionalHeaders.ifMatch[0] !== "*" &&
conditionalHeaders.ifMatch[0] !== resource.etag) {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
// Stop processing
// If a request specifies both the If-Match and If-Unmodified-Since headers,
// the request is evaluated based on the criteria specified in If-Match.
return;
}
if (conditionalHeaders.ifModifiedSince) {
if (resource.lastModified <= conditionalHeaders.ifModifiedSince) {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
return;
}
if (conditionalHeaders.ifUnmodifiedSince) {
if (conditionalHeaders.ifUnmodifiedSince < resource.lastModified) {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
return;
}
if (conditionalHeaders.ifTags) {
const validateFunction = (0, QueryInterpreter_1.generateQueryBlobWithTagsWhereFunction)(context, conditionalHeaders.ifTags, 'x-ms-if-tags');
if (conditionalHeaders?.ifTags !== undefined
&& validateFunction(resource.blobItemWithTags).length === 0) {
throw StorageErrorFactory_1.default.getConditionNotMet(context.contextId);
}
}
}
}
validateCombinations(context, conditionalHeaders) {
let ifMatch = 0;
if (conditionalHeaders.ifMatch && conditionalHeaders.ifMatch.length > 0) {
// RFC 2616 allows multiple ETag values in a single header,
// but requests to the Blob service can only include one ETag value.
// Specifying more than one ETag value results in status code 400 (Bad Request).
if (conditionalHeaders.ifMatch.length > 1) {
// throw 400 MultipleConditionHeadersNotSupported Multiple condition headers are not supported.
throw StorageErrorFactory_1.default.getMultipleConditionHeadersNotSupported(context.contextId);
}
ifMatch = 1;
}
let ifModifiedSince = 0;
if (conditionalHeaders.ifModifiedSince) {
ifModifiedSince = 1;
}
let ifNoneMatch = 0;
if (conditionalHeaders.ifNoneMatch &&
conditionalHeaders.ifNoneMatch.length > 0) {
// RFC 2616 allows multiple ETag values in a single header,
// but requests to the Blob service can only include one ETag value.
// Specifying more than one ETag value results in status code 400 (Bad Request).
if (conditionalHeaders.ifNoneMatch.length > 1) {
// throw 400 MultipleConditionHeadersNotSupported Multiple condition headers are not supported.
throw StorageErrorFactory_1.default.getMultipleConditionHeadersNotSupported(context.contextId);
}
ifNoneMatch = 1;
}
let ifUnmodifiedSince = 0;
if (conditionalHeaders.ifUnmodifiedSince) {
ifUnmodifiedSince = 1;
}
if (ifMatch + ifModifiedSince + ifNoneMatch + ifUnmodifiedSince > 2) {
// throw 400 MultipleConditionHeadersNotSupported Multiple condition headers are not supported.
throw StorageErrorFactory_1.default.getMultipleConditionHeadersNotSupported(context.contextId);
}
if (ifMatch + ifModifiedSince + ifNoneMatch + ifUnmodifiedSince === 2) {
if (ifNoneMatch + ifModifiedSince === 1) {
// throw 400 MultipleConditionHeadersNotSupported Multiple condition headers are not supported.
throw StorageErrorFactory_1.default.getMultipleConditionHeadersNotSupported(context.contextId);
}
}
}
}
exports.default = WriteConditionalHeadersValidator;
//# sourceMappingURL=WriteConditionalHeadersValidator.js.map
;