summaryrefslogtreecommitdiff
path: root/trove
diff options
context:
space:
mode:
authorZhao Chao <zhaochao1984@gmail.com>2018-01-22 22:14:32 +0800
committerZhao Chao <zhaochao1984@gmail.com>2018-01-23 20:06:40 +0800
commit08ea56b21c25d9a8eee125bfa8379fc0ab4b879c (patch)
tree70445506e5edd958a0bcf5be5be67623e8b4e6cd /trove
parent7232a2b857a023040d5027850aea3fb2ee8ba495 (diff)
downloadtrove-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.py4
-rw-r--r--trove/common/base_wsgi.py3
-rw-r--r--trove/common/utils.py26
-rw-r--r--trove/tests/unittests/common/test_auth.py35
-rw-r--r--trove/tests/unittests/common/test_utils.py13
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))