class IPasswordUtility(zope.interface.Interface): """Component to verify and generate passwords. The purpose of this utility is to make common password-related tasks, such as verification and creation simple. However, only the collection of those utilites provide an overall net worth. """ description = zope.schema.Text( title=_(u'Description'), description=_(u'A description of the password utility.'), required=False) def verify(new, ref=None): """Check whether the new password is valid. When a passward is good, the method simply returns, otherwise an ``InvalidPassword`` exception is raised. It is up to the implementation to define the semantics of a valid password. The sematics should ideally be described in the description. The ``ref`` argument is a reference password. In many scenarios it will be the old password, so that the method can ensure sufficient dissimilarity between the new and old password. """ def generate(ref=None): """Generate a valid password.
class AccountLocked(Exception): __doc__ = _('The account is locked, because the password was ' 'entered incorrectly too often.') def __init__(self, principal): self.principal = principal Exception.__init__(self, self.__doc__)
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))
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))
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))
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))
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))
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))
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(maxSimilarity * 100)))
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 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 TooFewGroupCharacters(InvalidPassword): __doc__ = _('''Password does not contain enough characters of one group.''')
class IHighSecurityPasswordUtility(IPasswordUtility): """A password utility for very secure passwords.""" minLength = zope.schema.Int( title=_(u'Minimum Length'), description=_(u'The minimum length of the password.'), required=False, default=None) maxLength = zope.schema.Int( title=_(u'Maximum Length'), description=_(u'The maximum length of the password.'), required=False, default=None) @zope.interface.invariant def minMaxLength(task): if task.minLength is not None and task.maxLength is not None: if task.minLength > task.maxLength: raise zope.interface.Invalid( u"Minimum length must not be greater than the maximum length.") groupMax = zope.schema.Int( title=_(u'Maximum Characters of Group'), description=_(u'The maximum amount of characters that a password can ' u'have from one group. The groups are: digits, letters, ' u'punctuation.'), required=False, default=None) maxSimilarity = zope.schema.Float( title=_(u'Old/New Similarity'), description=(u'The similarity ratio between the new and old password.'), 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 yet supported 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.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.") minUniqueLetters = zope.schema.Int( title=_(u'Minimum Number of Unique letters'), description=_(u'The minimum amount of unique letters that a ' u'password must have. This is against passwords ' u'like `aAaA0000`. All characters taken lowercase.'), required=False, default=None) @zope.interface.invariant def minUniqueLettersLength(task): if (task.minUniqueLetters is not None and task.minUniqueLetters is not None): if task.minUniqueLetters > task.maxLength: raise zope.interface.Invalid( u"Minimum unique letters number must not be greater than " u"the maximum length.") minUniqueCharacters = zope.schema.Int( title=_(u'Minimum Number of Unique characters'), description=_(u'The minimum amount of unique characters that a ' u'password must have. This is against passwords ' u'like `aAaA0000`. All characters taken lowercase.'), required=False, default=None) @zope.interface.invariant def minUniqueCharactersLength(task): if (task.minUniqueCharacters is not None and task.minUniqueCharacters is not None): if task.minUniqueCharacters > task.maxLength: raise zope.interface.Invalid( u"Minimum unique characters length must not be greater than " u"the maximum length.")
class PasswordExpired(Exception): __doc__ = _('''The password has expired.''') def __init__(self, principal): self.principal = principal Exception.__init__(self, self.__doc__)
class PreviousPasswordNotAllowed(InvalidPassword): __doc__ = _('''The password set was already used before.''') def __init__(self, principal): self.principal = principal Exception.__init__(self, self.__doc__)
class TooManyLoginFailures(Exception): __doc__ = _('''The password was entered incorrectly too often.''') def __init__(self, principal): self.principal = principal Exception.__init__(self, self.__doc__)
class NoPassword(InvalidPassword): __doc__ = _('''No new password specified.''')
class IPasswordOptionsUtility(zope.interface.Interface): """Different general security options. The purpose of this utility is to make common password-related options available """ changePasswordOnNextLogin = zope.schema.Bool( title=_(u'Password must be changed on next login'), description=_(u'Password must be changed on next login'), required=False, default=False) passwordExpiresAfter = zope.schema.Int( title=_(u'Password expires after (days)'), description=_(u'Password expires after (days)'), required=False, default=None) lockOutPeriod = zope.schema.Int( title=_(u'Lockout period (minutes)'), description=_(u'Lockout the user after too many failed password entries' 'for this many minutes. The user can try again after.'), required=False, default=None) maxFailedAttempts = zope.schema.Int( title=_(u'Max. number of failed password entries before account is locked'), description=_(u'Specifies the amount of failed attempts allowed to check ' 'the password before the password is locked and no new ' 'password can be provided.'), required=False, default=None) failedAttemptCheck = zope.schema.Choice( title=_(u'Failed password check method'), description=_(u'Failed password check method. ' 'All requests, non-reqource requests, POST requests.'), required=False, values=[TML_CHECK_ALL, TML_CHECK_NONRESOURCE, TML_CHECK_POSTONLY], default=TML_CHECK_ALL ) disallowPasswordReuse = zope.schema.Bool( title=_(u'Disallow Password Reuse'), description=_(u'Do not allow to set a previously set password again.'), required=False, default=False)