Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/api/apiUtils/object/bumpMicroVersionId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { versioning } = require('arsenal');
const { config } = require('../../../Config');

/**
* Bump objectMD.microVersionId. microVersionId is a generic
* metadata-revision marker, not a CRR-specific field, but cascaded CRR
* is its only consumer today - so we gate on replicationInfo to avoid
* inflating storage on objects that wouldn't use it. The gate can be
* widened later if another consumer needs it on every object.
* Pass `force = true` to bump unconditionally.
*
* @param {object} objectMD - object MD POJO or `md.getValue()`
* @param {boolean} [force] - bump even without replicationInfo
* @return {undefined}
*/
function bumpMicroVersionId(objectMD, force) {
if (!force && !objectMD?.replicationInfo) {
return;
}

const { instanceId, replicationGroupId } = config;

// eslint-disable-next-line no-param-reassign
objectMD.microVersionId = versioning.VersionID.generateVersionId(instanceId, replicationGroupId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't there a method ObjectMD.updateMicroVersionId() in arsenal ? should it not be used instead?

(if we can't because of POJO, then I wonder if that function was really useful....)

}

module.exports = bumpMicroVersionId;
151 changes: 83 additions & 68 deletions lib/api/objectDeleteTagging.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
const async = require('async');
const { errors } = require('arsenal');

const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions }
= require('./apiUtils/object/versioning');
const {
decodeVersionId,
getVersionIdResHeader,
getVersionSpecificMetadataOptions,
} = require('./apiUtils/object/versioning');

const { standardMetadataValidateBucketAndObj } = require('../metadata/metadataUtils');
const { pushMetric } = require('../utapi/utilities');
const monitoring = require('../utilities/monitoringHandler');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const metadata = require('../metadata/wrapper');
const getReplicationInfo = require('./apiUtils/object/getReplicationInfo');
const bumpMicroVersionId = require('./apiUtils/object/bumpMicroVersionId');
const { data } = require('../data/wrapper');
const { config } = require('../Config');
const REPLICATION_ACTION = 'DELETE_TAGGING';
Expand Down Expand Up @@ -48,75 +52,86 @@
request,
};

return async.waterfall([
next => standardMetadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed',
{ method: 'objectDeleteTagging', error: err });
return next(err);
}
if (!objectMD) {
const err = reqVersionId ? errors.NoSuchVersion :
errors.NoSuchKey;
log.trace('error no object metadata found',
{ method: 'objectDeleteTagging', error: err });
return next(err, bucket);
}
if (objectMD.isDeleteMarker) {
log.trace('version is a delete marker',
{ method: 'objectDeleteTagging' });
// FIXME we should return a `x-amz-delete-marker: true` header,
// see S3C-7592
return next(errors.MethodNotAllowed, bucket);
}
return next(null, bucket, objectMD);
}),
(bucket, objectMD, next) => {
// eslint-disable-next-line no-param-reassign
objectMD.tags = {};
const params = getVersionSpecificMetadataOptions(objectMD, config.nullVersionCompatMode);
const replicationInfo = getReplicationInfo(config,
objectKey, bucket, true, 0, REPLICATION_ACTION, objectMD);
if (replicationInfo) {
return async.waterfall(
[
next =>
standardMetadataValidateBucketAndObj(
metadataValParams,
request.actionImplicitDenies,
log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed', { method: 'objectDeleteTagging', error: err });
return next(err);
}
if (!objectMD) {
const err = reqVersionId ? errors.NoSuchVersion : errors.NoSuchKey;
log.trace('error no object metadata found', { method: 'objectDeleteTagging', error: err });
return next(err, bucket);
}
if (objectMD.isDeleteMarker) {
log.trace('version is a delete marker', { method: 'objectDeleteTagging' });
// FIXME we should return a `x-amz-delete-marker: true` header,
// see S3C-7592
return next(errors.MethodNotAllowed, bucket);
}
return next(null, bucket, objectMD);
},
),

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
(bucket, objectMD, next) => {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo = Object.assign({},
objectMD.replicationInfo, replicationInfo);
objectMD.tags = {};
const params = getVersionSpecificMetadataOptions(objectMD, config.nullVersionCompatMode);
if (objectMD.replicationInfo?.isReplica) {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo.isReplica = false;
}
const replicationInfo = getReplicationInfo(
config,
objectKey,
bucket,
true,
0,
REPLICATION_ACTION,
objectMD,
);
if (replicationInfo) {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo = Object.assign({}, objectMD.replicationInfo, replicationInfo);
}
bumpMicroVersionId(objectMD);
// eslint-disable-next-line no-param-reassign
objectMD.originOp = 's3:ObjectTagging:Delete';
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err =>
next(err, bucket, objectMD),
);
},

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
(bucket, objectMD, next) =>
// if external backends handles tagging
data.objectTagging('Delete', objectKey, bucket.getName(), objectMD, log, err =>
next(err, bucket, objectMD),
),

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
],
(err, bucket, objectMD) => {
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
if (err) {
log.trace('error processing request', { error: err, method: 'objectDeleteTagging' });
monitoring.promMetrics('DELETE', bucketName, err.code, 'deleteObjectTagging');
} else {
pushMetric('deleteObjectTagging', log, {
authInfo,
bucket: bucketName,
keys: [objectKey],
versionId: objectMD ? objectMD.versionId : undefined,
location: objectMD ? objectMD.dataStoreName : undefined,
});
monitoring.promMetrics('DELETE', bucketName, '200', 'deleteObjectTagging');
const verCfg = bucket.getVersioningConfiguration();
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
}
// eslint-disable-next-line no-param-reassign
objectMD.originOp = 's3:ObjectTagging:Delete';
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params,
log, err =>
next(err, bucket, objectMD));
return callback(err, additionalResHeaders);
},
(bucket, objectMD, next) =>
// if external backends handles tagging
data.objectTagging('Delete', objectKey, bucket.getName(), objectMD,
log, err => next(err, bucket, objectMD)),
], (err, bucket, objectMD) => {
const additionalResHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {
log.trace('error processing request', { error: err,
method: 'objectDeleteTagging' });
monitoring.promMetrics(
'DELETE', bucketName, err.code, 'deleteObjectTagging');
} else {
pushMetric('deleteObjectTagging', log, {
authInfo,
bucket: bucketName,
keys: [objectKey],
versionId: objectMD ? objectMD.versionId : undefined,
location: objectMD ? objectMD.dataStoreName : undefined,
});
monitoring.promMetrics(
'DELETE', bucketName, '200', 'deleteObjectTagging');
const verCfg = bucket.getVersioningConfiguration();
additionalResHeaders['x-amz-version-id'] =
getVersionIdResHeader(verCfg, objectMD);
}
return callback(err, additionalResHeaders);
});
);
}

module.exports = objectDeleteTagging;
158 changes: 88 additions & 70 deletions lib/api/objectPutLegalHold.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
const { errors, errorInstances, s3middleware } = require('arsenal');

const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const { decodeVersionId, getVersionIdResHeader, getVersionSpecificMetadataOptions } =
require('./apiUtils/object/versioning');
const {
decodeVersionId,
getVersionIdResHeader,
getVersionSpecificMetadataOptions,
} = require('./apiUtils/object/versioning');
const getReplicationInfo = require('./apiUtils/object/getReplicationInfo');
const bumpMicroVersionId = require('./apiUtils/object/bumpMicroVersionId');
const metadata = require('../metadata/wrapper');
const { standardMetadataValidateBucketAndObj } = require('../metadata/metadataUtils');
const { pushMetric } = require('../utapi/utilities');
Expand Down Expand Up @@ -47,78 +51,92 @@
request,
};

return async.waterfall([
next => standardMetadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
return async.waterfall(
[
next =>
standardMetadataValidateBucketAndObj(
metadataValParams,
request.actionImplicitDenies,
log,
(err, bucket, objectMD) => {
if (err) {
log.trace('request authorization failed', { method: 'objectPutLegalHold', error: err });
return next(err);
}
if (!objectMD) {
const err = versionId ? errors.NoSuchVersion : errors.NoSuchKey;
log.trace('error no object metadata found', { method: 'objectPutLegalHold', error: err });
return next(err, bucket);
}
if (objectMD.isDeleteMarker) {
log.trace('version is a delete marker', { method: 'objectPutLegalHold' });
// FIXME we should return a `x-amz-delete-marker: true` header,
// see S3C-7592
return next(errors.MethodNotAllowed, bucket);
}
if (!bucket.isObjectLockEnabled()) {
log.trace('object lock not enabled on bucket', { method: 'objectPutLegalHold' });
return next(
errorInstances.InvalidRequest.customizeDescription(
'Bucket is missing Object Lock Configuration',
),
bucket,
);
}
return next(null, bucket, objectMD);
},
),

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
(bucket, objectMD, next) => {
log.trace('parsing legal hold');
parseLegalHoldXml(request.post, log, (err, res) => next(err, bucket, res, objectMD));
},

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
(bucket, legalHold, objectMD, next) => {
// eslint-disable-next-line no-param-reassign
objectMD.legalHold = legalHold;
const params = getVersionSpecificMetadataOptions(objectMD, config.nullVersionCompatMode);
if (objectMD.replicationInfo?.isReplica) {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo.isReplica = false;
}
const replicationInfo = getReplicationInfo(
config,
objectKey,
bucket,
true,
0,
REPLICATION_ACTION,
objectMD,
);
if (replicationInfo) {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo = Object.assign({}, objectMD.replicationInfo, replicationInfo);
}
bumpMicroVersionId(objectMD);
// eslint-disable-next-line no-param-reassign
objectMD.originOp = 's3:ObjectLegalHold:Put';
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params, log, err =>
next(err, bucket, objectMD),
);
},

Check notice

Code scanning / CodeQL

Callback-style function (async migration) Note

This function uses a callback parameter ('next'). Refactor to async/await.
Comment thread
maeldonn marked this conversation as resolved.
Dismissed
],
(err, bucket, objectMD) => {
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
if (err) {
log.trace('request authorization failed',
{ method: 'objectPutLegalHold', error: err });
return next(err);
}
if (!objectMD) {
const err = versionId ? errors.NoSuchVersion :
errors.NoSuchKey;
log.trace('error no object metadata found',
{ method: 'objectPutLegalHold', error: err });
return next(err, bucket);
}
if (objectMD.isDeleteMarker) {
log.trace('version is a delete marker',
{ method: 'objectPutLegalHold' });
// FIXME we should return a `x-amz-delete-marker: true` header,
// see S3C-7592
return next(errors.MethodNotAllowed, bucket);
}
if (!bucket.isObjectLockEnabled()) {
log.trace('object lock not enabled on bucket',
{ method: 'objectPutLegalHold' });
return next(errorInstances.InvalidRequest.customizeDescription(
'Bucket is missing Object Lock Configuration'
), bucket);
}
return next(null, bucket, objectMD);
}),
(bucket, objectMD, next) => {
log.trace('parsing legal hold');
parseLegalHoldXml(request.post, log, (err, res) =>
next(err, bucket, res, objectMD));
},
(bucket, legalHold, objectMD, next) => {
// eslint-disable-next-line no-param-reassign
objectMD.legalHold = legalHold;
const params = getVersionSpecificMetadataOptions(objectMD, config.nullVersionCompatMode);
const replicationInfo = getReplicationInfo(config,
objectKey, bucket, true, 0, REPLICATION_ACTION, objectMD);
if (replicationInfo) {
// eslint-disable-next-line no-param-reassign
objectMD.replicationInfo = Object.assign({},
objectMD.replicationInfo, replicationInfo);
log.trace('error processing request', { error: err, method: 'objectPutLegalHold' });
} else {
pushMetric('putObjectLegalHold', log, {
authInfo,
bucket: bucketName,
keys: [objectKey],
versionId: objectMD ? objectMD.versionId : undefined,
location: objectMD ? objectMD.dataStoreName : undefined,
});
const verCfg = bucket.getVersioningConfiguration();
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
}
// eslint-disable-next-line no-param-reassign
objectMD.originOp = 's3:ObjectLegalHold:Put';
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params,
log, err => next(err, bucket, objectMD));
return callback(err, additionalResHeaders);
},
], (err, bucket, objectMD) => {
const additionalResHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {
log.trace('error processing request',
{ error: err, method: 'objectPutLegalHold' });
} else {
pushMetric('putObjectLegalHold', log, {
authInfo,
bucket: bucketName,
keys: [objectKey],
versionId: objectMD ? objectMD.versionId : undefined,
location: objectMD ? objectMD.dataStoreName : undefined,
});
const verCfg = bucket.getVersioningConfiguration();
additionalResHeaders['x-amz-version-id'] =
getVersionIdResHeader(verCfg, objectMD);
}
return callback(err, additionalResHeaders);
});
);
}

module.exports = objectPutLegalHold;
Loading
Loading