"Fossies" - the Fresh Open Source Software Archive

Member "keystone-17.0.0/keystone/tests/unit/test_exception.py" (13 May 2020, 11604 Bytes) of package /linux/misc/openstack/keystone-17.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_exception.py": 16.0.1_vs_17.0.0.

    1 # Copyright 2012 OpenStack Foundation
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 
   15 # NOTE(morgan): These test cases are used for AuthContextMiddleware exception
   16 # rendering.
   17 
   18 import uuid
   19 
   20 import fixtures
   21 from oslo_config import fixture as config_fixture
   22 from oslo_log import log
   23 from oslo_serialization import jsonutils
   24 
   25 import keystone.conf
   26 from keystone import exception
   27 from keystone.server.flask.request_processing.middleware import auth_context
   28 from keystone.tests import unit
   29 
   30 
   31 CONF = keystone.conf.CONF
   32 
   33 
   34 class ExceptionTestCase(unit.BaseTestCase):
   35     def assertValidJsonRendering(self, e):
   36         resp = auth_context.render_exception(e)
   37         self.assertEqual(e.code, resp.status_int)
   38         self.assertEqual('%s %s' % (e.code, e.title), resp.status)
   39 
   40         j = jsonutils.loads(resp.body)
   41         self.assertIsNotNone(j.get('error'))
   42         self.assertIsNotNone(j['error'].get('code'))
   43         self.assertIsNotNone(j['error'].get('title'))
   44         self.assertIsNotNone(j['error'].get('message'))
   45         self.assertNotIn('\n', j['error']['message'])
   46         self.assertNotIn('  ', j['error']['message'])
   47         self.assertIs(type(j['error']['code']), int)
   48 
   49     def test_all_json_renderings(self):
   50         """Everything callable in the exception module should be renderable.
   51 
   52         ... except for the base error class (exception.Error), which is not
   53         user-facing.
   54 
   55         This test provides a custom message to bypass docstring parsing, which
   56         should be tested separately.
   57 
   58         """
   59         for cls in [x for x in exception.__dict__.values() if callable(x)]:
   60             if cls is not exception.Error and isinstance(cls, exception.Error):
   61                 self.assertValidJsonRendering(cls(message='Overridden.'))
   62 
   63     def test_validation_error(self):
   64         target = uuid.uuid4().hex
   65         attribute = uuid.uuid4().hex
   66         e = exception.ValidationError(target=target, attribute=attribute)
   67         self.assertValidJsonRendering(e)
   68         self.assertIn(target, str(e))
   69         self.assertIn(attribute, str(e))
   70 
   71     def test_not_found(self):
   72         target = uuid.uuid4().hex
   73         e = exception.NotFound(target=target)
   74         self.assertValidJsonRendering(e)
   75         self.assertIn(target, str(e))
   76 
   77     def test_forbidden_title(self):
   78         e = exception.Forbidden()
   79         resp = auth_context.render_exception(e)
   80         j = jsonutils.loads(resp.body)
   81         self.assertEqual('Forbidden', e.title)
   82         self.assertEqual('Forbidden', j['error'].get('title'))
   83 
   84     def test_unicode_message(self):
   85         message = u'Comment \xe7a va'
   86         e = exception.Error(message)
   87 
   88         try:
   89             self.assertEqual(message, str(e))
   90         except UnicodeEncodeError:
   91             self.fail("unicode error message not supported")
   92 
   93     def test_unicode_string(self):
   94         e = exception.ValidationError(attribute='xx',
   95                                       target='Long \xe2\x80\x93 Dash')
   96         self.assertIn('Long \xe2\x80\x93 Dash', str(e))
   97 
   98     def test_invalid_unicode_string(self):
   99         # NOTE(jamielennox): This is a complete failure case so what is
  100         # returned in the exception message is not that important so long
  101         # as there is an error with a message
  102         e = exception.ValidationError(attribute='xx',
  103                                       target='\xe7a va')
  104         self.assertIn('\xe7a va', str(e))
  105 
  106 
  107 class UnexpectedExceptionTestCase(ExceptionTestCase):
  108     """Test if internal info is exposed to the API user on UnexpectedError."""
  109 
  110     class SubClassExc(exception.UnexpectedError):
  111         debug_message_format = 'Debug Message: %(debug_info)s'
  112 
  113     def setUp(self):
  114         super(UnexpectedExceptionTestCase, self).setUp()
  115         self.exc_str = uuid.uuid4().hex
  116         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  117 
  118     def test_unexpected_error_no_debug(self):
  119         self.config_fixture.config(debug=False)
  120         e = exception.UnexpectedError(exception=self.exc_str)
  121         self.assertNotIn(self.exc_str, str(e))
  122 
  123     def test_unexpected_error_debug(self):
  124         self.config_fixture.config(debug=True, insecure_debug=True)
  125         e = exception.UnexpectedError(exception=self.exc_str)
  126         self.assertIn(self.exc_str, str(e))
  127 
  128     def test_unexpected_error_subclass_no_debug(self):
  129         self.config_fixture.config(debug=False)
  130         e = UnexpectedExceptionTestCase.SubClassExc(
  131             debug_info=self.exc_str)
  132         self.assertEqual(exception.UnexpectedError.message_format,
  133                          str(e))
  134 
  135     def test_unexpected_error_subclass_debug(self):
  136         self.config_fixture.config(debug=True, insecure_debug=True)
  137         subclass = self.SubClassExc
  138 
  139         e = subclass(debug_info=self.exc_str)
  140         expected = subclass.debug_message_format % {'debug_info': self.exc_str}
  141         self.assertEqual(
  142             '%s %s' % (expected, exception.SecurityError.amendment),
  143             str(e))
  144 
  145     def test_unexpected_error_custom_message_no_debug(self):
  146         self.config_fixture.config(debug=False)
  147         e = exception.UnexpectedError(self.exc_str)
  148         self.assertEqual(exception.UnexpectedError.message_format,
  149                          str(e))
  150 
  151     def test_unexpected_error_custom_message_debug(self):
  152         self.config_fixture.config(debug=True, insecure_debug=True)
  153         e = exception.UnexpectedError(self.exc_str)
  154         self.assertEqual(
  155             '%s %s' % (self.exc_str, exception.SecurityError.amendment),
  156             str(e))
  157 
  158     def test_unexpected_error_custom_message_exception_debug(self):
  159         self.config_fixture.config(debug=True, insecure_debug=True)
  160         orig_e = exception.NotFound(target=uuid.uuid4().hex)
  161         e = exception.UnexpectedError(orig_e)
  162         self.assertEqual(
  163             '%s %s' % (str(orig_e),
  164                        exception.SecurityError.amendment),
  165             str(e))
  166 
  167     def test_unexpected_error_custom_message_binary_debug(self):
  168         self.config_fixture.config(debug=True, insecure_debug=True)
  169         binary_msg = b'something'
  170         e = exception.UnexpectedError(binary_msg)
  171         self.assertEqual(
  172             '%s %s' % (str(binary_msg),
  173                        exception.SecurityError.amendment),
  174             str(e))
  175 
  176 
  177 class SecurityErrorTestCase(ExceptionTestCase):
  178     """Test whether security-related info is exposed to the API user."""
  179 
  180     def setUp(self):
  181         super(SecurityErrorTestCase, self).setUp()
  182         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  183 
  184     def test_unauthorized_exposure(self):
  185         self.config_fixture.config(debug=False)
  186 
  187         risky_info = uuid.uuid4().hex
  188         e = exception.Unauthorized(message=risky_info)
  189         self.assertValidJsonRendering(e)
  190         self.assertNotIn(risky_info, str(e))
  191 
  192     def test_unauthorized_exposure_in_debug(self):
  193         self.config_fixture.config(debug=True, insecure_debug=True)
  194 
  195         risky_info = uuid.uuid4().hex
  196         e = exception.Unauthorized(message=risky_info)
  197         self.assertValidJsonRendering(e)
  198         self.assertIn(risky_info, str(e))
  199 
  200     def test_forbidden_exposure(self):
  201         self.config_fixture.config(debug=False)
  202 
  203         risky_info = uuid.uuid4().hex
  204         e = exception.Forbidden(message=risky_info)
  205         self.assertValidJsonRendering(e)
  206         self.assertNotIn(risky_info, str(e))
  207 
  208     def test_forbidden_exposure_in_debug(self):
  209         self.config_fixture.config(debug=True, insecure_debug=True)
  210 
  211         risky_info = uuid.uuid4().hex
  212         e = exception.Forbidden(message=risky_info)
  213         self.assertValidJsonRendering(e)
  214         self.assertIn(risky_info, str(e))
  215 
  216     def test_forbidden_action_exposure(self):
  217         self.config_fixture.config(debug=False)
  218 
  219         risky_info = uuid.uuid4().hex
  220         action = uuid.uuid4().hex
  221         e = exception.ForbiddenAction(message=risky_info, action=action)
  222         self.assertValidJsonRendering(e)
  223         self.assertNotIn(risky_info, str(e))
  224         self.assertIn(action, str(e))
  225         self.assertNotIn(exception.SecurityError.amendment, str(e))
  226 
  227         e = exception.ForbiddenAction(action=action)
  228         self.assertValidJsonRendering(e)
  229         self.assertIn(action, str(e))
  230         self.assertNotIn(exception.SecurityError.amendment, str(e))
  231 
  232     def test_forbidden_action_exposure_in_debug(self):
  233         self.config_fixture.config(debug=True, insecure_debug=True)
  234 
  235         risky_info = uuid.uuid4().hex
  236         action = uuid.uuid4().hex
  237 
  238         e = exception.ForbiddenAction(message=risky_info, action=action)
  239         self.assertValidJsonRendering(e)
  240         self.assertIn(risky_info, str(e))
  241         self.assertIn(exception.SecurityError.amendment, str(e))
  242 
  243         e = exception.ForbiddenAction(action=action)
  244         self.assertValidJsonRendering(e)
  245         self.assertIn(action, str(e))
  246         self.assertNotIn(exception.SecurityError.amendment, str(e))
  247 
  248     def test_forbidden_action_no_message(self):
  249         # When no custom message is given when the ForbiddenAction (or other
  250         # SecurityError subclass) is created the exposed message is the same
  251         # whether debug is enabled or not.
  252 
  253         action = uuid.uuid4().hex
  254 
  255         self.config_fixture.config(debug=False)
  256         e = exception.ForbiddenAction(action=action)
  257         exposed_message = str(e)
  258         self.assertIn(action, exposed_message)
  259         self.assertNotIn(exception.SecurityError.amendment, str(e))
  260 
  261         self.config_fixture.config(debug=True)
  262         e = exception.ForbiddenAction(action=action)
  263         self.assertEqual(exposed_message, str(e))
  264 
  265     def test_unicode_argument_message(self):
  266         self.config_fixture.config(debug=False)
  267 
  268         risky_info = u'\u7ee7\u7eed\u884c\u7f29\u8fdb\u6216'
  269         e = exception.Forbidden(message=risky_info)
  270         self.assertValidJsonRendering(e)
  271         self.assertNotIn(risky_info, str(e))
  272 
  273 
  274 class TestSecurityErrorTranslation(unit.BaseTestCase):
  275     """Test i18n for SecurityError exceptions."""
  276 
  277     def setUp(self):
  278         super(TestSecurityErrorTranslation, self).setUp()
  279         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  280         self.config_fixture.config(insecure_debug=False)
  281         self.warning_log = self.useFixture(fixtures.FakeLogger(level=log.WARN))
  282 
  283         exception._FATAL_EXCEPTION_FORMAT_ERRORS = False
  284         self.addCleanup(
  285             setattr, exception, '_FATAL_EXCEPTION_FORMAT_ERRORS', True)
  286 
  287     class CustomSecurityError(exception.SecurityError):
  288         message_format = 'We had a failure in the %(place)r'
  289 
  290     class CustomError(exception.Error):
  291         message_format = 'We had a failure in the %(place)r'
  292 
  293     def test_nested_translation_of_SecurityErrors(self):
  294         e = self.CustomSecurityError(place='code')
  295         ('Admiral found this in the log: %s') % e
  296         self.assertNotIn('programmer error', self.warning_log.output)
  297 
  298     def test_that_regular_Errors_can_be_deep_copied(self):
  299         e = self.CustomError(place='code')
  300         ('Admiral found this in the log: %s') % e
  301         self.assertNotIn('programmer error', self.warning_log.output)