s3 compat additions for swauth
This commit is contained in:
@@ -23,6 +23,9 @@ from traceback import format_exc
|
|||||||
from urllib import quote, unquote
|
from urllib import quote, unquote
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
from hashlib import md5, sha1
|
||||||
|
import hmac
|
||||||
|
import base64
|
||||||
|
|
||||||
from eventlet.timeout import Timeout
|
from eventlet.timeout import Timeout
|
||||||
from webob import Response, Request
|
from webob import Response, Request
|
||||||
@@ -123,8 +126,9 @@ class Swauth(object):
|
|||||||
env['HTTP_X_CF_TRANS_ID'] = 'tx' + str(uuid4())
|
env['HTTP_X_CF_TRANS_ID'] = 'tx' + str(uuid4())
|
||||||
if env.get('PATH_INFO', '').startswith(self.auth_prefix):
|
if env.get('PATH_INFO', '').startswith(self.auth_prefix):
|
||||||
return self.handle(env, start_response)
|
return self.handle(env, start_response)
|
||||||
|
s3 = env.get('HTTP_AUTHORIZATION')
|
||||||
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
|
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
|
||||||
if token and token.startswith(self.reseller_prefix):
|
if s3 or (token and token.startswith(self.reseller_prefix)):
|
||||||
# Note: Empty reseller_prefix will match all tokens.
|
# Note: Empty reseller_prefix will match all tokens.
|
||||||
groups = self.get_groups(env, token)
|
groups = self.get_groups(env, token)
|
||||||
if groups:
|
if groups:
|
||||||
@@ -132,7 +136,8 @@ class Swauth(object):
|
|||||||
user = groups and groups.split(',', 1)[0] or ''
|
user = groups and groups.split(',', 1)[0] or ''
|
||||||
# We know the proxy logs the token, so we augment it just a bit
|
# We know the proxy logs the token, so we augment it just a bit
|
||||||
# to also log the authenticated user.
|
# to also log the authenticated user.
|
||||||
env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
|
env['HTTP_X_AUTH_TOKEN'] = \
|
||||||
|
'%s,%s' % (user, 's3' if s3 else token)
|
||||||
env['swift.authorize'] = self.authorize
|
env['swift.authorize'] = self.authorize
|
||||||
env['swift.clean_acl'] = clean_acl
|
env['swift.clean_acl'] = clean_acl
|
||||||
else:
|
else:
|
||||||
@@ -192,6 +197,43 @@ class Swauth(object):
|
|||||||
expires, groups = cached_auth_data
|
expires, groups = cached_auth_data
|
||||||
if expires < time():
|
if expires < time():
|
||||||
groups = None
|
groups = None
|
||||||
|
|
||||||
|
if env.get('HTTP_AUTHORIZATION'):
|
||||||
|
account = env['HTTP_AUTHORIZATION'].split(' ')[1]
|
||||||
|
account, user, sign = account.split(':')
|
||||||
|
path = quote('/v1/%s/%s/%s' % (self.auth_account, account, user))
|
||||||
|
resp = self.make_request(env, 'GET', path).get_response(self.app)
|
||||||
|
if resp.status_int // 100 != 2:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if 'x-object-meta-account-id' in resp.headers:
|
||||||
|
account_id = resp.headers['x-object-meta-account-id']
|
||||||
|
else:
|
||||||
|
path = quote('/v1/%s/%s' % (self.auth_account, account))
|
||||||
|
resp2 = self.make_request(env, 'HEAD',
|
||||||
|
path).get_response(self.app)
|
||||||
|
if resp2.status_int // 100 != 2:
|
||||||
|
return None
|
||||||
|
account_id = resp2.headers['x-container-meta-account-id']
|
||||||
|
|
||||||
|
path = env['PATH_INFO']
|
||||||
|
env['PATH_INFO'] = path.replace("%s:%s" % (account, user),
|
||||||
|
account_id, 1)
|
||||||
|
detail = json.loads(resp.body)
|
||||||
|
|
||||||
|
password = detail['auth'].split(':')[-1]
|
||||||
|
msg = base64.urlsafe_b64decode(unquote(token))
|
||||||
|
s = base64.encodestring(hmac.new(detail['auth'].split(':')[-1],
|
||||||
|
msg, sha1).digest()).strip()
|
||||||
|
if s != sign:
|
||||||
|
return None
|
||||||
|
groups = [g['name'] for g in detail['groups']]
|
||||||
|
if '.admin' in groups:
|
||||||
|
groups.remove('.admin')
|
||||||
|
groups.append(account_id)
|
||||||
|
groups = ','.join(groups)
|
||||||
|
return groups
|
||||||
|
|
||||||
if not groups:
|
if not groups:
|
||||||
path = quote('/v1/%s/.token_%s/%s' %
|
path = quote('/v1/%s/.token_%s/%s' %
|
||||||
(self.auth_account, token[-1], token))
|
(self.auth_account, token[-1], token))
|
||||||
@@ -839,6 +881,15 @@ class Swauth(object):
|
|||||||
return HTTPForbidden(request=req)
|
return HTTPForbidden(request=req)
|
||||||
elif not self.is_account_admin(req, account):
|
elif not self.is_account_admin(req, account):
|
||||||
return HTTPForbidden(request=req)
|
return HTTPForbidden(request=req)
|
||||||
|
|
||||||
|
path = quote('/v1/%s/%s' % (self.auth_account, account))
|
||||||
|
resp = self.make_request(req.environ, 'HEAD',
|
||||||
|
path).get_response(self.app)
|
||||||
|
if resp.status_int // 100 != 2:
|
||||||
|
raise Exception('Could not retrieve account id value: %s %s' %
|
||||||
|
(path, resp.status))
|
||||||
|
headers = {'X-Object-Meta-Account-Id':
|
||||||
|
resp.headers['x-container-meta-account-id']}
|
||||||
# Create the object in the main auth account (this object represents
|
# Create the object in the main auth account (this object represents
|
||||||
# the user)
|
# the user)
|
||||||
path = quote('/v1/%s/%s/%s' % (self.auth_account, account, user))
|
path = quote('/v1/%s/%s/%s' % (self.auth_account, account, user))
|
||||||
@@ -847,9 +898,10 @@ class Swauth(object):
|
|||||||
groups.append('.admin')
|
groups.append('.admin')
|
||||||
if reseller_admin:
|
if reseller_admin:
|
||||||
groups.append('.reseller_admin')
|
groups.append('.reseller_admin')
|
||||||
resp = self.make_request(req.environ, 'PUT', path, json.dumps({'auth':
|
resp = self.make_request(req.environ, 'PUT', path,
|
||||||
'plaintext:%s' % key,
|
json.dumps({'auth': 'plaintext:%s' % key,
|
||||||
'groups': [{'name': g} for g in groups]})).get_response(self.app)
|
'groups': [{'name': g} for g in groups]}),
|
||||||
|
headers=headers).get_response(self.app)
|
||||||
if resp.status_int == 404:
|
if resp.status_int == 404:
|
||||||
return HTTPNotFound(request=req)
|
return HTTPNotFound(request=req)
|
||||||
if resp.status_int // 100 != 2:
|
if resp.status_int // 100 != 2:
|
||||||
|
@@ -2561,6 +2561,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
|
|
||||||
def test_put_user_regular_success(self):
|
def test_put_user_regular_success(self):
|
||||||
self.test_auth.app = FakeApp(iter([
|
self.test_auth.app = FakeApp(iter([
|
||||||
|
('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
|
||||||
# PUT of user object
|
# PUT of user object
|
||||||
('201 Created', {}, '')]))
|
('201 Created', {}, '')]))
|
||||||
resp = Request.blank('/auth/v2/act/usr',
|
resp = Request.blank('/auth/v2/act/usr',
|
||||||
@@ -2570,13 +2571,14 @@ class TestAuth(unittest.TestCase):
|
|||||||
'X-Auth-User-Key': 'key'}
|
'X-Auth-User-Key': 'key'}
|
||||||
).get_response(self.test_auth)
|
).get_response(self.test_auth)
|
||||||
self.assertEquals(resp.status_int, 201)
|
self.assertEquals(resp.status_int, 201)
|
||||||
self.assertEquals(self.test_auth.app.calls, 1)
|
self.assertEquals(self.test_auth.app.calls, 2)
|
||||||
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
||||||
{"groups": [{"name": "act:usr"}, {"name": "act"}],
|
{"groups": [{"name": "act:usr"}, {"name": "act"}],
|
||||||
"auth": "plaintext:key"})
|
"auth": "plaintext:key"})
|
||||||
|
|
||||||
def test_put_user_account_admin_success(self):
|
def test_put_user_account_admin_success(self):
|
||||||
self.test_auth.app = FakeApp(iter([
|
self.test_auth.app = FakeApp(iter([
|
||||||
|
('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
|
||||||
# PUT of user object
|
# PUT of user object
|
||||||
('201 Created', {}, '')]))
|
('201 Created', {}, '')]))
|
||||||
resp = Request.blank('/auth/v2/act/usr',
|
resp = Request.blank('/auth/v2/act/usr',
|
||||||
@@ -2587,7 +2589,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
'X-Auth-User-Admin': 'true'}
|
'X-Auth-User-Admin': 'true'}
|
||||||
).get_response(self.test_auth)
|
).get_response(self.test_auth)
|
||||||
self.assertEquals(resp.status_int, 201)
|
self.assertEquals(resp.status_int, 201)
|
||||||
self.assertEquals(self.test_auth.app.calls, 1)
|
self.assertEquals(self.test_auth.app.calls, 2)
|
||||||
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
||||||
{"groups": [{"name": "act:usr"}, {"name": "act"},
|
{"groups": [{"name": "act:usr"}, {"name": "act"},
|
||||||
{"name": ".admin"}],
|
{"name": ".admin"}],
|
||||||
@@ -2595,6 +2597,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
|
|
||||||
def test_put_user_reseller_admin_success(self):
|
def test_put_user_reseller_admin_success(self):
|
||||||
self.test_auth.app = FakeApp(iter([
|
self.test_auth.app = FakeApp(iter([
|
||||||
|
('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
|
||||||
# PUT of user object
|
# PUT of user object
|
||||||
('201 Created', {}, '')]))
|
('201 Created', {}, '')]))
|
||||||
resp = Request.blank('/auth/v2/act/usr',
|
resp = Request.blank('/auth/v2/act/usr',
|
||||||
@@ -2605,7 +2608,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
'X-Auth-User-Reseller-Admin': 'true'}
|
'X-Auth-User-Reseller-Admin': 'true'}
|
||||||
).get_response(self.test_auth)
|
).get_response(self.test_auth)
|
||||||
self.assertEquals(resp.status_int, 201)
|
self.assertEquals(resp.status_int, 201)
|
||||||
self.assertEquals(self.test_auth.app.calls, 1)
|
self.assertEquals(self.test_auth.app.calls, 2)
|
||||||
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
self.assertEquals(json.loads(self.test_auth.app.request.body),
|
||||||
{"groups": [{"name": "act:usr"}, {"name": "act"},
|
{"groups": [{"name": "act:usr"}, {"name": "act"},
|
||||||
{"name": ".admin"}, {"name": ".reseller_admin"}],
|
{"name": ".admin"}, {"name": ".reseller_admin"}],
|
||||||
@@ -2613,6 +2616,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
|
|
||||||
def test_put_user_fail_not_found(self):
|
def test_put_user_fail_not_found(self):
|
||||||
self.test_auth.app = FakeApp(iter([
|
self.test_auth.app = FakeApp(iter([
|
||||||
|
('200 Ok', {'X-Container-Meta-Account-Id': 'AUTH_cfa'}, ''),
|
||||||
# PUT of user object
|
# PUT of user object
|
||||||
('404 Not Found', {}, '')]))
|
('404 Not Found', {}, '')]))
|
||||||
resp = Request.blank('/auth/v2/act/usr',
|
resp = Request.blank('/auth/v2/act/usr',
|
||||||
@@ -2622,7 +2626,7 @@ class TestAuth(unittest.TestCase):
|
|||||||
'X-Auth-User-Key': 'key'}
|
'X-Auth-User-Key': 'key'}
|
||||||
).get_response(self.test_auth)
|
).get_response(self.test_auth)
|
||||||
self.assertEquals(resp.status_int, 404)
|
self.assertEquals(resp.status_int, 404)
|
||||||
self.assertEquals(self.test_auth.app.calls, 1)
|
self.assertEquals(self.test_auth.app.calls, 2)
|
||||||
|
|
||||||
def test_put_user_fail(self):
|
def test_put_user_fail(self):
|
||||||
self.test_auth.app = FakeApp(iter([
|
self.test_auth.app = FakeApp(iter([
|
||||||
|
Reference in New Issue
Block a user