diff --git a/lib/api/apiUtils/object/versioning.js b/lib/api/apiUtils/object/versioning.js index 7fb8deb6eb..b6b6c20117 100644 --- a/lib/api/apiUtils/object/versioning.js +++ b/lib/api/apiUtils/object/versioning.js @@ -494,47 +494,23 @@ function restoreMetadata(objMD, metadataStoreParams) { } }); + // System metadata, object lock info, ACL and replication info come from the + // archived object only, not from the restore request or the bucket config + metadataStoreParams.acl = objMD.acl; 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.replicationInfo = objMD.replicationInfo; metadataStoreParams.retentionMode = objMD.retentionMode; metadataStoreParams.retentionDate = objMD.retentionDate; - - // Object lock info and ACL come from the archived object only, not from - // the restore request or the bucket default retention + metadataStoreParams.legalHold = objMD.legalHold; 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 = {}; - } - metadataStoreParams.headers['x-amz-website-redirect-location'] = objMD['x-amz-website-redirect-location']; - } - if (objMD.replicationInfo) { - metadataStoreParams.replicationInfo = objMD.replicationInfo; - } - - if (objMD.legalHold) { - metadataStoreParams.legalHold = objMD.legalHold; - } - - if (objMD.acl) { - metadataStoreParams.acl = objMD.acl; - } + metadataStoreParams.headers = { + 'x-amz-website-redirect-location': objMD['x-amz-website-redirect-location'], + }; metadataStoreParams.creationTime = objMD['creation-time']; metadataStoreParams.lastModifiedDate = objMD['last-modified']; diff --git a/package.json b/package.json index bdc263d28a..bef7535445 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zenko/cloudserver", - "version": "9.4.6", + "version": "9.4.7", "description": "Zenko CloudServer, an open-source Node.js implementation of a server handling the Amazon S3 protocol", "main": "index.js", "engines": { diff --git a/tests/unit/api/apiUtils/versioning.js b/tests/unit/api/apiUtils/versioning.js index cead9bafab..6585f7b25d 100644 --- a/tests/unit/api/apiUtils/versioning.js +++ b/tests/unit/api/apiUtils/versioning.js @@ -705,8 +705,12 @@ describe('versioning helpers', () => { contentDisposition: undefined, contentEncoding: undefined, expires: undefined, + headers: { 'x-amz-website-redirect-location': undefined }, retentionMode: undefined, retentionDate: undefined, + legalHold: undefined, + acl: undefined, + replicationInfo: undefined, }; let clock; @@ -970,7 +974,6 @@ describe('versioning helpers', () => { }, metadataStoreParams: { contentType: 'binary/octet-stream', - headers: {}, }, expectedRes: { creationTime: now, @@ -1103,7 +1106,7 @@ describe('versioning helpers', () => { }, }, { - description: 'Should drop object lock headers', + description: 'Should drop the restore request headers', objMD: { versionId: '2345678', 'creation-time': now, @@ -1119,13 +1122,18 @@ describe('versioning helpers', () => { metadataStoreParams: { headers: { host: 'localhost', + 'x-amz-website-redirect-location': '/elsewhere', 'x-amz-object-lock-mode': 'GOVERNANCE', 'x-amz-object-lock-retain-until-date': new Date(now).toISOString(), 'x-amz-object-lock-legal-hold': 'ON', + '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, @@ -1142,7 +1150,7 @@ describe('versioning helpers', () => { }, }, { - description: 'Should drop ACL headers', + description: 'Should not take legal hold from the restore request', objMD: { versionId: '2345678', 'creation-time': now, @@ -1156,17 +1164,51 @@ describe('versioning helpers', () => { }, }, 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', + legalHold: true, + }, + 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 not take replication info from the bucket', + 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: { + replicationInfo: { + status: 'PENDING', + backends: [{ site: 'azure-blob', status: 'PENDING', dataStoreVersionId: '' }], + content: ['DATA', 'METADATA'], + destination: 'arn:aws:s3:::replicate-cold', + storageClass: 'azure-blob', + role: 'arn:aws:iam::root:role/s3-replication-role', + storageType: 'azure', + dataStoreVersionId: '', }, }, expectedRes: { - headers: { host: 'localhost' }, creationTime: now, lastModifiedDate: now, updateMicroVersionId: true, @@ -1273,9 +1315,12 @@ describe('versioning helpers', () => { if (testCase.metadataStoreParams) { Object.assign(metadataStoreParams, testCase.metadataStoreParams); } + const requestHeaders = { ...testCase.metadataStoreParams?.headers }; const options = overwritingVersioning(testCase.objMD, metadataStoreParams); assert.deepStrictEqual(options.versionId, testCase.objMD.versionId); assert.deepStrictEqual(metadataStoreParams, { ...clearedSystemMD, ...testCase.expectedRes }); + // request headers are shared with the request, they must not be modified + assert.deepStrictEqual({ ...testCase.metadataStoreParams?.headers }, requestHeaders); if (testCase.objMD.isNull) { assert.deepStrictEqual(options.extraMD.nullVersionId, 'vnull');