[Checkins] SVN: z3c.password/branches/adamg-evenhigher/src/z3c/password/ save my work
Adam Groszer
agroszer at gmail.com
Fri Jun 19 08:19:22 EDT 2009
Log message for revision 101136:
save my work
Changed:
U z3c.password/branches/adamg-evenhigher/src/z3c/password/interfaces.py
U z3c.password/branches/adamg-evenhigher/src/z3c/password/password.py
-=-
Modified: z3c.password/branches/adamg-evenhigher/src/z3c/password/interfaces.py
===================================================================
--- z3c.password/branches/adamg-evenhigher/src/z3c/password/interfaces.py 2009-06-19 09:45:51 UTC (rev 101135)
+++ z3c.password/branches/adamg-evenhigher/src/z3c/password/interfaces.py 2009-06-19 12:19:21 UTC (rev 101136)
@@ -40,6 +40,9 @@
class TooManyGroupCharacters(InvalidPassword):
__doc__ = _('''Password contains too many characters of one group.''')
+class TooFewGroupCharacters(InvalidPassword):
+ __doc__ = _('''Password does not contain enough characters of one group.''')
+
class PasswordExpired(Exception):
__doc__ = _('''The password has expired.''')
@@ -135,7 +138,95 @@
required=False,
default=None)
+ minLowerLetter = zope.schema.Int(
+ title=_(u'Minimum Number of Lowercase letters'),
+ description=_(u'The minimum amount of lowercase letters that a '
+ u'password must have.'),
+ required=False,
+ default=None)
+ minUpperLetter = zope.schema.Int(
+ title=_(u'Minimum Number of Uppercase letters'),
+ description=_(u'The minimum amount of uppercase letters that a '
+ u'password must have.'),
+ required=False,
+ default=None)
+
+ minDigits = zope.schema.Int(
+ title=_(u'Minimum Number of Numeric digits'),
+ description=_(u'The minimum amount of numeric digits that a '
+ u'password must have.'),
+ required=False,
+ default=None)
+
+ minSpecials = zope.schema.Int(
+ title=_(u'Minimum Number of Special characters'),
+ description=_(u'The minimum amount of special characters that a '
+ u'password must have.'),
+ required=False,
+ default=None)
+
+ #WARNING! generating a password with Others is... not always sane
+ #think twice before you use it
+ minOthers = zope.schema.Int(
+ title=_(u'Minimum Number of Other characters'),
+ description=_(u'The minimum amount of other characters that a '
+ u'password must have.'),
+ required=False,
+ default=None)
+
+ @zope.interface.invariant
+ def saneMinimums(task):
+ minl = 0
+ if task.minLowerLetter:
+ if task.minLowerLetter > task.groupMax:
+ raise zope.interface.Invalid(
+ u"Any group minimum length must NOT be greater than "
+ u"the maximum group length.")
+
+ minl += task.minLowerLetter
+ if task.minUpperLetter:
+ if task.minUpperLetter > task.groupMax:
+ raise zope.interface.Invalid(
+ u"Any group minimum length must NOT be greater than "
+ u"the maximum group length.")
+
+ minl += task.minUpperLetter
+ if task.minDigits:
+ if task.minDigits > task.groupMax:
+ raise zope.interface.Invalid(
+ u"Any group minimum length must NOT be greater than "
+ u"the maximum group length.")
+
+ minl += task.minDigits
+ if task.minSpecials:
+ if task.minSpecials > task.groupMax:
+ raise zope.interface.Invalid(
+ u"Any group minimum length must NOT be greater than "
+ u"the maximum group length.")
+
+ minl += task.minSpecials
+ if task.minOthers:
+ if task.minOthers > task.groupMax:
+ raise zope.interface.Invalid(
+ u"Any group minimum length must NOT be greater than "
+ u"the maximum group length.")
+
+ minl += task.minOthers
+
+ #if task.minLength is not None:
+ # if minl > task.minLength:
+ # raise zope.interface.Invalid(
+ # u"Sum of group minimum lengths must NOT be greater than "
+ # u"the minimum length.")
+
+ if task.maxLength is not None:
+ if minl > task.maxLength:
+ raise zope.interface.Invalid(
+ u"Sum of group minimum lengths must NOT be greater than "
+ u"the maximum password length.")
+
+
class IPasswordOptionsUtility(zope.interface.Interface):
"""Different general security options.
Modified: z3c.password/branches/adamg-evenhigher/src/z3c/password/password.py
===================================================================
--- z3c.password/branches/adamg-evenhigher/src/z3c/password/password.py 2009-06-19 09:45:51 UTC (rev 101135)
+++ z3c.password/branches/adamg-evenhigher/src/z3c/password/password.py 2009-06-19 12:19:21 UTC (rev 101136)
@@ -54,6 +54,16 @@
interfaces.IHighSecurityPasswordUtility['groupMax'])
maxSimilarity = FieldProperty(
interfaces.IHighSecurityPasswordUtility['maxSimilarity'])
+ minLowerLetter = FieldProperty(
+ interfaces.IHighSecurityPasswordUtility['minLowerLetter'])
+ minUpperLetter = FieldProperty(
+ interfaces.IHighSecurityPasswordUtility['minUpperLetter'])
+ minDigits = FieldProperty(
+ interfaces.IHighSecurityPasswordUtility['minDigits'])
+ minSpecials = FieldProperty(
+ interfaces.IHighSecurityPasswordUtility['minSpecials'])
+ minOthers = FieldProperty(
+ interfaces.IHighSecurityPasswordUtility['minOthers'])
LOWERLETTERS = string.letters[:26]
UPPERLETTERS = string.letters[26:]
@@ -72,6 +82,11 @@
self.maxSimilarity = maxSimilarity
self.random = random.Random(seed or time.time())
+ def _checkSimilarity(self, new, ref):
+ sm = difflib.SequenceMatcher(None, new, ref)
+ if sm.ratio() > self.maxSimilarity:
+ raise interfaces.TooSimilarPassword()
+
def verify(self, new, ref=None):
'''See interfaces.IHighSecurityPasswordUtility'''
# 0. Make sure we got a password.
@@ -85,9 +100,7 @@
# 2. Ensure that the password is sufficiently different to the old
# one.
if ref is not None:
- sm = difflib.SequenceMatcher(None, new, ref)
- if sm.ratio() > self.maxSimilarity:
- raise interfaces.TooSimilarPassword()
+ self._checkSimilarity(new, ref)
# 3. Ensure that the password's character set is complex enough.
num_lower_letters = 0
num_upper_letters = 0
@@ -111,8 +124,33 @@
num_specials > self.groupMax or
num_others > self.groupMax):
raise interfaces.TooManyGroupCharacters()
+
+ if (self.minLowerLetter is not None
+ and num_lower_letters < self.minLowerLetter):
+ raise interfaces.TooFewGroupCharacters()
+
+ if (self.minUpperLetter is not None
+ and num_upper_letters < self.minUpperLetter):
+ raise interfaces.TooFewGroupCharacters()
+
+ if (self.minDigits is not None
+ and num_digits < self.minDigits):
+ raise interfaces.TooFewGroupCharacters()
+
+ if (self.minSpecials is not None
+ and num_specials < self.minSpecials):
+ raise interfaces.TooFewGroupCharacters()
+
+ if (self.minOthers is not None
+ and num_others < self.minOthers):
+ raise interfaces.TooFewGroupCharacters()
+
return
+ def _randomOther(self):
+ #override if you want an other range
+ return unichr(self.random.randint(0x0a1, 0x0ff))
+
def generate(self, ref=None):
'''See interfaces.IHighSecurityPasswordUtility'''
verified = False
@@ -120,11 +158,32 @@
new = ''
# Determine the length of the password
length = self.random.randint(self.minLength, self.maxLength)
+
# Generate the password
chars = self.LOWERLETTERS + self.UPPERLETTERS + \
self.DIGITS + self.SPECIALS
+
+ if (self.minOthers is not None
+ and self.minOthers > 0):
+ # unichr(0x0ffff) is a placeholder for Others
+ # this is deliberately this way, because a unicode
+ # range of 0x0a1...0x010ffff is rather a big string
+ chars += unichr(0x0ffff)
+
for count in xrange(length):
new += self.random.choice(chars)
+
+ if (self.minOthers is not None
+ and self.minOthers > 0):
+ # replace now placeholders with random other characters
+ newest = ''
+ for c in new:
+ if c == unichr(0x0ffff):
+ newest += self._randomOther()
+ else:
+ newest += c
+ new = newest
+
# Verify the new password
try:
self.verify(new, ref)
More information about the Checkins
mailing list