Skip to content

Commit

Permalink
save my work
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Groszer committed Jun 19, 2009
1 parent 809d966 commit 2cacc73
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 3 deletions.
91 changes: 91 additions & 0 deletions src/z3c/password/interfaces.py
Expand Up @@ -40,6 +40,9 @@ class TooSimilarPassword(InvalidPassword):
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.''')

Expand Down Expand Up @@ -135,6 +138,94 @@ def minMaxLength(task):
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.
Expand Down
65 changes: 62 additions & 3 deletions src/z3c/password/password.py
Expand Up @@ -54,6 +54,16 @@ class HighSecurityPasswordUtility(object):
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:]
Expand All @@ -72,6 +82,11 @@ def __init__(self, minLength=8, maxLength=12, groupMax=6,
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.
Expand All @@ -85,9 +100,7 @@ def verify(self, new, ref=None):
# 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
Expand All @@ -111,20 +124,66 @@ def verify(self, new, ref=None):
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
while not verified:
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)
Expand Down

0 comments on commit 2cacc73

Please sign in to comment.