From 2e001431fd0907c97d44b81dd1f3d2d14ae1208b Mon Sep 17 00:00:00 2001 From: Clay Gerrard Date: Mon, 15 Jun 2020 16:19:47 -0500 Subject: [PATCH] s3api: Don't do naive HEAD request for auth Change-Id: If0fc8ec4d8056afb741bf74b82598a26683dfcd7 --- swift/common/middleware/s3api/s3request.py | 42 +++--- test/unit/common/middleware/s3api/__init__.py | 12 ++ .../middleware/s3api/test_multi_upload.py | 66 +++++++-- test/unit/common/middleware/s3api/test_obj.py | 125 +++++++++--------- .../common/middleware/s3api/test_s3api.py | 10 +- .../common/middleware/s3api/test_s3request.py | 4 +- 6 files changed, 164 insertions(+), 95 deletions(-) diff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py index 7e80547dc7..a45009c2ef 100644 --- a/swift/common/middleware/s3api/s3request.py +++ b/swift/common/middleware/s3api/s3request.py @@ -37,8 +37,7 @@ from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \ HTTP_TOO_MANY_REQUESTS, HTTP_RATE_LIMITED, is_success from swift.common.constraints import check_utf8 -from swift.proxy.controllers.base import get_container_info, \ - headers_to_container_info +from swift.proxy.controllers.base import get_container_info from swift.common.request_helpers import check_path_header from swift.common.middleware.s3api.controllers import ServiceController, \ @@ -1439,25 +1438,28 @@ class S3Request(swob.Request): :raises: NoSuchBucket when the container doesn't exist :raises: InternalError when the request failed without 404 """ - if self.is_authenticated: - # if we have already authenticated, yes we can use the account - # name like as AUTH_xxx for performance efficiency - sw_req = self.to_swift_req(app, self.container_name, None) - info = get_container_info(sw_req.environ, app, swift_source='S3') - if is_success(info['status']): - return info - elif info['status'] == 404: - raise NoSuchBucket(self.container_name) - else: - raise InternalError( - 'unexpected status code %d' % info['status']) + if not self.is_authenticated: + sw_req = self.to_swift_req('TEST', None, None, body='') + # don't show log message of this request + sw_req.environ['swift.proxy_access_log_made'] = True + + sw_resp = sw_req.get_response(app) + + if not sw_req.remote_user: + raise SignatureDoesNotMatch( + **self.signature_does_not_match_kwargs()) + + _, self.account, _ = split_path(sw_resp.environ['PATH_INFO'], + 2, 3, True) + sw_req = self.to_swift_req(app, self.container_name, None) + info = get_container_info(sw_req.environ, app, swift_source='S3') + if is_success(info['status']): + return info + elif info['status'] == 404: + raise NoSuchBucket(self.container_name) else: - # otherwise we do naive HEAD request with the authentication - resp = self.get_response(app, 'HEAD', self.container_name, '') - headers = resp.sw_headers.copy() - headers.update(resp.sysmeta_headers) - return headers_to_container_info( - headers, resp.status_int) # pylint: disable-msg=E1101 + raise InternalError( + 'unexpected status code %d' % info['status']) def gen_multipart_manifest_delete_query(self, app, obj=None, version=None): if not self.allow_multipart_uploads: diff --git a/test/unit/common/middleware/s3api/__init__.py b/test/unit/common/middleware/s3api/__init__.py index 551a358f42..007dda7dfd 100644 --- a/test/unit/common/middleware/s3api/__init__.py +++ b/test/unit/common/middleware/s3api/__init__.py @@ -50,6 +50,18 @@ class FakeApp(object): if 's3api.auth_details' in env: self._update_s3_path_info(env) + if env['REQUEST_METHOD'] == 'TEST': + + def authorize_cb(req): + # Assume swift owner, if not yet set + req.environ.setdefault('REMOTE_USER', 'authorized') + req.environ.setdefault('swift_owner', True) + # But then default to blocking authz, to ensure we've replaced + # the default auth system + return swob.HTTPForbidden(request=req) + + env['swift.authorize'] = authorize_cb + return self.swift(env, start_response) diff --git a/test/unit/common/middleware/s3api/test_multi_upload.py b/test/unit/common/middleware/s3api/test_multi_upload.py index 16aeb4126a..3a0dc4c1eb 100644 --- a/test/unit/common/middleware/s3api/test_multi_upload.py +++ b/test/unit/common/middleware/s3api/test_multi_upload.py @@ -137,6 +137,22 @@ class TestS3ApiMultiUpload(S3ApiTestCase): 'Date': self.get_date_header()}) status, headers, body = self.call_s3api(req) self.assertEqual(self._get_error_code(body), 'InvalidRequest') + self.assertEqual([], self.swift.calls) + + def test_bucket_upload_part_success(self): + req = Request.blank('/bucket/object?partNumber=1&uploadId=X', + method='PUT', + headers={'Authorization': 'AWS test:tester:hmac', + 'Date': self.get_date_header()}) + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', + lambda env, app, swift_source: {'status': 204}): + status, headers, body = self.call_s3api(req) + self.assertEqual(status, '200 OK') + self.assertEqual([ + ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), + ('PUT', '/v1/AUTH_test/bucket+segments/object/X/1'), + ], self.swift.calls) @s3acl def test_object_multipart_uploads_list(self): @@ -577,12 +593,10 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertNotIn('Content-MD5', req_headers) if bucket_exists: self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('PUT', '/v1/AUTH_test/bucket+segments/object/X'), ], self.swift.calls) else: self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('PUT', '/v1/AUTH_test/bucket+segments'), ('PUT', '/v1/AUTH_test/bucket+segments/object/X'), ], self.swift.calls) @@ -596,6 +610,8 @@ class TestS3ApiMultiUpload(S3ApiTestCase): fake_memcache = FakeMemcache() fake_memcache.store[get_cache_key( 'AUTH_test', 'bucket+segments')] = {'status': 204} + fake_memcache.store[get_cache_key( + 'AUTH_test', 'bucket')] = {'status': 204} self._test_object_multipart_upload_initiate({}, fake_memcache) self._test_object_multipart_upload_initiate({'Etag': 'blahblahblah'}, fake_memcache) @@ -826,6 +842,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self.swift.calls, [ # Bucket exists + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), # Upload marker exists ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), @@ -872,6 +889,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self.swift.calls, [ # Bucket exists + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), # Upload marker does not exist ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), @@ -907,6 +925,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self.swift.calls, [ # Bucket exists + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), # Upload marker does not exist ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), @@ -954,6 +973,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self.swift.calls, [ # Bucket exists + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), # Upload marker does not exist ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), @@ -1019,6 +1039,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): # NB: S3_ETAG includes quotes self.assertIn(('%s' % S3_ETAG).encode('ascii'), body) self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-ok/X'), ('PUT', '/v1/AUTH_test/bucket/heartbeat-ok?' @@ -1069,6 +1090,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self._get_error_message(body), 'some/object: 403 Forbidden') self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'), ('PUT', '/v1/AUTH_test/bucket/heartbeat-fail?' @@ -1118,6 +1140,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertIn('One or more of the specified parts could not be found', self._get_error_message(body)) self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'), ('PUT', '/v1/AUTH_test/bucket/heartbeat-fail?' @@ -1208,8 +1231,13 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self._get_error_code(body), 'EntityTooSmall') self.assertEqual(self._get_error_message(body), msg) # We punt to SLO to do the validation - self.assertEqual([method for method, _ in self.swift.calls], - ['HEAD', 'HEAD', 'PUT']) + self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), + ('HEAD', '/v1/AUTH_test/bucket'), + ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), + ('PUT', '/v1/AUTH_test/bucket/object' + '?heartbeat=on&multipart-manifest=put'), + ]) self.swift.clear_calls() self.s3api.conf.min_segment_size = 5242880 @@ -1229,8 +1257,13 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(self._get_error_code(body), 'EntityTooSmall') self.assertEqual(self._get_error_message(body), msg) # Again, we punt to SLO to do the validation - self.assertEqual([method for method, _ in self.swift.calls], - ['HEAD', 'HEAD', 'PUT']) + self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), + ('HEAD', '/v1/AUTH_test/bucket'), + ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), + ('PUT', '/v1/AUTH_test/bucket/object' + '?heartbeat=on&multipart-manifest=put'), + ]) def test_object_multipart_upload_complete_zero_segments(self): segment_bucket = '/v1/AUTH_test/empty-bucket+segments' @@ -1268,6 +1301,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): fromstring(body, 'Error') self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/empty-bucket'), ('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'), ]) @@ -1314,6 +1348,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(status.split()[0], '200') self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/empty-bucket'), ('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'), ('PUT', '/v1/AUTH_test/empty-bucket/object?' @@ -1383,6 +1418,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(elem.find('ETag').text, expected_etag) self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('PUT', '/v1/AUTH_test/bucket/object?' @@ -2023,7 +2059,13 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(status.split()[0], '200') - self.assertEqual(len(self.swift.calls_with_headers), 4) + self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), + ('HEAD', '/v1/AUTH_test/bucket'), + ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), + ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), + ('PUT', '/v1/AUTH_test/bucket+segments/object/X/1'), + ]) _, _, headers = self.swift.calls_with_headers[-2] self.assertEqual(headers['If-Match'], etag) self.assertEqual(headers['If-Modified-Since'], last_modified_since) @@ -2074,7 +2116,13 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self._test_copy_for_s3acl(account, put_header=header) self.assertEqual(status.split()[0], '200') - self.assertEqual(len(self.swift.calls_with_headers), 4) + self.assertEqual(self.swift.calls, [ + ('HEAD', '/v1/AUTH_test'), + ('HEAD', '/v1/AUTH_test/bucket'), + ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), + ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), + ('PUT', '/v1/AUTH_test/bucket+segments/object/X/1'), + ]) _, _, headers = self.swift.calls_with_headers[-2] self.assertEqual(headers['If-None-Match'], etag) self.assertEqual(headers['If-Unmodified-Since'], last_modified_since) @@ -2126,6 +2174,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): b'source object of size: 10', body) self.assertEqual([ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), @@ -2156,6 +2205,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase): self.assertEqual(status.split()[0], '200', body) self.assertEqual([ + ('HEAD', '/v1/AUTH_test'), ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), diff --git a/test/unit/common/middleware/s3api/test_obj.py b/test/unit/common/middleware/s3api/test_obj.py index 3b80cc3553..fdc327db43 100644 --- a/test/unit/common/middleware/s3api/test_obj.py +++ b/test/unit/common/middleware/s3api/test_obj.py @@ -34,7 +34,7 @@ from swift.common.middleware.s3api.subresource import ACL, User, encode_acl, \ from swift.common.middleware.s3api.etree import fromstring from swift.common.middleware.s3api.utils import mktime, S3Timestamp from swift.common.middleware.versioned_writes.object_versioning import \ - DELETE_MARKER_CONTENT_TYPE, SYSMETA_VERSIONS_CONT, SYSMETA_VERSIONS_ENABLED + DELETE_MARKER_CONTENT_TYPE class TestS3ApiObj(S3ApiTestCase): @@ -402,10 +402,6 @@ class TestS3ApiObj(S3ApiTestCase): @s3acl def test_object_GET_version_id(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, - {SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket'}, None) - # GET current version req = Request.blank('/bucket/object?versionId=null', environ={'REQUEST_METHOD': 'GET'}, @@ -473,8 +469,6 @@ class TestS3ApiObj(S3ApiTestCase): self.assertEqual(elem.find('Key').text, 'object') self.assertEqual(elem.find('VersionId').text, 'A') expected_calls = [] - if not self.swift.s3_acl: - expected_calls.append(('HEAD', '/v1/AUTH_test/bucket')) # NB: No actual backend GET! self.assertEqual(expected_calls, self.swift.calls) @@ -1126,9 +1120,6 @@ class TestS3ApiObj(S3ApiTestCase): def test_object_DELETE_old_version_id(self): self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, - {SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket'}, None) resp_headers = {'X-Object-Current-Version-Id': '1574360804.34906'} self.swift.register('DELETE', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293', @@ -1137,10 +1128,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1148,11 +1146,6 @@ class TestS3ApiObj(S3ApiTestCase): ], self.swift.calls) def test_object_DELETE_current_version_id(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) resp_headers = {'X-Object-Current-Version-Id': 'null'} @@ -1174,10 +1167,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1199,8 +1199,6 @@ class TestS3ApiObj(S3ApiTestCase): status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') expected_calls = [] - if not self.swift.s3_acl: - expected_calls.append(('HEAD', '/v1/AUTH_test/bucket')) # NB: No actual backend DELETE! self.assertEqual(expected_calls, self.swift.calls) @@ -1216,11 +1214,6 @@ class TestS3ApiObj(S3ApiTestCase): self.assertEqual(status.split()[0], '501', body) def test_object_DELETE_current_version_id_is_delete_marker(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) resp_headers = {'X-Object-Current-Version-Id': 'null'} @@ -1238,10 +1231,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1251,11 +1251,6 @@ class TestS3ApiObj(S3ApiTestCase): ], self.swift.calls) def test_object_DELETE_current_version_id_is_missing(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) resp_headers = {'X-Object-Current-Version-Id': 'null'} @@ -1283,10 +1278,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1300,11 +1302,6 @@ class TestS3ApiObj(S3ApiTestCase): ], self.swift.calls) def test_object_DELETE_current_version_id_GET_error(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) resp_headers = {'X-Object-Current-Version-Id': 'null'} @@ -1317,10 +1314,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '500') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1330,11 +1334,6 @@ class TestS3ApiObj(S3ApiTestCase): ], self.swift.calls) def test_object_DELETE_current_version_id_PUT_error(self): - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPOk, self.response_headers, None) resp_headers = {'X-Object-Current-Version-Id': 'null'} @@ -1355,10 +1354,17 @@ class TestS3ApiObj(S3ApiTestCase): method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '500') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574358170.12293'), ('DELETE', '/v1/AUTH_test/bucket/object' @@ -1401,28 +1407,25 @@ class TestS3ApiObj(S3ApiTestCase): 'X-Object-Version-Id': '1574701081.61553'} self.swift.register('DELETE', '/v1/AUTH_test/bucket/object', swob.HTTPNoContent, resp_headers, None) - self.swift.register( - 'HEAD', '/v1/AUTH_test/bucket', swob.HTTPNoContent, { - SYSMETA_VERSIONS_CONT: '\x00versions\x00bucket', - SYSMETA_VERSIONS_ENABLED: True}, - None) - self.swift.register('HEAD', '/v1/AUTH_test/\x00versions\x00bucket', - swob.HTTPNoContent, {}, None) self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', swob.HTTPNotFound, self.response_headers, None) req = Request.blank('/bucket/object?versionId=1574701081.61553', method='DELETE', headers={ 'Authorization': 'AWS test:tester:hmac', 'Date': self.get_date_header()}) - status, headers, body = self.call_s3api(req) + fake_info = { + 'status': 204, + 'sysmeta': { + 'versions-container': '\x00versions\x00bucket', + } + } + with patch('swift.common.middleware.s3api.s3request.' + 'get_container_info', return_value=fake_info): + status, headers, body = self.call_s3api(req) self.assertEqual(status.split()[0], '204') self.assertEqual([ - ('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574701081.61553'), - ('HEAD', '/v1/AUTH_test'), - ('HEAD', '/v1/AUTH_test/bucket'), - ('HEAD', '/v1/AUTH_test/\x00versions\x00bucket'), ('DELETE', '/v1/AUTH_test/bucket/object' '?symlink=get&version-id=1574701081.61553'), ], self.swift.calls) diff --git a/test/unit/common/middleware/s3api/test_s3api.py b/test/unit/common/middleware/s3api/test_s3api.py index 2df22a8ac1..73d3bdcded 100644 --- a/test/unit/common/middleware/s3api/test_s3api.py +++ b/test/unit/common/middleware/s3api/test_s3api.py @@ -500,8 +500,9 @@ class TestS3ApiMiddleware(S3ApiTestCase): 'S3Request.check_signature') as mock_cs: status, headers, body = self.call_s3api(req) self.assertIn('swift.backend_path', req.environ) - self.assertEqual('/v1/AUTH_test/bucket', - req.environ['swift.backend_path']) + self.assertEqual( + '/v1/AUTH_test/bucket+segments/object/123456789abcdef', + req.environ['swift.backend_path']) _, _, headers = self.swift.calls_with_headers[-1] self.assertEqual(req.environ['s3api.auth_details'], { @@ -530,8 +531,9 @@ class TestS3ApiMiddleware(S3ApiTestCase): 'S3Request.check_signature') as mock_cs: status, headers, body = self.call_s3api(req) self.assertIn('swift.backend_path', req.environ) - self.assertEqual('/v1/AUTH_test/bucket', - req.environ['swift.backend_path']) + self.assertEqual( + '/v1/AUTH_test/bucket+segments/object/123456789abcdef', + req.environ['swift.backend_path']) _, _, headers = self.swift.calls_with_headers[-1] self.assertEqual(req.environ['s3api.auth_details'], { diff --git a/test/unit/common/middleware/s3api/test_s3request.py b/test/unit/common/middleware/s3api/test_s3request.py index 55b5427aae..853ff1a8f6 100644 --- a/test/unit/common/middleware/s3api/test_s3request.py +++ b/test/unit/common/middleware/s3api/test_s3request.py @@ -312,7 +312,7 @@ class TestRequest(S3ApiTestCase): '[{"Grantee":"owner","Permission":"FULL_CONTROL"}]}' self.swift.register('HEAD', '/v1/AUTH_test/bucket', HTTPNoContent, {'x-container-read': 'foo', - 'X-container-object-count': 5, + 'X-container-object-count': '5', 'x-container-sysmeta-versions-location': 'bucket2', 'x-container-sysmeta-s3api-acl': s3api_acl, @@ -326,7 +326,7 @@ class TestRequest(S3ApiTestCase): self.assertTrue('status' in info) # sanity self.assertEqual(204, info['status']) # sanity self.assertEqual('foo', info['read_acl']) # sanity - self.assertEqual('5', info['object_count']) # sanity + self.assertEqual(5, info['object_count']) # sanity self.assertEqual( 'bucket2', info['sysmeta']['versions-location']) # sanity self.assertEqual(s3api_acl, info['sysmeta']['s3api-acl']) # sanity