s3api: Don't do naive HEAD request for auth

Change-Id: If0fc8ec4d8056afb741bf74b82598a26683dfcd7
This commit is contained in:
Clay Gerrard 2020-06-15 16:19:47 -05:00
parent 5c087ad7b9
commit 2e001431fd
6 changed files with 164 additions and 95 deletions
swift/common/middleware/s3api
test/unit/common/middleware/s3api

@ -37,8 +37,7 @@ from swift.common.http import HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED, \
HTTP_TOO_MANY_REQUESTS, HTTP_RATE_LIMITED, is_success HTTP_TOO_MANY_REQUESTS, HTTP_RATE_LIMITED, is_success
from swift.common.constraints import check_utf8 from swift.common.constraints import check_utf8
from swift.proxy.controllers.base import get_container_info, \ from swift.proxy.controllers.base import get_container_info
headers_to_container_info
from swift.common.request_helpers import check_path_header from swift.common.request_helpers import check_path_header
from swift.common.middleware.s3api.controllers import ServiceController, \ 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: NoSuchBucket when the container doesn't exist
:raises: InternalError when the request failed without 404 :raises: InternalError when the request failed without 404
""" """
if self.is_authenticated: if not self.is_authenticated:
# if we have already authenticated, yes we can use the account sw_req = self.to_swift_req('TEST', None, None, body='')
# name like as AUTH_xxx for performance efficiency # don't show log message of this request
sw_req = self.to_swift_req(app, self.container_name, None) sw_req.environ['swift.proxy_access_log_made'] = True
info = get_container_info(sw_req.environ, app, swift_source='S3')
if is_success(info['status']): sw_resp = sw_req.get_response(app)
return info
elif info['status'] == 404: if not sw_req.remote_user:
raise NoSuchBucket(self.container_name) raise SignatureDoesNotMatch(
else: **self.signature_does_not_match_kwargs())
raise InternalError(
'unexpected status code %d' % info['status']) _, 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: else:
# otherwise we do naive HEAD request with the authentication raise InternalError(
resp = self.get_response(app, 'HEAD', self.container_name, '') 'unexpected status code %d' % info['status'])
headers = resp.sw_headers.copy()
headers.update(resp.sysmeta_headers)
return headers_to_container_info(
headers, resp.status_int) # pylint: disable-msg=E1101
def gen_multipart_manifest_delete_query(self, app, obj=None, version=None): def gen_multipart_manifest_delete_query(self, app, obj=None, version=None):
if not self.allow_multipart_uploads: if not self.allow_multipart_uploads:

@ -50,6 +50,18 @@ class FakeApp(object):
if 's3api.auth_details' in env: if 's3api.auth_details' in env:
self._update_s3_path_info(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) return self.swift(env, start_response)

@ -137,6 +137,22 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
'Date': self.get_date_header()}) 'Date': self.get_date_header()})
status, headers, body = self.call_s3api(req) status, headers, body = self.call_s3api(req)
self.assertEqual(self._get_error_code(body), 'InvalidRequest') 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 @s3acl
def test_object_multipart_uploads_list(self): def test_object_multipart_uploads_list(self):
@ -577,12 +593,10 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertNotIn('Content-MD5', req_headers) self.assertNotIn('Content-MD5', req_headers)
if bucket_exists: if bucket_exists:
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('PUT', '/v1/AUTH_test/bucket+segments/object/X'), ('PUT', '/v1/AUTH_test/bucket+segments/object/X'),
], self.swift.calls) ], self.swift.calls)
else: else:
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('PUT', '/v1/AUTH_test/bucket+segments'), ('PUT', '/v1/AUTH_test/bucket+segments'),
('PUT', '/v1/AUTH_test/bucket+segments/object/X'), ('PUT', '/v1/AUTH_test/bucket+segments/object/X'),
], self.swift.calls) ], self.swift.calls)
@ -596,6 +610,8 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
fake_memcache = FakeMemcache() fake_memcache = FakeMemcache()
fake_memcache.store[get_cache_key( fake_memcache.store[get_cache_key(
'AUTH_test', 'bucket+segments')] = {'status': 204} '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({}, fake_memcache)
self._test_object_multipart_upload_initiate({'Etag': 'blahblahblah'}, self._test_object_multipart_upload_initiate({'Etag': 'blahblahblah'},
fake_memcache) fake_memcache)
@ -826,6 +842,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
# Bucket exists # Bucket exists
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
# Upload marker exists # Upload marker exists
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
@ -872,6 +889,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
# Bucket exists # Bucket exists
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
# Upload marker does not exist # Upload marker does not exist
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
@ -907,6 +925,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
# Bucket exists # Bucket exists
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
# Upload marker does not exist # Upload marker does not exist
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
@ -954,6 +973,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
# Bucket exists # Bucket exists
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
# Upload marker does not exist # Upload marker does not exist
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
@ -1019,6 +1039,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
# NB: S3_ETAG includes quotes # NB: S3_ETAG includes quotes
self.assertIn(('<ETag>%s</ETag>' % S3_ETAG).encode('ascii'), body) self.assertIn(('<ETag>%s</ETag>' % S3_ETAG).encode('ascii'), body)
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-ok/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-ok/X'),
('PUT', '/v1/AUTH_test/bucket/heartbeat-ok?' ('PUT', '/v1/AUTH_test/bucket/heartbeat-ok?'
@ -1069,6 +1090,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(self._get_error_message(body), self.assertEqual(self._get_error_message(body),
'some/object: 403 Forbidden') 'some/object: 403 Forbidden')
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'),
('PUT', '/v1/AUTH_test/bucket/heartbeat-fail?' ('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.assertIn('One or more of the specified parts could not be found',
self._get_error_message(body)) self._get_error_message(body))
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/heartbeat-fail/X'),
('PUT', '/v1/AUTH_test/bucket/heartbeat-fail?' ('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_code(body), 'EntityTooSmall')
self.assertEqual(self._get_error_message(body), msg) self.assertEqual(self._get_error_message(body), msg)
# We punt to SLO to do the validation # We punt to SLO to do the validation
self.assertEqual([method for method, _ in self.swift.calls], self.assertEqual(self.swift.calls, [
['HEAD', 'HEAD', 'PUT']) ('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.swift.clear_calls()
self.s3api.conf.min_segment_size = 5242880 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_code(body), 'EntityTooSmall')
self.assertEqual(self._get_error_message(body), msg) self.assertEqual(self._get_error_message(body), msg)
# Again, we punt to SLO to do the validation # Again, we punt to SLO to do the validation
self.assertEqual([method for method, _ in self.swift.calls], self.assertEqual(self.swift.calls, [
['HEAD', 'HEAD', 'PUT']) ('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): def test_object_multipart_upload_complete_zero_segments(self):
segment_bucket = '/v1/AUTH_test/empty-bucket+segments' segment_bucket = '/v1/AUTH_test/empty-bucket+segments'
@ -1268,6 +1301,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
fromstring(body, 'Error') fromstring(body, 'Error')
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/empty-bucket'), ('HEAD', '/v1/AUTH_test/empty-bucket'),
('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'),
]) ])
@ -1314,6 +1348,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(status.split()[0], '200') self.assertEqual(status.split()[0], '200')
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/empty-bucket'), ('HEAD', '/v1/AUTH_test/empty-bucket'),
('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/empty-bucket+segments/object/X'),
('PUT', '/v1/AUTH_test/empty-bucket/object?' ('PUT', '/v1/AUTH_test/empty-bucket/object?'
@ -1383,6 +1418,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(elem.find('ETag').text, expected_etag) self.assertEqual(elem.find('ETag').text, expected_etag)
self.assertEqual(self.swift.calls, [ self.assertEqual(self.swift.calls, [
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
('PUT', '/v1/AUTH_test/bucket/object?' ('PUT', '/v1/AUTH_test/bucket/object?'
@ -2023,7 +2059,13 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(status.split()[0], '200') 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] _, _, headers = self.swift.calls_with_headers[-2]
self.assertEqual(headers['If-Match'], etag) self.assertEqual(headers['If-Match'], etag)
self.assertEqual(headers['If-Modified-Since'], last_modified_since) 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._test_copy_for_s3acl(account, put_header=header)
self.assertEqual(status.split()[0], '200') 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] _, _, headers = self.swift.calls_with_headers[-2]
self.assertEqual(headers['If-None-Match'], etag) self.assertEqual(headers['If-None-Match'], etag)
self.assertEqual(headers['If-Unmodified-Since'], last_modified_since) self.assertEqual(headers['If-Unmodified-Since'], last_modified_since)
@ -2126,6 +2174,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
b'source object of size: 10', body) b'source object of size: 10', body)
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'),
@ -2156,6 +2205,7 @@ class TestS3ApiMultiUpload(S3ApiTestCase):
self.assertEqual(status.split()[0], '200', body) self.assertEqual(status.split()[0], '200', body)
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test'),
('HEAD', '/v1/AUTH_test/bucket'), ('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket+segments/object/X'), ('HEAD', '/v1/AUTH_test/bucket+segments/object/X'),
('HEAD', '/v1/AUTH_test/src_bucket/src_obj'), ('HEAD', '/v1/AUTH_test/src_bucket/src_obj'),

@ -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.etree import fromstring
from swift.common.middleware.s3api.utils import mktime, S3Timestamp from swift.common.middleware.s3api.utils import mktime, S3Timestamp
from swift.common.middleware.versioned_writes.object_versioning import \ 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): class TestS3ApiObj(S3ApiTestCase):
@ -402,10 +402,6 @@ class TestS3ApiObj(S3ApiTestCase):
@s3acl @s3acl
def test_object_GET_version_id(self): 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 # GET current version
req = Request.blank('/bucket/object?versionId=null', req = Request.blank('/bucket/object?versionId=null',
environ={'REQUEST_METHOD': 'GET'}, environ={'REQUEST_METHOD': 'GET'},
@ -473,8 +469,6 @@ class TestS3ApiObj(S3ApiTestCase):
self.assertEqual(elem.find('Key').text, 'object') self.assertEqual(elem.find('Key').text, 'object')
self.assertEqual(elem.find('VersionId').text, 'A') self.assertEqual(elem.find('VersionId').text, 'A')
expected_calls = [] expected_calls = []
if not self.swift.s3_acl:
expected_calls.append(('HEAD', '/v1/AUTH_test/bucket'))
# NB: No actual backend GET! # NB: No actual backend GET!
self.assertEqual(expected_calls, self.swift.calls) self.assertEqual(expected_calls, self.swift.calls)
@ -1126,9 +1120,6 @@ class TestS3ApiObj(S3ApiTestCase):
def test_object_DELETE_old_version_id(self): def test_object_DELETE_old_version_id(self):
self.swift.register('HEAD', '/v1/AUTH_test/bucket/object', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) 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'} resp_headers = {'X-Object-Current-Version-Id': '1574360804.34906'}
self.swift.register('DELETE', '/v1/AUTH_test/bucket/object' self.swift.register('DELETE', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293', '?symlink=get&version-id=1574358170.12293',
@ -1137,10 +1128,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '204')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1148,11 +1146,6 @@ class TestS3ApiObj(S3ApiTestCase):
], self.swift.calls) ], self.swift.calls)
def test_object_DELETE_current_version_id(self): 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) swob.HTTPOk, self.response_headers, None)
resp_headers = {'X-Object-Current-Version-Id': 'null'} resp_headers = {'X-Object-Current-Version-Id': 'null'}
@ -1174,10 +1167,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '204')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1199,8 +1199,6 @@ class TestS3ApiObj(S3ApiTestCase):
status, headers, body = self.call_s3api(req) status, headers, body = self.call_s3api(req)
self.assertEqual(status.split()[0], '204') self.assertEqual(status.split()[0], '204')
expected_calls = [] expected_calls = []
if not self.swift.s3_acl:
expected_calls.append(('HEAD', '/v1/AUTH_test/bucket'))
# NB: No actual backend DELETE! # NB: No actual backend DELETE!
self.assertEqual(expected_calls, self.swift.calls) self.assertEqual(expected_calls, self.swift.calls)
@ -1216,11 +1214,6 @@ class TestS3ApiObj(S3ApiTestCase):
self.assertEqual(status.split()[0], '501', body) self.assertEqual(status.split()[0], '501', body)
def test_object_DELETE_current_version_id_is_delete_marker(self): 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) swob.HTTPOk, self.response_headers, None)
resp_headers = {'X-Object-Current-Version-Id': 'null'} resp_headers = {'X-Object-Current-Version-Id': 'null'}
@ -1238,10 +1231,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '204')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1251,11 +1251,6 @@ class TestS3ApiObj(S3ApiTestCase):
], self.swift.calls) ], self.swift.calls)
def test_object_DELETE_current_version_id_is_missing(self): 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) swob.HTTPOk, self.response_headers, None)
resp_headers = {'X-Object-Current-Version-Id': 'null'} resp_headers = {'X-Object-Current-Version-Id': 'null'}
@ -1283,10 +1278,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '204')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1300,11 +1302,6 @@ class TestS3ApiObj(S3ApiTestCase):
], self.swift.calls) ], self.swift.calls)
def test_object_DELETE_current_version_id_GET_error(self): 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) swob.HTTPOk, self.response_headers, None)
resp_headers = {'X-Object-Current-Version-Id': 'null'} resp_headers = {'X-Object-Current-Version-Id': 'null'}
@ -1317,10 +1314,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '500')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1330,11 +1334,6 @@ class TestS3ApiObj(S3ApiTestCase):
], self.swift.calls) ], self.swift.calls)
def test_object_DELETE_current_version_id_PUT_error(self): 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPOk, self.response_headers, None) swob.HTTPOk, self.response_headers, None)
resp_headers = {'X-Object-Current-Version-Id': 'null'} resp_headers = {'X-Object-Current-Version-Id': 'null'}
@ -1355,10 +1354,17 @@ class TestS3ApiObj(S3ApiTestCase):
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '500')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574358170.12293'), '?symlink=get&version-id=1574358170.12293'),
('DELETE', '/v1/AUTH_test/bucket/object' ('DELETE', '/v1/AUTH_test/bucket/object'
@ -1401,28 +1407,25 @@ class TestS3ApiObj(S3ApiTestCase):
'X-Object-Version-Id': '1574701081.61553'} 'X-Object-Version-Id': '1574701081.61553'}
self.swift.register('DELETE', '/v1/AUTH_test/bucket/object', self.swift.register('DELETE', '/v1/AUTH_test/bucket/object',
swob.HTTPNoContent, resp_headers, None) 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', self.swift.register('HEAD', '/v1/AUTH_test/bucket/object',
swob.HTTPNotFound, self.response_headers, None) swob.HTTPNotFound, self.response_headers, None)
req = Request.blank('/bucket/object?versionId=1574701081.61553', req = Request.blank('/bucket/object?versionId=1574701081.61553',
method='DELETE', headers={ method='DELETE', headers={
'Authorization': 'AWS test:tester:hmac', 'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()}) '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(status.split()[0], '204')
self.assertEqual([ self.assertEqual([
('HEAD', '/v1/AUTH_test/bucket'),
('HEAD', '/v1/AUTH_test/bucket/object' ('HEAD', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574701081.61553'), '?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' ('DELETE', '/v1/AUTH_test/bucket/object'
'?symlink=get&version-id=1574701081.61553'), '?symlink=get&version-id=1574701081.61553'),
], self.swift.calls) ], self.swift.calls)

@ -500,8 +500,9 @@ class TestS3ApiMiddleware(S3ApiTestCase):
'S3Request.check_signature') as mock_cs: 'S3Request.check_signature') as mock_cs:
status, headers, body = self.call_s3api(req) status, headers, body = self.call_s3api(req)
self.assertIn('swift.backend_path', req.environ) self.assertIn('swift.backend_path', req.environ)
self.assertEqual('/v1/AUTH_test/bucket', self.assertEqual(
req.environ['swift.backend_path']) '/v1/AUTH_test/bucket+segments/object/123456789abcdef',
req.environ['swift.backend_path'])
_, _, headers = self.swift.calls_with_headers[-1] _, _, headers = self.swift.calls_with_headers[-1]
self.assertEqual(req.environ['s3api.auth_details'], { self.assertEqual(req.environ['s3api.auth_details'], {
@ -530,8 +531,9 @@ class TestS3ApiMiddleware(S3ApiTestCase):
'S3Request.check_signature') as mock_cs: 'S3Request.check_signature') as mock_cs:
status, headers, body = self.call_s3api(req) status, headers, body = self.call_s3api(req)
self.assertIn('swift.backend_path', req.environ) self.assertIn('swift.backend_path', req.environ)
self.assertEqual('/v1/AUTH_test/bucket', self.assertEqual(
req.environ['swift.backend_path']) '/v1/AUTH_test/bucket+segments/object/123456789abcdef',
req.environ['swift.backend_path'])
_, _, headers = self.swift.calls_with_headers[-1] _, _, headers = self.swift.calls_with_headers[-1]
self.assertEqual(req.environ['s3api.auth_details'], { self.assertEqual(req.environ['s3api.auth_details'], {

@ -312,7 +312,7 @@ class TestRequest(S3ApiTestCase):
'[{"Grantee":"owner","Permission":"FULL_CONTROL"}]}' '[{"Grantee":"owner","Permission":"FULL_CONTROL"}]}'
self.swift.register('HEAD', '/v1/AUTH_test/bucket', HTTPNoContent, self.swift.register('HEAD', '/v1/AUTH_test/bucket', HTTPNoContent,
{'x-container-read': 'foo', {'x-container-read': 'foo',
'X-container-object-count': 5, 'X-container-object-count': '5',
'x-container-sysmeta-versions-location': 'x-container-sysmeta-versions-location':
'bucket2', 'bucket2',
'x-container-sysmeta-s3api-acl': s3api_acl, 'x-container-sysmeta-s3api-acl': s3api_acl,
@ -326,7 +326,7 @@ class TestRequest(S3ApiTestCase):
self.assertTrue('status' in info) # sanity self.assertTrue('status' in info) # sanity
self.assertEqual(204, info['status']) # sanity self.assertEqual(204, info['status']) # sanity
self.assertEqual('foo', info['read_acl']) # sanity self.assertEqual('foo', info['read_acl']) # sanity
self.assertEqual('5', info['object_count']) # sanity self.assertEqual(5, info['object_count']) # sanity
self.assertEqual( self.assertEqual(
'bucket2', info['sysmeta']['versions-location']) # sanity 'bucket2', info['sysmeta']['versions-location']) # sanity
self.assertEqual(s3api_acl, info['sysmeta']['s3api-acl']) # sanity self.assertEqual(s3api_acl, info['sysmeta']['s3api-acl']) # sanity