diff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py index dcc0fb361b..9050745a39 100644 --- a/swift/common/middleware/s3api/s3request.py +++ b/swift/common/middleware/s3api/s3request.py @@ -1142,7 +1142,7 @@ class S3Request(swob.Request): Create a Swift request based on this request's environment. """ if self.account is None: - account = self.access_key + account = swob.str_to_wsgi(self.access_key) else: account = self.account diff --git a/swift/common/middleware/s3api/s3token.py b/swift/common/middleware/s3api/s3token.py index 3693b2f28d..23ebcc7086 100644 --- a/swift/common/middleware/s3api/s3token.py +++ b/swift/common/middleware/s3api/s3token.py @@ -65,7 +65,7 @@ import six from six.moves import urllib from swift.common.swob import Request, HTTPBadRequest, HTTPUnauthorized, \ - HTTPException + HTTPException, str_to_wsgi from swift.common.utils import config_true_value, split_path, get_logger, \ cache_from_env, append_underscore from swift.common.wsgi import ConfigFileError @@ -404,7 +404,7 @@ class S3Token(object): self._logger.debug('Connecting with tenant: %s', tenant_to_connect) new_tenant_name = '%s%s' % (self._reseller_prefix, tenant_to_connect) environ['PATH_INFO'] = environ['PATH_INFO'].replace( - account, new_tenant_name, 1) + str_to_wsgi(account), str_to_wsgi(new_tenant_name), 1) return self._app(environ, start_response) diff --git a/swift/common/middleware/tempauth.py b/swift/common/middleware/tempauth.py index 49541329b1..52d6659a72 100644 --- a/swift/common/middleware/tempauth.py +++ b/swift/common/middleware/tempauth.py @@ -184,9 +184,11 @@ import base64 from eventlet import Timeout import six from swift.common.memcached import MemcacheConnectionError -from swift.common.swob import Response, Request, wsgi_to_str -from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \ - HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable +from swift.common.swob import ( + Response, Request, wsgi_to_str, str_to_wsgi, wsgi_unquote, + HTTPBadRequest, HTTPForbidden, HTTPNotFound, + HTTPUnauthorized, HTTPMethodNotAllowed, HTTPServiceUnavailable, +) from swift.common.request_helpers import get_sys_meta_prefix from swift.common.middleware.acl import ( @@ -469,7 +471,7 @@ class TempAuth(object): if not s3_auth_details['check_signature'](user['key']): return None env['PATH_INFO'] = env['PATH_INFO'].replace( - account_user, account_id, 1) + str_to_wsgi(account_user), wsgi_unquote(account_id), 1) groups = self._get_user_groups(account, account_user, account_id) return groups diff --git a/test/unit/common/middleware/s3api/__init__.py b/test/unit/common/middleware/s3api/__init__.py index d34d7e83bf..3319a455fc 100644 --- a/test/unit/common/middleware/s3api/__init__.py +++ b/test/unit/common/middleware/s3api/__init__.py @@ -46,10 +46,12 @@ class FakeAuthApp(object): E.g. '/v1/test:tester/bucket/object' will become '/v1/AUTH_test/bucket/object'. This method emulates the behavior. """ - tenant_user = env['s3api.auth_details']['access_key'] + tenant_user = swob.str_to_wsgi(env['s3api.auth_details']['access_key']) tenant, user = tenant_user.rsplit(':', 1) path = env['PATH_INFO'] + # Make sure it's valid WSGI + swob.wsgi_to_str(path) env['PATH_INFO'] = path.replace(tenant_user, 'AUTH_' + tenant) @staticmethod diff --git a/test/unit/common/middleware/s3api/test_s3token.py b/test/unit/common/middleware/s3api/test_s3token.py index bef6cf9e7b..6a467bd3a0 100644 --- a/test/unit/common/middleware/s3api/test_s3token.py +++ b/test/unit/common/middleware/s3api/test_s3token.py @@ -199,7 +199,8 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): self.middleware(req.environ, self.start_fake_response) self.assertEqual(self.response_status, 200) - def _assert_authorized(self, req, account_path='/v1/AUTH_TENANT_ID/'): + def _assert_authorized(self, req, account_path='/v1/AUTH_TENANT_ID/', + access_key='access'): self.assertTrue( req.path.startswith(account_path), '%r does not start with %r' % (req.path, account_path)) @@ -224,7 +225,7 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): self.assertEqual(1, self.requests_mock.call_count) request_call = self.requests_mock.request_history[0] self.assertEqual(json.loads(request_call.body), {'credentials': { - 'access': 'access', + 'access': access_key, 'signature': 'signature', 'token': base64.urlsafe_b64encode(b'token').decode('ascii')}}) @@ -501,6 +502,18 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): self._assert_authorized(req, account_path='/v1/') self.assertEqual(req.environ['PATH_INFO'], '/v1/AUTH_TENANT_ID/c/o') + def test_authorize_with_unicode_access_key(self): + req = Request.blank('/v1/acc\xc3\xa9sskey/c/o') + req.environ['s3api.auth_details'] = { + 'access_key': u'acc\u00e9ss', + 'signature': u'signature', + 'string_to_sign': u'token', + } + req.get_response(self.middleware) + self._assert_authorized(req, account_path='/v1/', + access_key=u'acc\u00e9ss') + self.assertEqual(req.environ['PATH_INFO'], '/v1/AUTH_TENANT_ID/c/o') + def test_authorize_with_access_key_and_unquote_chars(self): req = Request.blank('/v1/access%key=/c/o') req.environ['s3api.auth_details'] = { diff --git a/test/unit/common/middleware/test_tempauth.py b/test/unit/common/middleware/test_tempauth.py index a29652fe2c..9d2cb38abc 100644 --- a/test/unit/common/middleware/test_tempauth.py +++ b/test/unit/common/middleware/test_tempauth.py @@ -308,6 +308,29 @@ class TestAuth(unittest.TestCase): self.assertEqual(req.environ['swift.authorize'], local_auth.authorize) + def test_auth_with_s3api_unicode_authorization_good(self): + local_app = FakeApp() + conf = {u'user_t\u00e9st_t\u00e9ster': u'p\u00e1ss .admin'} + access_key = u't\u00e9st:t\u00e9ster' + if six.PY2: + conf = {k.encode('utf8'): v.encode('utf8') + for k, v in conf.items()} + access_key = access_key.encode('utf8') + local_auth = auth.filter_factory(conf)(local_app) + req = self._make_request('/v1/t\xc3\xa9st:t\xc3\xa9ster', environ={ + 's3api.auth_details': { + 'access_key': access_key, + 'signature': b64encode('sig'), + 'string_to_sign': 't', + 'check_signature': lambda secret: True}}) + resp = req.get_response(local_auth) + + self.assertEqual(resp.status_int, 404) + self.assertEqual(local_app.calls, 1) + self.assertEqual(req.environ['PATH_INFO'], '/v1/AUTH_t\xc3\xa9st') + self.assertEqual(req.environ['swift.authorize'], + local_auth.authorize) + def test_auth_with_swift3_authorization_invalid(self): local_app = FakeApp() local_auth = auth.filter_factory(