Skip to content
Merged
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
23 changes: 23 additions & 0 deletions lib/api/apiUtils/object/versioning.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,29 @@ function restoreMetadata(objMD, metadataStoreParams) {
}
});

metadataStoreParams.contentType = objMD['content-type'];
metadataStoreParams.cacheControl = objMD['cache-control'];
metadataStoreParams.contentDisposition = objMD['content-disposition'];
metadataStoreParams.contentEncoding = objMD['content-encoding'];
metadataStoreParams.expires = objMD.expires;
metadataStoreParams.retentionMode = objMD.retentionMode;
metadataStoreParams.retentionDate = objMD.retentionDate;
Comment thread
francoisferrand marked this conversation as resolved.

// Object lock info and ACL come from the archived object only, not from
// the restore request or the bucket default retention
delete metadataStoreParams.defaultRetention;
if (metadataStoreParams.headers) {
metadataStoreParams.headers = { ...metadataStoreParams.headers };
delete metadataStoreParams.headers['x-amz-object-lock-mode'];
delete metadataStoreParams.headers['x-amz-object-lock-retain-until-date'];
delete metadataStoreParams.headers['x-amz-object-lock-legal-hold'];
delete metadataStoreParams.headers['x-amz-acl'];
delete metadataStoreParams.headers['x-amz-grant-full-control'];
delete metadataStoreParams.headers['x-amz-grant-read'];
delete metadataStoreParams.headers['x-amz-grant-read-acp'];
delete metadataStoreParams.headers['x-amz-grant-write-acp'];
}

if (objMD['x-amz-website-redirect-location']) {
if (!metadataStoreParams.headers) {
metadataStoreParams.headers = {};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenko/cloudserver",
"version": "9.4.5",
"version": "9.4.6",
"description": "Zenko CloudServer, an open-source Node.js implementation of a server handling the Amazon S3 protocol",
"main": "index.js",
"engines": {
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/aws-node-sdk/test/object/mpuVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -1019,8 +1019,8 @@ describe('MPU with x-scal-s3-version-id header', () => {
// make sure data related metadatas ar not the same before and after
assert.notStrictEqual(finalObjMD['x-amz-server-side-encryption'], 'aws:kms');
assert.notStrictEqual(finalObjMD['content-length'], 99);
assert.notStrictEqual(finalObjMD['content-encoding'], 'testencoding');
assert.notStrictEqual(finalObjMD['content-type'], 'testtype');
assert.strictEqual(finalObjMD['content-encoding'], 'testencoding');
assert.strictEqual(finalObjMD['content-type'], 'testtype');
// make sure we keep the same etag and add the new restored
// data's etag inside x-amz-restore
assert.strictEqual(finalObjMD['content-md5'], 'testmd5');
Expand Down
4 changes: 2 additions & 2 deletions tests/functional/aws-node-sdk/test/object/putVersion.js
Original file line number Diff line number Diff line change
Expand Up @@ -1159,8 +1159,8 @@ describe('PUT object with x-scal-s3-version-id header', () => {
// make sure data related metadatas ar not the same before and after
assert.notStrictEqual(objMD['x-amz-server-side-encryption'], 'aws:kms');
assert.notStrictEqual(objMD['content-length'], 99);
assert.notStrictEqual(objMD['content-encoding'], 'testencoding');
assert.notStrictEqual(objMD['content-type'], 'testtype');
assert.strictEqual(objMD['content-encoding'], 'testencoding');
assert.strictEqual(objMD['content-type'], 'testtype');
// make sure we keep the same etag and add the new restored
// data's etag inside x-amz-restore
assert.strictEqual(objMD['content-md5'], 'testmd5');
Expand Down
246 changes: 245 additions & 1 deletion tests/unit/api/apiUtils/versioning.js
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,16 @@ describe('versioning helpers', () => {
archiveID: '126783123678',
};
const now = Date.now();
// always taken from the archived object, so cleared when it had none
const clearedSystemMD = {
contentType: undefined,
cacheControl: undefined,
contentDisposition: undefined,
contentEncoding: undefined,
expires: undefined,
retentionMode: undefined,
retentionDate: undefined,
};
let clock;

beforeEach(() => {
Expand Down Expand Up @@ -938,6 +948,240 @@ describe('versioning helpers', () => {
},
},
},
{
description: 'Should keep the system metadata of the archived object',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
'content-type': 'application/zip',
'cache-control': 'no-cache',
'content-disposition': 'attachment; filename="archive.zip"',
'content-encoding': 'gzip',
expires: 'Wed, 21 Oct 2026 07:28:00 GMT',
'x-amz-website-redirect-location': '/elsewhere',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
metadataStoreParams: {
contentType: 'binary/octet-stream',
headers: {},
},
expectedRes: {
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
contentType: 'application/zip',
cacheControl: 'no-cache',
contentDisposition: 'attachment; filename="archive.zip"',
contentEncoding: 'gzip',
expires: 'Wed, 21 Oct 2026 07:28:00 GMT',
headers: {
'x-amz-website-redirect-location': '/elsewhere',
},
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should drop the system metadata sent with the restore request',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
metadataStoreParams: {
contentType: 'binary/octet-stream',
cacheControl: 'no-store',
contentDisposition: 'inline',
contentEncoding: 'identity',
expires: 'Wed, 21 Oct 2026 07:28:00 GMT',
},
expectedRes: {
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should keep the object lock retention',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
retentionMode: 'GOVERNANCE',
retentionDate: '2026-10-21T07:28:00.000Z',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
expectedRes: {
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
retentionMode: 'GOVERNANCE',
retentionDate: '2026-10-21T07:28:00.000Z',
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should not apply the bucket default retention',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
metadataStoreParams: {
defaultRetention: {
rule: { mode: 'COMPLIANCE', days: 1 },
},
},
expectedRes: {
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should drop object lock headers',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
metadataStoreParams: {
headers: {
host: 'localhost',
'x-amz-object-lock-mode': 'GOVERNANCE',
'x-amz-object-lock-retain-until-date': new Date(now).toISOString(),
'x-amz-object-lock-legal-hold': 'ON',
},
},
expectedRes: {
headers: { host: 'localhost' },
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should drop ACL headers',
objMD: {
versionId: '2345678',
'creation-time': now,
'last-modified': now,
originOp: 's3:PutObject',
'x-amz-storage-class': 'cold-location',
archive: {
restoreRequestedDays: days,
restoreRequestedAt: now,
archiveInfo,
},
},
metadataStoreParams: {
headers: {
host: 'localhost',
'x-amz-acl': 'public-read-write',
'x-amz-grant-full-control': 'uri=http://acs.amazonaws.com/groups/global/AllUsers',
'x-amz-grant-read': 'uri=http://acs.amazonaws.com/groups/global/AllUsers',
'x-amz-grant-read-acp': 'uri=http://acs.amazonaws.com/groups/global/AllUsers',
'x-amz-grant-write-acp': 'uri=http://acs.amazonaws.com/groups/global/AllUsers',
},
},
expectedRes: {
headers: { host: 'localhost' },
creationTime: now,
lastModifiedDate: now,
updateMicroVersionId: true,
originOp: 's3:ObjectRestore:Completed',
taggingCopy: undefined,
amzStorageClass: 'cold-location',
archive: {
archiveInfo,
restoreRequestedDays: days,
restoreRequestedAt: now,
restoreCompletedAt: new Date(now),
restoreWillExpireAt: new Date(now + days * scaledMsPerDay),
},
},
},
{
description: 'Should keep ACLs',
objMD: {
Expand Down Expand Up @@ -1031,7 +1275,7 @@ describe('versioning helpers', () => {
}
const options = overwritingVersioning(testCase.objMD, metadataStoreParams);
assert.deepStrictEqual(options.versionId, testCase.objMD.versionId);
assert.deepStrictEqual(metadataStoreParams, testCase.expectedRes);
assert.deepStrictEqual(metadataStoreParams, { ...clearedSystemMD, ...testCase.expectedRes });

if (testCase.objMD.isNull) {
assert.deepStrictEqual(options.extraMD.nullVersionId, 'vnull');
Expand Down
Loading
Loading