Merge "s3api: Stop mangling Authorization header for v4 signatures"
This commit is contained in:
commit
7b1679b9b2
|
@ -491,15 +491,6 @@ class S3Request(swob.Request):
|
||||||
self.user_id = None
|
self.user_id = None
|
||||||
self.slo_enabled = slo_enabled
|
self.slo_enabled = slo_enabled
|
||||||
|
|
||||||
# NOTE(andrey-mp): substitute authorization header for next modules
|
|
||||||
# in pipeline (s3token). it uses this and X-Auth-Token in specific
|
|
||||||
# format.
|
|
||||||
# (kota_): yeah, the reason we need this is s3token only supports
|
|
||||||
# v2 like header consists of AWS access:signature. Since the commit
|
|
||||||
# b626a3ca86e467fc7564eac236b9ee2efd49bdcc, the s3token is in swift3
|
|
||||||
# repo so probably we need to change s3token to support v4 format.
|
|
||||||
self.headers['Authorization'] = 'AWS %s:%s' % (
|
|
||||||
self.access_key, self.signature)
|
|
||||||
# Avoids that swift.swob.Response replaces Location header value
|
# Avoids that swift.swob.Response replaces Location header value
|
||||||
# by full URL when absolute path given. See swift.swob for more detail.
|
# by full URL when absolute path given. See swift.swob for more detail.
|
||||||
self.environ['swift.leave_relative_location'] = True
|
self.environ['swift.leave_relative_location'] = True
|
||||||
|
@ -1460,7 +1451,6 @@ class S3AclRequest(S3Request):
|
||||||
|
|
||||||
# Need to skip S3 authorization on subsequent requests to prevent
|
# Need to skip S3 authorization on subsequent requests to prevent
|
||||||
# overwriting the account in PATH_INFO
|
# overwriting the account in PATH_INFO
|
||||||
del self.headers['Authorization']
|
|
||||||
del self.environ['s3api.auth_details']
|
del self.environ['s3api.auth_details']
|
||||||
|
|
||||||
def to_swift_req(self, method, container, obj, query=None,
|
def to_swift_req(self, method, container, obj, query=None,
|
||||||
|
|
|
@ -38,15 +38,14 @@ class FakeApp(object):
|
||||||
E.g. '/v1/test:tester/bucket/object' will become
|
E.g. '/v1/test:tester/bucket/object' will become
|
||||||
'/v1/AUTH_test/bucket/object'. This method emulates the behavior.
|
'/v1/AUTH_test/bucket/object'. This method emulates the behavior.
|
||||||
"""
|
"""
|
||||||
_, authorization = env['HTTP_AUTHORIZATION'].split(' ')
|
tenant_user = env['s3api.auth_details']['access_key']
|
||||||
tenant_user, sign = authorization.rsplit(':', 1)
|
|
||||||
tenant, user = tenant_user.rsplit(':', 1)
|
tenant, user = tenant_user.rsplit(':', 1)
|
||||||
|
|
||||||
path = env['PATH_INFO']
|
path = env['PATH_INFO']
|
||||||
env['PATH_INFO'] = path.replace(tenant_user, 'AUTH_' + tenant)
|
env['PATH_INFO'] = path.replace(tenant_user, 'AUTH_' + tenant)
|
||||||
|
|
||||||
def __call__(self, env, start_response):
|
def __call__(self, env, start_response):
|
||||||
if 'HTTP_AUTHORIZATION' in env:
|
if 's3api.auth_details' in env:
|
||||||
self._update_s3_path_info(env)
|
self._update_s3_path_info(env)
|
||||||
|
|
||||||
return self.swift(env, start_response)
|
return self.swift(env, start_response)
|
||||||
|
|
|
@ -41,11 +41,10 @@ class FakeSwift(object):
|
||||||
if 'swift.authorize_override' in env:
|
if 'swift.authorize_override' in env:
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'HTTP_AUTHORIZATION' not in env:
|
if 's3api.auth_details' not in env:
|
||||||
return
|
return
|
||||||
|
|
||||||
_, authorization = env['HTTP_AUTHORIZATION'].split(' ')
|
tenant_user = env['s3api.auth_details']['access_key']
|
||||||
tenant_user, sign = authorization.rsplit(':', 1)
|
|
||||||
tenant, user = tenant_user.rsplit(':', 1)
|
tenant, user = tenant_user.rsplit(':', 1)
|
||||||
|
|
||||||
path = env['PATH_INFO']
|
path = env['PATH_INFO']
|
||||||
|
|
|
@ -30,27 +30,6 @@ from swift.common.middleware.s3api.subresource import ACL, User, encode_acl, \
|
||||||
Owner, Grant
|
Owner, Grant
|
||||||
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 test.unit.common.middleware.s3api.helpers import FakeSwift
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap_fake_auth_middleware(org_func):
|
|
||||||
def fake_fake_auth_middleware(self, env):
|
|
||||||
org_func(env)
|
|
||||||
|
|
||||||
if 'swift.authorize_override' in env:
|
|
||||||
return
|
|
||||||
|
|
||||||
if 'HTTP_AUTHORIZATION' not in env:
|
|
||||||
return
|
|
||||||
|
|
||||||
_, authorization = env['HTTP_AUTHORIZATION'].split(' ')
|
|
||||||
tenant_user, sign = authorization.rsplit(':', 1)
|
|
||||||
tenant, user = tenant_user.rsplit(':', 1)
|
|
||||||
|
|
||||||
env['HTTP_X_TENANT_NAME'] = tenant
|
|
||||||
env['HTTP_X_USER_NAME'] = user
|
|
||||||
|
|
||||||
return fake_fake_auth_middleware
|
|
||||||
|
|
||||||
|
|
||||||
class TestS3ApiObj(S3ApiTestCase):
|
class TestS3ApiObj(S3ApiTestCase):
|
||||||
|
@ -320,15 +299,20 @@ class TestS3ApiObj(S3ApiTestCase):
|
||||||
@s3acl(s3acl_only=True)
|
@s3acl(s3acl_only=True)
|
||||||
def test_object_GET_with_s3acl_and_keystone(self):
|
def test_object_GET_with_s3acl_and_keystone(self):
|
||||||
# for passing keystone authentication root
|
# for passing keystone authentication root
|
||||||
fake_auth = self.swift._fake_auth_middleware
|
orig_auth = self.swift._fake_auth_middleware
|
||||||
with patch.object(FakeSwift, '_fake_auth_middleware',
|
calls = []
|
||||||
_wrap_fake_auth_middleware(fake_auth)):
|
|
||||||
|
|
||||||
|
def wrapped_auth(env):
|
||||||
|
calls.append((env['REQUEST_METHOD'], 's3api.auth_details' in env))
|
||||||
|
orig_auth(env)
|
||||||
|
|
||||||
|
with patch.object(self.swift, '_fake_auth_middleware', wrapped_auth):
|
||||||
self._test_object_GETorHEAD('GET')
|
self._test_object_GETorHEAD('GET')
|
||||||
_, _, headers = self.swift.calls_with_headers[-1]
|
self.assertEqual(calls, [
|
||||||
self.assertNotIn('Authorization', headers)
|
('TEST', True),
|
||||||
_, _, headers = self.swift.calls_with_headers[0]
|
('HEAD', False),
|
||||||
self.assertNotIn('Authorization', headers)
|
('GET', False),
|
||||||
|
])
|
||||||
|
|
||||||
@s3acl
|
@s3acl
|
||||||
def test_object_GET_Range(self):
|
def test_object_GET_Range(self):
|
||||||
|
|
|
@ -267,8 +267,8 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||||
req.content_type = 'text/plain'
|
req.content_type = 'text/plain'
|
||||||
status, headers, body = self.call_s3api(req)
|
status, headers, body = self.call_s3api(req)
|
||||||
self.assertEqual(status.split()[0], '200')
|
self.assertEqual(status.split()[0], '200')
|
||||||
for _, _, headers in self.swift.calls_with_headers:
|
for _, path, headers in self.swift.calls_with_headers:
|
||||||
self.assertEqual(headers['Authorization'], 'AWS test:tester:X')
|
self.assertNotIn('Authorization', headers)
|
||||||
|
|
||||||
def test_signed_urls_no_timestamp(self):
|
def test_signed_urls_no_timestamp(self):
|
||||||
expire = '2147483647' # 19 Jan 2038 03:14:07
|
expire = '2147483647' # 19 Jan 2038 03:14:07
|
||||||
|
@ -281,7 +281,7 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||||
# for signed_url access and it also doesn't check timestamp
|
# for signed_url access and it also doesn't check timestamp
|
||||||
self.assertEqual(status.split()[0], '200')
|
self.assertEqual(status.split()[0], '200')
|
||||||
for _, _, headers in self.swift.calls_with_headers:
|
for _, _, headers in self.swift.calls_with_headers:
|
||||||
self.assertEqual(headers['Authorization'], 'AWS test:tester:X')
|
self.assertNotIn('Authorization', headers)
|
||||||
|
|
||||||
def test_signed_urls_invalid_expire(self):
|
def test_signed_urls_invalid_expire(self):
|
||||||
expire = 'invalid'
|
expire = 'invalid'
|
||||||
|
@ -332,8 +332,8 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||||
status, headers, body = self.call_s3api(req)
|
status, headers, body = self.call_s3api(req)
|
||||||
self.assertEqual(status.split()[0], '200', body)
|
self.assertEqual(status.split()[0], '200', body)
|
||||||
for _, _, headers in self.swift.calls_with_headers:
|
for _, _, headers in self.swift.calls_with_headers:
|
||||||
self.assertEqual('AWS test:tester:X', headers['Authorization'])
|
self.assertNotIn('Authorization', headers)
|
||||||
self.assertIn('X-Auth-Token', headers)
|
self.assertIsNone(headers['X-Auth-Token'])
|
||||||
|
|
||||||
def test_signed_urls_v4_bad_credential(self):
|
def test_signed_urls_v4_bad_credential(self):
|
||||||
def test(credential, message, extra=''):
|
def test(credential, message, extra=''):
|
||||||
|
@ -740,12 +740,14 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||||
def test_signature_v4(self):
|
def test_signature_v4(self):
|
||||||
environ = {
|
environ = {
|
||||||
'REQUEST_METHOD': 'GET'}
|
'REQUEST_METHOD': 'GET'}
|
||||||
|
authz_header = 'AWS4-HMAC-SHA256 ' + ', '.join([
|
||||||
|
'Credential=test:tester/%s/us-east-1/s3/aws4_request' %
|
||||||
|
self.get_v4_amz_date_header().split('T', 1)[0],
|
||||||
|
'SignedHeaders=host;x-amz-date',
|
||||||
|
'Signature=X',
|
||||||
|
])
|
||||||
headers = {
|
headers = {
|
||||||
'Authorization':
|
'Authorization': authz_header,
|
||||||
'AWS4-HMAC-SHA256 '
|
|
||||||
'Credential=test:tester/%s/us-east-1/s3/aws4_request, '
|
|
||||||
'SignedHeaders=host;x-amz-date,'
|
|
||||||
'Signature=X' % self.get_v4_amz_date_header().split('T', 1)[0],
|
|
||||||
'X-Amz-Date': self.get_v4_amz_date_header(),
|
'X-Amz-Date': self.get_v4_amz_date_header(),
|
||||||
'X-Amz-Content-SHA256': '0123456789'}
|
'X-Amz-Content-SHA256': '0123456789'}
|
||||||
req = Request.blank('/bucket/object', environ=environ, headers=headers)
|
req = Request.blank('/bucket/object', environ=environ, headers=headers)
|
||||||
|
@ -753,8 +755,8 @@ class TestS3ApiMiddleware(S3ApiTestCase):
|
||||||
status, headers, body = self.call_s3api(req)
|
status, headers, body = self.call_s3api(req)
|
||||||
self.assertEqual(status.split()[0], '200', body)
|
self.assertEqual(status.split()[0], '200', body)
|
||||||
for _, _, headers in self.swift.calls_with_headers:
|
for _, _, headers in self.swift.calls_with_headers:
|
||||||
self.assertEqual('AWS test:tester:X', headers['Authorization'])
|
self.assertEqual(authz_header, headers['Authorization'])
|
||||||
self.assertIn('X-Auth-Token', headers)
|
self.assertIsNone(headers['X-Auth-Token'])
|
||||||
|
|
||||||
def test_signature_v4_no_date(self):
|
def test_signature_v4_no_date(self):
|
||||||
environ = {
|
environ = {
|
||||||
|
|
|
@ -246,8 +246,6 @@ class TestRequest(S3ApiTestCase):
|
||||||
m_swift_resp.return_value = FakeSwiftResponse()
|
m_swift_resp.return_value = FakeSwiftResponse()
|
||||||
s3_req = S3AclRequest(req.environ, MagicMock())
|
s3_req = S3AclRequest(req.environ, MagicMock())
|
||||||
self.assertNotIn('s3api.auth_details', s3_req.environ)
|
self.assertNotIn('s3api.auth_details', s3_req.environ)
|
||||||
self.assertNotIn('HTTP_AUTHORIZATION', s3_req.environ)
|
|
||||||
self.assertNotIn('Authorization', s3_req.headers)
|
|
||||||
self.assertEqual(s3_req.token, 'token')
|
self.assertEqual(s3_req.token, 'token')
|
||||||
|
|
||||||
def test_to_swift_req_Authorization_not_exist_in_swreq(self):
|
def test_to_swift_req_Authorization_not_exist_in_swreq(self):
|
||||||
|
@ -265,8 +263,6 @@ class TestRequest(S3ApiTestCase):
|
||||||
s3_req = S3AclRequest(req.environ, MagicMock())
|
s3_req = S3AclRequest(req.environ, MagicMock())
|
||||||
sw_req = s3_req.to_swift_req(method, container, obj)
|
sw_req = s3_req.to_swift_req(method, container, obj)
|
||||||
self.assertNotIn('s3api.auth_details', sw_req.environ)
|
self.assertNotIn('s3api.auth_details', sw_req.environ)
|
||||||
self.assertNotIn('HTTP_AUTHORIZATION', sw_req.environ)
|
|
||||||
self.assertNotIn('Authorization', sw_req.headers)
|
|
||||||
self.assertEqual(sw_req.headers['X-Auth-Token'], 'token')
|
self.assertEqual(sw_req.headers['X-Auth-Token'], 'token')
|
||||||
|
|
||||||
def test_to_swift_req_subrequest_proxy_access_log(self):
|
def test_to_swift_req_subrequest_proxy_access_log(self):
|
||||||
|
|
Loading…
Reference in New Issue