From ca0c34445630e3c3e1df191490b987aa15f8b82a Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 9 Jun 2021 12:40:14 -0700 Subject: [PATCH] s3api: Return KeyTooLongError when upload name exceeds constraints Change-Id: I8ce73e2e21e9216484130ba3bd1e77b45eb1d77c --- swift/common/middleware/s3api/controllers/multi_upload.py | 8 +++++++- swift/common/middleware/s3api/controllers/obj.py | 5 ++++- swift/common/middleware/s3api/s3response.py | 2 +- test/functional/s3api/test_multi_upload.py | 6 ++++++ test/functional/s3api/test_object.py | 7 +++++++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/swift/common/middleware/s3api/controllers/multi_upload.py b/swift/common/middleware/s3api/controllers/multi_upload.py index 621f97c163..cdbb4e84fb 100644 --- a/swift/common/middleware/s3api/controllers/multi_upload.py +++ b/swift/common/middleware/s3api/controllers/multi_upload.py @@ -67,6 +67,7 @@ import time import six +from swift.common import constraints from swift.common.swob import Range, bytes_to_wsgi, normalize_etag, wsgi_to_str from swift.common.utils import json, public, reiterate, md5 from swift.common.db import utf8encode @@ -77,7 +78,7 @@ from six.moves.urllib.parse import quote, urlparse from swift.common.middleware.s3api.controllers.base import Controller, \ bucket_operation, object_operation, check_container_existence from swift.common.middleware.s3api.s3response import InvalidArgument, \ - ErrorResponse, MalformedXML, BadDigest, \ + ErrorResponse, MalformedXML, BadDigest, KeyTooLongError, \ InvalidPart, BucketAlreadyExists, EntityTooSmall, InvalidPartOrder, \ InvalidRequest, HTTPOk, HTTPNoContent, NoSuchKey, NoSuchUpload, \ NoSuchBucket, BucketAlreadyOwnedByYou @@ -403,6 +404,11 @@ class UploadsController(Controller): """ Handles Initiate Multipart Upload. """ + if len(req.object_name) > constraints.MAX_OBJECT_NAME_LENGTH: + # Note that we can still run into trouble where the MPU is just + # within the limit, which means the segment names will go over + raise KeyTooLongError() + # Create a unique S3 upload id from UUID to avoid duplicates. upload_id = unique_id() diff --git a/swift/common/middleware/s3api/controllers/obj.py b/swift/common/middleware/s3api/controllers/obj.py index 716c837b69..cae97b5ae2 100644 --- a/swift/common/middleware/s3api/controllers/obj.py +++ b/swift/common/middleware/s3api/controllers/obj.py @@ -15,6 +15,7 @@ import json +from swift.common import constraints from swift.common.http import HTTP_OK, HTTP_PARTIAL_CONTENT, HTTP_NO_CONTENT from swift.common.request_helpers import update_etag_is_at_header from swift.common.swob import Range, content_range_header_value, \ @@ -27,7 +28,7 @@ from swift.common.middleware.s3api.utils import S3Timestamp, sysmeta_header from swift.common.middleware.s3api.controllers.base import Controller from swift.common.middleware.s3api.s3response import S3NotImplemented, \ InvalidRange, NoSuchKey, NoSuchVersion, InvalidArgument, HTTPNoContent, \ - PreconditionFailed + PreconditionFailed, KeyTooLongError class ObjectController(Controller): @@ -138,6 +139,8 @@ class ObjectController(Controller): """ Handle PUT Object and PUT Object (Copy) request """ + if len(req.object_name) > constraints.MAX_OBJECT_NAME_LENGTH: + raise KeyTooLongError() # set X-Timestamp by s3api to use at copy resp body req_timestamp = S3Timestamp.now() req.headers['X-Timestamp'] = req_timestamp.internal diff --git a/swift/common/middleware/s3api/s3response.py b/swift/common/middleware/s3api/s3response.py index 3ba4018a65..e9e991cdb7 100644 --- a/swift/common/middleware/s3api/s3response.py +++ b/swift/common/middleware/s3api/s3response.py @@ -503,7 +503,7 @@ class InvalidURI(ErrorResponse): ErrorResponse.__init__(self, msg, uri=uri, *args, **kwargs) -class KeyTooLong(ErrorResponse): +class KeyTooLongError(ErrorResponse): _status = '400 Bad Request' _msg = 'Your key is too long.' diff --git a/test/functional/s3api/test_multi_upload.py b/test/functional/s3api/test_multi_upload.py index 3319e8b1ac..cc948cdd4c 100644 --- a/test/functional/s3api/test_multi_upload.py +++ b/test/functional/s3api/test_multi_upload.py @@ -475,6 +475,12 @@ class TestS3ApiMultiUpload(S3ApiBase): self.conn.make_request('POST', 'nothing', key, query=query) self.assertEqual(get_error_code(body), 'NoSuchBucket') + status, resp_headers, body = self.conn.make_request( + 'POST', bucket, + 'x' * (tf.cluster_info['swift']['max_object_name_length'] + 1), + query=query) + self.assertEqual(get_error_code(body), 'KeyTooLongError') + def test_list_multi_uploads_error(self): bucket = 'bucket' self.conn.make_request('PUT', bucket) diff --git a/test/functional/s3api/test_object.py b/test/functional/s3api/test_object.py index 6e09c3af52..f5e78b0785 100644 --- a/test/functional/s3api/test_object.py +++ b/test/functional/s3api/test_object.py @@ -158,6 +158,13 @@ class TestS3ApiObject(S3ApiBase): self.assertEqual(get_error_code(body), 'NoSuchBucket') self.assertEqual(headers['content-type'], 'application/xml') + def test_put_object_name_too_long(self): + status, headers, body = self.conn.make_request( + 'PUT', self.bucket, + 'x' * (tf.cluster_info['swift']['max_object_name_length'] + 1)) + self.assertEqual(get_error_code(body), 'KeyTooLongError') + self.assertEqual(headers['content-type'], 'application/xml') + def test_put_object_copy_error(self): obj = 'object' self.conn.make_request('PUT', self.bucket, obj)