summaryrefslogtreecommitdiff
path: root/src/mailman/utilities
diff options
context:
space:
mode:
authorBarry Warsaw2011-04-08 22:05:33 -0400
committerBarry Warsaw2011-04-08 22:05:33 -0400
commit5de5904af6dd97339a70630002d64c901b008c98 (patch)
treee9c01a2bd38587226745043b47106de1ac5efb84 /src/mailman/utilities
parent0a58c7a9f2fe97665fba102eea9287b28575de5c (diff)
parent25b407e24fe21dc46a4f9efa77734d55e0ed4bdd (diff)
downloadmailman-5de5904af6dd97339a70630002d64c901b008c98.tar.gz
mailman-5de5904af6dd97339a70630002d64c901b008c98.tar.zst
mailman-5de5904af6dd97339a70630002d64c901b008c98.zip
Diffstat (limited to 'src/mailman/utilities')
-rw-r--r--src/mailman/utilities/passwords.py41
-rw-r--r--src/mailman/utilities/tests/test_passwords.py34
2 files changed, 73 insertions, 2 deletions
diff --git a/src/mailman/utilities/passwords.py b/src/mailman/utilities/passwords.py
index 0de4255da..896872436 100644
--- a/src/mailman/utilities/passwords.py
+++ b/src/mailman/utilities/passwords.py
@@ -26,6 +26,7 @@ __metaclass__ = type
__all__ = [
'Schemes',
'check_response',
+ 'encrypt_password',
'make_secret',
'make_user_friendly_password',
]
@@ -50,6 +51,7 @@ from mailman.core import errors
SALT_LENGTH = 20 # bytes
ITERATIONS = 2000
EMPTYSTRING = ''
+SCHEME_RE = r'{(?P<scheme>[^}]+?)}(?P<rest>.*)'
@@ -294,8 +296,7 @@ def check_response(challenge, response):
:return: True if the response matches the challenge.
:rtype: bool
"""
- mo = re.match(r'{(?P<scheme>[^}]+?)}(?P<rest>.*)',
- challenge, re.IGNORECASE)
+ mo = re.match(SCHEME_RE, challenge, re.IGNORECASE)
if not mo:
return False
# See above for why we convert here. However because we should have
@@ -323,6 +324,42 @@ def lookup_scheme(scheme_name):
return _SCHEMES_BY_TAG.get(scheme_name.lower())
+def encrypt_password(password, scheme=None):
+ """Return an encrypted password.
+
+ If the given password is already encrypted (i.e. it has a scheme prefix),
+ then the password is return unchanged. Otherwise, it is encrypted with
+ the given scheme or the default scheme.
+
+ :param password: The plain text or encrypted password.
+ :type password: string
+ :param scheme: The scheme enum to use for encryption. If not given, the
+ system default scheme is used. This can be a `Schemes` enum item, or
+ the scheme name as a string.
+ :type scheme: `Schemes` enum, or string.
+ :return: The encrypted password.
+ :rtype: bytes
+ """
+ if not isinstance(password, (bytes, unicode)):
+ raise ValueError('Got {0}, expected unicode or bytes'.format(
+ type(password)))
+ if re.match(SCHEME_RE, password, re.IGNORECASE):
+ # Just ensure we're getting bytes back.
+ if isinstance(password, unicode):
+ return password.encode('us-ascii')
+ assert isinstance(password, bytes), 'Expected bytes'
+ return password
+ if scheme is None:
+ password_scheme = lookup_scheme(config.passwords.password_scheme)
+ elif scheme in Schemes:
+ password_scheme = scheme
+ else:
+ password_scheme = lookup_scheme(scheme)
+ if password_scheme is None:
+ raise ValueError('Bad password scheme: {0}'.format(scheme))
+ return make_secret(password, password_scheme)
+
+
# Password generation.
diff --git a/src/mailman/utilities/tests/test_passwords.py b/src/mailman/utilities/tests/test_passwords.py
index 7b6989779..c9b3d2e91 100644
--- a/src/mailman/utilities/tests/test_passwords.py
+++ b/src/mailman/utilities/tests/test_passwords.py
@@ -170,6 +170,40 @@ class TestPasswordGeneration(unittest.TestCase):
self.assertTrue(vowel in 'aeiou', vowel)
self.assertTrue(consonant not in 'aeiou', consonant)
+ def test_encrypt_password_plaintext_default_scheme(self):
+ # Test that a plain text password gets encrypted.
+ self.assertEqual(passwords.encrypt_password('abc'),
+ '{CLEARTEXT}abc')
+
+ def test_encrypt_password_plaintext(self):
+ # Test that a plain text password gets encrypted with the given scheme.
+ scheme = passwords.Schemes.sha
+ self.assertEqual(passwords.encrypt_password('abc', scheme),
+ '{SHA}qZk-NkcGgWq6PiVxeFDCbJzQ2J0=')
+
+ def test_encrypt_password_plaintext_by_scheme_name(self):
+ # Test that a plain text password gets encrypted with the given
+ # scheme, which is given by name.
+ self.assertEqual(passwords.encrypt_password('abc', 'cleartext'),
+ '{CLEARTEXT}abc')
+
+ def test_encrypt_password_already_encrypted_default_scheme(self):
+ # Test that a password which is already encrypted is return unchanged.
+ self.assertEqual(passwords.encrypt_password('{SHA}abc'), '{SHA}abc')
+
+ def test_encrypt_password_already_encrypted(self):
+ # Test that a password which is already encrypted is return unchanged,
+ # ignoring any requested scheme.
+ scheme = passwords.Schemes.cleartext
+ self.assertEqual(passwords.encrypt_password('{SHA}abc', scheme),
+ '{SHA}abc')
+
+ def test_encrypt_password_password_value_error(self):
+ self.assertRaises(ValueError, passwords.encrypt_password, 7)
+
+ def test_encrypt_password_scheme_value_error(self):
+ self.assertRaises(ValueError, passwords.encrypt_password, 'abc', 'foo')
+
def test_suite():