Compare commits

...

15 Commits

Author SHA1 Message Date
Ilke 56f36ff76d Merge remote-tracking branch 'origin/feature/S3C-2790_GetObjectLockConfiguration' into w/8.1/feature/S3C-2790_GetObjectLockConfiguration 2020-05-18 13:52:09 -07:00
Ilke eaab257d4d Merge branch 'development/7.7' into feature/S3C-2790_GetObjectLockConfiguration 2020-05-18 11:54:21 -07:00
Ilke 836a328c25 ft: S3C-2790 get object lock configuration 2020-05-18 11:40:58 -07:00
Ilke 2104954124 Merge branch 'feature/S3C-2789-put-object-lock-configuration' of https://github.com/scality/cloudserver into feature/S3C-2789-put-object-lock-configuration 2020-05-07 15:03:51 -07:00
Dora Korpar 3d386bdeaf update ModelVersion readme 2020-05-06 11:08:43 -07:00
Dora Korpar 0ac12c06d5 update Arsenal 2020-05-06 11:08:43 -07:00
Dora Korpar c503bce61e ft: S3C 2789 put obj lock config api 2020-05-06 11:08:43 -07:00
Ilke abf9dfea23 Merge branch 'feature/S3C-2785_AddObjectLockForPutBucket' of https://github.com/scality/cloudserver into feature/S3C-2785_AddObjectLockForPutBucket 2020-05-05 16:27:56 -07:00
Ilke c974ead19b update for aws compliance 2020-05-05 16:25:00 -07:00
ilkescality 9536181217
Merge branch 'development/7.7' into feature/S3C-2785_AddObjectLockForPutBucket 2020-05-04 18:26:25 -07:00
Dora Korpar 3cdb181bcc update Arsenal 2020-05-04 15:57:33 -07:00
Dora Korpar 0d359153df ft: S3C 2789 put obj lock config api 2020-05-04 15:57:29 -07:00
Ilke 07797d0970 linter fix 2020-05-01 16:31:07 -07:00
Ilke d4eae387b6 cr update and forgotten functional test 2020-05-01 16:28:31 -07:00
Ilke 4b994097b0 feature: S3C-2785 update putBucket objectLock 2020-04-24 11:04:09 -07:00
9 changed files with 405 additions and 2 deletions

View File

@ -101,7 +101,6 @@ const constants = {
'logging',
'metrics',
'notification',
'object-lock',
'policyStatus',
'publicAccessBlock',
'requestPayment',

View File

@ -12,6 +12,7 @@ const bucketGetVersioning = require('./bucketGetVersioning');
const bucketGetWebsite = require('./bucketGetWebsite');
const bucketGetLocation = require('./bucketGetLocation');
const bucketGetLifecycle = require('./bucketGetLifecycle');
const bucketGetObjectLock = require('./bucketGetObjectLock');
const bucketGetPolicy = require('./bucketGetPolicy');
const bucketHead = require('./bucketHead');
const { bucketPut } = require('./bucketPut');
@ -22,6 +23,7 @@ const bucketPutWebsite = require('./bucketPutWebsite');
const bucketPutReplication = require('./bucketPutReplication');
const bucketPutLifecycle = require('./bucketPutLifecycle');
const bucketPutPolicy = require('./bucketPutPolicy');
const bucketPutObjectLock = require('./bucketPutObjectLock');
const bucketGetReplication = require('./bucketGetReplication');
const bucketDeleteReplication = require('./bucketDeleteReplication');
const corsPreflight = require('./corsPreflight');
@ -172,6 +174,7 @@ const api = {
bucketGetVersioning,
bucketGetWebsite,
bucketGetLocation,
bucketGetObjectLock,
bucketHead,
bucketPut,
bucketPutACL,
@ -187,6 +190,7 @@ const api = {
bucketPutPolicy,
bucketGetPolicy,
bucketDeletePolicy,
bucketPutObjectLock,
corsPreflight,
completeMultipartUpload,
initiateMultipartUpload,

View File

@ -196,7 +196,7 @@ function createBucket(authInfo, bucketName, headers,
// when a bucket is created with object lock
const versioningConfiguration = {
Status: 'Enabled',
MfaDelete: 'Disabled',
MFADelete: 'Disabled',
};
bucket.setVersioningConfiguration(versioningConfiguration);
}

View File

@ -0,0 +1,68 @@
const { errors } = require('arsenal');
const { metadataValidateBucket } = require('../metadata/metadataUtils');
const { pushMetric } = require('../utapi/utilities');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const ObjectLockConfiguration =
require('arsenal').models.ObjectLockConfiguration;
// Format of the xml response:
/**
* <ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
* <ObjectLockEnabled>string</ObjectLockEnabled>
* <Rule>
* <DefaultRetention>
* <Mode>string</Mode>
* <Days>integer</Days>
* <Years>integer</Years>
* </DefaultRetention>
* </Rule>
* </ObjectLockConfiguration>
*/
/**
* bucketGetObjectLock - Return object lock configuration for the bucket
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
* @param {object} request - http request object
* @param {object} log - Werelogs logger
* @param {function} callback - callback to respond to http request
* @return {undefined}
*/
function bucketGetObjectLock(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketGetObjectLock' });
const { bucketName, headers, method } = request;
const metadataValParams = {
authInfo,
bucketName,
requestType: 'bucketGetObjectLock',
};
return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);
if (err) {
log.debug('error processing request', {
error: err,
method: 'bucketGetObjectLock',
});
return callback(err, null, corsHeaders);
}
const objectLockEnabled = bucket._objectLockEnabled;
const objectLockConfig = bucket._objectLockConfiguration ?
bucket._objectLockConfiguration :
{};
if (!objectLockEnabled) {
log.debug('error processing request', {
error: errors.ObjectLockConfigurationNotFoundError,
method: 'bucketGetObjectLock',
});
return callback(errors.ObjectLockConfigurationNotFoundError, null,
corsHeaders);
}
const xml = ObjectLockConfiguration.getConfigXML(objectLockConfig);
pushMetric('getBucketObjectLock', log, {
authInfo,
bucket: bucketName,
});
return callback(null, xml, corsHeaders);
});
}
module.exports = bucketGetObjectLock;

View File

@ -0,0 +1,82 @@
const { waterfall } = require('async');
const arsenal = require('arsenal');
const errors = arsenal.errors;
const ObjectLockConfiguration = arsenal.models.ObjectLockConfiguration;
const parseXML = require('../utilities/parseXML');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const metadata = require('../metadata/wrapper');
const { metadataValidateBucket } = require('../metadata/metadataUtils');
const { pushMetric } = require('../utapi/utilities');
/**
* Bucket Put Object Lock - Create or update bucket object lock configuration
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
* @param {object} request - http request object
* @param {object} log - Werelogs logger
* @param {function} callback - callback to server
* @return {undefined}
*/
function bucketPutObjectLock(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketPutObjectLock' });
const bucketName = request.bucketName;
const metadataValParams = {
authInfo,
bucketName,
requestType: 'bucketPutObjectLock',
};
return waterfall([
next => parseXML(request.post, log, next),
(parsedXml, next) => {
const lockConfigClass = new ObjectLockConfiguration(parsedXml);
// if there was an error getting object lock configuration,
// returned configObj will contain 'error' key
process.nextTick(() => {
const configObj = lockConfigClass.
getValidatedObjectLockConfiguration();
return next(configObj.error || null, configObj);
});
},
(objectLockConfig, next) => metadataValidateBucket(metadataValParams,
log, (err, bucket) => {
if (err) {
return next(err, bucket);
}
return next(null, bucket, objectLockConfig);
}),
(bucket, objectLockConfig, next) => {
const isObjectLockEnabled = bucket.isObjectLockEnabled();
process.nextTick(() => {
if (!isObjectLockEnabled) {
return next(errors.InvalidBucketState.customizeDescription(
'Object Lock configuration cannot be enabled on ' +
'existing buckets'), bucket);
}
return next(null, bucket, objectLockConfig);
});
},
(bucket, objectLockConfig, next) => {
bucket.setObjectLockConfiguration(objectLockConfig);
metadata.updateBucket(bucket.getName(), bucket, log, err =>
next(err, bucket));
},
], (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket);
if (err) {
log.trace('error processing request', { error: err,
method: 'bucketPutObjectLock' });
return callback(err, corsHeaders);
}
pushMetric('putBucketObjectLock', log, {
authInfo,
bucket: bucketName,
});
return callback(null, corsHeaders);
});
}
module.exports = bucketPutObjectLock;

View File

@ -88,6 +88,8 @@ Backwards compatible: add a uid to bucket if not exist. Otherwise, use existing
```javascript
this._uid = uid || uuid();
this._objectLockEnabled = objectLockEnabled || false;
this._objectLockConfiguration = objectLockConfiguration || null;
```
### Usage

View File

@ -0,0 +1,123 @@
const assert = require('assert');
const { bucketPut } = require('../../../lib/api/bucketPut');
const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers');
const bucketGetObjectLock = require('../../../lib/api/bucketGetObjectLock');
const bucketPutObjectLock = require('../../../lib/api/bucketPutObjectLock');
const ObjectLockConfiguration =
require('arsenal').models.ObjectLockConfiguration;
const log = new DummyRequestLogger();
const authInfo = makeAuthInfo('accessKey1');
const bucketName = 'bucketname';
const bucketPutReq = {
bucketName,
headers: {
host: `${bucketName}.s3.amazonaws.com`,
},
url: '/',
};
const testBucketPutReqWithObjLock = {
bucketName,
headers: {
host: `${bucketName}.s3.amazonaws.com`,
'x-amz-bucket-object-lock-enabled': true
},
url: '/',
};
function getObjectLockConfigRequest(bucketName, xml) {
const request = {
bucketName,
headers: {
host: `${bucketName}.s3.amazonaws.com`,
'x-amz-bucket-object-lock-enabled': true,
},
url: '/?object-lock',
};
if (xml) {
request.post = xml;
}
return request;
}
function getObjectLockXml(mode, type, time) {
const xml = {
link: 'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">',
objLockConfigOpen: '<ObjectLockConfiguration ',
objLockConfigClose: '</ObjectLockConfiguration>',
objectLockEnabled: '<ObjectLockEnabled>Enabled</ObjectLockEnabled>',
ruleOpen: '<Rule><DefaultRetention>',
ruleClose: '</DefaultRetention></Rule>',
}
const retentionMode = `<Mode>${mode}</Mode>`;
const retentionTime = `<${type}>${time}</${type}>`;
let xmlStr = '<?xml version="1.0" encoding="UTF-8"?>' +
xml.objLockConfigOpen +
xml.link +
xml.objectLockEnabled;
// object lock is enabled and object lock configuration is set
if (arguments.length === 3) {
xmlStr += xml.ruleOpen +
retentionMode +
retentionTime +
xml.ruleClose;
}
xmlStr += xml.objLockConfigClose;
return xmlStr;
};
describe('getBucketObjectLock API', () => {
before(done => bucketPut(authInfo, bucketPutReq, log, done));
after(cleanup);
it('should return ObjectLockConfigurationNotFoundError error if ' +
'object lock is not enabled on the bucket', done => {
const objectLockRequest = getObjectLockConfigRequest(bucketName);
bucketGetObjectLock(authInfo, objectLockRequest, log, err => {
assert.strictEqual(err.ObjectLockConfigurationNotFoundError, true);
done();
});
});
});
describe('bucketGetObjectLock API', () => {
before(cleanup);
beforeEach(done => bucketPut(authInfo, testBucketPutReqWithObjLock, log, done));
afterEach(cleanup);
it('should return config without \'rule\' if object lock configuration ' +
'not set on the bucket', done => {
const objectLockRequest = getObjectLockConfigRequest(bucketName);
bucketGetObjectLock(authInfo, objectLockRequest, log, (err, res) => {
assert.ifError(err);
const expectedXml = getObjectLockXml();
assert.equal(expectedXml, res);
done();
});
});
describe('after object lock configuration has been put', () => {
beforeEach(done => {
const xml = getObjectLockXml('COMPLIANCE', 'Days', 90);
const objectLockRequest = getObjectLockConfigRequest(bucketName, xml);
bucketPutObjectLock(authInfo, objectLockRequest, log, err => {
assert.ifError(err);
done();
});
});
it('should return object lock configuration XML', done => {
const getRequest = getObjectLockConfigRequest(bucketName);
bucketGetObjectLock(authInfo, getRequest, log, (err, res) => {
assert.ifError(err);
const expectedXml = getObjectLockXml('COMPLIANCE', 'Days', 90);
assert.strictEqual(expectedXml, res);
done();
});
});
});
});

View File

@ -0,0 +1,84 @@
const assert = require('assert');
const { bucketPut } = require('../../../lib/api/bucketPut');
const bucketPutObjectLock = require('../../../lib/api/bucketPutObjectLock');
const { cleanup,
DummyRequestLogger,
makeAuthInfo,
} = require('../helpers');
const metadata = require('../../../lib/metadata/wrapper');
const log = new DummyRequestLogger();
const authInfo = makeAuthInfo('accessKey1');
const bucketName = 'bucketputobjectlockbucket';
const bucketPutRequest = {
bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` },
url: '/',
};
const objectLockXml = '<ObjectLockConfiguration ' +
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
'<ObjectLockEnabled>Enabled</ObjectLockEnabled>' +
'<Rule><DefaultRetention>' +
'<Mode>GOVERNANCE</Mode>' +
'<Days>1</Days>' +
'</DefaultRetention></Rule>' +
'</ObjectLockConfiguration>';
const putObjLockRequest = {
bucketName,
headers: { host: `${bucketName}.s3.amazonaws.com` },
post: objectLockXml,
};
const expectedObjectLockConfig = {
rule: {
mode: 'GOVERNANCE',
days: 1,
},
};
describe('putBucketObjectLock API', () => {
before(() => cleanup());
describe('without Object Lock enabled on bucket', () => {
beforeEach(done => bucketPut(authInfo, bucketPutRequest, log, done));
afterEach(() => cleanup());
it('should return InvalidBucketState error', done => {
bucketPutObjectLock(authInfo, putObjLockRequest, log, err => {
assert.strictEqual(err.InvalidBucketState, true);
done();
});
});
});
describe('with Object Lock enabled on bucket', () => {
const bucketObjLockRequest = Object.assign({}, bucketPutRequest,
{ headers: { 'x-amz-bucket-object-lock-enabled': true } });
beforeEach(done => bucketPut(authInfo, bucketObjLockRequest, log, done));
afterEach(() => cleanup());
it('should update a bucket\'s metadata with object lock config', done => {
bucketPutObjectLock(authInfo, putObjLockRequest, log, err => {
if (err) {
process.stdout.write(`Err putting lifecycle config ${err}`);
return done(err);
}
return metadata.getBucket(bucketName, log, (err, bucket) => {
if (err) {
process.stdout.write(`Err retrieving bucket MD ${err}`);
return done(err);
}
const bucketObjectLockConfig = bucket.
getObjectLockConfiguration();
assert.deepStrictEqual(
bucketObjectLockConfig, expectedObjectLockConfig);
return done();
});
});
});
});
});

View File

@ -254,6 +254,7 @@ arraybuffer.slice@0.0.6:
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca"
integrity sha1-8zshWfBTKj8xB6JywMz70a0peco=
<<<<<<< HEAD
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
@ -262,6 +263,23 @@ arraybuffer.slice@~0.0.7:
"arsenal@github:scality/Arsenal#9f58044":
version "8.2.1"
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/9f580444f350470d878d4aa49bff7fec1d2b0181"
=======
<<<<<<< HEAD
<<<<<<< HEAD
"arsenal@github:scality/Arsenal#842ded1":
version "7.5.0"
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/842ded13094bf52c06844e8d870e1f9e8672143c"
=======
"arsenal@github:scality/Arsenal#c5e21e0":
version "7.5.0"
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/c5e21e07191df97a136ce57e5620390a4e79ced3"
>>>>>>> 3d386bdeaffb7f0a4ebf2873dfa2185df6a04b30
=======
"arsenal@github:scality/Arsenal#f988270":
version "7.5.0"
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/f988270a0c557f0d663cae5a6d3dc5b840ccc4ff"
>>>>>>> development/7.7
>>>>>>> origin/feature/S3C-2790_GetObjectLockConfiguration
dependencies:
"@hapi/joi" "^15.1.0"
JSONStream "^1.0.0"
@ -470,10 +488,18 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
<<<<<<< HEAD
<<<<<<< HEAD
aws-sdk@2.178.0:
version "2.178.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.178.0.tgz#e8257adde039cad2a1ac0abd278d172b6c39a9c7"
integrity sha1-6CV63eA5ytKhrAq9J40XK2w5qcc=
=======
aws-sdk@2.363.0:
version "2.363.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.363.0.tgz#6d366a78d5b008fd927d6ff24815d39d78b54778"
integrity sha512-kQOfjzCEllH45OFN0z3fvhpSWDFWu19715A7TztHx6IEWKwwIEyd3b2XhTZtQLJrI1Giv7iGALwH46gybH9HJw==
>>>>>>> development/7.7
dependencies:
buffer "4.9.1"
events "1.1.1"
@ -501,9 +527,21 @@ aws-sdk@2.80.0:
xmlbuilder "4.2.1"
aws-sdk@^2.2.23:
<<<<<<< HEAD
version "2.669.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.669.0.tgz#7e8e7985120102da6bdbf40a18d8b9d692dea1ba"
integrity sha512-kuVcSRpDzvkgmeSmMX6Q32eTOb8UeihhUdavMrvUOP6fzSU19cNWS9HAIkYOi/jrEDK85cCZxXjxqE3JGZIGcw==
=======
aws-sdk@^2.178.0, aws-sdk@^2.2.23:
version "2.670.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.670.0.tgz#d54d18b9245df7b89bea96102e5bdebd99587701"
integrity sha512-hGRnZtp1wDUh6hZRBHO0Ki7thx/xbRlIEiTKlWes+f/0E1Nhm3KpelsBZ3L/Q6y1ragwkQd4Q720AmWEqemLyA==
>>>>>>> 3d386bdeaffb7f0a4ebf2873dfa2185df6a04b30
=======
version "2.671.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.671.0.tgz#2c6e164a0f540d6fc428c123f2994ac081663ff5"
integrity sha512-i83+/TIOLlhAxvV2xVLz5+XGtNqJgQJwP/e8J49rzDkyMV6OE2FgxU8utujGrComrSJFpITqMFqug+ZfdHoLIQ==
>>>>>>> development/7.7
dependencies:
buffer "4.9.1"
events "1.1.1"
@ -4594,6 +4632,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
<<<<<<< HEAD
uuid@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
@ -4604,6 +4643,8 @@ uuid@3.1.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==
=======
>>>>>>> origin/feature/S3C-2790_GetObjectLockConfiguration
uuid@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"