summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2011-12-30 14:54:42 +0000
committerGerrit Code Review <review@openstack.org>2011-12-30 14:54:42 +0000
commitd014a0a1549715f8fd80c04d0ec4efc43a107271 (patch)
tree5da067774a4e7a5929c59415a0530ab7e34c68c3 /nova
parentd73a0046440b1c22cfb296fb0370905575739ab2 (diff)
parent52b96e7b45bd3528bcf66e18b4f1491655597775 (diff)
Merge "Ensure generated passwords meet minimum complexity"
Diffstat (limited to 'nova')
-rw-r--r--nova/tests/test_utils.py8
-rw-r--r--nova/utils.py35
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):