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, \ InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \
InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \ InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \
NoSuchBucket, BucketAlreadyOwnedByYou NoSuchBucket, BucketAlreadyOwnedByYou
from swift.common.middleware.s3api.exception import BadSwiftRequest
from swift.common.middleware.s3api.utils import unique_id, \ from swift.common.middleware.s3api.utils import unique_id, \
MULTIUPLOAD_SUFFIX, S3Timestamp, sysmeta_header MULTIUPLOAD_SUFFIX, S3Timestamp, sysmeta_header
from swift.common.middleware.s3api.etree import Element, SubElement, \ from swift.common.middleware.s3api.etree import Element, SubElement, \
@ -801,8 +800,8 @@ class UploadController(Controller):
status=body['Response Status'], status=body['Response Status'],
msg='\n'.join(': '.join(err) msg='\n'.join(': '.join(err)
for err in body['Errors'])) for err in body['Errors']))
except BadSwiftRequest as e: except InvalidRequest as err_resp:
msg = str(e) msg = err_resp._msg
if too_small_message in msg: if too_small_message in msg:
raise EntityTooSmall(msg) raise EntityTooSmall(msg)
elif ', Etag Mismatch' in msg: elif ', Etag Mismatch' in msg:

View File

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

View File

@ -57,8 +57,7 @@ from swift.common.middleware.s3api.s3response import AccessDenied, \
MalformedXML, InvalidRequest, RequestTimeout, InvalidBucketName, \ MalformedXML, InvalidRequest, RequestTimeout, InvalidBucketName, \
BadDigest, AuthorizationHeaderMalformed, SlowDown, \ BadDigest, AuthorizationHeaderMalformed, SlowDown, \
AuthorizationQueryParametersError, ServiceUnavailable, BrokenMPU AuthorizationQueryParametersError, ServiceUnavailable, BrokenMPU
from swift.common.middleware.s3api.exception import NotS3Request, \ from swift.common.middleware.s3api.exception import NotS3Request
BadSwiftRequest
from swift.common.middleware.s3api.utils import utf8encode, \ from swift.common.middleware.s3api.utils import utf8encode, \
S3Timestamp, mktime, MULTIUPLOAD_SUFFIX S3Timestamp, mktime, MULTIUPLOAD_SUFFIX
from swift.common.middleware.s3api.subresource import decode_acl, encode_acl 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): bucket, self.conf.dns_compliant_bucket_names):
# Ignore GET service case # Ignore GET service case
raise InvalidBucketName(bucket) raise InvalidBucketName(bucket)
return (bucket, obj) return bucket, obj
def _parse_query_authentication(self): def _parse_query_authentication(self):
""" """
@ -779,7 +778,6 @@ class S3Request(swob.Request):
raise InvalidArgument('x-amz-copy-source', raise InvalidArgument('x-amz-copy-source',
self.headers['X-Amz-Copy-Source'], self.headers['X-Amz-Copy-Source'],
msg) msg)
if 'x-amz-metadata-directive' in self.headers: if 'x-amz-metadata-directive' in self.headers:
value = self.headers['x-amz-metadata-directive'] value = self.headers['x-amz-metadata-directive']
if value not in ('COPY', 'REPLACE'): if value not in ('COPY', 'REPLACE'):
@ -1399,7 +1397,17 @@ class S3Request(swob.Request):
raise err_resp() raise err_resp()
if status == HTTP_BAD_REQUEST: 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: if status == HTTP_UNAUTHORIZED:
raise SignatureDoesNotMatch( raise SignatureDoesNotMatch(
**self.signature_does_not_match_kwargs()) **self.signature_does_not_match_kwargs())

View File

@ -29,6 +29,7 @@ from time import mktime
import six import six
import test.functional as tf import test.functional as tf
from swift.common import utils
from swift.common.middleware.s3api.etree import fromstring from swift.common.middleware.s3api.etree import fromstring
from swift.common.middleware.s3api.utils import S3Timestamp 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 import S3ApiBase
from test.functional.s3api.s3_test_client import Connection 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) DAY = 86400.0 # 60 * 60 * 24 (sec)
@ -417,6 +419,53 @@ class TestS3ApiObject(S3ApiBase):
self.assertCommonResponseHeaders(headers) self.assertCommonResponseHeaders(headers)
self._assertObjectEtag(self.bucket, obj, etag) 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): def test_put_object_copy_source_params(self):
obj = 'object' obj = 'object'
src_headers = {'X-Amz-Meta-Test': 'src'} src_headers = {'X-Amz-Meta-Test': 'src'}