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>
This commit is contained in:
parent
7232a2b857
commit
08ea56b21c
@ -21,6 +21,7 @@ import webob.exc
|
|||||||
|
|
||||||
from trove.common import exception
|
from trove.common import exception
|
||||||
from trove.common.i18n import _
|
from trove.common.i18n import _
|
||||||
|
from trove.common.utils import req_to_text
|
||||||
from trove.common import wsgi
|
from trove.common import wsgi
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -64,7 +65,8 @@ class TenantBasedAuth(object):
|
|||||||
LOG.debug(strutils.mask_password(
|
LOG.debug(strutils.mask_password(
|
||||||
_("Authorized tenant '%(tenant_id)s' request: "
|
_("Authorized tenant '%(tenant_id)s' request: "
|
||||||
"%(request)s") %
|
"%(request)s") %
|
||||||
{'tenant_id': tenant_id, 'request': request}))
|
{'tenant_id': tenant_id,
|
||||||
|
'request': req_to_text(request)}))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
msg = _(
|
msg = _(
|
||||||
|
@ -41,6 +41,7 @@ from xml.parsers import expat
|
|||||||
|
|
||||||
from trove.common import base_exception
|
from trove.common import base_exception
|
||||||
from trove.common.i18n import _
|
from trove.common.i18n import _
|
||||||
|
from trove.common.utils import req_to_text
|
||||||
from trove.common import xmlutils
|
from trove.common import xmlutils
|
||||||
|
|
||||||
socket_opts = [
|
socket_opts = [
|
||||||
@ -332,6 +333,8 @@ class Request(webob.Request):
|
|||||||
raise base_exception.InvalidContentType(content_type=content_type)
|
raise base_exception.InvalidContentType(content_type=content_type)
|
||||||
return content_type
|
return content_type
|
||||||
|
|
||||||
|
__str__ = req_to_text
|
||||||
|
|
||||||
|
|
||||||
class Resource(object):
|
class Resource(object):
|
||||||
"""
|
"""
|
||||||
|
@ -26,6 +26,7 @@ import jinja2
|
|||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_service import loopingcall
|
from oslo_service import loopingcall
|
||||||
|
from oslo_utils.encodeutils import safe_encode
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
from passlib import pwd
|
from passlib import pwd
|
||||||
@ -383,3 +384,28 @@ def to_mb(bytes):
|
|||||||
size = bytes / 1024.0 ** 2
|
size = bytes / 1024.0 ** 2
|
||||||
# Make sure we don't return 0.0 if the size is greater than 0
|
# Make sure we don't return 0.0 if the size is greater than 0
|
||||||
return max(round(size, 2), 0.01)
|
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)
|
||||||
|
35
trove/tests/unittests/common/test_auth.py
Normal file
35
trove/tests/unittests/common/test_auth.py
Normal file
@ -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)
|
@ -22,6 +22,7 @@ from trove.common import exception
|
|||||||
from trove.common import utils
|
from trove.common import utils
|
||||||
from trove.tests.unittests import trove_testtools
|
from trove.tests.unittests import trove_testtools
|
||||||
from trove.tests.util import utils as test_utils
|
from trove.tests.util import utils as test_utils
|
||||||
|
import webob
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(trove_testtools.TestCase):
|
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, TestEx3, 1, TestEx3)
|
||||||
assert_retry(te.test_foo_2, TestEx2, 3, TestEx2)
|
assert_retry(te.test_foo_2, TestEx2, 3, TestEx2)
|
||||||
assert_retry(te.test_foo_2, [TestEx1, TestEx3, TestEx2], 2, TestEx3)
|
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))
|
||||||
|
Loading…
Reference in New Issue
Block a user