[Checkins] SVN: z3c.password/trunk/ Better error messages for invalid password exceptions.
Marius Gedminas
cvs-admin at zope.org
Thu Aug 9 12:22:11 UTC 2012
Log message for revision 127450:
Better error messages for invalid password exceptions.
When you reject the user's password for being too short or too long,
it's only polite to tell them the minimum/maximum password length.
This introduces new translatable strings which haven't been translated yet.
Changed:
U z3c.password/trunk/CHANGES.txt
U z3c.password/trunk/setup.py
U z3c.password/trunk/src/z3c/password/README.txt
U z3c.password/trunk/src/z3c/password/interfaces.py
U z3c.password/trunk/src/z3c/password/password.py
-=-
Modified: z3c.password/trunk/CHANGES.txt
===================================================================
--- z3c.password/trunk/CHANGES.txt 2012-08-09 12:22:02 UTC (rev 127449)
+++ z3c.password/trunk/CHANGES.txt 2012-08-09 12:22:07 UTC (rev 127450)
@@ -2,12 +2,16 @@
CHANGES
=======
-0.10.2 (unreleased)
+0.11.0 (unreleased)
-------------------
-- Nothing changed yet.
+- Better error messages for invalid password exceptions (when you reject the
+ user's password for being too short or too long, it's only polite to tell
+ them what the minimum/maximum password length is).
+ This introduces new translatable strings which haven't been translated yet.
+
0.10.1 (2011-03-28)
-------------------
Modified: z3c.password/trunk/setup.py
===================================================================
--- z3c.password/trunk/setup.py 2012-08-09 12:22:02 UTC (rev 127449)
+++ z3c.password/trunk/setup.py 2012-08-09 12:22:07 UTC (rev 127450)
@@ -23,7 +23,7 @@
setup (
name='z3c.password',
- version='0.10.2dev',
+ version='0.11.0dev',
author = "Stephan Richter, Roger Ineichen and the Zope Community",
author_email = "zope3-dev at zope.org",
description = "Password generation and verification utility for Zope3",
@@ -62,9 +62,10 @@
'zope.component',
'zope.exceptions',
'zope.i18nmessageid',
+ 'zope.i18n',
'zope.interface',
'zope.schema',
'zope.security',
],
zip_safe = False,
-)
\ No newline at end of file
+)
Modified: z3c.password/trunk/src/z3c/password/README.txt
===================================================================
--- z3c.password/trunk/src/z3c/password/README.txt 2012-08-09 12:22:02 UTC (rev 127449)
+++ z3c.password/trunk/src/z3c/password/README.txt 2012-08-09 12:22:07 UTC (rev 127450)
@@ -81,12 +81,12 @@
>>> pwd.verify('foo')
Traceback (most recent call last):
...
- TooShortPassword
+ TooShortPassword: Password is too short (minimum length: 8).
>>> pwd.verify('foobar-foobar')
Traceback (most recent call last):
...
- TooLongPassword
+ TooLongPassword: Password is too long (maximum length: 12).
>>> pwd.verify('fooBar12')
@@ -100,7 +100,7 @@
>>> pwd.verify('fooBar12', 'foobar12')
Traceback (most recent call last):
...
- TooSimilarPassword
+ TooSimilarPassword: Password is too similar to old one (similarity 88%, should be at most 60%).
- The final check ensures that the password does not have too many characters
of one group. The groups are: lower letters, upper letters, digits,
@@ -109,27 +109,27 @@
>>> pwd.verify('fooBarBlah')
Traceback (most recent call last):
...
- TooManyGroupCharacters
+ TooManyGroupCharacters: Password contains too many characters of one group (should have at most 6).
>>> pwd.verify('FOOBARBlah')
Traceback (most recent call last):
...
- TooManyGroupCharacters
+ TooManyGroupCharacters: Password contains too many characters of one group (should have at most 6).
>>> pwd.verify('12345678')
Traceback (most recent call last):
...
- TooManyGroupCharacters
+ TooManyGroupCharacters: Password contains too many characters of one group (should have at most 6).
>>> pwd.verify('........')
Traceback (most recent call last):
...
- TooManyGroupCharacters
+ TooManyGroupCharacters: Password contains too many characters of one group (should have at most 6).
>>> pwd.verify(unichr(0x0e1)*8)
Traceback (most recent call last):
...
- TooManyGroupCharacters
+ TooManyGroupCharacters: Password contains too many characters of one group (should have at most 6).
Let's now verify a list of password that were provided by a bank:
@@ -167,12 +167,12 @@
>>> pwd.verify('FOOBAR123')
Traceback (most recent call last):
...
- TooFewGroupCharactersLowerLetter
+ TooFewGroupCharactersLowerLetter: Password does not contain enough characters of lowercase letters (should have at least 5).
>>> pwd.verify('foobAR123')
Traceback (most recent call last):
...
- TooFewGroupCharactersLowerLetter
+ TooFewGroupCharactersLowerLetter: Password does not contain enough characters of lowercase letters (should have at least 5).
>>> pwd.verify('foobaR123')
@@ -191,12 +191,12 @@
>>> pwd.verify('foobar123')
Traceback (most recent call last):
...
- TooFewGroupCharactersUpperLetter
+ TooFewGroupCharactersUpperLetter: Password does not contain enough characters of uppercase letters (should have at least 5).
>>> pwd.verify('FOOBar123')
Traceback (most recent call last):
...
- TooFewGroupCharactersUpperLetter
+ TooFewGroupCharactersUpperLetter: Password does not contain enough characters of uppercase letters (should have at least 5).
>>> pwd.verify('fOOBAR123')
@@ -215,12 +215,12 @@
>>> pwd.verify('foobar123')
Traceback (most recent call last):
...
- TooFewGroupCharactersDigits
+ TooFewGroupCharactersDigits: Password does not contain enough characters of digits (should have at least 5).
>>> pwd.verify('FOOBa1234')
Traceback (most recent call last):
...
- TooFewGroupCharactersDigits
+ TooFewGroupCharactersDigits: Password does not contain enough characters of digits (should have at least 5).
>>> pwd.verify('fOBA12345')
@@ -239,12 +239,12 @@
>>> pwd.verify('foo(bar)')
Traceback (most recent call last):
...
- TooFewGroupCharactersSpecials
+ TooFewGroupCharactersSpecials: Password does not contain enough characters of special characters (should have at least 5).
>>> pwd.verify('FO.#(Ba1)')
Traceback (most recent call last):
...
- TooFewGroupCharactersSpecials
+ TooFewGroupCharactersSpecials: Password does not contain enough characters of special characters (should have at least 5).
>>> pwd.verify('fO.,;()5')
@@ -262,12 +262,12 @@
>>> pwd.verify('foobar'+unichr(0x0c3)+unichr(0x0c4))
Traceback (most recent call last):
...
- TooFewGroupCharactersOthers
+ TooFewGroupCharactersOthers: Password does not contain enough characters of other characters (should have at least 5).
>>> pwd.verify('foobar'+unichr(0x0c3)+unichr(0x0c4)+unichr(0x0e1))
Traceback (most recent call last):
...
- TooFewGroupCharactersOthers
+ TooFewGroupCharactersOthers: Password does not contain enough characters of other characters (should have at least 5).
>>> pwd.verify('fOO'+unichr(0x0e1)*5)
@@ -288,12 +288,12 @@
>>> pwd.verify('foofoo1212')
Traceback (most recent call last):
...
- TooFewUniqueCharacters
+ TooFewUniqueCharacters: Password does not contain enough unique characters (should have at least 5).
>>> pwd.verify('FOOfoo2323')
Traceback (most recent call last):
...
- TooFewUniqueCharacters
+ TooFewUniqueCharacters: Password does not contain enough unique characters (should have at least 5).
>>> pwd.verify('fOOBAR123')
@@ -312,12 +312,12 @@
>>> pwd.verify('foofoo1212')
Traceback (most recent call last):
...
- TooFewUniqueLetters
+ TooFewUniqueLetters: Password does not contain enough unique letters (should have at least 5).
>>> pwd.verify('FOOBfoob2323')
Traceback (most recent call last):
...
- TooFewUniqueLetters
+ TooFewUniqueLetters: Password does not contain enough unique letters (should have at least 5).
>>> pwd.verify('fOOBAR123')
@@ -356,7 +356,7 @@
>>> pwdField.validate(u'fooBar')
Traceback (most recent call last):
...
- TooShortPassword
+ TooShortPassword: Password is too short (minimum length: 8).
Validation must work on bound fields too:
@@ -379,7 +379,7 @@
>>> bound.validate(u'fooBar')
Traceback (most recent call last):
...
- TooShortPassword
+ TooShortPassword: Password is too short (minimum length: 8).
Let's create a principal without the PrincipalMixIn:
@@ -394,7 +394,7 @@
>>> bound.validate(u'fooBar')
Traceback (most recent call last):
...
- TooShortPassword
+ TooShortPassword: Password is too short (minimum length: 8).
Other common usecase is to do a utility and specify it's name as checker.
@@ -415,7 +415,7 @@
>>> pwdField.validate(u'fooBar')
Traceback (most recent call last):
...
- TooShortPassword
+ TooShortPassword: Password is too short (minimum length: 8).
Edge cases.
@@ -456,4 +456,4 @@
Validation silently succeeds:
- >>> bound.validate(u'fooBar12')
\ No newline at end of file
+ >>> bound.validate(u'fooBar12')
Modified: z3c.password/trunk/src/z3c/password/interfaces.py
===================================================================
--- z3c.password/trunk/src/z3c/password/interfaces.py 2012-08-09 12:22:02 UTC (rev 127449)
+++ z3c.password/trunk/src/z3c/password/interfaces.py 2012-08-09 12:22:07 UTC (rev 127450)
@@ -18,27 +18,77 @@
__docformat__ = "reStructuredText"
import zope.interface
import zope.schema
+from zope.i18n import translate
from z3c.password import MessageFactory as _
class InvalidPassword(zope.schema.ValidationError):
"""Invalid Password"""
+ i18n_message = None
+
+ def __str__(self):
+ if self.i18n_message:
+ return translate(self.i18n_message)
+ return super(InvalidPassword, self).__str__()
+
+ def doc(self):
+ if self.i18n_message:
+ return self.i18n_message
+ return self.__class__.__doc__
+
+
class NoPassword(InvalidPassword):
__doc__ = _('''No new password specified.''')
class TooShortPassword(InvalidPassword):
__doc__ = _('''Password is too short.''')
+ def __init__(self, minLength=None):
+ super(TooShortPassword, self).__init__()
+ self.minLength = minLength
+ if minLength is not None:
+ self.i18n_message = _(
+ 'Password is too short (minimum length: ${minLength}).',
+ mapping=dict(minLength=minLength))
+
class TooLongPassword(InvalidPassword):
__doc__ = _('''Password is too long.''')
+ def __init__(self, maxLength=None):
+ super(TooLongPassword, self).__init__()
+ self.maxLength = maxLength
+ if maxLength is not None:
+ self.i18n_message = _(
+ 'Password is too long (maximum length: ${maxLength}).',
+ mapping=dict(maxLength=maxLength))
+
class TooSimilarPassword(InvalidPassword):
__doc__ = _('''Password is too similar to old one.''')
+ def __init__(self, similarity=None, maxSimilarity=None):
+ super(TooSimilarPassword, self).__init__()
+ self.similarity = similarity
+ self.maxSimilarity = maxSimilarity
+ if similarity is not None and maxSimilarity is not None:
+ self.i18n_message = _(
+ 'Password is too similar to old one'
+ ' (similarity ${similarity}%, should be at most ${maxSimilarity}%).',
+ mapping=dict(similarity=int(round(similarity * 100)),
+ maxSimilarity=int(round(maxSimilarity * 100))))
+
class TooManyGroupCharacters(InvalidPassword):
__doc__ = _('''Password contains too many characters of one group.''')
+ def __init__(self, groupMax=None):
+ super(TooManyGroupCharacters, self).__init__()
+ self.groupMax = groupMax
+ if groupMax is not None:
+ self.i18n_message = _(
+ 'Password contains too many characters of one group'
+ ' (should have at most ${groupMax}).',
+ mapping=dict(groupMax=groupMax))
+
class TooFewGroupCharacters(InvalidPassword):
__doc__ = _('''Password does not contain enough characters of one group.''')
@@ -46,27 +96,90 @@
__doc__ = _(
'''Password does not contain enough characters of lowercase letters.''')
+ def __init__(self, minLowerLetter=None):
+ super(TooFewGroupCharactersLowerLetter, self).__init__()
+ self.minLowerLetter = minLowerLetter
+ if minLowerLetter is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough characters of lowercase letters'
+ ' (should have at least ${minLowerLetter}).',
+ mapping=dict(minLowerLetter=minLowerLetter))
+
class TooFewGroupCharactersUpperLetter(TooFewGroupCharacters):
__doc__ = _(
'''Password does not contain enough characters of uppercase letters.''')
+ def __init__(self, minUpperLetter=None):
+ super(TooFewGroupCharactersUpperLetter, self).__init__()
+ self.minUpperLetter = minUpperLetter
+ if minUpperLetter is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough characters of uppercase letters'
+ ' (should have at least ${minUpperLetter}).',
+ mapping=dict(minUpperLetter=minUpperLetter))
+
class TooFewGroupCharactersDigits(TooFewGroupCharacters):
__doc__ = _('''Password does not contain enough characters of digits.''')
+ def __init__(self, minDigits=None):
+ super(TooFewGroupCharactersDigits, self).__init__()
+ self.minDigits = minDigits
+ if minDigits is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough characters of digits'
+ ' (should have at least ${minDigits}).',
+ mapping=dict(minDigits=minDigits))
+
class TooFewGroupCharactersSpecials(TooFewGroupCharacters):
__doc__ = _(
'''Password does not contain enough characters of special characters.''')
+ def __init__(self, minSpecials=None):
+ super(TooFewGroupCharactersSpecials, self).__init__()
+ self.minSpecials = minSpecials
+ if minSpecials is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough characters of special characters'
+ ' (should have at least ${minSpecials}).',
+ mapping=dict(minSpecials=minSpecials))
+
class TooFewGroupCharactersOthers(TooFewGroupCharacters):
__doc__ = _(
'''Password does not contain enough characters of other characters.''')
+ def __init__(self, minOthers=None):
+ super(TooFewGroupCharactersOthers, self).__init__()
+ self.minOthers = minOthers
+ if minOthers is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough characters of other characters'
+ ' (should have at least ${minOthers}).',
+ mapping=dict(minOthers=minOthers))
+
class TooFewUniqueCharacters(InvalidPassword):
__doc__ = _('''Password does not contain enough unique characters.''')
+ def __init__(self, minUniqueCharacters=None):
+ super(TooFewUniqueCharacters, self).__init__()
+ self.minUniqueCharacters = minUniqueCharacters
+ if minUniqueCharacters is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough unique characters'
+ ' (should have at least ${minUniqueCharacters}).',
+ mapping=dict(minUniqueCharacters=minUniqueCharacters))
+
class TooFewUniqueLetters(InvalidPassword):
__doc__ = _('''Password does not contain enough unique letters.''')
+ def __init__(self, minUniqueLetters=None):
+ super(TooFewUniqueLetters, self).__init__()
+ self.minUniqueLetters = minUniqueLetters
+ if minUniqueLetters is not None:
+ self.i18n_message = _(
+ 'Password does not contain enough unique letters'
+ ' (should have at least ${minUniqueLetters}).',
+ mapping=dict(minUniqueLetters=minUniqueLetters))
+
class PasswordExpired(Exception):
__doc__ = _('''The password has expired.''')
Modified: z3c.password/trunk/src/z3c/password/password.py
===================================================================
--- z3c.password/trunk/src/z3c/password/password.py 2012-08-09 12:22:02 UTC (rev 127449)
+++ z3c.password/trunk/src/z3c/password/password.py 2012-08-09 12:22:07 UTC (rev 127450)
@@ -97,9 +97,10 @@
self.minUniqueLetters = minUniqueLetters
def _checkSimilarity(self, new, ref):
- sm = difflib.SequenceMatcher(None, new, ref)
- if sm.ratio() > self.maxSimilarity:
- raise interfaces.TooSimilarPassword()
+ similarity = difflib.SequenceMatcher(None, new, ref).ratio()
+ if similarity > self.maxSimilarity:
+ raise interfaces.TooSimilarPassword(
+ similarity=similarity, maxSimilarity=self.maxSimilarity)
def verify(self, new, ref=None):
'''See interfaces.IHighSecurityPasswordUtility'''
@@ -108,9 +109,9 @@
raise interfaces.NoPassword()
# 1. Make sure the password has the right length.
if len(new) < self.minLength:
- raise interfaces.TooShortPassword()
+ raise interfaces.TooShortPassword(minLength=self.minLength)
if len(new) > self.maxLength:
- raise interfaces.TooLongPassword()
+ raise interfaces.TooLongPassword(maxLength=self.maxLength)
# 2. Ensure that the password is sufficiently different to the old
# one.
if ref is not None:
@@ -142,35 +143,43 @@
num_digits > self.groupMax or
num_specials > self.groupMax or
num_others > self.groupMax):
- raise interfaces.TooManyGroupCharacters()
+ raise interfaces.TooManyGroupCharacters(
+ groupMax=self.groupMax)
if (self.minLowerLetter is not None
and num_lower_letters < self.minLowerLetter):
- raise interfaces.TooFewGroupCharactersLowerLetter()
+ raise interfaces.TooFewGroupCharactersLowerLetter(
+ minLowerLetter=self.minLowerLetter)
if (self.minUpperLetter is not None
and num_upper_letters < self.minUpperLetter):
- raise interfaces.TooFewGroupCharactersUpperLetter()
+ raise interfaces.TooFewGroupCharactersUpperLetter(
+ minUpperLetter=self.minUpperLetter)
if (self.minDigits is not None
and num_digits < self.minDigits):
- raise interfaces.TooFewGroupCharactersDigits()
+ raise interfaces.TooFewGroupCharactersDigits(
+ minDigits=self.minDigits)
if (self.minSpecials is not None
and num_specials < self.minSpecials):
- raise interfaces.TooFewGroupCharactersSpecials()
+ raise interfaces.TooFewGroupCharactersSpecials(
+ minSpecials=self.minSpecials)
if (self.minOthers is not None
and num_others < self.minOthers):
- raise interfaces.TooFewGroupCharactersOthers()
+ raise interfaces.TooFewGroupCharactersOthers(
+ minOthers=self.minOthers)
if (self.minUniqueCharacters is not None
and len(uniqueChars) < self.minUniqueCharacters):
- raise interfaces.TooFewUniqueCharacters()
+ raise interfaces.TooFewUniqueCharacters(
+ minUniqueCharacters=self.minUniqueCharacters)
if (self.minUniqueLetters is not None
and len(uniqueLetters) < self.minUniqueLetters):
- raise interfaces.TooFewUniqueLetters()
+ raise interfaces.TooFewUniqueLetters(
+ minUniqueLetters=self.minUniqueLetters)
return
@@ -225,4 +234,4 @@
self.lockOutPeriod = lockOutPeriod
self.maxFailedAttempts = maxFailedAttempts
self.disallowPasswordReuse = disallowPasswordReuse
- self.failedAttemptCheck = failedAttemptCheck
\ No newline at end of file
+ self.failedAttemptCheck = failedAttemptCheck
More information about the checkins
mailing list