s3api errors for unsupported headers x-delete-at, x-delete-after

We need to support the aforementioned headers in our s3 apis
and raise an InvalidArgumentError if a s3 client makes a request

Change-Id: I2c5b18e52da7f33b31ba386cdbd042f90b69ef97
This commit is contained in:
indianwhocodes 2022-11-16 12:03:07 -08:00 committed by Tim Burke
parent 38124221d7
commit d363236a24
4 changed files with 65 additions and 13 deletions

View File

@ -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:

View File

@ -22,10 +22,6 @@ class NotS3Request(S3Exception):
pass
class BadSwiftRequest(S3Exception):
pass
class ACLError(S3Exception):
pass

View File

@ -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())

View File

@ -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'}