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
[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]
use = egg:swift#proxy
@ -521,7 +521,7 @@ use = egg:swift#bulk
[filter: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]
use = egg:swift#slo
# max_manifest_segments = 1000
@ -538,8 +538,7 @@ use = egg:swift#slo
# Time limit on GET requests (seconds)
# max_get_time = 86400
# Note: Put before both ratelimit and auth in the pipeline, but after
# gatekeeper, catch_errors, and proxy_logging (the first instance).
# Note: Put after auth and staticweb in the pipeline.
# If you don't put it in the pipeline, it will be inserted for you.
[filter: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.swob import HTTPNotFound, HTTPForbidden, HTTPUnauthorized
from swift.common.utils import register_swift_info
import functools
class KeystoneAuth(object):
@ -103,7 +104,9 @@ class KeystoneAuth(object):
self.logger.debug('Using identity: %r', identity)
environ['keystone.identity'] = identity
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', []))
if self.reseller_admin_role in user_roles:
environ['reseller_request'] = True
@ -177,9 +180,7 @@ class KeystoneAuth(object):
return s
return None
def authorize(self, req):
env = req.environ
env_identity = self._integral_keystone_identity(env)
def authorize(self, env_identity, req):
tenant_id, tenant_name = env_identity['tenant']
user_id, user_name = env_identity['user']
referrers, roles = swift_acl.parse_acl(getattr(req, 'acl', None))

View File

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

View File

@ -214,7 +214,9 @@ class TestAuthorize(unittest.TestCase):
default_env.update(env)
req = self._make_request(path, headers=headers, environ=default_env)
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 exception and not result:
@ -398,5 +400,20 @@ class TestAuthorize(unittest.TestCase):
env={'REQUEST_METHOD': 'DELETE'})
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__':
unittest.main()