diff --git a/src/authentic2/passwords.py b/src/authentic2/passwords.py index d58aab648..65d138890 100644 --- a/src/authentic2/passwords.py +++ b/src/authentic2/passwords.py @@ -35,18 +35,19 @@ def generate_password(): Beware that A2_PASSWORD_POLICY_REGEX cannot be validated. ''' digits = string.digits - lower = string.lowercase - upper = string.uppercase + lower = string.ascii_lowercase + upper = string.ascii_uppercase punc = string.punctuation min_len = max(app_settings.A2_PASSWORD_POLICY_MIN_LENGTH, 8) min_class_count = max(app_settings.A2_PASSWORD_POLICY_MIN_CLASSES, 3) new_password = [] + generator = random.SystemRandom() while len(new_password) < min_len: for cls in (digits, lower, upper, punc)[:min_class_count]: - new_password.append(random.choice(cls)) - random.shuffle(new_password) + new_password.append(generator.choice(cls)) + generator.shuffle(new_password) return ''.join(new_password) diff --git a/tests/test_passwords.py b/tests/test_passwords.py new file mode 100644 index 000000000..f229c34d5 --- /dev/null +++ b/tests/test_passwords.py @@ -0,0 +1,33 @@ +# authentic2 - versatile identity manager +# Copyright (C) 2010-2019 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals + +import string + +from authentic2 import app_settings +from authentic2.passwords import generate_password + + +def test_generate_password(): + passwords = set(generate_password() for i in range(10)) + + char_classes = [string.digits, string.ascii_lowercase, string.ascii_uppercase, string.punctuation] + assert len(passwords) == 10 + for password in passwords: + assert len(password) >= max(app_settings.A2_PASSWORD_POLICY_MIN_LENGTH, 8) + assert (sum(any(char in char_class for char in password) for char_class in char_classes) + == max(app_settings.A2_PASSWORD_POLICY_MIN_CLASSES, 3))