diff options
author | Zhao Chao <zhaochao1984@gmail.com> | 2018-01-22 22:14:32 +0800 |
---|---|---|
committer | Zhao Chao <zhaochao1984@gmail.com> | 2018-01-23 20:06:40 +0800 |
commit | 08ea56b21c25d9a8eee125bfa8379fc0ab4b879c (patch) | |
tree | 70445506e5edd958a0bcf5be5be67623e8b4e6cd /trove | |
parent | 7232a2b857a023040d5027850aea3fb2ee8ba495 (diff) | |
download | trove-08ea56b21c25d9a8eee125bfa8379fc0ab4b879c.tar.gz |
Fix api exception with unicode tenant name.
There are a lot request debug logging in Trove, when some values of
headers are encoded in utf8, UnicodeEncodeError will be raised by
webob.Request. Override how webob.Request is represented will fix.
Closes-Bug: #1720121
Change-Id: I91683b8dd24262b0f643e8d2bc7886a7c03be40a
Signed-off-by: Zhao Chao <zhaochao1984@gmail.com>
Diffstat (limited to 'trove')
-rw-r--r-- | trove/common/auth.py | 4 | ||||
-rw-r--r-- | trove/common/base_wsgi.py | 3 | ||||
-rw-r--r-- | trove/common/utils.py | 26 | ||||
-rw-r--r-- | trove/tests/unittests/common/test_auth.py | 35 | ||||
-rw-r--r-- | trove/tests/unittests/common/test_utils.py | 13 |
5 files changed, 80 insertions, 1 deletions
diff --git a/trove/common/auth.py b/trove/common/auth.py index 674a9634..9db8a7ab 100644 --- a/trove/common/auth.py +++ b/trove/common/auth.py @@ -21,6 +21,7 @@ import webob.exc from trove.common import exception from trove.common.i18n import _ +from trove.common.utils import req_to_text from trove.common import wsgi LOG = logging.getLogger(__name__) @@ -64,7 +65,8 @@ class TenantBasedAuth(object): LOG.debug(strutils.mask_password( _("Authorized tenant '%(tenant_id)s' request: " "%(request)s") % - {'tenant_id': tenant_id, 'request': request})) + {'tenant_id': tenant_id, + 'request': req_to_text(request)})) return True msg = _( diff --git a/trove/common/base_wsgi.py b/trove/common/base_wsgi.py index a43016cf..da0a0275 100644 --- a/trove/common/base_wsgi.py +++ b/trove/common/base_wsgi.py @@ -41,6 +41,7 @@ from xml.parsers import expat from trove.common import base_exception from trove.common.i18n import _ +from trove.common.utils import req_to_text from trove.common import xmlutils socket_opts = [ @@ -332,6 +333,8 @@ class Request(webob.Request): raise base_exception.InvalidContentType(content_type=content_type) return content_type + __str__ = req_to_text + class Resource(object): """ diff --git a/trove/common/utils.py b/trove/common/utils.py index 2481d82a..fa5b1fc2 100644 --- a/trove/common/utils.py +++ b/trove/common/utils.py @@ -26,6 +26,7 @@ import jinja2 from oslo_concurrency import processutils from oslo_log import log as logging from oslo_service import loopingcall +from oslo_utils.encodeutils import safe_encode from oslo_utils import importutils from oslo_utils import strutils from passlib import pwd @@ -383,3 +384,28 @@ def to_mb(bytes): size = bytes / 1024.0 ** 2 # Make sure we don't return 0.0 if the size is greater than 0 return max(round(size, 2), 0.01) + + +def req_to_text(req): + """ + We do a lot request logging for debug, but if the value of one + requst header is encoded in utf-8, an UnicodeEncodeError will + be raised. So we should carefully encode request headers. + + To be consitent with webob, main procedures are copied from + webob.Request.as_bytes. + """ + url = req.url + host = req.host_url + assert url.startswith(host) + url = url[len(host):] + parts = [safe_encode('%s %s %s' % (req.method, url, req.http_version))] + + for k, v in sorted(req.headers.items()): + header = safe_encode('%s: %s' % (k, v)) + parts.append(header) + + if req.body: + parts.extend([b'', safe_encode(req.body)]) + + return b'\r\n'.join(parts).decode(req.charset) diff --git a/trove/tests/unittests/common/test_auth.py b/trove/tests/unittests/common/test_auth.py new file mode 100644 index 00000000..68772719 --- /dev/null +++ b/trove/tests/unittests/common/test_auth.py @@ -0,0 +1,35 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import webob + +from trove.common import auth +from trove.tests.unittests import trove_testtools + + +class TestAuth(trove_testtools.TestCase): + def test_unicode_characters_in_headers(self): + middleware = auth.AuthorizationMiddleware( + "test_trove", + [auth.TenantBasedAuth()]) + tenant_id = 'test_tenant_id' + url = '/%s/instances' % tenant_id + req = webob.Request.blank(url) + + # test string with chinese characters + test_str = u'\u6d4b\u8bd5' + req.headers = { + 'X-Tenant-ID': tenant_id, + 'X-Auth-Project-Id': test_str + } + # invocation + middleware.process_request(req) diff --git a/trove/tests/unittests/common/test_utils.py b/trove/tests/unittests/common/test_utils.py index 87b3d31b..e334dec5 100644 --- a/trove/tests/unittests/common/test_utils.py +++ b/trove/tests/unittests/common/test_utils.py @@ -22,6 +22,7 @@ from trove.common import exception from trove.common import utils from trove.tests.unittests import trove_testtools from trove.tests.util import utils as test_utils +import webob class TestUtils(trove_testtools.TestCase): @@ -173,3 +174,15 @@ class TestUtils(trove_testtools.TestCase): assert_retry(te.test_foo_2, TestEx3, 1, TestEx3) assert_retry(te.test_foo_2, TestEx2, 3, TestEx2) assert_retry(te.test_foo_2, [TestEx1, TestEx3, TestEx2], 2, TestEx3) + + def test_req_to_text(self): + req = webob.Request.blank('/') + expected = u'GET / HTTP/1.0\r\nHost: localhost:80' + self.assertEqual(expected, utils.req_to_text(req)) + + # add a header containing unicode characters + req.headers.update({ + 'X-Auth-Project-Id': u'\u6d4b\u8bd5'}) + expected = (u'GET / HTTP/1.0\r\nHost: localhost:80\r\n' + u'X-Auth-Project-Id: \u6d4b\u8bd5') + self.assertEqual(expected, utils.req_to_text(req)) |