Skip to content

Commit

Permalink
Keep things backwards compatible by creating an extended interface.
Browse files Browse the repository at this point in the history
By moving the match method to a IMatchingPasswordManager, we keep the original interface unchanged and thus backwards compatible. Users of zope.password that require the new functionality can test for the new interface.
  • Loading branch information
Martijn Pieters committed Feb 20, 2011
1 parent 7739431 commit d91ba41
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 48 deletions.
9 changes: 5 additions & 4 deletions CHANGES.txt
Expand Up @@ -2,12 +2,13 @@
CHANGES
=======

4.0.0 (unreleased)
3.7.0 (unreleased)
------------------

- Add a 'match' method to the IPasswordManager interface, which returns True
if a given password hash was encdoded with the scheme implemented by the
specific manager.
- Add a new IMatchingPasswordManager interface with a 'match' method, which
returns True if a given password hash was encdoded with the scheme
implemented by the specific manager. All managers in this package implement
this interface.

- Use {SHA} as the prefix for SHA1-encoded passwords to be compatible with
RFC 2307, but support matching against {SHA1} for backwards compatibility.
Expand Down
5 changes: 4 additions & 1 deletion README.txt
Expand Up @@ -39,14 +39,17 @@ Usage

It's very easy to use password managers. The
``zope.password.interfaces.IPasswordManager`` interface defines only
three methods::
two methods::

def encodePassword(password):
"""Return encoded data for the given password"""

def checkPassword(encoded_password, password):
"""Return whether the given encoded data coincide with the given password"""

An extended interface, ``zope.password.interfaces.IMatchingPasswordManager``,
adds one additional method::

def match(encoded_password):
"""
Returns True when the given data was encoded with the scheme
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -17,7 +17,7 @@


setup(name='zope.password',
version='4.0.0dev',
version='3.7.0dev',
author='Zope Foundation and Contributors',
author_email='zope-dev@zope.org',
description='Password encoding and checking utilities',
Expand Down
22 changes: 11 additions & 11 deletions src/zope/password/configure.zcml
Expand Up @@ -5,38 +5,38 @@

<utility
name="Plain Text"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".password.PlainTextPasswordManager"
/>

<utility
name="MD5"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".password.MD5PasswordManager"
/>

<utility
name="SHA1"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".password.SHA1PasswordManager"
/>

<utility
name="SSHA"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".password.SSHAPasswordManager"
/>

<utility
name="MYSQL"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".legacy.MySQLPasswordManager"
/>

<configure zcml:condition="installed crypt">
<utility
name="Crypt"
provides=".interfaces.IPasswordManager"
provides=".interfaces.IMatchingPasswordManager"
factory=".legacy.CryptPasswordManager"
/>
</configure>
Expand All @@ -50,23 +50,23 @@
<configure zcml:condition="installed zope.security">

<class class=".password.PlainTextPasswordManager">
<allow interface=".interfaces.IPasswordManager" />
<allow interface=".interfaces.IMatchingPasswordManager" />
</class>

<class class=".password.MD5PasswordManager">
<allow interface=".interfaces.IPasswordManager" />
<allow interface=".interfaces.IMatchingPasswordManager" />
</class>

<class class=".password.SHA1PasswordManager">
<allow interface=".interfaces.IPasswordManager" />
<allow interface=".interfaces.IMatchingPasswordManager" />
</class>

<class class=".password.SSHAPasswordManager">
<allow interface=".interfaces.IPasswordManager" />
<allow interface=".interfaces.IMatchingPasswordManager" />
</class>

<class class=".password.SSHAPasswordManager">
<allow interface=".interfaces.IPasswordManager" />
<allow interface=".interfaces.IMatchingPasswordManager" />
</class>

</configure>
Expand Down
3 changes: 3 additions & 0 deletions src/zope/password/interfaces.py
Expand Up @@ -24,6 +24,9 @@ def encodePassword(password):
def checkPassword(encoded_password, password):
"""Does the given encoded data coincide with the given password"""

class IMatchingPasswordManager(IPasswordManager):
"""Password manager with hash matching support"""

def match(encoded_password):
"""
Returns True when the given data was encoded with the scheme
Expand Down
10 changes: 5 additions & 5 deletions src/zope/password/legacy.py
Expand Up @@ -25,7 +25,7 @@
crypt = None

from zope.interface import implements
from zope.password.interfaces import IPasswordManager
from zope.password.interfaces import IMatchingPasswordManager

_encoder = getencoder("utf-8")

Expand All @@ -41,7 +41,7 @@ class CryptPasswordManager(object):
>>> from zope.interface.verify import verifyObject
>>> manager = CryptPasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand Down Expand Up @@ -95,7 +95,7 @@ class CryptPasswordManager(object):
"""

implements(IPasswordManager)
implements(IMatchingPasswordManager)

def encodePassword(self, password, salt=None):
if salt is None:
Expand All @@ -122,7 +122,7 @@ class MySQLPasswordManager(object):
>>> from zope.interface.verify import verifyObject
>>> manager = MySQLPasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand Down Expand Up @@ -161,7 +161,7 @@ class MySQLPasswordManager(object):
"""

implements(IPasswordManager)
implements(IMatchingPasswordManager)

def encodePassword(self, password):
nr = 1345345333L
Expand Down
18 changes: 6 additions & 12 deletions src/zope/password/password.py
Expand Up @@ -28,7 +28,7 @@
from sha import new as sha1

from zope.interface import implements
from zope.password.interfaces import IPasswordManager
from zope.password.interfaces import IMatchingPasswordManager

_encoder = getencoder("utf-8")

Expand All @@ -39,7 +39,7 @@ class PlainTextPasswordManager(object):
>>> from zope.interface.verify import verifyObject
>>> manager = PlainTextPasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand All @@ -61,7 +61,7 @@ class PlainTextPasswordManager(object):
False
"""

implements(IPasswordManager)
implements(IMatchingPasswordManager)

def encodePassword(self, password):
return password
Expand Down Expand Up @@ -92,7 +92,7 @@ class SSHAPasswordManager(PlainTextPasswordManager):
>>> from zope.interface.verify import verifyObject
>>> manager = SSHAPasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand Down Expand Up @@ -145,8 +145,6 @@ class SSHAPasswordManager(PlainTextPasswordManager):
"""

implements(IPasswordManager)

def encodePassword(self, password, salt=None):
if salt is None:
salt = urandom(4)
Expand Down Expand Up @@ -176,7 +174,7 @@ class MD5PasswordManager(PlainTextPasswordManager):
>>> from zope.interface.verify import verifyObject
>>> manager = MD5PasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand Down Expand Up @@ -222,8 +220,6 @@ class MD5PasswordManager(PlainTextPasswordManager):
"""

implements(IPasswordManager)

def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
Expand All @@ -249,7 +245,7 @@ class SHA1PasswordManager(PlainTextPasswordManager):
>>> from zope.interface.verify import verifyObject
>>> manager = SHA1PasswordManager()
>>> verifyObject(IPasswordManager, manager)
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
Expand Down Expand Up @@ -308,8 +304,6 @@ class SHA1PasswordManager(PlainTextPasswordManager):
"""

implements(IPasswordManager)

def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
Expand Down
29 changes: 15 additions & 14 deletions src/zope/password/testing.py
Expand Up @@ -18,7 +18,7 @@
from zope.component import provideUtility
from zope.schema.interfaces import IVocabularyFactory

from zope.password.interfaces import IPasswordManager
from zope.password.interfaces import IMatchingPasswordManager
from zope.password.password import PlainTextPasswordManager
from zope.password.password import MD5PasswordManager
from zope.password.password import SHA1PasswordManager
Expand All @@ -38,15 +38,15 @@ def setUpPasswordManagers():
>>> from zope.component import getUtility
>>> setUpPasswordManagers()
>>> getUtility(IPasswordManager, 'Plain Text')
>>> getUtility(IMatchingPasswordManager, 'Plain Text')
<zope.password.password.PlainTextPasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'SSHA')
>>> getUtility(IMatchingPasswordManager, 'SSHA')
<zope.password.password.SSHAPasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'MD5')
>>> getUtility(IMatchingPasswordManager, 'MD5')
<zope.password.password.MD5PasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'SHA1')
>>> getUtility(IMatchingPasswordManager, 'SHA1')
<zope.password.password.SHA1PasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'MYSQL')
>>> getUtility(IMatchingPasswordManager, 'MYSQL')
<zope.password.legacy.MySQLPasswordManager object at 0x...>
>>> try:
Expand All @@ -55,8 +55,8 @@ def setUpPasswordManagers():
... CryptPasswordManager = None
... True
... else:
... from zope.password.legacy import CryptPasswordManager
... getUtility(IPasswordManager, 'Crypt') is CryptPasswordManager
... from zope.password.legacy import CryptPasswordManager as cpm
... getUtility(IMatchingPasswordManager, 'Crypt') is cpm
True
>>> voc = getUtility(IVocabularyFactory, 'Password Manager Names')
Expand All @@ -78,14 +78,15 @@ def setUpPasswordManagers():
True
"""
provideUtility(PlainTextPasswordManager(), IPasswordManager, 'Plain Text')
provideUtility(SSHAPasswordManager(), IPasswordManager, 'SSHA')
provideUtility(MD5PasswordManager(), IPasswordManager, 'MD5')
provideUtility(SHA1PasswordManager(), IPasswordManager, 'SHA1')
provideUtility(MySQLPasswordManager(), IPasswordManager, 'MYSQL')
provideUtility(PlainTextPasswordManager(), IMatchingPasswordManager,
'Plain Text')
provideUtility(SSHAPasswordManager(), IMatchingPasswordManager, 'SSHA')
provideUtility(MD5PasswordManager(), IMatchingPasswordManager, 'MD5')
provideUtility(SHA1PasswordManager(), IMatchingPasswordManager, 'SHA1')
provideUtility(MySQLPasswordManager(), IMatchingPasswordManager, 'MYSQL')

if CryptPasswordManager is not None:
provideUtility(CryptPasswordManager, IPasswordManager, 'Crypt')
provideUtility(CryptPasswordManager, IMatchingPasswordManager, 'Crypt')

provideUtility(PasswordManagerNamesVocabulary,
IVocabularyFactory, 'Password Manager Names')

0 comments on commit d91ba41

Please sign in to comment.