[Checkins] SVN: z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/ let's see how this works

Adam Groszer agroszer at gmail.com
Fri Jan 29 09:33:15 EST 2010


Log message for revision 108635:
  let's see how this works

Changed:
  U   z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/interfaces.py
  U   z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.py
  U   z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.txt
  U   z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/testing.py

-=-
Modified: z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/interfaces.py
===================================================================
--- z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/interfaces.py	2010-01-29 14:24:57 UTC (rev 108634)
+++ z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/interfaces.py	2010-01-29 14:33:14 UTC (rev 108635)
@@ -70,6 +70,10 @@
         self.principal = principal
         Exception.__init__(self, self.__doc__)
 
+TML_CHECK_ALL = 'all'
+TML_CHECK_NONRESOURCE = 'nonres'
+TML_CHECK_POSTONLY = 'post'
+
 class AccountLocked(Exception):
     __doc__ = _('The account is locked, because the password was '
                 'entered incorrectly too often.')
@@ -302,6 +306,14 @@
         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.'),

Modified: z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.py
===================================================================
--- z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.py	2010-01-29 14:24:57 UTC (rev 108634)
+++ z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.py	2010-01-29 14:33:14 UTC (rev 108635)
@@ -19,6 +19,8 @@
 import datetime
 import persistent.list
 import zope.component
+from zope.security.management import getInteraction
+
 from z3c.password import interfaces
 
 class PrincipalMixIn(object):
@@ -31,6 +33,7 @@
                              #e.g. for changePasswordOnNextLogin
 
     failedAttempts = 0
+    failedAttemptCheck = interfaces.TML_CHECK_ALL
     maxFailedAttempts = None
     lastFailedAttempt = None
     lockOutPeriod = None
@@ -115,9 +118,7 @@
             add = 0
         else:
             #failed attempt, record it, increase counter
-            self.failedAttempts += 1
-            self.lastFailedAttempt = self.now()
-            add = 1
+            add = self.checkFailedAttempt()
 
         # If the maximum amount of failures has been reached notify the
         # system by raising an error.
@@ -132,6 +133,46 @@
 
         return same
 
+    def _getRequest(self):
+        interaction = getInteraction()
+        try:
+            return interaction.participations[0]
+        except IndexError:
+            return None
+
+    def checkFailedAttempt(self):
+        #failed attempt, record it, increase counter
+        #(in case we have to)
+        validRequest = True
+        fac = self._failedAttemptCheck()
+        if fac == interfaces.TML_CHECK_ALL:
+            validRequest = True
+        else:
+            request = self._getRequest()
+            if request is None:
+                validRequest = True
+            else:
+                if fac == interfaces.TML_CHECK_NONRESOURCE:
+                    url = request.getURL()
+                    if '/@@/' in url:
+                        #this is a resource
+                        validRequest = False
+                    else:
+                        validRequest = True
+                elif fac == interfaces.TML_CHECK_POSTONLY:
+                    if request.method == 'POST':
+                        #this is a POST request
+                        validRequest = True
+                    else:
+                        validRequest = False
+
+        if validRequest:
+            self.failedAttempts += 1
+            self.lastFailedAttempt = self.now()
+            return 1
+        else:
+            return 0
+
     def tooManyLoginFailures(self, add = 0):
         attempts = self._maxFailedAttempts()
         #this one needs to be >=, because... data just does not
@@ -199,6 +240,19 @@
             else:
                 return self.lockOutPeriod
 
+    def _failedAttemptCheck(self):
+        if self.failedAttemptCheck is not None:
+            return self.failedAttemptCheck
+
+        options = self._optionsUtility()
+        if options is None:
+            return self.failedAttemptCheck
+        else:
+            if options.failedAttemptCheck is not None:
+                return options.failedAttemptCheck
+            else:
+                return self.failedAttemptCheck
+
     def _maxFailedAttempts(self):
         if self.maxFailedAttempts is not None:
             return self.maxFailedAttempts

Modified: z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.txt
===================================================================
--- z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.txt	2010-01-29 14:24:57 UTC (rev 108634)
+++ z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/principal.txt	2010-01-29 14:33:14 UTC (rev 108635)
@@ -104,6 +104,7 @@
 Let's now create a principal:
 
   >>> from zope.app.authentication import principalfolder
+  >>> from z3c.password import interfaces
   >>> from z3c.password import principal
 
   >>> class MyPrincipal(principal.PrincipalMixIn,
@@ -130,6 +131,9 @@
   >>> user.checkPassword('123123')
   True
 
+failedAttempts
+--------------
+
 Initially, the amount of failed attempts is zero, ...
 
   >>> user.failedAttempts
@@ -180,6 +184,97 @@
 
   >>> user.failedAttempts = 0
 
+
+failedAttempts, non-resource
+----------------------------
+
+  >>> import zope.security.management
+  >>> from z3c.password import testing
+
+  >>> user.failedAttemptCheck = interfaces.TML_CHECK_NONRESOURCE
+
+  >>> request = testing.TestBrowserRequest('http://localhost/@@/logo.gif')
+
+  >>> zope.security.management.getInteraction().add(request)
+
+  >>> user.failedAttempts
+  0
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  0
+
+  >>> zope.security.management.getInteraction().remove(request)
+
+  >>> request = testing.TestBrowserRequest('http://localhost/loginform.html',
+  ...     'POST')
+
+  >>> zope.security.management.getInteraction().add(request)
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  1
+
+  >>> user.failedAttempts = 0
+
+  >>> zope.security.management.getInteraction().remove(request)
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  1
+
+failedAttempts, POST
+--------------------
+
+  >>> user.failedAttempts = 0
+
+  >>> user.failedAttemptCheck = interfaces.TML_CHECK_POSTONLY
+
+  >>> request = testing.TestBrowserRequest('http://localhost/index.html', 'GET')
+
+  >>> zope.security.management.getInteraction().add(request)
+
+  >>> user.failedAttempts
+  0
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  0
+
+  >>> zope.security.management.getInteraction().remove(request)
+
+  >>> request = testing.TestBrowserRequest('http://localhost/loginform.html',
+  ...     'POST')
+
+  >>> zope.security.management.getInteraction().add(request)
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  1
+
+  >>> user.failedAttempts = 0
+
+  >>> zope.security.management.getInteraction().remove(request)
+
+  >>> user.checkPassword('456456')
+  False
+
+  >>> user.failedAttempts
+  1
+
+expired password
+----------------
+
 Next we expire the password:
 
   >>> NOW = datetime.datetime(2009, 6, 14, 13, 0) + datetime.timedelta(181)
@@ -220,7 +315,6 @@
 
   >>> import zope.interface
   >>> import zope.component
-  >>> from z3c.password import interfaces
   >>> from z3c.password.password import PasswordOptionsUtility
   >>> poptions = PasswordOptionsUtility()
   >>> zope.component.provideUtility(poptions)

Modified: z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/testing.py
===================================================================
--- z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/testing.py	2010-01-29 14:24:57 UTC (rev 108634)
+++ z3c.password/branches/adamg-tooManyLoginFailures/src/z3c/password/testing.py	2010-01-29 14:33:14 UTC (rev 108635)
@@ -30,3 +30,15 @@
 
 def tearDown(test):
     placelesssetup.tearDown(test)
+
+
+class TestBrowserRequest():
+    """pretty dumb test request"""
+
+    def __init__(self, url, method='GET'):
+        self.URL = url
+        self.method = method
+        self.interaction = None
+
+    def getURL(self):
+        return self.URL



More information about the checkins mailing list