[Checkins] SVN: zope.app.authentication/tags/3.5.0a2/ Tag 3.5.0a2

Dan Korostelev nadako at gmail.com
Sun Feb 1 08:24:06 EST 2009


Log message for revision 95853:
  Tag 3.5.0a2

Changed:
  A   zope.app.authentication/tags/3.5.0a2/
  D   zope.app.authentication/tags/3.5.0a2/CHANGES.txt
  A   zope.app.authentication/tags/3.5.0a2/CHANGES.txt
  D   zope.app.authentication/tags/3.5.0a2/setup.py
  A   zope.app.authentication/tags/3.5.0a2/setup.py
  D   zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py
  A   zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py

-=-
Deleted: zope.app.authentication/tags/3.5.0a2/CHANGES.txt
===================================================================
--- zope.app.authentication/trunk/CHANGES.txt	2009-02-01 13:00:27 UTC (rev 95850)
+++ zope.app.authentication/tags/3.5.0a2/CHANGES.txt	2009-02-01 13:24:06 UTC (rev 95853)
@@ -1,57 +0,0 @@
-=======
-Changes
-=======
-
-3.5.0a2 (unreleased)
---------------------
-
-* ...
-
-3.5.0a1 (2009-01-31)
---------------------
-
-* Use ``zope.container`` instead of ``zope.app.container``.
-
-* Encoded passwords are now stored with a prefix ({MD5}, {SHA1},
-  {SSHA}) indicating the used encoding schema. Old (encoded) passwords
-  can still be used.
-
-* Add an SSHA password manager that is compatible with standard LDAP
-  passwords. As this encoding gives better security agains dictionary
-  attacks, users are encouraged to switch to this new password schema.
-
-* InternalPrincipal now uses SSHA password manager by default.
-
-3.4.4 (2008-12-12)
-------------------
-
-* Depend on zope.session instead of zope.app.session. The first one
-  currently has all functionality we need.
-* Fix deprecation warnings for ``md5`` and ``sha`` on Python 2.6.
-
-3.4.3 (2008-08-07)
-------------------
-
-* No changes. Retag for correct release on PyPI.
-
-3.4.2 (2008-07-09)
--------------------
-
-* Make it compatible with zope.app.container 3.6.1 and 3.5.4 changes,
-  Changed ``super(BTreeContainer, self).__init__()`` to 
-  ``super(GroupFolder, self).__init__()`` in ``GroupFolder`` class.
-
-3.4.1 (2007-10-24)
-------------------
-
-* Avoid deprecation warning.
-
-3.4.0 (2007-10-11)
-------------------
-
-* Updated package meta-data.
-
-3.4.0b1 (2007-09-27)
---------------------
-
-* First release independent of Zope.

Copied: zope.app.authentication/tags/3.5.0a2/CHANGES.txt (from rev 95851, zope.app.authentication/trunk/CHANGES.txt)
===================================================================
--- zope.app.authentication/tags/3.5.0a2/CHANGES.txt	                        (rev 0)
+++ zope.app.authentication/tags/3.5.0a2/CHANGES.txt	2009-02-01 13:24:06 UTC (rev 95853)
@@ -0,0 +1,57 @@
+=======
+Changes
+=======
+
+3.5.0a2 (2009-02-01)
+--------------------
+
+* Make old encoded passwords really work.
+
+3.5.0a1 (2009-01-31)
+--------------------
+
+* Use ``zope.container`` instead of ``zope.app.container``.
+
+* Encoded passwords are now stored with a prefix ({MD5}, {SHA1},
+  {SSHA}) indicating the used encoding schema. Old (encoded) passwords
+  can still be used.
+
+* Add an SSHA password manager that is compatible with standard LDAP
+  passwords. As this encoding gives better security agains dictionary
+  attacks, users are encouraged to switch to this new password schema.
+
+* InternalPrincipal now uses SSHA password manager by default.
+
+3.4.4 (2008-12-12)
+------------------
+
+* Depend on zope.session instead of zope.app.session. The first one
+  currently has all functionality we need.
+* Fix deprecation warnings for ``md5`` and ``sha`` on Python 2.6.
+
+3.4.3 (2008-08-07)
+------------------
+
+* No changes. Retag for correct release on PyPI.
+
+3.4.2 (2008-07-09)
+-------------------
+
+* Make it compatible with zope.app.container 3.6.1 and 3.5.4 changes,
+  Changed ``super(BTreeContainer, self).__init__()`` to 
+  ``super(GroupFolder, self).__init__()`` in ``GroupFolder`` class.
+
+3.4.1 (2007-10-24)
+------------------
+
+* Avoid deprecation warning.
+
+3.4.0 (2007-10-11)
+------------------
+
+* Updated package meta-data.
+
+3.4.0b1 (2007-09-27)
+--------------------
+
+* First release independent of Zope.

Deleted: zope.app.authentication/tags/3.5.0a2/setup.py
===================================================================
--- zope.app.authentication/trunk/setup.py	2009-02-01 13:00:27 UTC (rev 95850)
+++ zope.app.authentication/tags/3.5.0a2/setup.py	2009-02-01 13:24:06 UTC (rev 95853)
@@ -1,85 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Setup for zope.app.authentication package
-
-$Id$
-"""
-import os
-from setuptools import setup, find_packages
-
-def read(*rnames):
-    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
-
-setup(name='zope.app.authentication',
-      version = '3.5.0a2dev',
-      author='Zope Corporation and Contributors',
-      author_email='zope-dev at zope.org',
-      description='Pluggable Authentication Utility',
-      long_description=(
-        read('README.txt')
-        + '\n\n' +
-        'Detailed Documentation\n' +
-        '----------------------\n'
-        + '\n' +
-        read('src', 'zope', 'app', 'authentication', 'README.txt')
-        + '\n\n' +
-        read('src', 'zope', 'app', 'authentication', 'principalfolder.txt')
-        + '\n\n' +
-        read('src', 'zope', 'app', 'authentication', 'vocabulary.txt')
-        + '\n\n' +
-        read('CHANGES.txt')
-        ),
-      url='http://cheeseshop.python.org/pypi/zope.app.authentication',
-      license='ZPL 2.1',
-      classifiers = [
-          'Development Status :: 5 - Production/Stable',
-          'Environment :: Web Environment',
-          'Intended Audience :: Developers',
-          'License :: OSI Approved :: Zope Public License',
-          'Programming Language :: Python',
-          'Natural Language :: English',
-          'Operating System :: OS Independent',
-          'Topic :: Internet :: WWW/HTTP',
-          'Framework :: Zope3'],
-      keywords='zope3 authentication pluggable principal group',
-      packages=find_packages('src'),
-      package_dir = {'': 'src'},
-      extras_require=dict(test=['zope.app.testing',
-                                'zope.app.securitypolicy',
-                                'zope.app.zcmlfiles',
-                                'zope.securitypolicy',
-                                'zope.testbrowser']),
-      namespace_packages=['zope', 'zope.app'],
-      install_requires=['setuptools',
-                        'zope.app.component',
-                        'zope.app.container',
-                        'zope.app.form',
-                        'zope.app.security',
-                        'zope.dublincore',
-                        'zope.event',
-                        'zope.exceptions',
-                        'zope.i18n',
-                        'zope.i18nmessageid',
-                        'zope.interface',
-                        'zope.location',
-                        'zope.publisher',
-                        'zope.schema',
-                        'zope.security',
-                        'zope.session',
-                        'zope.traversing',
-                        'ZODB3',
-                        ],
-      include_package_data = True,
-      zip_safe = False,
-      )

Copied: zope.app.authentication/tags/3.5.0a2/setup.py (from rev 95852, zope.app.authentication/trunk/setup.py)
===================================================================
--- zope.app.authentication/tags/3.5.0a2/setup.py	                        (rev 0)
+++ zope.app.authentication/tags/3.5.0a2/setup.py	2009-02-01 13:24:06 UTC (rev 95853)
@@ -0,0 +1,85 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Setup for zope.app.authentication package
+
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(name='zope.app.authentication',
+      version = '3.5.0a2',
+      author='Zope Corporation and Contributors',
+      author_email='zope-dev at zope.org',
+      description='Pluggable Authentication Utility',
+      long_description=(
+        read('README.txt')
+        + '\n\n' +
+        'Detailed Documentation\n' +
+        '----------------------\n'
+        + '\n' +
+        read('src', 'zope', 'app', 'authentication', 'README.txt')
+        + '\n\n' +
+        read('src', 'zope', 'app', 'authentication', 'principalfolder.txt')
+        + '\n\n' +
+        read('src', 'zope', 'app', 'authentication', 'vocabulary.txt')
+        + '\n\n' +
+        read('CHANGES.txt')
+        ),
+      url='http://pypi.python.org/pypi/zope.app.authentication',
+      license='ZPL 2.1',
+      classifiers = [
+          'Development Status :: 5 - Production/Stable',
+          'Environment :: Web Environment',
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: Zope Public License',
+          'Programming Language :: Python',
+          'Natural Language :: English',
+          'Operating System :: OS Independent',
+          'Topic :: Internet :: WWW/HTTP',
+          'Framework :: Zope3'],
+      keywords='zope3 authentication pluggable principal group',
+      packages=find_packages('src'),
+      package_dir = {'': 'src'},
+      extras_require=dict(test=['zope.app.testing',
+                                'zope.app.securitypolicy',
+                                'zope.app.zcmlfiles',
+                                'zope.securitypolicy',
+                                'zope.testbrowser']),
+      namespace_packages=['zope', 'zope.app'],
+      install_requires=['setuptools',
+                        'zope.app.component',
+                        'zope.app.container',
+                        'zope.app.form',
+                        'zope.app.security',
+                        'zope.dublincore',
+                        'zope.event',
+                        'zope.exceptions',
+                        'zope.i18n',
+                        'zope.i18nmessageid',
+                        'zope.interface',
+                        'zope.location',
+                        'zope.publisher',
+                        'zope.schema',
+                        'zope.security',
+                        'zope.session',
+                        'zope.traversing',
+                        'ZODB3',
+                        ],
+      include_package_data = True,
+      zip_safe = False,
+      )

Deleted: zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/password.py	2009-02-01 13:00:27 UTC (rev 95850)
+++ zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py	2009-02-01 13:24:06 UTC (rev 95853)
@@ -1,245 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Password managers
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-try:
-    from hashlib import md5, sha1
-except ImportError:
-    # Python 2.4
-    from md5 import new as md5
-    from sha import new as sha1
-
-from base64 import urlsafe_b64encode
-from base64 import urlsafe_b64decode
-from os import urandom
-from random import randint
-from codecs import getencoder
-
-from zope.interface import implements, classProvides
-from zope.schema.interfaces import IVocabularyFactory
-from zope.app.component.vocabulary import UtilityVocabulary
-
-from zope.app.authentication.interfaces import IPasswordManager
-
-
-_encoder = getencoder("utf-8")
-
-
-class PlainTextPasswordManager(object):
-    """Plain text password manager.
-
-    >>> from zope.interface.verify import verifyObject
-
-    >>> manager = PlainTextPasswordManager()
-    >>> verifyObject(IPasswordManager, manager)
-    True
-
-    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
-    >>> encoded = manager.encodePassword(password)
-    >>> encoded
-    u'right \u0410'
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-    """
-
-    implements(IPasswordManager)
-
-    def encodePassword(self, password):
-        return password
-
-    def checkPassword(self, storedPassword, password):
-        return storedPassword == self.encodePassword(password)
-
-
-class MD5PasswordManager(PlainTextPasswordManager):
-    """MD5 password manager.
-
-    Note: use of salt in this password manager is purely
-    cosmetical. Use SSHA if you want increased security.
-    
-    >>> from zope.interface.verify import verifyObject
-
-    >>> manager = MD5PasswordManager()
-    >>> verifyObject(IPasswordManager, manager)
-    True
-
-    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
-    >>> encoded = manager.encodePassword(password, salt="")
-    >>> encoded
-    '{MD5}86dddccec45db4599f1ac00018e54139'
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    >>> encoded = manager.encodePassword(password)
-    >>> encoded[-32:]
-    '86dddccec45db4599f1ac00018e54139'
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    >>> manager.encodePassword(password) != manager.encodePassword(password)
-    True
-    """
-
-    implements(IPasswordManager)
-
-    def encodePassword(self, password, salt=None):
-        if salt is None:
-            salt = "%08x" % randint(0, 0xffffffff)
-        return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest())
-
-    def checkPassword(self, storedPassword, password):
-        if storedPassword.startswith('{MD5}'):
-            salt = storedPassword[5:-32]
-            return storedPassword == self.encodePassword(password, salt)
-        salt = storedPassword[:-32]
-        return storedPassword == self.encodePassword(password, salt)[6:]
-
-
-class SHA1PasswordManager(PlainTextPasswordManager):
-    """SHA1 password manager.
-
-    Note: use of salt in this password manager is purely
-    cosmetical. Use SSHA if you want increased security.
-    
-    >>> from zope.interface.verify import verifyObject
-
-    >>> manager = SHA1PasswordManager()
-    >>> verifyObject(IPasswordManager, manager)
-    True
-
-    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
-    >>> encoded = manager.encodePassword(password, salt="")
-    >>> encoded
-    '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    >>> encoded = manager.encodePassword(password)
-    >>> encoded[-40:]
-    '04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    >>> manager.encodePassword(password) != manager.encodePassword(password)
-    True
-    """
-
-    implements(IPasswordManager)
-
-    def encodePassword(self, password, salt=None):
-        if salt is None:
-            salt = "%08x" % randint(0, 0xffffffff)
-        return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
-
-    def checkPassword(self, storedPassword, password):
-        if storedPassword.startswith('{SHA1}'):
-            salt = storedPassword[6:-40]
-            return storedPassword == self.encodePassword(password, salt)
-        salt = storedPassword[:-40]
-        return storedPassword == self.encodePassword(password, salt)[7:]
-
-class SSHAPasswordManager(PlainTextPasswordManager):
-    """SSHA password manager.
-
-    SSHA is basically SHA1-encoding which also incorporates a salt
-    into the encoded string. This way, stored passwords are more
-    robust against dictionary attacks of attackers that could get
-    access to lists of encoded passwords.
-
-    SSHA is regularly used in LDAP databases and we should be
-    compatible with passwords used there.
-    
-    >>> from zope.interface.verify import verifyObject
-
-    >>> manager = SSHAPasswordManager()
-    >>> verifyObject(IPasswordManager, manager)
-    True
-
-    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
-    >>> encoded = manager.encodePassword(password, salt="")
-    >>> encoded
-    '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI='
-
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    Using the `slappasswd` utility to encode ``secret``, we get
-    ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash.
-
-    Our password manager generates the same value when seeded with the
-    same salt, so we can be sure, our output is compatible with
-    standard LDAP tools that also use SSHA::
-    
-    >>> from base64 import urlsafe_b64decode
-    >>> salt = urlsafe_b64decode('XkOZbw==')
-    >>> encoded = manager.encodePassword('secret', salt)
-    >>> encoded
-    '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv'
-    
-    >>> encoded = manager.encodePassword(password)
-    >>> manager.checkPassword(encoded, password)
-    True
-    >>> manager.checkPassword(encoded, password + u"wrong")
-    False
-
-    >>> manager.encodePassword(password) != manager.encodePassword(password)
-    True
-    """
-
-    implements(IPasswordManager)
-
-    def encodePassword(self, password, salt=None):
-        if salt is None:
-            salt = urandom(4)
-        hash = sha1(_encoder(password)[0])
-        hash.update(salt)
-        return '{SSHA}' + urlsafe_b64encode(
-            hash.digest() + salt)
-
-    def checkPassword(self, storedPassword, password):
-        byte_string = urlsafe_b64decode(storedPassword[6:])
-        salt = byte_string[20:]
-        return storedPassword == self.encodePassword(password, salt)
-
-# Simple registry used by mkzopeinstance script
-managers = [
-    ("Plain Text", PlainTextPasswordManager()), # default
-    ("MD5", MD5PasswordManager()),
-    ("SHA1", SHA1PasswordManager()),
-    ("SSHA", SSHAPasswordManager()),
-]
-
-
-class PasswordManagerNamesVocabulary(UtilityVocabulary):
-    """Vocabulary of password managers."""
-
-    classProvides(IVocabularyFactory)
-    interface = IPasswordManager
-    nameOnly = True

Copied: zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py (from rev 95851, zope.app.authentication/trunk/src/zope/app/authentication/password.py)
===================================================================
--- zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py	                        (rev 0)
+++ zope.app.authentication/tags/3.5.0a2/src/zope/app/authentication/password.py	2009-02-01 13:24:06 UTC (rev 95853)
@@ -0,0 +1,245 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Password managers
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+try:
+    from hashlib import md5, sha1
+except ImportError:
+    # Python 2.4
+    from md5 import new as md5
+    from sha import new as sha1
+
+from base64 import urlsafe_b64encode
+from base64 import urlsafe_b64decode
+from os import urandom
+from random import randint
+from codecs import getencoder
+
+from zope.interface import implements, classProvides
+from zope.schema.interfaces import IVocabularyFactory
+from zope.app.component.vocabulary import UtilityVocabulary
+
+from zope.app.authentication.interfaces import IPasswordManager
+
+
+_encoder = getencoder("utf-8")
+
+
+class PlainTextPasswordManager(object):
+    """Plain text password manager.
+
+    >>> from zope.interface.verify import verifyObject
+
+    >>> manager = PlainTextPasswordManager()
+    >>> verifyObject(IPasswordManager, manager)
+    True
+
+    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
+    >>> encoded = manager.encodePassword(password)
+    >>> encoded
+    u'right \u0410'
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+    """
+
+    implements(IPasswordManager)
+
+    def encodePassword(self, password):
+        return password
+
+    def checkPassword(self, storedPassword, password):
+        return storedPassword == self.encodePassword(password)
+
+
+class MD5PasswordManager(PlainTextPasswordManager):
+    """MD5 password manager.
+
+    Note: use of salt in this password manager is purely
+    cosmetical. Use SSHA if you want increased security.
+    
+    >>> from zope.interface.verify import verifyObject
+
+    >>> manager = MD5PasswordManager()
+    >>> verifyObject(IPasswordManager, manager)
+    True
+
+    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
+    >>> encoded = manager.encodePassword(password, salt="")
+    >>> encoded
+    '{MD5}86dddccec45db4599f1ac00018e54139'
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    >>> encoded = manager.encodePassword(password)
+    >>> encoded[-32:]
+    '86dddccec45db4599f1ac00018e54139'
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    >>> manager.encodePassword(password) != manager.encodePassword(password)
+    True
+    """
+
+    implements(IPasswordManager)
+
+    def encodePassword(self, password, salt=None):
+        if salt is None:
+            salt = "%08x" % randint(0, 0xffffffff)
+        return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest())
+
+    def checkPassword(self, storedPassword, password):
+        if storedPassword.startswith('{MD5}'):
+            salt = storedPassword[5:-32]
+            return storedPassword == self.encodePassword(password, salt)
+        salt = storedPassword[:-32]
+        return storedPassword == self.encodePassword(password, salt)[5:]
+
+
+class SHA1PasswordManager(PlainTextPasswordManager):
+    """SHA1 password manager.
+
+    Note: use of salt in this password manager is purely
+    cosmetical. Use SSHA if you want increased security.
+    
+    >>> from zope.interface.verify import verifyObject
+
+    >>> manager = SHA1PasswordManager()
+    >>> verifyObject(IPasswordManager, manager)
+    True
+
+    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
+    >>> encoded = manager.encodePassword(password, salt="")
+    >>> encoded
+    '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    >>> encoded = manager.encodePassword(password)
+    >>> encoded[-40:]
+    '04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    >>> manager.encodePassword(password) != manager.encodePassword(password)
+    True
+    """
+
+    implements(IPasswordManager)
+
+    def encodePassword(self, password, salt=None):
+        if salt is None:
+            salt = "%08x" % randint(0, 0xffffffff)
+        return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
+
+    def checkPassword(self, storedPassword, password):
+        if storedPassword.startswith('{SHA1}'):
+            salt = storedPassword[6:-40]
+            return storedPassword == self.encodePassword(password, salt)
+        salt = storedPassword[:-40]
+        return storedPassword == self.encodePassword(password, salt)[6:]
+
+class SSHAPasswordManager(PlainTextPasswordManager):
+    """SSHA password manager.
+
+    SSHA is basically SHA1-encoding which also incorporates a salt
+    into the encoded string. This way, stored passwords are more
+    robust against dictionary attacks of attackers that could get
+    access to lists of encoded passwords.
+
+    SSHA is regularly used in LDAP databases and we should be
+    compatible with passwords used there.
+    
+    >>> from zope.interface.verify import verifyObject
+
+    >>> manager = SSHAPasswordManager()
+    >>> verifyObject(IPasswordManager, manager)
+    True
+
+    >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
+    >>> encoded = manager.encodePassword(password, salt="")
+    >>> encoded
+    '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI='
+
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    Using the `slappasswd` utility to encode ``secret``, we get
+    ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash.
+
+    Our password manager generates the same value when seeded with the
+    same salt, so we can be sure, our output is compatible with
+    standard LDAP tools that also use SSHA::
+    
+    >>> from base64 import urlsafe_b64decode
+    >>> salt = urlsafe_b64decode('XkOZbw==')
+    >>> encoded = manager.encodePassword('secret', salt)
+    >>> encoded
+    '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv'
+    
+    >>> encoded = manager.encodePassword(password)
+    >>> manager.checkPassword(encoded, password)
+    True
+    >>> manager.checkPassword(encoded, password + u"wrong")
+    False
+
+    >>> manager.encodePassword(password) != manager.encodePassword(password)
+    True
+    """
+
+    implements(IPasswordManager)
+
+    def encodePassword(self, password, salt=None):
+        if salt is None:
+            salt = urandom(4)
+        hash = sha1(_encoder(password)[0])
+        hash.update(salt)
+        return '{SSHA}' + urlsafe_b64encode(
+            hash.digest() + salt)
+
+    def checkPassword(self, storedPassword, password):
+        byte_string = urlsafe_b64decode(storedPassword[6:])
+        salt = byte_string[20:]
+        return storedPassword == self.encodePassword(password, salt)
+
+# Simple registry used by mkzopeinstance script
+managers = [
+    ("Plain Text", PlainTextPasswordManager()), # default
+    ("MD5", MD5PasswordManager()),
+    ("SHA1", SHA1PasswordManager()),
+    ("SSHA", SSHAPasswordManager()),
+]
+
+
+class PasswordManagerNamesVocabulary(UtilityVocabulary):
+    """Vocabulary of password managers."""
+
+    classProvides(IVocabularyFactory)
+    interface = IPasswordManager
+    nameOnly = True



More information about the Checkins mailing list