diff options
| author | Jenkins <jenkins@review.openstack.org> | 2011-12-30 14:54:42 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2011-12-30 14:54:42 +0000 |
| commit | d014a0a1549715f8fd80c04d0ec4efc43a107271 (patch) | |
| tree | 5da067774a4e7a5929c59415a0530ab7e34c68c3 /nova | |
| parent | d73a0046440b1c22cfb296fb0370905575739ab2 (diff) | |
| parent | 52b96e7b45bd3528bcf66e18b4f1491655597775 (diff) | |
Merge "Ensure generated passwords meet minimum complexity"
Diffstat (limited to 'nova')
| -rw-r--r-- | nova/tests/test_utils.py | 8 | ||||
| -rw-r--r-- | nova/utils.py | 35 |
2 files changed, 37 insertions, 6 deletions
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py index 513461dcc..92376f1f2 100644 --- a/nova/tests/test_utils.py +++ b/nova/tests/test_utils.py @@ -360,6 +360,14 @@ class GenericUtilsTestCase(test.TestCase): self.mox.UnsetStubs() self.assertEqual(data, fake_contents) + def test_generate_password(self): + password = utils.generate_password() + self.assertTrue([c for c in password if c in '0123456789']) + self.assertTrue([c for c in password + if c in 'abcdefghijklmnopqrstuvwxyz']) + self.assertTrue([c for c in password + if c in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']) + class IsUUIDLikeTestCase(test.TestCase): def assertUUIDLike(self, val, expected): diff --git a/nova/utils.py b/nova/utils.py index 164ee08dd..7f37cc801 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -350,13 +350,13 @@ def generate_uid(topic, size=8): # Default symbols to use for passwords. Avoids visually confusing characters. # ~6 bits per symbol -DEFAULT_PASSWORD_SYMBOLS = ('23456789' # Removed: 0,1 - 'ABCDEFGHJKLMNPQRSTUVWXYZ' # Removed: I, O +DEFAULT_PASSWORD_SYMBOLS = ('23456789', # Removed: 0,1 + 'ABCDEFGHJKLMNPQRSTUVWXYZ', # Removed: I, O 'abcdefghijkmnopqrstuvwxyz') # Removed: l # ~5 bits per symbol -EASIER_PASSWORD_SYMBOLS = ('23456789' # Removed: 0, 1 +EASIER_PASSWORD_SYMBOLS = ('23456789', # Removed: 0, 1 'ABCDEFGHJKLMNPQRSTUVWXYZ') # Removed: I, O @@ -420,14 +420,37 @@ def usage_from_instance(instance_ref, **kw): return usage_info -def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS): - """Generate a random password from the supplied symbols. +def generate_password(length=20, symbolgroups=DEFAULT_PASSWORD_SYMBOLS): + """Generate a random password from the supplied symbol groups. + + At least one symbol from each group will be included. Unpredictable + results if length is less than the number of symbol groups. Believed to be reasonably secure (with a reasonable password length!) """ r = random.SystemRandom() - return ''.join([r.choice(symbols) for _i in xrange(length)]) + + # NOTE(jerdfelt): Some password policies require at least one character + # from each group of symbols, so start off with one random character + # from each symbol group + password = [r.choice(s) for s in symbolgroups] + # If length < len(symbolgroups), the leading characters will only + # be from the first length groups. Try our best to not be predictable + # by shuffling and then truncating. + r.shuffle(password) + password = password[:length] + length -= len(password) + + # then fill with random characters from all symbol groups + symbols = ''.join(symbolgroups) + password.extend([r.choice(symbols) for _i in xrange(length)]) + + # finally shuffle to ensure first x characters aren't from a + # predictable group + r.shuffle(password) + + return ''.join(password) def last_octet(address): |
