xLO bug with auth tokens expiring during download.

Just put SLO and DLO after any auth middleware. This works because when
the request goes through that middleware in the pipeline the
authentication takes place: validation of the token, setting up who the
user is, and setting the authorization call back. Each subrequest made
for the segments will be subjected to that authorization call back which
verifies the user has access to the individual segments.

To get this to work with keystone, the keystone identity is set up
during __call__ and applied to the authorize function using a
functools.partial. When the authorize function is later called from the
environ by the proxy server the idenity that was set up when the request
passed through the auth middleware is used, not what can be pulled out
of the possibly altered state of the request's environment.

DocImpact
fixes bug: 1315133

Change-Id: I7827dd2d9dfbb3c6424773fb2891355d47e372ba
This commit is contained in:
David Goetz 2014-05-27 09:52:39 -07:00 committed by Peter Portante
parent b519424b91
commit ab510952ef
4 changed files with 30 additions and 12 deletions

View File

@ -70,7 +70,7 @@
# eventlet_debug = false # eventlet_debug = false
[pipeline:main] [pipeline:main]
pipeline = catch_errors gatekeeper healthcheck proxy-logging cache container_sync bulk tempurl slo dlo ratelimit tempauth container-quotas account-quotas proxy-logging proxy-server pipeline = catch_errors gatekeeper healthcheck proxy-logging cache container_sync bulk tempurl ratelimit tempauth container-quotas account-quotas slo dlo proxy-logging proxy-server
[app:proxy-server] [app:proxy-server]
use = egg:swift#proxy use = egg:swift#proxy
@ -521,7 +521,7 @@ use = egg:swift#bulk
[filter:container-quotas] [filter:container-quotas]
use = egg:swift#container_quotas use = egg:swift#container_quotas
# Note: Put before both ratelimit and auth in the pipeline. # Note: Put after auth and staticweb in the pipeline.
[filter:slo] [filter:slo]
use = egg:swift#slo use = egg:swift#slo
# max_manifest_segments = 1000 # max_manifest_segments = 1000
@ -538,8 +538,7 @@ use = egg:swift#slo
# Time limit on GET requests (seconds) # Time limit on GET requests (seconds)
# max_get_time = 86400 # max_get_time = 86400
# Note: Put before both ratelimit and auth in the pipeline, but after # Note: Put after auth and staticweb in the pipeline.
# gatekeeper, catch_errors, and proxy_logging (the first instance).
# If you don't put it in the pipeline, it will be inserted for you. # If you don't put it in the pipeline, it will be inserted for you.
[filter:dlo] [filter:dlo]
use = egg:swift#dlo use = egg:swift#dlo

View File

@ -16,6 +16,7 @@ from swift.common import utils as swift_utils
from swift.common.middleware import acl as swift_acl from swift.common.middleware import acl as swift_acl
from swift.common.swob import HTTPNotFound, HTTPForbidden, HTTPUnauthorized from swift.common.swob import HTTPNotFound, HTTPForbidden, HTTPUnauthorized
from swift.common.utils import register_swift_info from swift.common.utils import register_swift_info
import functools
class KeystoneAuth(object): class KeystoneAuth(object):
@ -103,7 +104,9 @@ class KeystoneAuth(object):
self.logger.debug('Using identity: %r', identity) self.logger.debug('Using identity: %r', identity)
environ['keystone.identity'] = identity environ['keystone.identity'] = identity
environ['REMOTE_USER'] = identity.get('tenant') environ['REMOTE_USER'] = identity.get('tenant')
environ['swift.authorize'] = self.authorize env_identity = self._integral_keystone_identity(environ)
environ['swift.authorize'] = functools.partial(
self.authorize, env_identity)
user_roles = (r.lower() for r in identity.get('roles', [])) user_roles = (r.lower() for r in identity.get('roles', []))
if self.reseller_admin_role in user_roles: if self.reseller_admin_role in user_roles:
environ['reseller_request'] = True environ['reseller_request'] = True
@ -177,9 +180,7 @@ class KeystoneAuth(object):
return s return s
return None return None
def authorize(self, req): def authorize(self, env_identity, req):
env = req.environ
env_identity = self._integral_keystone_identity(env)
tenant_id, tenant_name = env_identity['tenant'] tenant_id, tenant_name = env_identity['tenant']
user_id, user_name = env_identity['user'] user_id, user_name = env_identity['user']
referrers, roles = swift_acl.parse_acl(getattr(req, 'acl', None)) referrers, roles = swift_acl.parse_acl(getattr(req, 'acl', None))

View File

@ -56,10 +56,11 @@ required_filters = [
{'name': 'catch_errors'}, {'name': 'catch_errors'},
{'name': 'gatekeeper', {'name': 'gatekeeper',
'after_fn': lambda pipe: (['catch_errors'] 'after_fn': lambda pipe: (['catch_errors']
if pipe.startswith("catch_errors") if pipe.startswith('catch_errors')
else [])}, else [])},
{'name': 'dlo', 'after_fn': lambda _junk: ['catch_errors', 'gatekeeper', {'name': 'dlo', 'after_fn': lambda _junk: [
'proxy_logging']}] 'staticweb', 'tempauth', 'keystoneauth',
'catch_errors', 'gatekeeper', 'proxy_logging']}]
class Application(object): class Application(object):

View File

@ -214,7 +214,9 @@ class TestAuthorize(unittest.TestCase):
default_env.update(env) default_env.update(env)
req = self._make_request(path, headers=headers, environ=default_env) req = self._make_request(path, headers=headers, environ=default_env)
req.acl = acl req.acl = acl
result = self.test_auth.authorize(req)
env_identity = self.test_auth._integral_keystone_identity(req.environ)
result = self.test_auth.authorize(env_identity, req)
# if we have requested an exception but nothing came back then # if we have requested an exception but nothing came back then
if exception and not result: if exception and not result:
@ -398,5 +400,20 @@ class TestAuthorize(unittest.TestCase):
env={'REQUEST_METHOD': 'DELETE'}) env={'REQUEST_METHOD': 'DELETE'})
self.assertEqual(bool(req.environ.get('swift_owner')), True) self.assertEqual(bool(req.environ.get('swift_owner')), True)
def test_identity_set_up_at_call(self):
def fake_start_response(*args, **kwargs):
pass
the_env = self._get_identity(
tenant_id='test', roles=['reselleradmin'])
self.test_auth(the_env, fake_start_response)
subreq = Request.blank(
'/v1/%s/c/o' % self.test_auth._get_account_for_tenant('test'))
subreq.environ.update(
self._get_identity(tenant_id='test', roles=['got_erased']))
authorize_resp = the_env['swift.authorize'](subreq)
self.assertEqual(authorize_resp, None)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()