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
38 changes: 24 additions & 14 deletions lib/api/bucketUpdateQuota.js
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@francoisferrand I tried the curl command provided in the doc we gave to the client :
curl -X PUT "http://localhost:8000/testb/?quota=true" --user "accessKey1:verySecretKey1" --aws-sigv4 "aws:amz:us-east-1:s3" -d '{"quota" : 32}'

In that case, curl will not set the content-type header. So I think it's tricky to modify the logic around the body parsing by using content type, because clients can't be expected to set that header so no matter what we will have to use a fallback which is basically the code that we already have with the try/catch

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we should check the contentType if set (and default to JSON if not) ?

switch(contentType) {
case 'xml':
   return parseString(requestBody, { explicitArray: false }, (xmlError, xmlData) => { [...] };

case 'json':
default:
   const jsonData = JSON.parse(requestBody);
   return next(null, jsonData);
}

could be a followup ticket, or just this one as it is probably simple:

  • probably a good time to make this change: 9.3 already has "risky" changes, and we have time before we ship it
  • should be fine if we keep JSON as fallback (according to the docs which were shared)
  • esp. quota API is not widely used at the moment
  • but we need to test it explicitly to avoid any surprise

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes let's make that change and add it in 9.3

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const monitoring = require('../utilities/monitoringHandler');
const { parseString } = require('xml2js');

function validateBucketQuotaProperty(requestBody, next) {
const quota = requestBody.quota;
let quota = requestBody.quota;
if (quota === undefined) {
quota = requestBody.QuotaConfiguration?.Quota;
}
const quotaValue = parseInt(quota, 10);
if (Number.isNaN(quotaValue)) {
return next(errorInstances.InvalidArgument.customizeDescription('Quota Value should be a number'));
Expand All @@ -19,20 +22,26 @@ function validateBucketQuotaProperty(requestBody, next) {
return next(null, quotaValue);
}

function parseRequestBody(requestBody, next) {
try {
const jsonData = JSON.parse(requestBody);
if (typeof jsonData !== 'object') {
throw new Error('Invalid JSON');
}
return next(null, jsonData);
} catch {
return parseString(requestBody, (xmlError, xmlData) => {
if (xmlError) {
function parseRequestBody(requestBody, contentType, next) {
switch (contentType) {
case 'application/xml':
return parseString(requestBody, { explicitArray: false }, (xmlError, xmlData) => {
if (xmlError) {
return next(errorInstances.InvalidArgument.customizeDescription('Invalid XML format'));
}
return next(null, xmlData);
});
case 'application/json':
default:
try {
const jsonData = JSON.parse(requestBody);
if (typeof jsonData !== 'object') {
throw new Error('Invalid JSON');
}
return next(null, jsonData);
} catch {
return next(errorInstances.InvalidArgument.customizeDescription('Request body must be a JSON object'));
}
return next(null, xmlData);
});
}
}

Expand All @@ -53,7 +62,8 @@ function bucketUpdateQuota(authInfo, request, log, callback) {
bucket = b;
return next(err, bucket);
}),
(bucket, next) => parseRequestBody(request.post, (err, requestBody) => next(err, bucket, requestBody)),
(bucket, next) => parseRequestBody(request.post, request.headers['content-type'], (err, requestBody) =>
next(err, bucket, requestBody)),
(bucket, requestBody, next) => validateBucketQuotaProperty(requestBody, (err, quotaValue) =>
next(err, bucket, quotaValue)),
(bucket, quotaValue, next) => {
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.2.14",
"version": "9.3.0-preview.1",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

wanna do this now, because this change is for the cloudserver quota client feature, and I need an image with this new code to run in the test pipeline. Otherwise can't merge the new client

"description": "Zenko CloudServer, an open-source Node.js implementation of a server handling the Amazon S3 protocol",
"main": "index.js",
"engines": {
Expand Down
18 changes: 17 additions & 1 deletion tests/functional/aws-node-sdk/test/bucket/updateBucketQuota.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,25 @@ describe('Test update bucket quota', () => {

afterEach(() => s3.send(new DeleteBucketCommand({ Bucket: bucket })));

it('should update the quota', () => sendRequest('PUT',
it('should update the quota, using json parsing by default', () => sendRequest('PUT',
'127.0.0.1:8000', `/${bucket}/?quota=true`, JSON.stringify(quota)));

it('should update quota with explicit JSON content-type', async () => {
await sendRequest('PUT', '127.0.0.1:8000', `/${bucket}/?quota=true`,
JSON.stringify(quota), null, new Date(), { 'Content-Type': 'application/json' });
});

it('should update quota with XML format', async () => {
try {
const xmlQuota = '<QuotaConfiguration><Quota>3000</Quota></QuotaConfiguration>';
await sendRequest('PUT', '127.0.0.1:8000', `/${bucket}/?quota=true`,
xmlQuota, null, new Date(), { 'Content-Type': 'application/xml' });
assert.ok(true);
} catch (err) {
assert.fail(`Expected no error, but got ${err}`);
}
});

it('should return no such bucket error', async () => {
try {
await sendRequest('PUT', '127.0.0.1:8000', `/${nonExistantBucket}/?quota=true`, JSON.stringify(quota));
Expand Down
3 changes: 2 additions & 1 deletion tests/functional/aws-node-sdk/test/quota/tooling.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const { Sha256 } = require('@aws-crypto/sha256-js');
const xml2js = require('xml2js');
const { getCredentials } = require('../support/credentials');

const sendRequest = async (method, host, path, body = '', config = null, signingDate = new Date()) => {
const sendRequest = async (method, host, path, body = '', config = null, signingDate = new Date(), headers = {}) => {
const service = 's3';
const region = 'us-east-1';

Expand All @@ -27,6 +27,7 @@ const sendRequest = async (method, host, path, body = '', config = null, signing
headers: {
Host: host, // Explicitly set Host: 127.0.0.1:8000
'X-Amz-Date': signingDate.toISOString().replace(/[:\-]|\.\d{3}/g, ''),
...headers,
},
});

Expand Down
Loading