[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