Fix Swift3 to skip S3 authorization after initial authentication

Fix Swift3 never to send "Authorization" header again after the
initial authentication at S3AclRequest with keystone authentication.

This problem occurs following operations which check permission of object.
 - HEAD Object
 - GET Object
 - PUT Object Copy
 - Upload Part Copy

The keystone authentication expects both a token generated by
_canonical_string() and an user name written in the "Authorization"
header.

S3AclRequest will bypass the keystone authentication process after
authenticate() method call for some reasons (e.g. performance and
object acl). To bypass the authentication, Swift3 has a couple of
things to do. One is to delete "Authentication" header. The other is
to keep (and pass) a token retrieved from keystone server.(NOTE: the
token is different from a token generated by _canonical_string())

However, current Swift3 still tries to keep the "Authorization"
header in Request class and might pass it to the keystone
authentication. It causes unexpected (unnecessary) authentication
failure. To prevent the failure, Swift3 should delete the
"Authentication" header explicitly from Request.headers.

Change-Id: Id81e393d51b389610d9fa470f307f61e846a78a3
This commit is contained in:
Naoto Nishizono
2015-01-16 15:38:02 +09:00
parent a0834c83a4
commit 452d2fc6f3
3 changed files with 78 additions and 3 deletions

View File

@@ -437,10 +437,7 @@ class Request(swob.Request):
env['REQUEST_METHOD'] = method
if self.keystone_token:
# Need to skip S3 authorization since authtoken middleware
# overwrites account in PATH_INFO
env['HTTP_X_AUTH_TOKEN'] = self.keystone_token
del env['HTTP_AUTHORIZATION']
else:
env['HTTP_X_AUTH_TOKEN'] = self.token
@@ -715,6 +712,9 @@ class S3AclRequest(Request):
sw_resp.environ['HTTP_X_USER_NAME'])
self.user_id = utf8encode(self.user_id)
self.keystone_token = sw_req.environ['HTTP_X_AUTH_TOKEN']
# Need to skip S3 authorization since authtoken middleware
# overwrites account in PATH_INFO
del self.headers['Authorization']
else:
# tempauth
self.user_id = self.access_key

View File

@@ -17,6 +17,7 @@ import unittest
from datetime import datetime
import hashlib
from os.path import join
from mock import patch
from swift.common import swob
from swift.common.swob import Request
@@ -25,6 +26,27 @@ from swift3.test.unit import Swift3TestCase
from swift3.test.unit.test_s3_acl import s3acl
from swift3.subresource import ACL, User, encode_acl, Owner, Grant
from swift3.etree import fromstring
from swift3.test.unit.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 TestSwift3Obj(Swift3TestCase):
@@ -217,6 +239,19 @@ class TestSwift3Obj(Swift3TestCase):
def test_object_GET(self):
self._test_object_GETorHEAD('GET')
@s3acl(s3acl_only=True)
def test_object_GET_with_s3acl_and_keystone(self):
# for passing keystone authentication root
fake_auth = self.swift._fake_auth_middleware
with patch.object(FakeSwift, '_fake_auth_middleware',
_wrap_fake_auth_middleware(fake_auth)):
self._test_object_GETorHEAD('GET')
_, _, headers = self.swift.calls_with_headers[-1]
self.assertTrue('Authorization' not in headers)
_, _, headers = self.swift.calls_with_headers[0]
self.assertTrue('Authorization' not in headers)
@s3acl
def test_object_GET_Range(self):
req = Request.blank('/bucket/object',

View File

@@ -70,6 +70,16 @@ class FakeResponse(object):
resource='object'))
class FakeSwiftResponse(object):
def __init__(self):
self.environ = {
'PATH_INFO': '/v1/AUTH_test',
'HTTP_X_TENANT_NAME': 'test',
'HTTP_X_USER_NAME': 'tester',
'HTTP_X_AUTH_TOKEN': 'token',
}
class TestRequest(Swift3TestCase):
def setUp(self):
@@ -186,6 +196,36 @@ class TestRequest(Swift3TestCase):
self.assertTrue(
'not an integer or within integer range' in result.exception.body)
def test_authenticate_delete_Authorization_from_s3req_headers(self):
req = Request.blank('/bucket/obj',
environ={'REQUEST_METHOD': 'GET'},
headers={'Authorization': 'AWS test:tester:hmac'})
with nested(patch.object(Request, 'get_response'),
patch.object(Request, 'remote_user', 'authorized')) \
as (m_swift_resp, m_remote_user):
m_swift_resp.return_value = FakeSwiftResponse()
s3_req = S3AclRequest(req.environ, MagicMock())
self.assertTrue('HTTP_AUTHORIZATION' not in s3_req.environ)
self.assertTrue('Authorization' not in s3_req.headers)
def test_to_swift_req_Authorization_not_exist_in_swreq_headers(self):
container = 'bucket'
obj = 'obj'
method = 'GET'
req = Request.blank('/%s/%s' % (container, obj),
environ={'REQUEST_METHOD': method},
headers={'Authorization': 'AWS test:tester:hmac'})
with nested(patch.object(Request, 'get_response'),
patch.object(Request, 'remote_user', 'authorized')) \
as (m_swift_resp, m_remote_user):
m_swift_resp.return_value = FakeSwiftResponse()
s3_req = S3AclRequest(req.environ, MagicMock())
sw_req = s3_req.to_swift_req(method, container, obj)
self.assertTrue('HTTP_AUTHORIZATION' not in sw_req.environ)
self.assertTrue('Authorization' not in sw_req.headers)
if __name__ == '__main__':
unittest.main()