"Fossies" - the Fresh Open Source Software Archive

Member "keystone-17.0.0/keystone/tests/unit/common/test_utils.py" (13 May 2020, 11951 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_utils.py": 16.0.1_vs_17.0.0.

    1 # encoding: utf-8
    2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    3 # not use this file except in compliance with the License. You may obtain
    4 # a copy of the License at
    5 #
    6 #      http://www.apache.org/licenses/LICENSE-2.0
    7 #
    8 # Unless required by applicable law or agreed to in writing, software
    9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   11 # License for the specific language governing permissions and limitations
   12 # under the License.
   13 
   14 import datetime
   15 import fixtures
   16 import uuid
   17 
   18 import freezegun
   19 from oslo_config import fixture as config_fixture
   20 from oslo_log import log
   21 
   22 from keystone.common import fernet_utils
   23 from keystone.common import utils as common_utils
   24 import keystone.conf
   25 from keystone.credential.providers import fernet as credential_fernet
   26 from keystone import exception
   27 from keystone.server.flask import application
   28 from keystone.tests import unit
   29 from keystone.tests.unit import ksfixtures
   30 from keystone.tests.unit import utils
   31 
   32 
   33 CONF = keystone.conf.CONF
   34 
   35 TZ = utils.TZ
   36 
   37 
   38 class UtilsTestCase(unit.BaseTestCase):
   39     OPTIONAL = object()
   40 
   41     def setUp(self):
   42         super(UtilsTestCase, self).setUp()
   43         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
   44 
   45     def test_resource_uuid(self):
   46         # Basic uuid test, most IDs issued by keystone look like this:
   47         value = u'536e28c2017e405e89b25a1ed777b952'
   48         self.assertEqual(value, common_utils.resource_uuid(value))
   49 
   50     def test_resource_64_char_uuid(self):
   51         # Exact 64 length string, like ones used by mapping_id backend, are not
   52         # valid UUIDs, so they will be UUID5 namespaced
   53         value = u'f13de678ac714bb1b7d1e9a007c10db5' * 2
   54         expected_id = uuid.uuid5(common_utils.RESOURCE_ID_NAMESPACE, value).hex
   55         self.assertEqual(expected_id, common_utils.resource_uuid(value))
   56 
   57     def test_resource_non_ascii_chars(self):
   58         # IDs with non-ASCII characters will be UUID5 namespaced
   59         value = u'ß' * 32
   60         expected_id = uuid.uuid5(common_utils.RESOURCE_ID_NAMESPACE, value).hex
   61         self.assertEqual(expected_id, common_utils.resource_uuid(value))
   62 
   63     def test_resource_invalid_id(self):
   64         # This input is invalid because it's length is more than 64.
   65         value = u'x' * 65
   66         self.assertRaises(ValueError, common_utils.resource_uuid,
   67                           value)
   68 
   69     def test_hash(self):
   70         password = 'right'
   71         wrong = 'wrongwrong'  # Two wrongs don't make a right
   72         hashed = common_utils.hash_password(password)
   73         self.assertTrue(common_utils.check_password(password, hashed))
   74         self.assertFalse(common_utils.check_password(wrong, hashed))
   75 
   76     def test_verify_normal_password_strict(self):
   77         self.config_fixture.config(strict_password_check=False)
   78         password = uuid.uuid4().hex
   79         verified = common_utils.verify_length_and_trunc_password(password)
   80         self.assertEqual(password, verified)
   81 
   82     def test_that_a_hash_can_not_be_validated_against_a_hash(self):
   83         # NOTE(dstanek): Bug 1279849 reported a problem where passwords
   84         # were not being hashed if they already looked like a hash. This
   85         # would allow someone to hash their password ahead of time
   86         # (potentially getting around password requirements, like
   87         # length) and then they could auth with their original password.
   88         password = uuid.uuid4().hex
   89         hashed_password = common_utils.hash_password(password)
   90         new_hashed_password = common_utils.hash_password(hashed_password)
   91         self.assertFalse(common_utils.check_password(password,
   92                                                      new_hashed_password))
   93 
   94     def test_verify_long_password_strict(self):
   95         self.config_fixture.config(strict_password_check=False)
   96         self.config_fixture.config(group='identity', max_password_length=5)
   97         max_length = CONF.identity.max_password_length
   98         invalid_password = 'passw0rd'
   99         trunc = common_utils.verify_length_and_trunc_password(invalid_password)
  100         self.assertEqual(invalid_password[:max_length], trunc)
  101 
  102     def test_verify_long_password_strict_raises_exception(self):
  103         self.config_fixture.config(strict_password_check=True)
  104         self.config_fixture.config(group='identity', max_password_length=5)
  105         invalid_password = 'passw0rd'
  106         self.assertRaises(exception.PasswordVerificationError,
  107                           common_utils.verify_length_and_trunc_password,
  108                           invalid_password)
  109 
  110     def test_verify_length_and_trunc_password_throws_validation_error(self):
  111         class SpecialObject(object):
  112             pass
  113 
  114         special_object = SpecialObject()
  115         invalid_passwords = [True, special_object, 4.3, 5]
  116         for invalid_password in invalid_passwords:
  117             self.assertRaises(
  118                 exception.ValidationError,
  119                 common_utils.verify_length_and_trunc_password,
  120                 invalid_password
  121             )
  122 
  123     def test_hash_long_password_truncation(self):
  124         self.config_fixture.config(strict_password_check=False)
  125         invalid_length_password = '0' * 9999999
  126         hashed = common_utils.hash_password(invalid_length_password)
  127         self.assertTrue(common_utils.check_password(invalid_length_password,
  128                                                     hashed))
  129 
  130     def test_hash_long_password_strict(self):
  131         self.config_fixture.config(strict_password_check=True)
  132         invalid_length_password = '0' * 9999999
  133         self.assertRaises(exception.PasswordVerificationError,
  134                           common_utils.hash_password,
  135                           invalid_length_password)
  136 
  137     def _create_test_user(self, password=OPTIONAL):
  138         user = {"name": "hthtest"}
  139         if password is not self.OPTIONAL:
  140             user['password'] = password
  141 
  142         return user
  143 
  144     def test_hash_user_password_without_password(self):
  145         user = self._create_test_user()
  146         hashed = common_utils.hash_user_password(user)
  147         self.assertEqual(user, hashed)
  148 
  149     def test_hash_user_password_with_null_password(self):
  150         user = self._create_test_user(password=None)
  151         hashed = common_utils.hash_user_password(user)
  152         self.assertEqual(user, hashed)
  153 
  154     def test_hash_user_password_with_empty_password(self):
  155         password = ''
  156         user = self._create_test_user(password=password)
  157         user_hashed = common_utils.hash_user_password(user)
  158         password_hashed = user_hashed['password']
  159         self.assertTrue(common_utils.check_password(password, password_hashed))
  160 
  161     def test_hash_edge_cases(self):
  162         hashed = common_utils.hash_password('secret')
  163         self.assertFalse(common_utils.check_password('', hashed))
  164         self.assertFalse(common_utils.check_password(None, hashed))
  165 
  166     def test_hash_unicode(self):
  167         password = u'Comment \xe7a va'
  168         wrong = 'Comment ?a va'
  169         hashed = common_utils.hash_password(password)
  170         self.assertTrue(common_utils.check_password(password, hashed))
  171         self.assertFalse(common_utils.check_password(wrong, hashed))
  172 
  173     def test_auth_str_equal(self):
  174         self.assertTrue(common_utils.auth_str_equal('abc123', 'abc123'))
  175         self.assertFalse(common_utils.auth_str_equal('a', 'aaaaa'))
  176         self.assertFalse(common_utils.auth_str_equal('aaaaa', 'a'))
  177         self.assertFalse(common_utils.auth_str_equal('ABC123', 'abc123'))
  178 
  179     def test_url_safe_check(self):
  180         base_str = 'i am safe'
  181         self.assertFalse(common_utils.is_not_url_safe(base_str))
  182         for i in common_utils.URL_RESERVED_CHARS:
  183             self.assertTrue(common_utils.is_not_url_safe(base_str + i))
  184 
  185     def test_url_safe_with_unicode_check(self):
  186         base_str = u'i am \xe7afe'
  187         self.assertFalse(common_utils.is_not_url_safe(base_str))
  188         for i in common_utils.URL_RESERVED_CHARS:
  189             self.assertTrue(common_utils.is_not_url_safe(base_str + i))
  190 
  191     def test_isotime_returns_microseconds_when_subsecond_is_true(self):
  192         time = datetime.datetime.utcnow().replace(microsecond=500000)
  193         with freezegun.freeze_time(time):
  194             string_time = common_utils.isotime(subsecond=True)
  195         expected_string_ending = str(time.second) + '.000000Z'
  196         self.assertTrue(string_time.endswith(expected_string_ending))
  197 
  198     def test_isotime_returns_seconds_when_subsecond_is_false(self):
  199         time = datetime.datetime.utcnow().replace(microsecond=500000)
  200         with freezegun.freeze_time(time):
  201             string_time = common_utils.isotime(subsecond=False)
  202         expected_string_ending = str(time.second) + 'Z'
  203         self.assertTrue(string_time.endswith(expected_string_ending))
  204 
  205     def test_isotime_rounds_microseconds_of_objects_passed_in(self):
  206         time = datetime.datetime.utcnow().replace(microsecond=500000)
  207         string_time = common_utils.isotime(at=time, subsecond=True)
  208         expected_string_ending = str(time.second) + '.000000Z'
  209         self.assertTrue(string_time.endswith(expected_string_ending))
  210 
  211     def test_isotime_truncates_microseconds_of_objects_passed_in(self):
  212         time = datetime.datetime.utcnow().replace(microsecond=500000)
  213         string_time = common_utils.isotime(at=time, subsecond=False)
  214         expected_string_ending = str(time.second) + 'Z'
  215         self.assertTrue(string_time.endswith(expected_string_ending))
  216 
  217 
  218 class ServiceHelperTests(unit.BaseTestCase):
  219 
  220     @application.fail_gracefully
  221     def _do_test(self):
  222         raise Exception("Test Exc")
  223 
  224     def test_fail_gracefully(self):
  225         self.assertRaises(unit.UnexpectedExit, self._do_test)
  226 
  227 
  228 class FernetUtilsTestCase(unit.BaseTestCase):
  229 
  230     def setUp(self):
  231         super(FernetUtilsTestCase, self).setUp()
  232         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  233 
  234     def test_debug_message_logged_when_loading_fernet_token_keys(self):
  235         self.useFixture(
  236             ksfixtures.KeyRepository(
  237                 self.config_fixture,
  238                 'fernet_tokens',
  239                 CONF.fernet_tokens.max_active_keys
  240             )
  241         )
  242         logging_fixture = self.useFixture(fixtures.FakeLogger(level=log.DEBUG))
  243         fernet_utilities = fernet_utils.FernetUtils(
  244             CONF.fernet_tokens.key_repository,
  245             CONF.fernet_tokens.max_active_keys,
  246             'fernet_tokens'
  247         )
  248         fernet_utilities.load_keys()
  249         expected_debug_message = (
  250             'Loaded 2 Fernet keys from %(dir)s, but `[fernet_tokens] '
  251             'max_active_keys = %(max)d`; perhaps there have not been enough '
  252             'key rotations to reach `max_active_keys` yet?') % {
  253                 'dir': CONF.fernet_tokens.key_repository,
  254                 'max': CONF.fernet_tokens.max_active_keys}
  255         self.assertIn(expected_debug_message, logging_fixture.output)
  256 
  257     def test_debug_message_not_logged_when_loading_fernet_credential_key(self):
  258         self.useFixture(
  259             ksfixtures.KeyRepository(
  260                 self.config_fixture,
  261                 'credential',
  262                 CONF.fernet_tokens.max_active_keys
  263             )
  264         )
  265         logging_fixture = self.useFixture(fixtures.FakeLogger(level=log.DEBUG))
  266         fernet_utilities = fernet_utils.FernetUtils(
  267             CONF.credential.key_repository,
  268             credential_fernet.MAX_ACTIVE_KEYS,
  269             'credential'
  270         )
  271         fernet_utilities.load_keys()
  272         debug_message = (
  273             'Loaded 2 Fernet keys from %(dir)s, but `[credential] '
  274             'max_active_keys = %(max)d`; perhaps there have not been enough '
  275             'key rotations to reach `max_active_keys` yet?') % {
  276                 'dir': CONF.credential.key_repository,
  277                 'max': credential_fernet.MAX_ACTIVE_KEYS}
  278         self.assertNotIn(debug_message, logging_fixture.output)