diff --git a/swift/common/middleware/s3api/controllers/multi_upload.py b/swift/common/middleware/s3api/controllers/multi_upload.py index fc7615e62b..7b784b12da 100644 --- a/swift/common/middleware/s3api/controllers/multi_upload.py +++ b/swift/common/middleware/s3api/controllers/multi_upload.py @@ -83,7 +83,6 @@ from swift.common.middleware.s3api.s3response import InvalidArgument, \ InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \ InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \ NoSuchBucket, BucketAlreadyOwnedByYou -from swift.common.middleware.s3api.exception import BadSwiftRequest from swift.common.middleware.s3api.utils import unique_id, \ MULTIUPLOAD_SUFFIX, S3Timestamp, sysmeta_header from swift.common.middleware.s3api.etree import Element, SubElement, \ @@ -801,8 +800,8 @@ class UploadController(Controller): status=body['Response Status'], msg='\n'.join(': '.join(err) for err in body['Errors'])) - except BadSwiftRequest as e: - msg = str(e) + except InvalidRequest as err_resp: + msg = err_resp._msg if too_small_message in msg: raise EntityTooSmall(msg) elif ', Etag Mismatch' in msg: diff --git a/swift/common/middleware/s3api/exception.py b/swift/common/middleware/s3api/exception.py index 0e060395dd..d7433c5175 100644 --- a/swift/common/middleware/s3api/exception.py +++ b/swift/common/middleware/s3api/exception.py @@ -22,10 +22,6 @@ class NotS3Request(S3Exception): pass -class BadSwiftRequest(S3Exception): - pass - - class ACLError(S3Exception): pass diff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py index 6f8963b5f9..19a8304e11 100644 --- a/swift/common/middleware/s3api/s3request.py +++ b/swift/common/middleware/s3api/s3request.py @@ -57,8 +57,7 @@ from swift.common.middleware.s3api.s3response import AccessDenied, \ MalformedXML, InvalidRequest, RequestTimeout, InvalidBucketName, \ BadDigest, AuthorizationHeaderMalformed, SlowDown, \ AuthorizationQueryParametersError, ServiceUnavailable, BrokenMPU -from swift.common.middleware.s3api.exception import NotS3Request, \ - BadSwiftRequest +from swift.common.middleware.s3api.exception import NotS3Request from swift.common.middleware.s3api.utils import utf8encode, \ S3Timestamp, mktime, MULTIUPLOAD_SUFFIX from swift.common.middleware.s3api.subresource import decode_acl, encode_acl @@ -642,7 +641,7 @@ class S3Request(swob.Request): bucket, self.conf.dns_compliant_bucket_names): # Ignore GET service case raise InvalidBucketName(bucket) - return (bucket, obj) + return bucket, obj def _parse_query_authentication(self): """ @@ -779,7 +778,6 @@ class S3Request(swob.Request): raise InvalidArgument('x-amz-copy-source', self.headers['X-Amz-Copy-Source'], msg) - if 'x-amz-metadata-directive' in self.headers: value = self.headers['x-amz-metadata-directive'] if value not in ('COPY', 'REPLACE'): @@ -1399,7 +1397,17 @@ class S3Request(swob.Request): raise err_resp() if status == HTTP_BAD_REQUEST: - raise BadSwiftRequest(err_msg.decode('utf8')) + err_str = err_msg.decode('utf8') + if 'X-Delete-At' in err_str: + raise InvalidArgument('X-Delete-At', + self.headers['X-Delete-At'], + err_str) + if 'X-Delete-After' in err_msg.decode('utf8'): + raise InvalidArgument('X-Delete-After', + self.headers['X-Delete-After'], + err_str) + else: + raise InvalidRequest(msg=err_str) if status == HTTP_UNAUTHORIZED: raise SignatureDoesNotMatch( **self.signature_does_not_match_kwargs()) diff --git a/test/functional/s3api/test_object.py b/test/functional/s3api/test_object.py index bbca440684..ca4e692dbd 100644 --- a/test/functional/s3api/test_object.py +++ b/test/functional/s3api/test_object.py @@ -29,6 +29,7 @@ from time import mktime import six import test.functional as tf +from swift.common import utils from swift.common.middleware.s3api.etree import fromstring from swift.common.middleware.s3api.utils import S3Timestamp @@ -36,7 +37,8 @@ from swift.common.utils import md5, quote from test.functional.s3api import S3ApiBase from test.functional.s3api.s3_test_client import Connection -from test.functional.s3api.utils import get_error_code, calculate_md5 +from test.functional.s3api.utils import get_error_code, calculate_md5, \ + get_error_msg DAY = 86400.0 # 60 * 60 * 24 (sec) @@ -417,6 +419,53 @@ class TestS3ApiObject(S3ApiBase): self.assertCommonResponseHeaders(headers) self._assertObjectEtag(self.bucket, obj, etag) + def test_put_object_valid_delete_headers(self): + obj = 'object' + content = b'abcdefghij' + ts = utils.Timestamp.now() + delete_at = {'X-Delete-At': str(int(ts) + 70)} + delete_after = {'X-Delete-After': str(int(ts) + 130)} + status, delete_at, body = \ + self.conn.make_request('PUT', self.bucket, obj, delete_at, content) + self.assertEqual(status, 200) + status, delete_after, body = \ + self.conn.make_request('PUT', self.bucket, obj, delete_after, + content) + self.assertEqual(status, 200) + + def test_put_object_invalid_x_delete_at(self): + obj = 'object' + content = b'abcdefghij' + ts = utils.Timestamp.now() + headers = {'X-Delete-At': str(int(ts) - 140)} + status, headers, body = \ + self.conn.make_request('PUT', self.bucket, obj, headers, content) + self.assertEqual(status, 400) + self.assertEqual(get_error_code(body), 'InvalidArgument') + self.assertEqual(get_error_msg(body), 'X-Delete-At in past') + headers = {'X-Delete-At': 'test'} + status, headers, body = \ + self.conn.make_request('PUT', self.bucket, obj, headers, content) + self.assertEqual(status, 400) + self.assertEqual(get_error_code(body), 'InvalidArgument') + self.assertEqual(get_error_msg(body), 'Non-integer X-Delete-At') + + def test_put_object_invalid_x_delete_after(self): + obj = 'object' + content = b'abcdefghij' + headers = {'X-Delete-After': 'test'} + status, headers, body = \ + self.conn.make_request('PUT', self.bucket, obj, headers, content) + self.assertEqual(status, 400) + self.assertEqual(get_error_code(body), 'InvalidArgument') + self.assertEqual(get_error_msg(body), 'Non-integer X-Delete-After') + headers = {'X-Delete-After': '-140'} + status, headers, body = \ + self.conn.make_request('PUT', self.bucket, obj, headers, content) + self.assertEqual(status, 400) + self.assertEqual(get_error_code(body), 'InvalidArgument') + self.assertEqual(get_error_msg(body), 'X-Delete-After in past') + def test_put_object_copy_source_params(self): obj = 'object' src_headers = {'X-Amz-Meta-Test': 'src'}