Skip to content

Commit fd6298d

Browse files
author
Will Toozs
committed
CLDSRV-527: accomodate POST obj API call
- Use busboy to separate out form data fields - Handle POST Object separately from other POST API calls - Update the API call waterfall with this new logic
1 parent 65d46c6 commit fd6298d

File tree

1 file changed

+107
-35
lines changed

1 file changed

+107
-35
lines changed

lib/api/api.js

Lines changed: 107 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const objectGetRetention = require('./objectGetRetention');
5252
const objectGetTagging = require('./objectGetTagging');
5353
const objectHead = require('./objectHead');
5454
const objectPut = require('./objectPut');
55+
const objectPost = require('./objectPost');
5556
const objectPutACL = require('./objectPutACL');
5657
const objectPutLegalHold = require('./objectPutLegalHold');
5758
const objectPutTagging = require('./objectPutTagging');
@@ -68,6 +69,10 @@ const validateQueryAndHeaders = require('../utilities/validateQueryAndHeaders');
6869
const parseCopySource = require('./apiUtils/object/parseCopySource');
6970
const { tagConditionKeyAuth } = require('./apiUtils/authorization/tagConditionKeys');
7071
const checkHttpHeadersSize = require('./apiUtils/object/checkHttpHeadersSize');
72+
const { decryptToken } = require('./apiUtils/object/continueToken');
73+
const busboy = require('busboy');
74+
75+
7176

7277
const monitoringMap = policies.actionMaps.actionMonitoringMapS3;
7378

@@ -184,8 +189,98 @@ const api = {
184189
}
185190
return { returnTagCount, isImplicitDeny };
186191
}
192+
let bb;
193+
let fileEventData = null;
194+
195+
if (apiMethod === 'objectPost' && request.headers['content-type'].includes('multipart/form-data')) {
196+
bb = busboy({ headers: request.headers });
197+
}
187198

188199
return async.waterfall([
200+
next => {
201+
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
202+
return next(null);
203+
}
204+
if (apiMethod === 'objectPost' && request.headers['content-type'].includes('multipart/form-data')) {
205+
writeContinue(request, response);
206+
207+
let algoOK = false;
208+
let credOK = false;
209+
let dateOK = false;
210+
let sigOK = false;
211+
let policyOK = false;
212+
request.formData = {};
213+
bb.on('field', (fieldname, val) => {
214+
request.formData[fieldname] = val;
215+
if (request.formData.Policy) {
216+
request.formData.decryptedPolicy = JSON.parse(decryptToken(request.formData.Policy));
217+
}
218+
219+
// TODO - put content type field for file in request
220+
if (fieldname === 'X-Amz-Algorithm') {
221+
algoOK = true;
222+
}
223+
if (fieldname === 'X-Amz-Credential') {
224+
credOK = true;
225+
}
226+
if (fieldname === 'X-Amz-Date') {
227+
dateOK = true;
228+
}
229+
if (fieldname === 'X-Amz-Signature') {
230+
sigOK = true;
231+
}
232+
if (fieldname === 'Policy') {
233+
policyOK = true;
234+
}
235+
});
236+
237+
bb.on('file', (fieldname, file, filename, encoding, mimetype) => {
238+
fileEventData = { fieldname, file, filename, encoding, mimetype };
239+
if (algoOK && credOK && dateOK && sigOK && policyOK) {
240+
return next(null);
241+
}
242+
});
243+
244+
bb.on('finish', () => {
245+
// if authorization field is not found, return error
246+
if (!algoOK || !credOK || !dateOK || !sigOK || !policyOK) {
247+
return next(errors.InvalidRequest);
248+
}
249+
});
250+
request.pipe(bb);
251+
} else {
252+
// issue 100 Continue to the client
253+
writeContinue(request, response);
254+
const MAX_POST_LENGTH = request.method === 'POST' ?
255+
1024 * 1024 : 1024 * 1024 / 2; // 1 MB or 512 KB
256+
const post = [];
257+
let postLength = 0;
258+
request.on('data', chunk => {
259+
postLength += chunk.length;
260+
// Sanity check on post length
261+
if (postLength <= MAX_POST_LENGTH) {
262+
post.push(chunk);
263+
}
264+
});
265+
266+
request.on('error', err => {
267+
log.trace('error receiving request', {
268+
error: err,
269+
});
270+
return next(errors.InternalError);
271+
});
272+
273+
request.on('end', () => {
274+
if (postLength > MAX_POST_LENGTH) {
275+
log.error('body length is too long for request type',
276+
{ postLength });
277+
return next(errors.InvalidRequest);
278+
}
279+
return next(null);
280+
});
281+
}
282+
return undefined;
283+
},
189284
next => auth.server.doAuth(
190285
request, log, (err, userInfo, authorizationResults, streamingV4Params) => {
191286
if (err) {
@@ -200,41 +295,7 @@ const api = {
200295
authNames.userName = userInfo.getIAMdisplayName();
201296
}
202297
log.addDefaultFields(authNames);
203-
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
204-
return next(null, userInfo, authorizationResults, streamingV4Params);
205-
}
206-
// issue 100 Continue to the client
207-
writeContinue(request, response);
208-
const MAX_POST_LENGTH = request.method === 'POST' ?
209-
1024 * 1024 : 1024 * 1024 / 2; // 1 MB or 512 KB
210-
const post = [];
211-
let postLength = 0;
212-
request.on('data', chunk => {
213-
postLength += chunk.length;
214-
// Sanity check on post length
215-
if (postLength <= MAX_POST_LENGTH) {
216-
post.push(chunk);
217-
}
218-
});
219-
220-
request.on('error', err => {
221-
log.trace('error receiving request', {
222-
error: err,
223-
});
224-
return next(errors.InternalError);
225-
});
226-
227-
request.on('end', () => {
228-
if (postLength > MAX_POST_LENGTH) {
229-
log.error('body length is too long for request type',
230-
{ postLength });
231-
return next(errors.InvalidRequest);
232-
}
233-
// Convert array of post buffers into one string
234-
request.post = Buffer.concat(post, postLength).toString();
235-
return next(null, userInfo, authorizationResults, streamingV4Params);
236-
});
237-
return undefined;
298+
return next(null, userInfo, authorizationResults, streamingV4Params);
238299
},
239300
// Tag condition keys require information from CloudServer for evaluation
240301
(userInfo, authorizationResults, streamingV4Params, next) => tagConditionKeyAuth(
@@ -244,6 +305,10 @@ const api = {
244305
apiMethod,
245306
log,
246307
(err, authResultsWithTags) => {
308+
// TODO CLDSRV-527 remove ignore for POST object here
309+
if (apiMethod === 'objectPost') {
310+
return next(null, userInfo, authorizationResults, streamingV4Params);
311+
}
247312
if (err) {
248313
log.trace('tag authentication error', { error: err });
249314
return next(err);
@@ -271,6 +336,12 @@ const api = {
271336
return acc;
272337
}, {});
273338
}
339+
if (apiMethod === 'objectPost' && fileEventData) {
340+
request._response = response;
341+
request.file = fileEventData.file;
342+
return this[apiMethod](userInfo, request, streamingV4Params,
343+
log, callback, authorizationResults);
344+
}
274345
if (apiMethod === 'objectPut' || apiMethod === 'objectPutPart') {
275346
request._response = response;
276347
return this[apiMethod](userInfo, request, streamingV4Params,
@@ -337,6 +408,7 @@ const api = {
337408
objectCopy,
338409
objectHead,
339410
objectPut,
411+
objectPost,
340412
objectPutACL,
341413
objectPutLegalHold,
342414
objectPutTagging,

0 commit comments

Comments
 (0)