Compare commits
4 Commits
developmen
...
refactor/r
Author | SHA1 | Date |
---|---|---|
Bennett Buchanan | 1ab37bb05c | |
Bennett Buchanan | dc6b9c712f | |
Bennett Buchanan | 822ae24ed2 | |
Bennett Buchanan | 8b5dcf2d7c |
|
@ -0,0 +1,74 @@
|
|||
const { responseNoBody, parseContentMD5, responseXMLBody } =
|
||||
require('../routesUtils');
|
||||
const errors = require('../../errors');
|
||||
|
||||
class AuthController {
|
||||
static bucketPut(req, res, log) {
|
||||
// content-length for object is handled separately below
|
||||
const contentLength = req.headers['content-length'];
|
||||
if ((contentLength && (isNaN(contentLength) || contentLength < 0)) ||
|
||||
contentLength === '') {
|
||||
log.debug('invalid content-length header');
|
||||
return responseNoBody(errors.BadRequest, null, res, null, log);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
static objectPutAction(req, res, log) {
|
||||
// PUT object, PUT object ACL, PUT object multipart or
|
||||
// PUT object copy
|
||||
// if content-md5 is not present in the headers, try to
|
||||
// parse content-md5 from meta headers
|
||||
|
||||
if (req.headers['content-md5'] === '') {
|
||||
log.debug('empty content-md5 header', {
|
||||
method: 'routePUT',
|
||||
});
|
||||
return responseNoBody(errors.InvalidDigest, null, res, 200, log);
|
||||
}
|
||||
if (req.headers['content-md5']) {
|
||||
req.contentMD5 = req.headers['content-md5'];
|
||||
} else {
|
||||
req.contentMD5 = parseContentMD5(req.headers);
|
||||
}
|
||||
if (req.contentMD5 && req.contentMD5.length !== 32) {
|
||||
req.contentMD5 = Buffer.from(req.contentMD5, 'base64')
|
||||
.toString('hex');
|
||||
if (req.contentMD5 && req.contentMD5.length !== 32) {
|
||||
log.debug('invalid md5 digest', { contentMD5: req.contentMD5 });
|
||||
return responseNoBody(errors.InvalidDigest, null, res, 200,
|
||||
log);
|
||||
}
|
||||
}
|
||||
const encryptionHeaders = [
|
||||
'x-amz-server-side-encryption',
|
||||
'x-amz-server-side-encryption-customer-algorithm',
|
||||
'x-amz-server-side-encryption-aws-kms-key-id',
|
||||
'x-amz-server-side-encryption-context',
|
||||
'x-amz-server-side-encryption-customer-key',
|
||||
'x-amz-server-side-encryption-customer-key-md5',
|
||||
];
|
||||
// object level encryption
|
||||
if (encryptionHeaders.some(i => req.headers[i] !== undefined)) {
|
||||
return responseXMLBody(errors.NotImplemented, null, res, log);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
static objectPut(req, res, log) {
|
||||
if (req.headers['content-length'] === undefined &&
|
||||
req.headers['x-amz-decoded-content-length'] === undefined) {
|
||||
return responseNoBody(errors.MissingContentLength, null, res, 411,
|
||||
log);
|
||||
}
|
||||
if (Number.isNaN(req.parsedContentLength) ||
|
||||
req.parsedContentLength < 0) {
|
||||
return responseNoBody(errors.BadRequest, null, res, 400, log);
|
||||
}
|
||||
log.end().addDefaultFields({ contentLength: req.parsedContentLength });
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthController;
|
|
@ -0,0 +1,50 @@
|
|||
const { statsReport500, responseNoBody } = require('../routesUtils');
|
||||
|
||||
class BucketController {
|
||||
static bucketPutACL(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static bucketPutVersioning(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static bucketPutWebsite(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static bucketPutCors(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static bucketPutReplication(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static bucketPut(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, corsHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
const location = { Location: `/${req.bucketName}` };
|
||||
const resHeaders = corsHeaders ?
|
||||
Object.assign({}, location, corsHeaders) : location;
|
||||
return responseNoBody(err, resHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BucketController;
|
|
@ -0,0 +1,57 @@
|
|||
const { statsReport500, responseNoBody, responseXMLBody } =
|
||||
require('../routesUtils');
|
||||
|
||||
class ObjectController {
|
||||
static objectPutCopyPart(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log,
|
||||
(err, xml, additionalHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseXMLBody(err, xml, res, log, additionalHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
static objectPutPart(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log,
|
||||
(err, calculatedHash, corsHeaders) => {
|
||||
if (err) {
|
||||
return responseNoBody(err, corsHeaders, res, 200, log);
|
||||
}
|
||||
const resMetaHeaders = corsHeaders || {};
|
||||
// ETag's hex should always be enclosed in quotes
|
||||
resMetaHeaders.ETag = `"${calculatedHash}"`;
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, resMetaHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static objectPutACL(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, resHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, resHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static objectPutTagging(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, resHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, resHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
|
||||
static objectCopy(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log,
|
||||
(err, xml, additionalHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
responseXMLBody(err, xml, res, log, additionalHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
static objectPut(action, req, res, api, log, statsClient) {
|
||||
return api.callApiMethod(action, req, res, log, (err, resHeaders) => {
|
||||
statsReport500(err, statsClient);
|
||||
return responseNoBody(err, resHeaders, res, 200, log);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ObjectController;
|
|
@ -9,6 +9,8 @@ const routePOST = require('./routes/routePOST');
|
|||
const routeOPTIONS = require('./routes/routeOPTIONS');
|
||||
const routesUtils = require('./routesUtils');
|
||||
const routeWebsite = require('./routes/routeWebsite');
|
||||
const routesObjects = require('./routes/routesObjects.json');
|
||||
const Router = require('./routes/Router');
|
||||
|
||||
const routeMap = {
|
||||
GET: routeGET,
|
||||
|
@ -212,6 +214,10 @@ function routes(req, res, params, logger) {
|
|||
return routeWebsite(req, res, api, log, statsClient, dataRetrievalFn);
|
||||
}
|
||||
|
||||
if (reqMethod === 'PUT') {
|
||||
return new Router(routesObjects).exec(req, res, api, log, statsClient);
|
||||
}
|
||||
|
||||
return method(req, res, api, log, statsClient, dataRetrievalFn);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
const BucketController = require('../controller/BucketController');
|
||||
const ObjectController = require('../controller/ObjectController');
|
||||
const AuthController = require('../controller/AuthController');
|
||||
|
||||
class Router {
|
||||
constructor(routes) {
|
||||
this._routes = routes;
|
||||
this._controllers = { BucketController, ObjectController };
|
||||
return this;
|
||||
}
|
||||
|
||||
_matchRoute(route, req) {
|
||||
const query = route.query || [];
|
||||
const headers = route.headers || [];
|
||||
return req.method === route.method &&
|
||||
Boolean(req.bucketName) === Boolean(route.bucket) &&
|
||||
Boolean(req.objectKey) === Boolean(route.object) &&
|
||||
query.every(q => q in req.query) &&
|
||||
headers.every(h => h in req.headers);
|
||||
}
|
||||
|
||||
exec(req, res, api, log, statsClient) {
|
||||
const route = this._routes.find(r => this._matchRoute(r, req));
|
||||
const { controller, action, auth } = route;
|
||||
for (let i = 0; i < auth.length; i++) {
|
||||
const authMethod = auth[i];
|
||||
const authResponse = AuthController[authMethod](req, res, log);
|
||||
if (authResponse !== undefined) {
|
||||
return authResponse;
|
||||
}
|
||||
}
|
||||
const routeMethod = this._controllers[controller][action];
|
||||
return routeMethod(action, req, res, api, log, statsClient);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Router;
|
|
@ -1,181 +1,7 @@
|
|||
const errors = require('../../errors');
|
||||
const routesUtils = require('../routesUtils');
|
||||
|
||||
const encryptionHeaders = [
|
||||
'x-amz-server-side-encryption',
|
||||
'x-amz-server-side-encryption-customer-algorithm',
|
||||
'x-amz-server-side-encryption-aws-kms-key-id',
|
||||
'x-amz-server-side-encryption-context',
|
||||
'x-amz-server-side-encryption-customer-key',
|
||||
'x-amz-server-side-encryption-customer-key-md5',
|
||||
];
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
function routePUT(request, response, api, log, statsClient) {
|
||||
function routePUT(request, response, api, log) {
|
||||
log.debug('routing request', { method: 'routePUT' });
|
||||
|
||||
if (request.objectKey === undefined) {
|
||||
// PUT bucket - PUT bucket ACL
|
||||
|
||||
// content-length for object is handled separately below
|
||||
const contentLength = request.headers['content-length'];
|
||||
if ((contentLength && (isNaN(contentLength) || contentLength < 0)) ||
|
||||
contentLength === '') {
|
||||
log.debug('invalid content-length header');
|
||||
return routesUtils.responseNoBody(
|
||||
errors.BadRequest, null, response, null, log);
|
||||
}
|
||||
|
||||
// PUT bucket ACL
|
||||
if (request.query.acl !== undefined) {
|
||||
api.callApiMethod('bucketPutACL', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, corsHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
} else if (request.query.versioning !== undefined) {
|
||||
api.callApiMethod('bucketPutVersioning', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
routesUtils.responseNoBody(err, corsHeaders, response, 200,
|
||||
log);
|
||||
});
|
||||
} else if (request.query.website !== undefined) {
|
||||
api.callApiMethod('bucketPutWebsite', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, corsHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
} else if (request.query.cors !== undefined) {
|
||||
api.callApiMethod('bucketPutCors', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, corsHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
} else if (request.query.replication !== undefined) {
|
||||
api.callApiMethod('bucketPutReplication', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
routesUtils.responseNoBody(err, corsHeaders, response, 200,
|
||||
log);
|
||||
});
|
||||
} else {
|
||||
// PUT bucket
|
||||
return api.callApiMethod('bucketPut', request, response, log,
|
||||
(err, corsHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
const location = { Location: `/${request.bucketName}` };
|
||||
const resHeaders = corsHeaders ?
|
||||
Object.assign({}, location, corsHeaders) : location;
|
||||
return routesUtils.responseNoBody(err, resHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// PUT object, PUT object ACL, PUT object multipart or
|
||||
// PUT object copy
|
||||
// if content-md5 is not present in the headers, try to
|
||||
// parse content-md5 from meta headers
|
||||
|
||||
if (request.headers['content-md5'] === '') {
|
||||
log.debug('empty content-md5 header', {
|
||||
method: 'routePUT',
|
||||
});
|
||||
return routesUtils
|
||||
.responseNoBody(errors.InvalidDigest, null, response, 200, log);
|
||||
}
|
||||
if (request.headers['content-md5']) {
|
||||
request.contentMD5 = request.headers['content-md5'];
|
||||
} else {
|
||||
request.contentMD5 = routesUtils.parseContentMD5(request.headers);
|
||||
}
|
||||
if (request.contentMD5 && request.contentMD5.length !== 32) {
|
||||
request.contentMD5 = Buffer.from(request.contentMD5, 'base64')
|
||||
.toString('hex');
|
||||
if (request.contentMD5 && request.contentMD5.length !== 32) {
|
||||
log.debug('invalid md5 digest', {
|
||||
contentMD5: request.contentMD5,
|
||||
});
|
||||
return routesUtils
|
||||
.responseNoBody(errors.InvalidDigest, null, response, 200,
|
||||
log);
|
||||
}
|
||||
}
|
||||
// object level encryption
|
||||
if (encryptionHeaders.some(i => request.headers[i] !== undefined)) {
|
||||
return routesUtils.responseXMLBody(errors.NotImplemented, null,
|
||||
response, log);
|
||||
}
|
||||
if (request.query.partNumber) {
|
||||
if (request.headers['x-amz-copy-source']) {
|
||||
api.callApiMethod('objectPutCopyPart', request, response, log,
|
||||
(err, xml, additionalHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseXMLBody(err, xml, response, log,
|
||||
additionalHeaders);
|
||||
});
|
||||
} else {
|
||||
api.callApiMethod('objectPutPart', request, response, log,
|
||||
(err, calculatedHash, corsHeaders) => {
|
||||
if (err) {
|
||||
return routesUtils.responseNoBody(err, corsHeaders,
|
||||
response, 200, log);
|
||||
}
|
||||
// ETag's hex should always be enclosed in quotes
|
||||
const resMetaHeaders = corsHeaders || {};
|
||||
resMetaHeaders.ETag = `"${calculatedHash}"`;
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, resMetaHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
}
|
||||
} else if (request.query.acl !== undefined) {
|
||||
api.callApiMethod('objectPutACL', request, response, log,
|
||||
(err, resHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, resHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
} else if (request.query.tagging !== undefined) {
|
||||
api.callApiMethod('objectPutTagging', request, response, log,
|
||||
(err, resHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, resHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
} else if (request.headers['x-amz-copy-source']) {
|
||||
return api.callApiMethod('objectCopy', request, response, log,
|
||||
(err, xml, additionalHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
routesUtils.responseXMLBody(err, xml, response, log,
|
||||
additionalHeaders);
|
||||
});
|
||||
} else {
|
||||
if (request.headers['content-length'] === undefined &&
|
||||
request.headers['x-amz-decoded-content-length'] === undefined) {
|
||||
return routesUtils.responseNoBody(errors.MissingContentLength,
|
||||
null, response, 411, log);
|
||||
}
|
||||
if (Number.isNaN(request.parsedContentLength) ||
|
||||
request.parsedContentLength < 0) {
|
||||
return routesUtils.responseNoBody(errors.BadRequest,
|
||||
null, response, 400, log);
|
||||
}
|
||||
log.end().addDefaultFields({
|
||||
contentLength: request.parsedContentLength,
|
||||
});
|
||||
|
||||
api.callApiMethod('objectPut', request, response, log,
|
||||
(err, resHeaders) => {
|
||||
routesUtils.statsReport500(err, statsClient);
|
||||
return routesUtils.responseNoBody(err, resHeaders,
|
||||
response, 200, log);
|
||||
});
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/* eslint-enable no-param-reassign */
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
[
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPutACL",
|
||||
"query": [ "acl" ],
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPutVersioning",
|
||||
"query": [ "versioning" ],
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPutWebsite",
|
||||
"query": [ "website" ],
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPutCors",
|
||||
"query": [ "cors" ],
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPutReplication",
|
||||
"query": [ "replication" ],
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"method": "PUT",
|
||||
"controller": "BucketController",
|
||||
"action": "bucketPut",
|
||||
"auth": [ "bucketPut" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectPutCopyPart",
|
||||
"headers": [ "x-amz-copy-source" ],
|
||||
"query": [ "partNumber" ],
|
||||
"auth": [ "objectPutAction" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectPutPart",
|
||||
"query": [ "partNumber" ],
|
||||
"auth": [ "objectPutAction" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectPutACL",
|
||||
"query": [ "acl" ],
|
||||
"auth": [ "objectPutAction" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectPutTagging",
|
||||
"query": [ "tagging" ],
|
||||
"auth": [ "objectPutAction" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectCopy",
|
||||
"headers": [ "x-amz-copy-source" ],
|
||||
"auth": [ "objectPutAction" ]
|
||||
},
|
||||
{
|
||||
"bucket": true,
|
||||
"object": true,
|
||||
"method": "PUT",
|
||||
"controller": "ObjectController",
|
||||
"action": "objectPut",
|
||||
"auth": [ "objectPutAction", "objectPut" ]
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue