summaryrefslogtreecommitdiff
path: root/heat/common/password_gen.py
diff options
context:
space:
mode:
Diffstat (limited to 'heat/common/password_gen.py')
-rw-r--r--heat/common/password_gen.py109
1 files changed, 109 insertions, 0 deletions
diff --git a/heat/common/password_gen.py b/heat/common/password_gen.py
new file mode 100644
index 000000000..93b03c6b9
--- /dev/null
+++ b/heat/common/password_gen.py
@@ -0,0 +1,109 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import collections
+import random as random_module
+import string
+
+import six
+
+
+# NOTE(pas-ha) Heat officially supports only POSIX::Linux platform
+# where os.urandom() and random.SystemRandom() are available
+random = random_module.SystemRandom()
+
+
+CHARACTER_CLASSES = (
+ LETTERS_DIGITS, LETTERS, LOWERCASE, UPPERCASE,
+ DIGITS, HEXDIGITS, OCTDIGITS,
+) = (
+ 'lettersdigits', 'letters', 'lowercase', 'uppercase',
+ 'digits', 'hexdigits', 'octdigits',
+)
+
+_char_class_members = {
+ LETTERS_DIGITS: string.ascii_letters + string.digits,
+ LETTERS: string.ascii_letters,
+ LOWERCASE: string.ascii_lowercase,
+ UPPERCASE: string.ascii_uppercase,
+ DIGITS: string.digits,
+ HEXDIGITS: string.digits + 'ABCDEF',
+ OCTDIGITS: string.octdigits,
+}
+
+
+CharClass = collections.namedtuple('CharClass',
+ ('allowed_chars', 'min_count'))
+
+
+def named_char_class(char_class, min_count=0):
+ """Return a predefined character class.
+
+ The result of this function can be passed to :func:`generate_password` as
+ one of the character classes to use in generating a password.
+
+ :param char_class: Any of the character classes named in
+ :const:`CHARACTER_CLASSES`
+ :param min_count: The minimum number of members of this class to appear in
+ a generated password
+ """
+ assert char_class in CHARACTER_CLASSES
+ return CharClass(frozenset(_char_class_members[char_class]), min_count)
+
+
+def special_char_class(allowed_chars, min_count=0):
+ """Return a character class containing custom characters.
+
+ The result of this function can be passed to :func:`generate_password` as
+ one of the character classes to use in generating a password.
+
+ :param allowed_chars: Iterable of the characters in the character class
+ :param min_count: The minimum number of members of this class to appear in
+ a generated password
+ """
+ return CharClass(frozenset(allowed_chars), min_count)
+
+
+def generate_password(length, char_classes):
+ """Generate a random password.
+
+ The password will be of the specified length, and comprised of characters
+ from the specified character classes, which can be generated using the
+ :func:`named_char_class` and :func:`special_char_class` functions. Where
+ a minimum count is specified in the character class, at least that number
+ of characters in the resulting password are guaranteed to be from that
+ character class.
+
+ :param length: The length of the password to generate, in characters
+ :param char_classes: Iterable over classes of characters from which to
+ generate a password
+ """
+ char_buffer = six.StringIO()
+ all_allowed_chars = set()
+
+ # Add the minimum number of chars from each char class
+ for char_class in char_classes:
+ all_allowed_chars |= char_class.allowed_chars
+ allowed_chars = tuple(char_class.allowed_chars)
+ for i in six.moves.xrange(char_class.min_count):
+ char_buffer.write(random.choice(allowed_chars))
+
+ # Fill up rest with random chars from provided classes
+ combined_chars = tuple(all_allowed_chars)
+ for i in six.moves.xrange(max(0, length - char_buffer.tell())):
+ char_buffer.write(random.choice(combined_chars))
+
+ # Shuffle string
+ selected_chars = char_buffer.getvalue()
+ char_buffer.close()
+ return ''.join(random.sample(selected_chars, length))