Adds test for SecurityError's translation behavior

A confusing log message is logged when translating SecurityErrors within
another translated string.

Related-bug: #1607107
Change-Id: I315e68462d7096a2cbc38c1717b549d59719b71b
This commit is contained in:
David Stanek 2016-07-27 22:39:50 +00:00
parent 3d9a1f1b97
commit fd861dca5f
1 changed files with 59 additions and 0 deletions

View File

@ -14,14 +14,18 @@
import uuid
import fixtures
from oslo_config import fixture as config_fixture
from oslo_log import log
from oslo_serialization import jsonutils
import six
from keystone.common import wsgi
import keystone.conf
from keystone import exception
from keystone.i18n import _LE
from keystone.tests import unit
from keystone.tests.unit import utils
CONF = keystone.conf.CONF
@ -274,3 +278,58 @@ class SecurityErrorTestCase(ExceptionTestCase):
e = exception.Forbidden(message=risky_info)
self.assertValidJsonRendering(e)
self.assertNotIn(risky_info, six.text_type(e))
class TestSecurityErrorTranslation(unit.BaseTestCase):
"""Test i18n for SecurityError exceptions.
Keystone ``Error`` accepts an optional message that will be used when
rendering the exception object as a string. If not provided the object's
message_format attribute is used instead. ``SecurityError``s are a little
different in that they only use the message provided to the initializer
when keystone is in insecure_debug mode. Instead they will use the message
format. This is to ensure that sensitive details are not leaked back to
the caller in a production deployment.
This dual mode for string rendering causes some odd behaviour when
combined with oslo_i18n translation. Any object used as a value for
formatting a translated string is deep copied.
The copy causes an issue. The deep copy process actually creates a new
exception instance with the rendered string. Then when that new instance
is rendered as a string to use for substitution a warning is logged. This
is because the code tries to use the ``message_format`` in secure mode,
but the required kwargs are not in the deep copy.
The end result is not an error because when the KeyError is cause the
instance's ``message`` is used instead and this has the properly
translated message. The only indication that something is wonky is a
message in the warning log.
"""
def setUp(self):
super(TestSecurityErrorTranslation, self).setUp()
self.config_fixture = self.useFixture(config_fixture.Config(CONF))
self.config_fixture.config(insecure_debug=False)
self.warning_log = self.useFixture(fixtures.FakeLogger(level=log.WARN))
exception._FATAL_EXCEPTION_FORMAT_ERRORS = False
self.addCleanup(
setattr, exception, '_FATAL_EXCEPTION_FORMAT_ERRORS', True)
class CustomSecurityError(exception.SecurityError):
message_format = _LE('We had a failure in the %(place)r')
class CustomError(exception.Error):
message_format = _LE('We had a failure in the %(place)r')
@utils.wip('this will fail until we fix the deepcopy issue')
def test_nested_translation_of_SecurityErrors(self):
e = self.CustomSecurityError(place='code')
_LE('Admiral found this in the log: %s') % e
self.assertNotIn('programmer error', self.warning_log.output)
def test_that_regular_Errors_can_be_deep_copied(self):
e = self.CustomError(place='code')
_LE('Admiral found this in the log: %s') % e
self.assertNotIn('programmer error', self.warning_log.output)