trying to make sense of auth middleware and reseller prefix
This commit is contained in:
parent
01059884b3
commit
d5770ee214
@ -60,9 +60,10 @@ Example Authentication with DevAuth:
|
||||
* The external DevAuth server responds with "X-Auth-Groups:
|
||||
test:tester,test,AUTH_storage_xyz"
|
||||
* Now this user will have full access (via authorization procedures later)
|
||||
to the AUTH_storage_xyz Swift storage account and access to other storage
|
||||
accounts with the same `AUTH_` reseller prefix and has an ACL specifying
|
||||
at least one of those three groups returned.
|
||||
to the AUTH_storage_xyz Swift storage account and access to containers in
|
||||
other storage accounts, provided the storage account begins with the same
|
||||
`AUTH_` reseller prefix and the container has an ACL specifying at least
|
||||
one of those three groups returned.
|
||||
|
||||
Authorization is performed through callbacks by the Swift Proxy server to the
|
||||
WSGI environment's swift.authorize value, if one is set. The swift.authorize
|
||||
|
@ -454,7 +454,7 @@ YOU HAVE A FEW OPTIONS:
|
||||
if create_reseller_admin and (
|
||||
request.headers.get('X-Auth-Admin-User') != '.super_admin' or
|
||||
request.headers.get('X-Auth-Admin-Key') != self.super_admin_key):
|
||||
return HTTPForbidden(request=request)
|
||||
return HTTPUnauthorized(request=request)
|
||||
create_account_admin = \
|
||||
request.headers.get('x-auth-user-admin') == 'true'
|
||||
if create_account_admin and \
|
||||
@ -484,7 +484,7 @@ YOU HAVE A FEW OPTIONS:
|
||||
"""
|
||||
if request.headers.get('X-Auth-Admin-User') != '.super_admin' or \
|
||||
request.headers.get('X-Auth-Admin-Key') != self.super_admin_key:
|
||||
return HTTPForbidden(request=request)
|
||||
return HTTPUnauthorized(request=request)
|
||||
result = self.recreate_accounts()
|
||||
return Response(result, 200, request=request)
|
||||
|
||||
|
@ -20,7 +20,7 @@ from webob.exc import HTTPForbidden, HTTPUnauthorized
|
||||
|
||||
from swift.common.bufferedhttp import http_connect_raw as http_connect
|
||||
from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
|
||||
from swift.common.utils import cache_from_env, split_path
|
||||
from swift.common.utils import cache_from_env, split_path, TRUE_VALUES
|
||||
|
||||
|
||||
class DevAuth(object):
|
||||
@ -35,9 +35,33 @@ class DevAuth(object):
|
||||
self.auth_host = conf.get('ip', '127.0.0.1')
|
||||
self.auth_port = int(conf.get('port', 11000))
|
||||
self.ssl = \
|
||||
conf.get('ssl', 'false').lower() in ('true', 'on', '1', 'yes')
|
||||
conf.get('ssl', 'false').lower() in TRUE_VALUES
|
||||
self.timeout = int(conf.get('node_timeout', 10))
|
||||
|
||||
def get_groups(self, token):
|
||||
memcache_client = cache_from_env(env)
|
||||
key = '%s/token/%s' % (self.reseller_prefix, token)
|
||||
cached_auth_data = memcache_client.get(key)
|
||||
if cached_auth_data:
|
||||
start, expiration, groups = cached_auth_data
|
||||
if time() - start > expiration:
|
||||
groups = None
|
||||
if not groups:
|
||||
with Timeout(self.timeout):
|
||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
||||
'/token/%s' % token, ssl=self.ssl)
|
||||
resp = conn.getresponse()
|
||||
resp.read()
|
||||
conn.close()
|
||||
if resp.status // 100 != 2:
|
||||
return None
|
||||
|
||||
expiration = float(resp.getheader('x-auth-ttl'))
|
||||
groups = resp.getheader('x-auth-groups')
|
||||
memcache_client.set(key, (time(), expiration, groups),
|
||||
timeout=expiration)
|
||||
return groups
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
"""
|
||||
Accepts a standard WSGI application call, authenticating the request
|
||||
@ -45,57 +69,48 @@ class DevAuth(object):
|
||||
validation. For an authenticated request, REMOTE_USER will be set to a
|
||||
comma separated list of the user's groups.
|
||||
"""
|
||||
|
||||
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
|
||||
if token and token.startswith(self.reseller_prefix):
|
||||
groups = None
|
||||
memcache_client = cache_from_env(env)
|
||||
key = '%s/token/%s' % (self.reseller_prefix, token)
|
||||
cached_auth_data = memcache_client.get(key)
|
||||
if cached_auth_data:
|
||||
start, expiration, groups = cached_auth_data
|
||||
if time() - start > expiration:
|
||||
groups = None
|
||||
if not groups:
|
||||
with Timeout(self.timeout):
|
||||
conn = http_connect(self.auth_host, self.auth_port, 'GET',
|
||||
'/token/%s' % token, ssl=self.ssl)
|
||||
resp = conn.getresponse()
|
||||
resp.read()
|
||||
conn.close()
|
||||
if resp.status // 100 != 2:
|
||||
if self.reseller_prefix:
|
||||
return HTTPUnauthorized()(env, start_response)
|
||||
else:
|
||||
# If we have no reseller prefix, we can't deny the
|
||||
# request just yet because another auth middleware
|
||||
# might be able to approve.
|
||||
if 'swift.authorize' not in env:
|
||||
env['swift.authorize'] = self.denied_response
|
||||
return self.app(env, start_response)
|
||||
expiration = float(resp.getheader('x-auth-ttl'))
|
||||
groups = resp.getheader('x-auth-groups')
|
||||
memcache_client.set(key, (time(), expiration, groups),
|
||||
timeout=expiration)
|
||||
env['REMOTE_USER'] = groups
|
||||
|
||||
if not self.reseller_prefix:
|
||||
# all requests belong to me
|
||||
if token:
|
||||
# I should attempt to auth any token
|
||||
groups = self.get_groups(token)
|
||||
else:
|
||||
groups = None # no token is same as an unauthorized token
|
||||
if groups:
|
||||
env['REMOTE_USER'] = groups
|
||||
user = groups and groups.split(',', 1)[0] or ''
|
||||
env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
|
||||
env['swift.authorize'] = self.authorize
|
||||
env['swift.clean_acl'] = clean_acl
|
||||
# We know the proxy logs the token, so we augment it just a bit to
|
||||
# also log the authenticated user.
|
||||
user = groups and groups.split(',', 1)[0] or ''
|
||||
env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
|
||||
else:
|
||||
version, rest = split_path(env.get('PATH_INFO', ''), 1, 2, True)
|
||||
if rest and rest.startswith(self.reseller_prefix):
|
||||
# If we don't have a reseller prefix we have no way of knowing
|
||||
# if we should be handling the request, so we only set
|
||||
# swift.authorize if it isn't set already (or we have a
|
||||
# reseller prefix that matches so we know we should handle the
|
||||
# request).
|
||||
if self.reseller_prefix or 'swift.authorize' not in env:
|
||||
# as a reseller, I must respect that just can my auth can't provide
|
||||
# groups for a token, others may
|
||||
if token and token.startswith(self.reseller_prefix)::
|
||||
# attempt to auth my token with my auth server
|
||||
groups = self.get_groups(token)
|
||||
if groups:
|
||||
# authenticated!
|
||||
env['REMOTE_USER'] = groups
|
||||
user = groups and groups.split(',', 1)[0] or ''
|
||||
env['HTTP_X_AUTH_TOKEN'] = '%s,%s' % (user, token)
|
||||
env['swift.authorize'] = self.authorize
|
||||
env['swift.clean_acl'] = clean_acl
|
||||
else:
|
||||
# I can't claim this token, but I might claim the annoynomous request
|
||||
version, rest = split_path(env.get('PATH_INFO', ''), 1, 2, True)
|
||||
if rest and rest.startswith(self.reseller_prefix):
|
||||
# annoynomous access to my reseller's accounts
|
||||
env['swift.authorize'] = self.authorize
|
||||
env['swift.clean_acl'] = clean_acl
|
||||
elif 'swift.authorize' not in env:
|
||||
env['swift.authorize'] = self.denied_response
|
||||
else:
|
||||
# not my token, not my account
|
||||
# good idea regardless...
|
||||
if 'swift.authorize' not in env:
|
||||
env['swift.authorize'] = self.denied_response
|
||||
|
||||
return self.app(env, start_response)
|
||||
|
||||
def authorize(self, req):
|
||||
|
@ -685,7 +685,8 @@ class TestAuthServer(unittest.TestCase):
|
||||
conf = {'swift_dir': self.testdir, 'log_name': 'auth'}
|
||||
self.assertRaises(ValueError, auth_server.AuthController, conf)
|
||||
conf['super_admin_key'] = 'testkey'
|
||||
auth_server.AuthController(conf)
|
||||
controller = auth_server.AuthController(conf)
|
||||
self.assertEquals(controller.super_admin_key, conf['super_admin_key'])
|
||||
|
||||
def test_add_storage_account(self):
|
||||
auth_server.http_connect = fake_http_connect(201)
|
||||
|
Loading…
Reference in New Issue
Block a user