[Checkins] SVN: z3c.password/trunk/ - Refactor PrincipalMixIn now() into a separate method to facilitate

Adam Groszer agroszer at gmail.com
Thu Jun 11 09:05:53 EDT 2009


Log message for revision 100870:
  - Refactor PrincipalMixIn now() into a separate method to facilitate
    override and testing
  
  - Changed the order the password is checked:
    1. check password against stored
    2. check maxFailedAttempts, raise TooManyLoginFailures if over
    3. if password is OK, check expirationDate, raise PasswordExpired if over
    4. return whether password matches
  
    This is because I need to be sure that PasswordExpired is raised only if the
    password *IS* valid. Entering an invalid password *MUST NOT* raise
    PasswordExpired, because I want to use PasswordExpired to allow the user
    to change it's password. This should not happen if the user did not enter a
    valid password.
  

Changed:
  U   z3c.password/trunk/CHANGES.txt
  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/principal.py

-=-
Modified: z3c.password/trunk/CHANGES.txt
===================================================================
--- z3c.password/trunk/CHANGES.txt	2009-06-11 11:28:14 UTC (rev 100869)
+++ z3c.password/trunk/CHANGES.txt	2009-06-11 13:05:53 UTC (rev 100870)
@@ -6,6 +6,21 @@
 
 - Added Russian translation
 
+- Refactor PrincipalMixIn now() into a separate method to facilitate
+  override and testing
+
+- Changed the order the password is checked:
+  1. check password against stored
+  2. check maxFailedAttempts, raise TooManyLoginFailures if over
+  3. if password is OK, check expirationDate, raise PasswordExpired if over
+  4. return whether password matches
+
+  This is because I need to be sure that PasswordExpired is raised only if the
+  password *IS* valid. Entering an invalid password *MUST NOT* raise
+  PasswordExpired, because I want to use PasswordExpired to allow the user
+  to change it's password. This should not happen if the user did not enter a
+  valid password.
+
 0.5.0 (2008-10-21)
 ------------------
 

Modified: z3c.password/trunk/src/z3c/password/README.txt
===================================================================
--- z3c.password/trunk/src/z3c/password/README.txt	2009-06-11 11:28:14 UTC (rev 100869)
+++ z3c.password/trunk/src/z3c/password/README.txt	2009-06-11 13:05:53 UTC (rev 100870)
@@ -261,13 +261,20 @@
 
   >>> user.failedAttempts = 0
 
-Next we expire password:
+Next we expire the password:
 
   >>> user.passwordSetOn = datetime.datetime.now() + datetime.timedelta(-181)
 
 A corresponding exception should be raised:
 
   >>> user.checkPassword('456456')
+  False
+
+Not yet, because the password did not match.
+
+Once we match the password it is raised:
+
+  >>> user.checkPassword('123123')
   Traceback (most recent call last):
   ...
   PasswordExpired: The password has expired.

Modified: z3c.password/trunk/src/z3c/password/interfaces.py
===================================================================
--- z3c.password/trunk/src/z3c/password/interfaces.py	2009-06-11 11:28:14 UTC (rev 100869)
+++ z3c.password/trunk/src/z3c/password/interfaces.py	2009-06-11 13:05:53 UTC (rev 100870)
@@ -111,7 +111,7 @@
         if task.minLength is not None and task.maxLength is not None:
             if task.minLength > task.minLength:
                 raise zope.interface.Invalid(
-                    u"Minimum lnegth must be greater than the maximum length.")
+                    u"Minimum length must be greater than the maximum length.")
 
     groupMax = zope.schema.Int(
         title=_(u'Maximum Characters of Group'),

Modified: z3c.password/trunk/src/z3c/password/principal.py
===================================================================
--- z3c.password/trunk/src/z3c/password/principal.py	2009-06-11 11:28:14 UTC (rev 100869)
+++ z3c.password/trunk/src/z3c/password/principal.py	2009-06-11 13:05:53 UTC (rev 100870)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Principal MixIn for Advnaced Password Management
+"""Principal MixIn for Advanced Password Management
 
 $Id$
 """
@@ -34,17 +34,16 @@
 
     def setPassword(self, password, passwordManagerName=None):
         super(PrincipalMixIn, self).setPassword(password, passwordManagerName)
-        self.passwordSetOn = datetime.datetime.now()
+        self.passwordSetOn = self.now()
         self.failedAttempts = 0
 
     password = property(getPassword, setPassword)
 
+    def now(self):
+        #hook to facilitate testing and easier override
+        return datetime.datetime.now()
+
     def checkPassword(self, pwd, ignoreExpiration=False, ignoreFailures=False):
-        # Make sure the password has not been expired
-        if not ignoreExpiration and self.passwordExpiresAfter is not None:
-            expirationDate = self.passwordSetOn + self.passwordExpiresAfter
-            if expirationDate < datetime.datetime.now():
-                raise interfaces.PasswordExpired(self)
         # Check the password
         same = super(PrincipalMixIn, self).checkPassword(pwd)
         # If this was a failed attempt, record it, otherwise reset the failures
@@ -53,9 +52,17 @@
         if not same:
             self.failedAttempts += 1
         # If the maximum amount of failures has been reached notify the system
-        # by sending an event and then raising an error.
+        # by raising an error.
         if not ignoreFailures and self.maxFailedAttempts is not None:
             if (self.maxFailedAttempts and
                 self.failedAttempts > self.maxFailedAttempts):
                 raise interfaces.TooManyLoginFailures(self)
+
+        if same:
+            # Make sure the password has not been expired
+            if not ignoreExpiration and self.passwordExpiresAfter is not None:
+                expirationDate = self.passwordSetOn + self.passwordExpiresAfter
+                if expirationDate < self.now():
+                    raise interfaces.PasswordExpired(self)
+
         return same



More information about the Checkins mailing list