Added swauth_remote mode. Fixes #3

This commit is contained in:
gholt 2011-06-05 23:24:55 +00:00
parent 62e74cb585
commit 96e1a78428
2 changed files with 84 additions and 31 deletions

View File

@ -15,29 +15,33 @@ allow_account_management = true
[filter:swauth]
use = egg:swauth#swauth
# You can override the default log routing for this filter here:
# set log_name = swauth
# set log_facility = LOG_LOCAL0
# set log_level = INFO
# set log_headers = False
# set log_name = swauth
# set log_facility = LOG_LOCAL0
# set log_level = INFO
# set log_headers = False
# The reseller prefix will verify a token begins with this prefix before even
# attempting to validate it. Also, with authorization, only Swift storage
# accounts with this prefix will be authorized by this middleware. Useful if
# multiple auth systems are in use for one Swift cluster.
# reseller_prefix = AUTH
# reseller_prefix = AUTH
# If you wish to use a Swauth service on a remote cluster with this cluster:
# swauth_remote = http://remotehost:port/auth
# swauth_remote_timeout = 10
# When using swauth_remote, the rest of these settings have no effect.
#
# The auth prefix will cause requests beginning with this prefix to be routed
# to the auth subsystem, for granting tokens, creating accounts, users, etc.
# auth_prefix = /auth/
# auth_prefix = /auth/
# Cluster strings are of the format name#url where name is a short name for the
# Swift cluster and url is the url to the proxy server(s) for the cluster.
# default_swift_cluster = local#http://127.0.0.1:8080/v1
# default_swift_cluster = local#http://127.0.0.1:8080/v1
# You may also use the format name#url#url where the first url is the one
# given to users to access their account (public url) and the second is the one
# used by swauth itself to create and delete accounts (private url). This is
# useful when a load balancer url should be used by users, but swauth itself is
# behind the load balancer. Example:
# default_swift_cluster = local#https://public.com:8080/v1#http://private.com:8080/v1
# token_life = 86400
# node_timeout = 10
# default_swift_cluster = local#https://public.com:8080/v1#http://private.com:8080/v1
# token_life = 86400
# Highly recommended to change this. If you comment this out, the Swauth
# administration features will be disabled for this proxy.
super_admin_key = swauthkey

View File

@ -35,7 +35,8 @@ from webob.exc import HTTPAccepted, HTTPBadRequest, HTTPConflict, \
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, get_logger, split_path, urlparse
from swift.common.utils import cache_from_env, get_logger, split_path, \
TRUE_VALUES, urlparse
# NOTE: This should be removed after some time when anyone upgrading Swauth
@ -70,7 +71,7 @@ class Swauth(object):
self.app = app
self.conf = conf
self.logger = get_logger(conf, log_route='swauth')
self.log_headers = conf.get('log_headers') == 'True'
self.log_headers = conf.get('log_headers', 'no').lower() in TRUE_VALUES
self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
if self.reseller_prefix and self.reseller_prefix[-1] != '_':
self.reseller_prefix += '_'
@ -81,6 +82,26 @@ class Swauth(object):
self.auth_prefix = '/' + self.auth_prefix
if self.auth_prefix[-1] != '/':
self.auth_prefix += '/'
self.swauth_remote = conf.get('swauth_remote')
if self.swauth_remote:
self.swauth_remote = self.swauth_remote.rstrip('/')
if not self.swauth_remote:
msg = _('Invalid swauth_remote set in conf file! Exiting.')
try:
self.logger.critical(msg)
except Exception:
pass
raise ValueError(msg)
self.swauth_remote_parsed = urlparse(self.swauth_remote)
if self.swauth_remote_parsed.scheme not in ('http', 'https'):
msg = _('Cannot handle protocol scheme %s for url %s!') % \
(self.swauth_remote_parsed.scheme, repr(self.swauth_remote))
try:
self.logger.critical(msg)
except Exception:
pass
raise ValueError(msg)
self.swauth_remote_timeout = int(conf.get('swauth_remote_timeout', 10))
self.auth_account = '%s.auth' % self.reseller_prefix
self.default_swift_cluster = conf.get('default_swift_cluster',
'local#http://127.0.0.1:8080/v1')
@ -109,7 +130,7 @@ class Swauth(object):
raise Exception('Cannot handle protocol scheme %s for url %s' %
(self.dsc_parsed2.scheme, repr(self.dsc_url2)))
self.super_admin_key = conf.get('super_admin_key')
if not self.super_admin_key:
if not self.super_admin_key and not self.swauth_remote:
msg = _('No super_admin_key set in conf file; Swauth '
'administration features will be disabled.')
try:
@ -146,7 +167,8 @@ class Swauth(object):
"""
if 'HTTP_X_CF_TRANS_ID' not in env:
env['HTTP_X_CF_TRANS_ID'] = 'tx' + str(uuid4())
if env.get('PATH_INFO', '').startswith(self.auth_prefix):
if not self.swauth_remote and \
env.get('PATH_INFO', '').startswith(self.auth_prefix):
return self.handle(env, start_response)
s3 = env.get('HTTP_AUTHORIZATION')
token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
@ -221,6 +243,11 @@ class Swauth(object):
groups = None
if env.get('HTTP_AUTHORIZATION'):
if self.swauth_remote:
# TODO: Support S3-style authorization with swauth_remote mode
self.logger.warn('S3-style authorization not supported yet '
'with swauth_remote mode.')
return None
account = env['HTTP_AUTHORIZATION'].split(' ')[1]
account, user, sign = account.split(':')
path = quote('/v1/%s/%s/%s' % (self.auth_account, account, user))
@ -257,23 +284,45 @@ class Swauth(object):
return groups
if not groups:
path = quote('/v1/%s/.token_%s/%s' %
(self.auth_account, token[-1], token))
resp = self.make_request(env, 'GET', path).get_response(self.app)
if resp.status_int // 100 != 2:
return None
detail = json.loads(resp.body)
if detail['expires'] < time():
self.make_request(env, 'DELETE', path).get_response(self.app)
return None
groups = [g['name'] for g in detail['groups']]
if '.admin' in groups:
groups.remove('.admin')
groups.append(detail['account_id'])
groups = ','.join(groups)
if memcache_client:
memcache_client.set(memcache_key, (detail['expires'], groups),
timeout=float(detail['expires'] - time()))
if self.swauth_remote:
with Timeout(self.swauth_remote_timeout):
conn = http_connect(self.swauth_remote_parsed.hostname,
self.swauth_remote_parsed.port, 'GET',
'%s/v2/.token/%s' % (self.swauth_remote_parsed.path,
quote(token)),
ssl=(self.swauth_remote_parsed.scheme == 'https'))
resp = conn.getresponse()
resp.read()
conn.close()
if resp.status // 100 != 2:
return None
expires_from_now = float(resp.getheader('x-auth-ttl'))
groups = resp.getheader('x-auth-groups')
if memcache_client:
memcache_client.set(memcache_key,
(time() + expires_from_now, groups),
timeout=expires_from_now)
else:
path = quote('/v1/%s/.token_%s/%s' %
(self.auth_account, token[-1], token))
resp = \
self.make_request(env, 'GET', path).get_response(self.app)
if resp.status_int // 100 != 2:
return None
detail = json.loads(resp.body)
if detail['expires'] < time():
self.make_request(env, 'DELETE',
path).get_response(self.app)
return None
groups = [g['name'] for g in detail['groups']]
if '.admin' in groups:
groups.remove('.admin')
groups.append(detail['account_id'])
groups = ','.join(groups)
if memcache_client:
memcache_client.set(memcache_key,
(detail['expires'], groups),
timeout=float(detail['expires'] - time()))
return groups
def authorize(self, req):