Compare commits

...

1 Commits

Author SHA1 Message Date
Cristian Tovar cc21581faa improvement: CLDSRV-419 improve test coverage for object lock 2023-09-21 08:45:41 -07:00
2 changed files with 319 additions and 365 deletions

View File

@ -99,165 +99,37 @@ describe('DELETE object', () => {
const bucketName = 'testdeleteobjectlockbucket'; const bucketName = 'testdeleteobjectlockbucket';
let versionIdOne; let versionIdOne;
let versionIdTwo; let versionIdTwo;
const retainDate = moment().add(10, 'days').toISOString(); [
before(() => { { bucketMode: 'GOVERNANCE', ratainDate: moment().add(5, 'days').toISOString(), putObjectLock: false },
process.stdout.write('creating bucket\n'); { bucketMode: 'COMPLIANCE', ratainDate: moment().add(5, 'days').toISOString(), putObjectLock: false },
return s3.createBucket({ { bucketMode: 'GOVERNANCE', ratainDate: moment().add(90, 'days').toISOString(), putObjectLock: true },
Bucket: bucketName, { bucketMode: 'COMPLIANCE', ratainDate: moment().add(90, 'days').toISOString(), putObjectLock: true },
ObjectLockEnabledForBucket: true, ].forEach((response) => {
}).promise() before(() => {
.catch(err => { process.stdout.write('creating bucket\n');
process.stdout.write(`Error creating bucket ${err}\n`); return s3.createBucket({
throw err;
})
.then(() => {
process.stdout.write('putting object\n');
return s3.putObject({
Bucket: bucketName, Bucket: bucketName,
Key: objectName, ObjectLockEnabledForBucket: true,
}).promise(); }).promise()
})
.catch(err => {
process.stdout.write('Error putting object');
throw err;
})
.then(res => {
versionIdOne = res.VersionId;
process.stdout.write('putting object retention\n');
return s3.putObjectRetention({
Bucket: bucketName,
Key: objectName,
Retention: {
Mode: 'GOVERNANCE',
RetainUntilDate: retainDate,
},
}).promise();
})
.catch(err => {
process.stdout.write('Err putting object retention\n');
throw err;
})
.then(() => {
process.stdout.write('putting object\n');
return s3.putObject({
Bucket: bucketName,
Key: objectNameTwo,
}).promise();
})
.catch(err => {
process.stdout.write(('Err putting second object\n'));
throw err;
})
.then(res => {
versionIdTwo = res.VersionId;
process.stdout.write('putting object legal hold\n');
return s3.putObjectLegalHold({
Bucket: bucketName,
Key: objectNameTwo,
LegalHold: {
Status: 'ON',
},
}).promise();
})
.catch(err => {
process.stdout.write('Err putting object legal hold\n');
throw err;
});
});
after(() => {
process.stdout.write('Emptying bucket\n');
return bucketUtil.empty(bucketName)
.then(() => {
process.stdout.write('Deleting bucket\n');
return bucketUtil.deleteOne(bucketName);
})
.catch(err => {
process.stdout.write('Error in after\n');
throw err;
});
});
it('should put delete marker if no version id specified', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
}, err => {
assert.ifError(err);
done();
});
});
it('should not delete object version locked with object ' +
'retention', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
VersionId: versionIdOne,
}, err => {
assert.strictEqual(err.code, 'AccessDenied');
done();
});
});
it('should delete locked object version with GOVERNANCE ' +
'retention mode and correct header', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
VersionId: versionIdOne,
BypassGovernanceRetention: true,
}, err => {
assert.ifError(err);
done();
});
});
it('should not delete object locked with legal hold', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectNameTwo,
VersionId: versionIdTwo,
}, err => {
assert.strictEqual(err.code, 'AccessDenied');
changeObjectLock(
[{
bucket: bucketName,
key: objectNameTwo,
versionId: versionIdTwo,
}], '', done);
});
});
});
describe('with object lock and legal hold', () => {
const bucketName = 'testdeletelocklegalholdbucket';
const objectName = 'key';
let versionId;
before(() => {
process.stdout.write('creating bucket\n');
return s3.createBucket({
Bucket: bucketName,
ObjectLockEnabledForBucket: true,
}).promise()
.catch(err => { .catch(err => {
process.stdout.write(`Error creating bucket ${err}\n`); process.stdout.write(`Error creating bucket ${err}\n`);
throw err; throw err;
}) })
.then(() => { .then(() => {
process.stdout.write('putting object lock configuration\n'); process.stdout.write('putting object lock configuration\n');
return s3.putObjectLockConfiguration({ return response.putObjectLock ?
Bucket: bucketName, s3.putObjectLockConfiguration({
ObjectLockConfiguration: { Bucket: bucketName,
ObjectLockEnabled: 'Enabled', ObjectLockConfiguration: {
Rule: { ObjectLockEnabled: 'Enabled',
DefaultRetention: { Rule: {
Mode: 'GOVERNANCE', DefaultRetention: {
Days: 1, Mode: response.bucketMode,
Days: parseInt(response.ratainDate, 0),
},
}, },
}, },
}, }).promise() : true;
}).promise();
}) })
.catch(err => { .catch(err => {
process.stdout.write('Error putting object lock configuration\n'); process.stdout.write('Error putting object lock configuration\n');
@ -275,11 +147,40 @@ describe('DELETE object', () => {
throw err; throw err;
}) })
.then(res => { .then(res => {
versionId = res.VersionId; versionIdOne = res.VersionId;
process.stdout.write('putting object retention\n');
return !response.putObjectLock ?
s3.putObjectRetention({
Bucket: bucketName,
Key: objectName,
Retention: {
Mode: response.bucketMode,
RetainUntilDate: response.ratainDate,
},
}).promise()
: true;
})
.catch(err => {
process.stdout.write('Err putting object retention\n');
throw err;
})
.then(() => {
process.stdout.write('putting object\n');
return s3.putObject({
Bucket: bucketName,
Key: objectNameTwo,
}).promise();
})
.catch(err => {
process.stdout.write(('Err putting second object\n'));
throw err;
})
.then(res => {
versionIdTwo = res.VersionId;
process.stdout.write('putting object legal hold\n'); process.stdout.write('putting object legal hold\n');
return s3.putObjectLegalHold({ return s3.putObjectLegalHold({
Bucket: bucketName, Bucket: bucketName,
Key: objectName, Key: objectNameTwo,
LegalHold: { LegalHold: {
Status: 'ON', Status: 'ON',
}, },
@ -289,11 +190,11 @@ describe('DELETE object', () => {
process.stdout.write('Err putting object legal hold\n'); process.stdout.write('Err putting object legal hold\n');
throw err; throw err;
}); });
}); });
after(() => { after(() => {
process.stdout.write('Emptying bucket\n'); process.stdout.write('Emptying bucket\n');
return bucketUtil.empty(bucketName) return bucketUtil.empty(bucketName)
.then(() => { .then(() => {
process.stdout.write('Deleting bucket\n'); process.stdout.write('Deleting bucket\n');
return bucketUtil.deleteOne(bucketName); return bucketUtil.deleteOne(bucketName);
@ -302,25 +203,87 @@ describe('DELETE object', () => {
process.stdout.write('Error in after\n'); process.stdout.write('Error in after\n');
throw err; throw err;
}); });
}); });
it('should not delete locked object version with GOVERNANCE ' + it('should put delete marker if no version id specified', done => {
'retention mode and bypass header when object is legal-hold enabled', done => s3.deleteObject({
s3.deleteObject({ Bucket: bucketName,
Bucket: bucketName, Key: objectName,
Key: objectName, }, err => {
VersionId: versionId, assert.ifError(err);
BypassGovernanceRetention: true, done();
}, err => { });
assert.strictEqual(err.code, 'AccessDenied'); });
changeObjectLock(
[{ it('should not delete object version locked with object ' +
bucket: bucketName, 'retention', done => {
key: objectName, s3.deleteObject({
versionId, Bucket: bucketName,
}], '', done); Key: objectName,
} VersionId: versionIdOne,
)); }, err => {
assert.strictEqual(err.code, 'AccessDenied');
done();
});
});
it(`should delete locked object version with ${response.bucketMode} ` +
'retention mode and correct header', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
VersionId: versionIdOne,
BypassGovernanceRetention: true,
}, err => {
assert.ifError(err);
done();
});
});
it('should not delete object locked with legal hold', done => {
s3.deleteObject({
Bucket: bucketName,
Key: objectNameTwo,
VersionId: versionIdTwo,
}, err => {
assert.strictEqual(err.code, 'AccessDenied');
changeObjectLock(
[{
bucket: bucketName,
key: objectNameTwo,
versionId: versionIdTwo,
}], '', done);
});
});
it(`should not delete locked object version with ${response.bucketMode} ` +
'retention mode and bypass header when object is legal-hold enabled', done =>
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
VersionId: versionIdOne,
BypassGovernanceRetention: true,
}, err => {
assert.strictEqual(err.code, 'AccessDenied');
changeObjectLock(
[{
bucket: bucketName,
key: objectName,
versionIdOne,
}], '', done);
}
));
it(`should delete object in ${response.bucketMode} mode`, () => {
s3.deleteObject({
Bucket: bucketName,
Key: objectName,
VersionId: versionIdOne,
}, err => {
assert.ifError(err);
});
});
});
}); });
}); });
}); });

View File

@ -210,221 +210,212 @@ describe('Multi-Object Delete Error Responses', () => {
}); });
}); });
describe('Multi-Object Delete Access', function access() { describe('Delete Multi Object', function access() {
this.timeout(360000); this.timeout(360000);
let bucketUtil; let bucketUtil;
let s3; let s3;
before(() => {
const createObjects = [];
bucketUtil = new BucketUtility('default', {
signatureVersion: 'v4',
});
s3 = bucketUtil.s3;
return s3.createBucket({ Bucket: bucketName }).promise()
.catch(err => {
process.stdout.write(`Error creating bucket: ${err}\n`);
throw err;
})
.then(() => {
for (let i = 1; i < 501; i++) {
createObjects.push(s3.putObject({
Bucket: bucketName,
Key: `${key}${i}`,
Body: 'somebody',
}).promise());
}
return Promise.all(createObjects)
.catch(err => {
process.stdout.write(`Error creating objects: ${err}\n`);
throw err;
});
});
});
after(() => s3.deleteBucket({ Bucket: bucketName }).promise());
it('should return access denied error for each object where no acl ' +
'permission', () => {
const objects = createObjectsList(500);
const errorList = createObjectsList(500);
errorList.forEach(obj => {
const item = obj;
item.Code = 'AccessDenied';
item.Message = 'Access Denied';
});
return otherAccountS3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Deleted.length, 0);
assert.deepStrictEqual(sortList(res.Errors), sortList(errorList));
assert.strictEqual(res.Errors.length, 500);
}).catch(err => {
checkNoError(err);
});
});
it('should batch delete objects where requester has permission', () => {
const objects = createObjectsList(500);
return s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Deleted.length, 500);
assert.strictEqual(res.Errors.length, 0);
}).catch(err => {
checkNoError(err);
});
});
});
describe('Multi-Object Delete with Object Lock', () => {
let bucketUtil;
let s3;
const versionIds = []; const versionIds = [];
const parameters = [
before(() => { { bucketMode: 'GOVERNANCE', ObjectLock: true, ratainDate: moment().add(5, 'days').toISOString() },
const createObjects = []; { bucketMode: 'COMPLIANCE', ObjectLock: true, ratainDate: moment().add(5, 'days').toISOString() },
bucketUtil = new BucketUtility('default', { { bucketMode: 'GOVERNANCE', ObjectLock: false, ratainDate: moment().add(5, 'days').toISOString() },
signatureVersion: 'v4', { bucketMode: 'COMPLIANCE', ObjectLock: false, ratainDate: moment().add(5, 'days').toISOString() },
}); ];
s3 = bucketUtil.s3; parameters.forEach(response => {
return s3.createBucket({ before(() => {
Bucket: bucketName, const createObjects = [];
ObjectLockEnabledForBucket: true, bucketUtil = new BucketUtility('default', {
}).promise() signatureVersion: 'v4',
.then(() => s3.putObjectLockConfiguration({ });
Bucket: bucketName, s3 = bucketUtil.s3;
ObjectLockConfiguration: { return s3.createBucket({
ObjectLockEnabled: 'Enabled', Bucket: bucketName,
Rule: { ObjectLockEnabledForBucket: response.ObjectLock,
DefaultRetention: { }).promise()
Days: 1, .then(() => {
Mode: 'GOVERNANCE', process.stdout.write('putting object lock configuration\n');
}, return response.ObjectLock ? s3.putObjectLockConfiguration({
},
},
}).promise())
.catch(err => {
process.stdout.write(`Error creating bucket: ${err}\n`);
throw err;
})
.then(() => {
for (let i = 1; i < 6; i++) {
createObjects.push(s3.putObject({
Bucket: bucketName, Bucket: bucketName,
Key: `${key}${i}`, ObjectLockConfiguration: {
Body: 'somebody', ObjectLockEnabled: 'Enabled',
}).promise()); Rule: {
} DefaultRetention: {
return Promise.all(createObjects) Days: parseInt(response.ratainDate, 0),
.then(res => { Mode: response.bucketMode,
res.forEach(r => { },
versionIds.push(r.VersionId); },
}); },
}).promise() : true;
}) })
.catch(err => { .catch(err => {
process.stdout.write(`Error creating objects: ${err}\n`); process.stdout.write(`Error creating bucket: ${err}\n`);
throw err; throw err;
})
.then(() => {
for (let i = 1; i < 6; i++) {
createObjects.push(s3.putObject({
Bucket: bucketName,
Key: `${key}${i}`,
Body: 'somebody',
}).promise());
}
return Promise.all(createObjects)
.then(res => {
res.forEach(r => {
versionIds.push(r.VersionId);
});
})
.catch(err => {
process.stdout.write(`Error creating objects: ${err}\n`);
throw err;
});
}); });
}); });
});
after(() => s3.deleteBucket({ Bucket: bucketName }).promise()); after(() => s3.deleteBucket({ Bucket: bucketName }).promise());
it('should not delete locked objects', () => { it(`delete objects in ${response.bucketMode}`, () => {
const objects = createObjectsList(5, versionIds); const objects = createObjectsList(5, versionIds);
return s3.deleteObjects({ return s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Errors.length, 5);
res.Errors.forEach(err => assert.strictEqual(err.Code, 'AccessDenied'));
});
});
it('should not delete locked objects with GOVERNANCE ' +
'retention mode and bypass header when object is legal hold enabled', () => {
const objects = createObjectsList(5, versionIds);
const putObjectLegalHolds = [];
for (let i = 1; i < 6; i++) {
putObjectLegalHolds.push(s3.putObjectLegalHold({
Bucket: bucketName, Bucket: bucketName,
Key: `${key}${i}`, Delete: {
LegalHold: { Objects: objects,
Status: 'ON', Quiet: false,
}, },
}).promise()); }).promise().then(res => {
} assert.strictEqual(res.Errors.length, 5);
return Promise.all(putObjectLegalHolds) res.Errors.forEach(err => assert.strictEqual(err.Code, 'AccessDenied'));
});
});
it('should not delete locked objects', () => {
const objects = createObjectsList(5, versionIds);
return s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Errors.length, 5);
res.Errors.forEach(err => assert.strictEqual(err.Code, 'AccessDenied'));
});
});
it(`should not delete locked objects with ${response.bucketMode} ` +
'retention mode and bypass header when object is legal hold enabled', () => {
const objects = createObjectsList(5, versionIds);
const putObjectLegalHolds = [];
for (let i = 1; i < 6; i++) {
putObjectLegalHolds.push(s3.putObjectLegalHold({
Bucket: bucketName,
Key: `${key}${i}`,
LegalHold: {
Status: 'ON',
},
}).promise());
}
return Promise.all(putObjectLegalHolds)
.then(() => s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
BypassGovernanceRetention: true,
}).promise()).then(res => {
assert.strictEqual(res.Errors.length, 5);
res.Errors.forEach(err => assert.strictEqual(err.Code, 'AccessDenied'));
});
});
it('should delete locked objects after retention period has expired', () => {
const objects = createObjectsList(5, versionIds);
const objectsCopy = JSON.parse(JSON.stringify(objects));
for (let i = 0; i < objectsCopy.length; i++) {
objectsCopy[i].key = objectsCopy[i].Key;
objectsCopy[i].versionId = objectsCopy[i].VersionId;
objectsCopy[i].bucket = bucketName;
delete objectsCopy[i].Key;
delete objectsCopy[i].VersionId;
}
const newRetention = {
mode: response.bucketMode,
date: moment().subtract(10, 'days').toISOString(),
};
return changeLockPromise(objectsCopy, newRetention)
.then(() => s3.deleteObjects({ .then(() => s3.deleteObjects({
Bucket: bucketName, Bucket: bucketName,
Delete: { Delete: {
Objects: objects, Objects: objects,
Quiet: false, Quiet: false,
}, },
BypassGovernanceRetention: true,
}).promise()).then(res => { }).promise()).then(res => {
assert.strictEqual(res.Errors.length, 5); assert.strictEqual(res.Deleted.length, 5);
res.Errors.forEach(err => assert.strictEqual(err.Code, 'AccessDenied')); assert.strictEqual(res.Errors.length, 0);
}).catch(err => {
checkNoError(err);
}); });
});
it('should delete locked objects after retention period has expired', () => {
const objects = createObjectsList(5, versionIds);
const objectsCopy = JSON.parse(JSON.stringify(objects));
for (let i = 0; i < objectsCopy.length; i++) {
objectsCopy[i].key = objectsCopy[i].Key;
objectsCopy[i].versionId = objectsCopy[i].VersionId;
objectsCopy[i].bucket = bucketName;
delete objectsCopy[i].Key;
delete objectsCopy[i].VersionId;
}
const newRetention = {
mode: 'GOVERNANCE',
date: moment().subtract(10, 'days').toISOString(),
};
return changeLockPromise(objectsCopy, newRetention)
.then(() => s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise()).then(res => {
assert.strictEqual(res.Deleted.length, 5);
assert.strictEqual(res.Errors.length, 0);
}).catch(err => {
checkNoError(err);
}); });
});
it('should delete locked objects with GOVERNANCE ' + it(`should delete locked objects with ${response.bucketMode} ` +
'retention mode and bypass header', () => { 'retention mode and bypass header', () => {
const objects = createObjectsList(5, versionIds); const objects = createObjectsList(5, versionIds);
return s3.deleteObjects({ return s3.deleteObjects({
Bucket: bucketName, Bucket: bucketName,
Delete: { Delete: {
Objects: objects, Objects: objects,
Quiet: false, Quiet: false,
}, },
BypassGovernanceRetention: true, BypassGovernanceRetention: true,
}).promise().then(res => { }).promise().then(res => {
assert.strictEqual(res.Deleted.length, 5); assert.strictEqual(res.Deleted.length, 5);
assert.strictEqual(res.Errors.length, 0); assert.strictEqual(res.Errors.length, 0);
}).catch(err => { }).catch(err => {
checkNoError(err); checkNoError(err);
});
});
it('should return access denied error for each object where no acl ' +
'permission', () => {
const objects = createObjectsList(500);
const errorList = createObjectsList(500);
errorList.forEach(obj => {
const item = obj;
item.Code = 'AccessDenied';
item.Message = 'Access Denied';
});
return otherAccountS3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Deleted.length, 0);
assert.deepStrictEqual(sortList(res.Errors), sortList(errorList));
assert.strictEqual(res.Errors.length, 500);
}).catch(err => {
checkNoError(err);
});
});
it('should batch delete objects where requester has permission', () => {
const objects = createObjectsList(500);
return s3.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objects,
Quiet: false,
},
}).promise().then(res => {
assert.strictEqual(res.Deleted.length, 500);
assert.strictEqual(res.Errors.length, 0);
}).catch(err => {
checkNoError(err);
});
}); });
}); });
}); });