diff options
| -rw-r--r-- | Mailman/Errors.py | 25 | ||||
| -rw-r--r-- | Mailman/Utils.py | 4 | ||||
| -rw-r--r-- | Mailman/bin/mmsitepass.py | 7 | ||||
| -rw-r--r-- | Mailman/passwords.py | 19 | ||||
| -rw-r--r-- | Mailman/testing/base.py | 6 | ||||
| -rw-r--r-- | Mailman/testing/test_handlers.py | 2 | ||||
| -rw-r--r-- | Mailman/testing/test_membership.py | 2 | ||||
| -rw-r--r-- | Mailman/testing/test_passwords.py | 48 | ||||
| -rw-r--r-- | Mailman/testing/test_security_mgr.py | 2 |
9 files changed, 91 insertions, 24 deletions
diff --git a/Mailman/Errors.py b/Mailman/Errors.py index 2c1686fdb..10081f08d 100644 --- a/Mailman/Errors.py +++ b/Mailman/Errors.py @@ -58,8 +58,6 @@ class MembershipIsBanned(MemberError): pass # Exception hierarchy for various authentication failures, can be # raised from functions in SecurityManager.py class MMAuthenticationError(MailmanException): pass -class MMBadPasswordError(MMAuthenticationError): pass -class MMPasswordsMustMatch(MMAuthenticationError): pass class MMCookieError(MMAuthenticationError): pass class MMExpiredCookieError(MMCookieError): pass class MMInvalidCookieError(MMCookieError): pass @@ -191,3 +189,26 @@ class SchemaVersionMismatchError(DatabaseError): from Mailman.Version import DATABASE_SCHEMA_VERSION return 'Incompatible database schema version (got: %d, expected: %d)' \ % (self._got, DATABASE_SCHEMA_VERSION) + + + +class PasswordError(MailmanError): + """A password related error.""" + + +class MMBadPasswordError(PasswordError, MMAuthenticationError): + """A bad password was given.""" + + +class MMPasswordsMustMatch(PasswordError, MMAuthenticationError): + """The given passwords don't match.""" + + +class BadPasswordSchemeError(PasswordError): + """A bad password scheme was given.""" + + def __init__(self, scheme_name='unknown'): + self.scheme_name = scheme_name + + def __str__(self): + return 'A bad password scheme was given: %s' % self.scheme_name diff --git a/Mailman/Utils.py b/Mailman/Utils.py index 958a3e959..afb2f1fd5 100644 --- a/Mailman/Utils.py +++ b/Mailman/Utils.py @@ -335,7 +335,9 @@ def GetRandomSeed(): -def set_global_password(pw, siteadmin=True, scheme='ssha'): +def set_global_password(pw, siteadmin=True, scheme=None): + if scheme is None: + scheme = passwords.Schemes.ssha if siteadmin: filename = config.SITE_PW_FILE else: diff --git a/Mailman/bin/mmsitepass.py b/Mailman/bin/mmsitepass.py index 0246856bd..c8b0d27ec 100644 --- a/Mailman/bin/mmsitepass.py +++ b/Mailman/bin/mmsitepass.py @@ -68,7 +68,7 @@ case-insensitive.""")) parser.error(_('Unexpected arguments')) if opts.list_hash_schemes: for label in passwords.Schemes: - print label.upper() + print str(label).upper() sys.exit(0) return parser, opts, args @@ -77,9 +77,10 @@ def check_password_scheme(parser, password_scheme): # shoule be checked after config is loaded. if password_scheme == '': password_scheme = config.PASSWORD_SCHEME - if password_scheme.lower() not in passwords.Schemes: + scheme = passwords.lookup_scheme(password_scheme.lower()) + if not scheme: parser.error(_('Invalid password scheme')) - return password_scheme + return scheme diff --git a/Mailman/passwords.py b/Mailman/passwords.py index 2b7d38f2d..a46c11a16 100644 --- a/Mailman/passwords.py +++ b/Mailman/passwords.py @@ -29,6 +29,7 @@ from array import array from base64 import urlsafe_b64decode as decode from base64 import urlsafe_b64encode as encode +from Mailman import Errors from Mailman.enum import Enum SALT_LENGTH = 20 # bytes @@ -202,8 +203,8 @@ _SCHEMES_BY_ENUM = { # Some scheme tags have arguments, but the key for this dictionary should just # be the lowercased scheme name. -_SCHEMES_BY_TAG = dict((c.TAG.split(' ')[0].lower(), c) - for c in _SCHEMES_BY_ENUM.values()) +_SCHEMES_BY_TAG = dict((_SCHEMES_BY_ENUM[e].TAG.split(' ')[0].lower(), e) + for e in _SCHEMES_BY_ENUM) _DEFAULT_SCHEME = NoPasswordScheme @@ -218,7 +219,9 @@ def make_secret(password, scheme=None): # be a unicode. if isinstance(password, unicode): password = password.encode('utf-8') - scheme_class = _SCHEMES_BY_TAG.get(scheme, _DEFAULT_SCHEME) + scheme_class = _SCHEMES_BY_ENUM.get(scheme) + if not scheme_class: + raise Errors.BadPasswordSchemeError(scheme) secret = scheme_class.make_secret(password) return '{%s}%s' % (scheme_class.TAG, secret) @@ -235,8 +238,10 @@ def check_response(challenge, response): scheme_group, rest_group = mo.group('scheme', 'rest') scheme_parts = scheme_group.split() scheme = scheme_parts[0].lower() - scheme_class = _SCHEMES_BY_TAG.get(scheme, _DEFAULT_SCHEME) - if isinstance(rest_group, unicode): - # decode() fails. (challenge is from database) - rest_group = str(rest_group) + scheme_enum = _SCHEMES_BY_TAG.get(scheme, _DEFAULT_SCHEME) + scheme_class = _SCHEMES_BY_ENUM[scheme_enum] return scheme_class.check_response(rest_group, response, *scheme_parts[1:]) + + +def lookup_scheme(scheme_name): + return _SCHEMES_BY_TAG.get(scheme_name.lower()) diff --git a/Mailman/testing/base.py b/Mailman/testing/base.py index 2979ad18b..8a131175e 100644 --- a/Mailman/testing/base.py +++ b/Mailman/testing/base.py @@ -118,3 +118,9 @@ class TestBase(unittest.TestCase): archives=True, quiet=True) os.unlink(self._config) os.unlink(self._dbfile) + # Clear out any site locks, which can be left over if tests fail. + for filename in os.listdir(config.LOCK_DIR): + if filename.startswith('<site>'): + path = os.path.join(config.LOCK_DIR, filename) + print >> sys.stderr, '@@@@@ removing:', path + os.unlink(path) diff --git a/Mailman/testing/test_handlers.py b/Mailman/testing/test_handlers.py index 849ccfb91..b2fb5ce27 100644 --- a/Mailman/testing/test_handlers.py +++ b/Mailman/testing/test_handlers.py @@ -59,7 +59,7 @@ from Mailman.Handlers import ToUsenet def password(cleartext): - return passwords.make_secret(cleartext, 'ssha') + return passwords.make_secret(cleartext, passwords.Schemes.ssha) diff --git a/Mailman/testing/test_membership.py b/Mailman/testing/test_membership.py index ed8ebb1f4..8e8285034 100644 --- a/Mailman/testing/test_membership.py +++ b/Mailman/testing/test_membership.py @@ -33,7 +33,7 @@ from Mailman.testing.base import TestBase def password(cleartext): - return passwords.make_secret(cleartext, 'ssha') + return passwords.make_secret(cleartext, passwords.Schemes.ssha) diff --git a/Mailman/testing/test_passwords.py b/Mailman/testing/test_passwords.py index b957b10ae..8298c22f6 100644 --- a/Mailman/testing/test_passwords.py +++ b/Mailman/testing/test_passwords.py @@ -19,6 +19,7 @@ import unittest +from Mailman import Errors from Mailman import passwords @@ -72,6 +73,27 @@ class TestBogusPasswords(TestPasswordsBase): scheme = -1 def test_passwords(self): + self.assertRaises(Errors.BadPasswordSchemeError, + passwords.make_secret, self.pw8a, self.scheme) + + def test_unicode_passwords(self): + self.assertRaises(Errors.BadPasswordSchemeError, + passwords.make_secret, self.pwua, self.scheme) + + def test_passwords_with_funky_chars(self): + self.assertRaises(Errors.BadPasswordSchemeError, + passwords.make_secret, self.pw8b, self.scheme) + + def test_unicode_passwords_with_funky_chars(self): + self.assertRaises(Errors.BadPasswordSchemeError, + passwords.make_secret, self.pwub, self.scheme) + + + +class TestNonePasswords(TestPasswordsBase): + scheme = passwords.Schemes.no_scheme + + def test_passwords(self): failif = self.failIf secret = passwords.make_secret(self.pw8a, self.scheme) failif(passwords.check_response(secret, self.pw8a)) @@ -97,24 +119,33 @@ class TestBogusPasswords(TestPasswordsBase): -class TestNonePasswords(TestBogusPasswords): - scheme = 'no_scheme' - - class TestCleartextPasswords(TestPasswordsBase): - scheme = 'cleartext' + scheme = passwords.Schemes.cleartext class TestSHAPasswords(TestPasswordsBase): - scheme = 'sha' + scheme = passwords.Schemes.sha class TestSSHAPasswords(TestPasswordsBase): - scheme = 'ssha' + scheme = passwords.Schemes.ssha class TestPBKDF2Passwords(TestPasswordsBase): - scheme = 'pbkdf2' + scheme = passwords.Schemes.pbkdf2 + + + +class TestSchemeLookup(unittest.TestCase): + def test_scheme_name_lookup(self): + unless = self.failUnless + unless(passwords.lookup_scheme('NONE') is passwords.Schemes.no_scheme) + unless(passwords.lookup_scheme('CLEARTEXT') is + passwords.Schemes.cleartext) + unless(passwords.lookup_scheme('SHA') is passwords.Schemes.sha) + unless(passwords.lookup_scheme('SSHA') is passwords.Schemes.ssha) + unless(passwords.lookup_scheme('PBKDF2') is passwords.Schemes.pbkdf2) + unless(passwords.lookup_scheme(' -bogus- ') is None) @@ -126,4 +157,5 @@ def test_suite(): suite.addTest(unittest.makeSuite(TestSHAPasswords)) suite.addTest(unittest.makeSuite(TestSSHAPasswords)) suite.addTest(unittest.makeSuite(TestPBKDF2Passwords)) + suite.addTest(unittest.makeSuite(TestSchemeLookup)) return suite diff --git a/Mailman/testing/test_security_mgr.py b/Mailman/testing/test_security_mgr.py index 543330aed..089f4b0ef 100644 --- a/Mailman/testing/test_security_mgr.py +++ b/Mailman/testing/test_security_mgr.py @@ -36,7 +36,7 @@ from Mailman.testing.base import TestBase def password(cleartext): - return passwords.make_secret(cleartext, 'ssha') + return passwords.make_secret(cleartext, passwords.Schemes.ssha) |
