From 818362147d0622752d051bd78ae839497a602e44 Mon Sep 17 00:00:00 2001 From: wangxiyuan Date: Wed, 18 Jul 2018 11:30:10 +0800 Subject: [PATCH] Do not quote '+' for token header The token in request header may contain url char, such as '+', if quote it, '+' will change to '%2B' which will lead to 401 error. Our CI doesn't notice this bug because Keystone use fernet token which doesn't contain url char by default. But token format in keystone is plugable, some out-tree token formats may contain url char (for example, PKI/PKIZ token). So we should skip quote token to avoiding information changing. Closes-bug: #1783290 Change-Id: I5aa71b3e2b9b19581e46ccf8a80eda5d637f17d1 --- glanceclient/common/http.py | 25 +++++++++++++++---------- glanceclient/tests/unit/test_http.py | 6 +++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py index 84cdc698..b5bea8a9 100644 --- a/glanceclient/common/http.py +++ b/glanceclient/common/http.py @@ -42,6 +42,7 @@ LOG = logging.getLogger(__name__) USER_AGENT = 'python-glanceclient' CHUNKSIZE = 1024 * 64 # 64kB REQ_ID_HEADER = 'X-OpenStack-Request-ID' +TOKEN_HEADERS = ['X-Auth-Token', 'X-Service-Token'] def encode_headers(headers): @@ -61,16 +62,20 @@ def encode_headers(headers): # Bug #1766235: According to RFC 8187, headers must be encoded as ASCII. # So we first %-encode them to get them into range < 128 and then turn # them into ASCII. - if six.PY2: - # incoming items may be unicode, so get them into something - # the py2 version of urllib can handle before percent encoding - encoded_dict = dict((urlparse.quote(encodeutils.safe_encode(h)), - urlparse.quote(encodeutils.safe_encode(v))) - for h, v in headers.items() if v is not None) - else: - encoded_dict = dict((urlparse.quote(h), urlparse.quote(v)) - for h, v in headers.items() if v is not None) - + encoded_dict = {} + for h, v in headers.items(): + if v is not None: + # if the item is token, do not quote '+' as well. + safe = '+/' if h in TOKEN_HEADERS else '/' + if six.PY2: + # incoming items may be unicode, so get them into something + # the py2 version of urllib can handle before percent encoding + key = urlparse.quote(encodeutils.safe_encode(h), safe) + value = urlparse.quote(encodeutils.safe_encode(v), safe) + else: + key = urlparse.quote(h, safe) + value = urlparse.quote(v, safe) + encoded_dict[key] = value return dict((encodeutils.safe_encode(h, encoding='ascii'), encodeutils.safe_encode(v, encoding='ascii')) for h, v in encoded_dict.items()) diff --git a/glanceclient/tests/unit/test_http.py b/glanceclient/tests/unit/test_http.py index efd15bfb..cdc18953 100644 --- a/glanceclient/tests/unit/test_http.py +++ b/glanceclient/tests/unit/test_http.py @@ -467,11 +467,11 @@ class TestClient(testtools.TestCase): headers = self.mock.last_request.headers self.assertEqual(refreshed_token, headers['X-Auth-Token']) # regression check for bug 1448080 - unicode_token = u'ni\xf1o' + unicode_token = u'ni\xf1o+' http_client.auth_token = unicode_token http_client.get(path) headers = self.mock.last_request.headers # Bug #1766235: According to RFC 8187, headers must be # encoded as 7-bit ASCII, so expect to see only displayable - # chars in percent-encoding - self.assertEqual(b'ni%C3%B1o', headers['X-Auth-Token']) + # chars in percent-encoding. The '+' char will be not be changed. + self.assertEqual(b'ni%C3%B1o+', headers['X-Auth-Token'])