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
11 changes: 9 additions & 2 deletions constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ const constants = {
'accelerate',
'analytics',
'inventory',
'logging',
'metrics',
'policyStatus',
'publicAccessBlock',
Expand Down Expand Up @@ -191,6 +190,8 @@ const constants = {
'bucketPutReplication',
'bucketPutVersioning',
'bucketPutWebsite',
'bucketPutLogging',
'bucketGetLogging',
'objectDeleteTagging',
'objectGetTagging',
'objectPutTagging',
Expand Down Expand Up @@ -267,7 +268,13 @@ const constants = {
],
// if requester is not bucket owner, bucket policy actions should be denied with
// MethodNotAllowed error
onlyOwnerAllowed: ['bucketDeletePolicy', 'bucketGetPolicy', 'bucketPutPolicy'],
onlyOwnerAllowed: [
'bucketDeletePolicy',
'bucketGetPolicy',
'bucketPutPolicy',
'bucketPutLogging',
'bucketGetLogging',
],
};

module.exports = constants;
4 changes: 4 additions & 0 deletions lib/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const bucketPutObjectLock = require('./bucketPutObjectLock');
const bucketUpdateQuota = require('./bucketUpdateQuota');
const bucketGetReplication = require('./bucketGetReplication');
const bucketDeleteReplication = require('./bucketDeleteReplication');
const bucketGetLogging = require('./bucketGetLogging');
const bucketPutLogging = require('./bucketPutLogging');
const corsPreflight = require('./corsPreflight');
const completeMultipartUpload = require('./completeMultipartUpload');
const initiateMultipartUpload = require('./initiateMultipartUpload');
Expand Down Expand Up @@ -368,6 +370,8 @@ const api = {
bucketPutNotification,
bucketGetNotification,
bucketPutEncryption,
bucketGetLogging,
bucketPutLogging,
corsPreflight,
completeMultipartUpload,
initiateMultipartUpload,
Expand Down
55 changes: 55 additions & 0 deletions lib/api/bucketGetLogging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const { standardMetadataValidateBucket } = require('../metadata/metadataUtils');
const { checkExpectedBucketOwner } = require('./apiUtils/authorization/bucketOwner');
const monitoring = require('../utilities/monitoringHandler');
const { waterfall } = require('async');

const BucketLoggingStatusNotFoundBody = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/2006-03-01" />';

function bucketGetLogging(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketGetLogging' });

const bucketName = request.bucketName;
const metadataValParams = {
authInfo,
bucketName,
requestType: request.apiMethods || 'bucketGetLogging',
request,
};

return waterfall([
next => standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
if (err) {
return next(err);
}

return next(null, bucket);
}),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => {
if (err) {
return next(err);
}

return next(null, bucket);
}),
(bucket, next) => {
const bucketLoggingStatus = bucket.getBucketLoggingStatus();
if (!bucketLoggingStatus) {
return next(null, BucketLoggingStatusNotFoundBody);
}

return next(null, bucketLoggingStatus.toXML());
}
], (err, body) => {
if (err) {
log.trace('error processing request', { error: err, method: 'bucketGetLogging' });
monitoring.promMetrics('GET', bucketName, err.code, 'bucketGetLogging');
return callback(err);
}

monitoring.promMetrics('GET', bucketName, '200', 'bucketGetLogging');
return callback(null, body);
});
}

module.exports = bucketGetLogging;
78 changes: 78 additions & 0 deletions lib/api/bucketPutLogging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const { waterfall } = require('async');
const { standardMetadataValidateBucket } = require('../metadata/metadataUtils');
const { checkExpectedBucketOwner } = require('./apiUtils/authorization/bucketOwner');
const BucketLoggingStatus = require('arsenal').models.BucketLoggingStatus;
const metadata = require('../metadata/wrapper');
const monitoring = require('../utilities/monitoringHandler');
const { errorInstances } = require('arsenal');

function bucketPutLogging(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketPutLogging' });

const bucketName = request.bucketName;
const parsed = BucketLoggingStatus.fromXML(request.post);
if (parsed.error) {
log.trace('error processing request', { error: parsed.error, method: 'bucketPutLogging' });
monitoring.promMetrics('PUT', bucketName, parsed.error.arsenalError, 'bucketPutLogging');
return callback(parsed.error.arsenalError);
}

const metadataValParams = {
authInfo,
bucketName,
requestType: request.apiMethods || 'bucketPutLogging',
request,
};

return waterfall([
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we check if the target bucket exists? Do you know if AWS returns an error if it does not exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

An error occurred (InvalidTargetBucketForLogging) when calling the PutBucketLogging operation: The target bucket for logging does not exist

Copy link
Contributor

Choose a reason for hiding this comment

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

We can do the same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added a check and functional tests

next => standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
if (err) {
return next(err);
}

return next(null, bucket);
}),
(bucket, next) => {
const loggingEnabled = parsed.res.getLoggingEnabled();
if (!loggingEnabled) {
return next(null, bucket);
}

return metadata.getBucket(loggingEnabled.TargetBucket, log, err => {
if (err) {
return next(errorInstances.InvalidTargetBucketForLogging.customizeDescription(err.description));
}

return next(null, bucket);
});
},
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => {
if (err) {
return next(err);
}

return next(null, bucket);
}),
(bucket, next) => {
bucket.setBucketLoggingStatus(parsed.res);
return metadata.updateBucket(bucket.getName(), bucket, log, err => {
if (err) {
return next(err);
}

return next();
});
},
], err => {
if (err) {
log.trace('error processing request', { error: err, method: 'bucketPutLogging' });
monitoring.promMetrics('PUT', bucketName, err.code, 'bucketPutLogging');
return callback(err);
}

monitoring.promMetrics('PUT', bucketName, '200', 'bucketPutLogging');
return callback();
});
}

module.exports = bucketPutLogging;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"dependencies": {
"@azure/storage-blob": "^12.28.0",
"@hapi/joi": "^17.1.1",
"arsenal": "git+https://github.com/scality/Arsenal#8.2.32",
"arsenal": "git+https://github.com/scality/Arsenal#8.2.33",
"async": "2.6.4",
"aws-sdk": "^2.1692.0",
"bucketclient": "scality/bucketclient#8.2.5",
Expand Down
100 changes: 100 additions & 0 deletions tests/functional/aws-node-sdk/test/bucket/getBucketLogging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const assert = require('assert');

const withV4 = require('../support/withV4');
const BucketUtility = require('../../lib/utility/bucket-util');

const bucketName = 'testgetloggingbucket';
const targetBucket = 'testloggingtargetbucket';

const validLoggingConfig = {
LoggingEnabled: {
TargetBucket: targetBucket,
TargetPrefix: 'logs/',
},
};

describe('GET bucket logging', () => {
withV4(sigCfg => {
const bucketUtil = new BucketUtility('default', sigCfg);
const s3 = bucketUtil.s3;

afterEach(done => {
process.stdout.write('Deleting buckets\n');
bucketUtil.deleteOne(bucketName).then(() => bucketUtil.deleteOne(targetBucket)).then(() => done())
.catch(err => {
if (err && err.code !== 'NoSuchBucket') {
return done(err);
}
return done();
});
});

describe('without existing bucket', () => {
it('should return NoSuchBucket', done => {
s3.getBucketLogging({ Bucket: bucketName }, err => {
assert(err);
assert.strictEqual(err.code, 'NoSuchBucket');
assert.strictEqual(err.statusCode, 404);
return done();
});
});
});

describe('on bucket without logging configuration', () => {
before(done => {
process.stdout.write('Creating bucket without logging\n');
s3.createBucket({ Bucket: bucketName }, err => {
if (err) {
process.stdout.write('error creating bucket', err);
return done(err);
}
return done();
});
});

it('should return empty BucketLoggingStatus', done => {
s3.getBucketLogging({ Bucket: bucketName }, (err, data) => {
assert.strictEqual(err, null,
`Found unexpected err ${err}`);
// When no logging is configured, AWS returns empty object
assert(data);
assert.strictEqual(Object.keys(data).length, 0, 'Expected data to have no keys');
return done();
});
});
});

describe('with existing logging configuration', () => {
before(done => {
process.stdout.write('Creating buckets and setting logging\n');
return s3.createBucket({ Bucket: bucketName }, err => {
if (err) {
return done(err);
}
return s3.createBucket({ Bucket: targetBucket }, err => {
if (err) {
return done(err);
}
return s3.putBucketLogging({
Bucket: bucketName,
BucketLoggingStatus: validLoggingConfig,
}, done);
});
});
});

it('should return bucket logging configuration successfully', done => {
s3.getBucketLogging({ Bucket: bucketName }, (err, data) => {
assert.strictEqual(err, null,
`Found unexpected err ${err}`);
assert(data.LoggingEnabled);
assert.strictEqual(data.LoggingEnabled.TargetBucket,
targetBucket);
assert.strictEqual(data.LoggingEnabled.TargetPrefix, 'logs/');
return done();
});
});
});
});
});

Loading
Loading