s3api: fix AWSAccessKeyId

We use cfaccount as AWSAccessKeyId (something like
AUTH_89308df71f274e33af17779606f08fa0). However, users with the same
account use the same cfaccount. In such case, we can't know which
password should be used as a secret key to calculate the HMAC.

This changes AWSAccessKeyId to the combination of account and user:

Authorization: AWS test:tester:xQE0diMbLRepdf3YB+FIEXAMPLE=

The auth validates the HMAC and sends a cfaccount back to the
proxy. The proxy rewrites the path with the cfaccount.
This commit is contained in:
FUJITA Tomonori 2011-01-17 23:01:54 +00:00 committed by Tarmac
commit 0996cd9b3a
4 changed files with 37 additions and 29 deletions

View File

@ -243,18 +243,17 @@ YOU HAVE A FEW OPTIONS:
raise err
def validate_s3_sign(self, request, token):
cfaccount, sign = request.headers['Authorization'].split(' ')[-1].split(':')
account, user, sign = request.headers['Authorization'].split(' ')[-1].split(':')
msg = base64.urlsafe_b64decode(unquote(token))
rv = False
with self.get_conn() as conn:
row = conn.execute('''
SELECT account, user, password FROM account
WHERE cfaccount = ?''',
(cfaccount,)).fetchone()
rv = (84000, row[0], row[1], cfaccount)
SELECT password, cfaccount FROM account
WHERE account = ? AND user = ?''',
(account, user)).fetchone()
rv = (84000, account, user, row[1])
if rv:
s = base64.encodestring(hmac.new(row[2], msg, sha1).digest()).strip()
s = base64.encodestring(hmac.new(row[0], msg, sha1).digest()).strip()
self.logger.info("orig %s, calc %s" % (sign, s))
if sign != s:
rv = False
@ -440,8 +439,10 @@ YOU HAVE A FEW OPTIONS:
except ValueError:
return HTTPBadRequest()
# Retrieves (TTL, account, user, cfaccount) if valid, False otherwise
headers = {}
if 'Authorization' in request.headers:
validation = self.validate_s3_sign(request, token)
headers['X-Auth-Account-Suffix'] = validation[3]
else:
validation = self.validate_token(token)
if not validation:
@ -451,8 +452,9 @@ YOU HAVE A FEW OPTIONS:
# admin access to a cfaccount or ".reseller_admin" to access to all
# accounts, including creating new ones.
groups.append(validation[3])
return HTTPNoContent(headers={'X-Auth-TTL': validation[0],
'X-Auth-Groups': ','.join(groups)})
headers['X-Auth-TTL'] = validation[0]
headers['X-Auth-Groups'] = ','.join(groups)
return HTTPNoContent(headers=headers)
def handle_add_user(self, request):
"""

View File

@ -134,8 +134,7 @@ class DevAuth(object):
headers = {}
if env.get('HTTP_AUTHORIZATION'):
groups = None
if env.get('HTTP_AUTHORIZATION'):
headers["Authorization"] = env.get('HTTP_AUTHORIZATION')
headers["Authorization"] = env.get('HTTP_AUTHORIZATION')
if not groups:
with Timeout(self.timeout):
@ -153,6 +152,13 @@ class DevAuth(object):
if memcache_client:
memcache_client.set(key, (time(), expiration, groups),
timeout=expiration)
if env.get('HTTP_AUTHORIZATION'):
account, user, sign = env['HTTP_AUTHORIZATION'].split(' ')[-1].split(':')
cfaccount = resp.getheader('x-auth-account-suffix')
path = env['PATH_INFO']
env['PATH_INFO'] = path.replace("%s:%s" % (account, user), cfaccount, 1)
return groups
def authorize(self, req):

View File

@ -400,11 +400,11 @@ class Swift3Middleware(object):
h += header.lower() + ":" + str(req.headers[header]) + "\n"
h += req.path
try:
account, _ = req.headers['Authorization'].split(' ')[-1].split(':')
account, user, _ = req.headers['Authorization'].split(' ')[-1].split(':')
except:
return None, None
token = base64.urlsafe_b64encode(h)
return account, token
return '%s:%s' % (account, user), token
def __call__(self, env, start_response):
req = Request(env)

View File

@ -209,7 +209,7 @@ class TestSwift3(unittest.TestCase):
def test_bad_path(self):
req = Request.blank('/bucket/object/bad',
environ={'REQUEST_METHOD': 'GET'},
headers={'Authorization': 'AUTH_something:hoge'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = self.app(req.environ, start_response)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.firstChild.nodeName, 'Error')
@ -219,7 +219,7 @@ class TestSwift3(unittest.TestCase):
def test_bad_method(self):
req = Request.blank('/',
environ={'REQUEST_METHOD': 'PUT'},
headers={'Authorization': 'AUTH_something:hoge'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = self.app(req.environ, start_response)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.firstChild.nodeName, 'Error')
@ -230,7 +230,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(cl(status))
req = Request.blank(path,
environ={'REQUEST_METHOD': method},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, start_response)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.firstChild.nodeName, 'Error')
@ -246,7 +246,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppService())
req = Request.blank('/',
environ={'REQUEST_METHOD': 'GET'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '200')
@ -279,7 +279,7 @@ class TestSwift3(unittest.TestCase):
bucket_name = 'junk'
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '200')
@ -307,7 +307,7 @@ class TestSwift3(unittest.TestCase):
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'max-keys=3'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.getElementsByTagName('IsTruncated')[0].
@ -316,7 +316,7 @@ class TestSwift3(unittest.TestCase):
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'max-keys=2'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.getElementsByTagName('IsTruncated')[0].
@ -335,7 +335,7 @@ class TestSwift3(unittest.TestCase):
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'max-keys=5'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, lambda *args: None)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.getElementsByTagName('MaxKeys')[0].
@ -346,7 +346,7 @@ class TestSwift3(unittest.TestCase):
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'max-keys=5000'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, lambda *args: None)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.getElementsByTagName('MaxKeys')[0].
@ -366,7 +366,7 @@ class TestSwift3(unittest.TestCase):
req = Request.blank('/%s' % bucket_name,
environ={'REQUEST_METHOD': 'GET', 'QUERY_STRING':
'delimiter=a&marker=b&prefix=c'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, lambda *args: None)
dom = xml.dom.minidom.parseString("".join(resp))
self.assertEquals(dom.getElementsByTagName('Prefix')[0].
@ -392,7 +392,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppBucket(201))
req = Request.blank('/bucket',
environ={'REQUEST_METHOD': 'PUT'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '200')
@ -410,7 +410,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppBucket(204))
req = Request.blank('/bucket',
environ={'REQUEST_METHOD': 'DELETE'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '204')
@ -418,7 +418,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppObject())
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': method},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '200')
@ -468,7 +468,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppObject(201))
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'PUT'},
headers={'Authorization': 'AUTH_who:password',
headers={'Authorization': 'AWS test:tester:hmac',
'x-amz-storage-class': 'REDUCED_REDUNDANCY',
'Content-MD5': 'Gyz1NfJ3Mcl0NDZFo5hTKA=='})
req.date = datetime.now()
@ -490,7 +490,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(app)
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'PUT'},
headers={'Authorization': 'AUTH_who:password',
headers={'Authorization': 'AWS test:tester:hmac',
'X-Amz-Storage-Class': 'REDUCED_REDUNDANCY',
'X-Amz-Meta-Something': 'oh hai',
'X-Amz-Copy-Source': '/some/source',
@ -518,7 +518,7 @@ class TestSwift3(unittest.TestCase):
local_app = swift3.filter_factory({})(FakeAppObject(204))
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'DELETE'},
headers={'Authorization': 'AUTH_who:password'})
headers={'Authorization': 'AWS test:tester:hmac'})
resp = local_app(req.environ, local_app.app.do_start_response)
self.assertEquals(local_app.app.response_args[0].split()[0], '204')