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
This commit is contained in:
wangxiyuan 2018-07-18 11:30:10 +08:00 committed by wangxiyuan
parent ccbd86ba13
commit 818362147d
2 changed files with 18 additions and 13 deletions

View File

@ -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())

View File

@ -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'])