From ab510952efdd5333e197d23042816ba6ecfa3339 Mon Sep 17 00:00:00 2001 From: David Goetz Date: Tue, 27 May 2014 09:52:39 -0700 Subject: [PATCH] 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 --- etc/proxy-server.conf-sample | 7 +++---- swift/common/middleware/keystoneauth.py | 9 +++++---- swift/proxy/server.py | 7 ++++--- .../common/middleware/test_keystoneauth.py | 19 ++++++++++++++++++- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 7cb87ac68e..63ebf4146f 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -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 diff --git a/swift/common/middleware/keystoneauth.py b/swift/common/middleware/keystoneauth.py index 096af45083..fb15195d4f 100644 --- a/swift/common/middleware/keystoneauth.py +++ b/swift/common/middleware/keystoneauth.py @@ -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)) diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 057abab38c..59123ae8f2 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -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): diff --git a/test/unit/common/middleware/test_keystoneauth.py b/test/unit/common/middleware/test_keystoneauth.py index 70a4b33262..575dd7757e 100644 --- a/test/unit/common/middleware/test_keystoneauth.py +++ b/test/unit/common/middleware/test_keystoneauth.py @@ -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()