[CMF-checkins] SVN: CMF/trunk/C Replaced deprecated
'manage_afterAdd' and 'manage_beforeDelete' hooks in
CookieCrumbler with a Z3-style event subscriber
Tres Seaver
tseaver at palladion.com
Tue Jan 31 12:37:30 EST 2006
Log message for revision 41514:
Replaced deprecated 'manage_afterAdd' and 'manage_beforeDelete' hooks in CookieCrumbler with a Z3-style event subscriber
o This subscriber does the same registration with the container's
__before_traverse__ which the old hooks did.
o Tidied up the CookieCrumbler tests.
Changed:
U CMF/trunk/CHANGES.txt
U CMF/trunk/CMFCore/CookieCrumbler.py
U CMF/trunk/CMFCore/configure.zcml
U CMF/trunk/CMFCore/tests/test_CookieCrumbler.py
-=-
Modified: CMF/trunk/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt 2006-01-31 16:13:13 UTC (rev 41513)
+++ CMF/trunk/CHANGES.txt 2006-01-31 17:37:30 UTC (rev 41514)
@@ -181,6 +181,10 @@
Others
+ - Replaced deprecated 'manage_afterAdd' and 'manage_beforeDelete' hooks
+ in CookieCrumbler with a Z3-style event subscriber which does the
+ registration with the container's __before_traverse__.
+
- Moved GenericSetup out of the CMF package, it is now a standalone
product, but still distributed as part of the CMF package.
Modified: CMF/trunk/CMFCore/CookieCrumbler.py
===================================================================
--- CMF/trunk/CMFCore/CookieCrumbler.py 2006-01-31 16:13:13 UTC (rev 41513)
+++ CMF/trunk/CMFCore/CookieCrumbler.py 2006-01-31 17:37:30 UTC (rev 41514)
@@ -28,6 +28,8 @@
from OFS.Folder import Folder
from zExceptions import Redirect
from zope.interface import implements
+from zope.app.container.interfaces import IObjectMovedEvent
+from OFS.interfaces import IObjectWillBeMovedEvent
from interfaces import ICookieCrumbler
@@ -370,20 +372,6 @@
# We should not normally get here.
return 'Logged out.'
- # Installation and removal of traversal hooks.
-
- def manage_beforeDelete(self, item, container):
- if item is self:
- handle = self.meta_type + '/' + self.getId()
- BeforeTraverse.unregisterBeforeTraverse(container, handle)
-
- def manage_afterAdd(self, item, container):
- if item is self:
- handle = self.meta_type + '/' + self.getId()
- container = container.this()
- nc = BeforeTraverse.NameCaller(self.getId())
- BeforeTraverse.registerBeforeTraverse(container, nc, handle)
-
security.declarePublic('propertyLabel')
def propertyLabel(self, id):
"""Return a label for the given property id
@@ -395,7 +383,24 @@
Globals.InitializeClass(CookieCrumbler)
+def handleCookieCrumblerEvent(ob, event):
+ """ Event subscriber for (un)registering a CC as a before traverse hook.
+ """
+ if not ICookieCrumbler.providedBy(ob):
+ return
+ if IObjectMovedEvent.providedBy(event):
+ if event.newParent is not None:
+ # register before traverse hook
+ handle = ob.meta_type + '/' + ob.getId()
+ nc = BeforeTraverse.NameCaller(ob.getId())
+ BeforeTraverse.registerBeforeTraverse(event.newParent, nc, handle)
+ elif IObjectWillBeMovedEvent.providedBy(event):
+ if event.oldParent is not None:
+ # unregister before traverse hook
+ handle = ob.meta_type + '/' + ob.getId()
+ BeforeTraverse.unregisterBeforeTraverse(event.newParent, handle)
+
class ResponseCleanup:
def __init__(self, resp):
self.resp = resp
Modified: CMF/trunk/CMFCore/configure.zcml
===================================================================
--- CMF/trunk/CMFCore/configure.zcml 2006-01-31 16:13:13 UTC (rev 41513)
+++ CMF/trunk/CMFCore/configure.zcml 2006-01-31 17:37:30 UTC (rev 41514)
@@ -39,4 +39,10 @@
global="False"
/>
+ <subscriber
+ for=".interfaces.ICookieCrumbler
+ zope.app.event.interfaces.IObjectEvent"
+ handler=".CookieCrumbler.handleCookieCrumblerEvent"
+ />
+
</configure>
Modified: CMF/trunk/CMFCore/tests/test_CookieCrumbler.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_CookieCrumbler.py 2006-01-31 16:13:13 UTC (rev 41513)
+++ CMF/trunk/CMFCore/tests/test_CookieCrumbler.py 2006-01-31 17:37:30 UTC (rev 41514)
@@ -14,27 +14,15 @@
$Id$
"""
-
-import base64
-from cStringIO import StringIO
import unittest
-import urllib
+from Products.CMFCore.tests.base.testcase import PlacelessSetup
-import Testing
-from OFS.DTMLMethod import DTMLMethod
-from OFS.Folder import Folder
-from zExceptions.unauthorized import Unauthorized
-from AccessControl.User import UserFolder
-from AccessControl.SecurityManagement import noSecurityManager
-from ZPublisher.HTTPRequest import HTTPRequest
-from ZPublisher.HTTPResponse import HTTPResponse
-
-from Products.CMFCore.CookieCrumbler \
- import CookieCrumbler, manage_addCC, Redirect
-
-
def makerequest(root, stdout, stdin=None):
# Customized version of Testing.makerequest.makerequest()
+ from cStringIO import StringIO
+ from ZPublisher.HTTPRequest import HTTPRequest
+ from ZPublisher.HTTPResponse import HTTPResponse
+
resp = HTTPResponse(stdout=stdout)
environ = {}
environ['SERVER_NAME'] = 'example.com'
@@ -47,11 +35,48 @@
return req
-class CookieCrumblerTests (unittest.TestCase):
+class CookieCrumblerTests(unittest.TestCase, PlacelessSetup):
+ _CC_ID = 'cookie_authentication'
+
+ def _getTargetClass(self):
+ from Products.CMFCore.CookieCrumbler import CookieCrumbler
+ return CookieCrumbler
+
+ def _makeOne(self, *args, **kw):
+ return self._getTargetClass()(*args, **kw)
+
def setUp(self):
+ from zope.component import provideHandler
+ from zope.app.event.interfaces import IObjectEvent
+ from Products.CMFCore.interfaces import ICookieCrumbler
+ from Products.CMFCore.CookieCrumbler import handleCookieCrumblerEvent
+
+ PlacelessSetup.setUp(self)
+ self._finally = None
+
+ provideHandler(handleCookieCrumblerEvent,
+ adapts=(ICookieCrumbler, IObjectEvent))
+
+ def tearDown(self):
+ from AccessControl.SecurityManagement import noSecurityManager
+
+ if self._finally is not None:
+ self._finally()
+
+ noSecurityManager()
+ PlacelessSetup.tearDown(self)
+
+ def _makeSite(self):
+ import base64
+ from cStringIO import StringIO
+ import urllib
+
+ from AccessControl.User import UserFolder
+ from OFS.Folder import Folder
+ from OFS.DTMLMethod import DTMLMethod
+
root = Folder()
- self.root = root
root.isTopLevelPrincipiaApplicationObject = 1 # User folder needs this
root.getPhysicalPath = lambda: () # hack
root._View_Permission = ('Anonymous',)
@@ -62,10 +87,9 @@
users._doAddUser('isaac', 'pass-w', ('Son',), ())
root._setObject(users.id, users)
- cc = CookieCrumbler()
- cc.id = 'cookie_authentication'
+ cc = self._makeOne()
+ cc.id = self._CC_ID
root._setObject(cc.id, cc)
- self.cc = getattr(root, cc.id)
index = DTMLMethod()
index.munge('This is the default view')
@@ -83,105 +107,113 @@
protected._setId('protected')
root._setObject(protected.getId(), protected)
- self.responseOut = StringIO()
- self.req = makerequest(root, self.responseOut)
+ req = makerequest(root, StringIO())
+ self._finally = req.close
- self.credentials = urllib.quote(
+ credentials = urllib.quote(
base64.encodestring('abraham:pass-w').rstrip())
+ return root, cc, req, credentials
- def tearDown(self):
- self.req.close()
- noSecurityManager()
-
def test_z3interfaces(self):
from zope.interface.verify import verifyClass
from Products.CMFCore.interfaces import ICookieCrumbler
- verifyClass(ICookieCrumbler, CookieCrumbler)
+ verifyClass(ICookieCrumbler, self._getTargetClass())
def testNoCookies(self):
# verify the cookie crumbler doesn't break when no cookies are given
- self.req.traverse('/')
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ root, cc, req, credentials = self._makeSite()
+ req.traverse('/')
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'Anonymous User')
-
def testCookieLogin(self):
# verify the user and auth cookie get set
- self.req.cookies['__ac_name'] = 'abraham'
- self.req.cookies['__ac_password'] = 'pass-w'
- self.req.traverse('/')
+ root, cc, req, credentials = self._makeSite()
- self.assert_(self.req.has_key('AUTHENTICATED_USER'))
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ req.cookies['__ac_name'] = 'abraham'
+ req.cookies['__ac_password'] = 'pass-w'
+ req.traverse('/')
+
+ self.failUnless(req.has_key('AUTHENTICATED_USER'))
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'abraham')
- resp = self.req.response
- self.assert_(resp.cookies.has_key('__ac'))
+ resp = req.response
+ self.failUnless(resp.cookies.has_key('__ac'))
self.assertEqual(resp.cookies['__ac']['value'],
- self.credentials)
+ credentials)
self.assertEqual(resp.cookies['__ac']['path'], '/')
-
def testCookieResume(self):
# verify the cookie crumbler continues the session
- self.req.cookies['__ac'] = self.credentials
- self.req.traverse('/')
- self.assert_(self.req.has_key('AUTHENTICATED_USER'))
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ req.traverse('/')
+ self.failUnless(req.has_key('AUTHENTICATED_USER'))
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'abraham')
-
def testPasswordShredding(self):
# verify the password is shredded before the app gets the request
- self.req.cookies['__ac_name'] = 'abraham'
- self.req.cookies['__ac_password'] = 'pass-w'
- self.assert_(self.req.has_key('__ac_password'))
- self.req.traverse('/')
- self.assert_(not self.req.has_key('__ac_password'))
- self.assert_(not self.req.has_key('__ac'))
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac_name'] = 'abraham'
+ req.cookies['__ac_password'] = 'pass-w'
+ self.failUnless(req.has_key('__ac_password'))
+ req.traverse('/')
+ self.failIf( req.has_key('__ac_password'))
+ self.failIf( req.has_key('__ac'))
-
def testCredentialsNotRevealed(self):
# verify the credentials are shredded before the app gets the request
- self.req.cookies['__ac'] = self.credentials
- self.assert_(self.req.has_key('__ac'))
- self.req.traverse('/')
- self.assert_(not self.req.has_key('__ac'))
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ self.failUnless(req.has_key('__ac'))
+ req.traverse('/')
+ self.failIf( req.has_key('__ac'))
-
def testAutoLoginRedirection(self):
# Redirect unauthorized anonymous users to the login page
- self.assertRaises(Redirect, self.req.traverse, '/protected')
+ from Products.CMFCore.CookieCrumbler import Redirect
+ root, cc, req, credentials = self._makeSite()
+ self.assertRaises(Redirect, req.traverse, '/protected')
def testDisabledAutoLoginRedirection(self):
# When disable_cookie_login__ is set, don't redirect.
- self.req['disable_cookie_login__'] = 1
- self.assertRaises(Unauthorized, self.req.traverse, '/protected')
+ from zExceptions.unauthorized import Unauthorized
+ root, cc, req, credentials = self._makeSite()
+ req['disable_cookie_login__'] = 1
+ self.assertRaises(Unauthorized, req.traverse, '/protected')
+
def testNoRedirectAfterAuthenticated(self):
# Don't redirect already-authenticated users to the login page,
# even when they try to access things they can't get.
- self.req.cookies['__ac'] = self.credentials
- self.assertRaises(Unauthorized, self.req.traverse, '/protected')
+ from zExceptions.unauthorized import Unauthorized
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ self.assertRaises(Unauthorized, req.traverse, '/protected')
def testRetryLogin(self):
# After a failed login, CookieCrumbler should give the user an
# opportunity to try to log in again.
- self.req.cookies['__ac_name'] = 'israel'
- self.req.cookies['__ac_password'] = 'pass-w'
+ from Products.CMFCore.CookieCrumbler import Redirect
+
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac_name'] = 'israel'
+ req.cookies['__ac_password'] = 'pass-w'
try:
- self.req.traverse('/protected')
+ req.traverse('/protected')
except Redirect, s:
# Test passed
if hasattr(s, 'args'):
s = s.args[0]
- self.assert_(s.find('came_from=') >= 0)
- self.assert_(s.find('retry=1') >= 0)
- self.assert_(s.find('disable_cookie_login__=1') >= 0)
+ self.failUnless(s.find('came_from=') >= 0)
+ self.failUnless(s.find('retry=1') >= 0)
+ self.failUnless(s.find('disable_cookie_login__=1') >= 0)
else:
self.fail('Did not redirect')
@@ -189,135 +221,173 @@
def testLoginRestoresQueryString(self):
# When redirecting for login, the came_from form field should
# include the submitted URL as well as the query string.
- self.req['PATH_INFO'] = '/protected'
- self.req['QUERY_STRING'] = 'a:int=1&x:string=y'
+ import urllib
+ from Products.CMFCore.CookieCrumbler import Redirect
+
+ root, cc, req, credentials = self._makeSite()
+ req['PATH_INFO'] = '/protected'
+ req['QUERY_STRING'] = 'a:int=1&x:string=y'
try:
- self.req.traverse('/protected')
+ req.traverse('/protected')
except Redirect, s:
if hasattr(s, 'args'):
s = s.args[0]
- to_find = urllib.quote('/protected?' + self.req['QUERY_STRING'])
- self.assert_(s.find(to_find) >= 0, s)
+ to_find = urllib.quote('/protected?' + req['QUERY_STRING'])
+ self.failUnless(s.find(to_find) >= 0, s)
else:
self.fail('Did not redirect')
-
def testCacheHeaderAnonymous(self):
# Should not set cache-control
- self.req.traverse('/')
+ root, cc, req, credentials = self._makeSite()
+ req.traverse('/')
self.assertEqual(
- self.req.response.headers.get('cache-control', ''), '')
+ req.response.headers.get('cache-control', ''), '')
-
def testCacheHeaderLoggingIn(self):
# Should set cache-control
- self.req.cookies['__ac_name'] = 'abraham'
- self.req.cookies['__ac_password'] = 'pass-w'
- self.req.traverse('/')
- self.assertEqual(self.req.response['cache-control'], 'private')
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac_name'] = 'abraham'
+ req.cookies['__ac_password'] = 'pass-w'
+ req.traverse('/')
+ self.assertEqual(req.response['cache-control'], 'private')
-
def testCacheHeaderAuthenticated(self):
# Should set cache-control
- self.req.cookies['__ac'] = self.credentials
- self.req.traverse('/')
- self.assertEqual(self.req.response['cache-control'], 'private')
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ req.traverse('/')
+ self.assertEqual(req.response['cache-control'], 'private')
-
def testCacheHeaderDisabled(self):
# Should not set cache-control
- self.cc.cache_header_value = ''
- self.req.cookies['__ac'] = self.credentials
- self.req.traverse('/')
+ root, cc, req, credentials = self._makeSite()
+ cc.cache_header_value = ''
+ req.cookies['__ac'] = credentials
+ req.traverse('/')
self.assertEqual(
- self.req.response.headers.get('cache-control', ''), '')
+ req.response.headers.get('cache-control', ''), '')
-
def testDisableLoginDoesNotPreventPasswordShredding(self):
# Even if disable_cookie_login__ is set, read the cookies
# anyway to avoid revealing the password to the app.
# (disable_cookie_login__ does not mean disable cookie
# authentication, it only means disable the automatic redirect
# to the login page.)
- self.req.cookies['__ac_name'] = 'abraham'
- self.req.cookies['__ac_password'] = 'pass-w'
- self.req['disable_cookie_login__'] = 1
- self.req.traverse('/')
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac_name'] = 'abraham'
+ req.cookies['__ac_password'] = 'pass-w'
+ req['disable_cookie_login__'] = 1
+ req.traverse('/')
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'abraham')
# Here is the real test: the password should have been shredded.
- self.assert_(not self.req.has_key('__ac_password'))
+ self.failIf( req.has_key('__ac_password'))
-
def testDisableLoginDoesNotPreventPasswordShredding2(self):
- self.req.cookies['__ac'] = self.credentials
- self.req['disable_cookie_login__'] = 1
- self.req.traverse('/')
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ req['disable_cookie_login__'] = 1
+ req.traverse('/')
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'abraham')
- self.assert_(not self.req.has_key('__ac'))
+ self.failIf( req.has_key('__ac'))
-
def testMidApplicationAutoLoginRedirection(self):
# Redirect anonymous users to login page if Unauthorized
# occurs in the middle of the app
- self.req.traverse('/')
+ from zExceptions.unauthorized import Unauthorized
+
+ root, cc, req, credentials = self._makeSite()
+ req.traverse('/')
try:
raise Unauthorized
except:
- self.req.response.exception()
- self.assertEqual(self.req.response.status, 302)
+ req.response.exception()
+ self.assertEqual(req.response.status, 302)
-
def testMidApplicationAuthenticationButUnauthorized(self):
# Don't redirect already-authenticated users to the login page,
# even when Unauthorized happens in the middle of the app.
- self.req.cookies['__ac'] = self.credentials
- self.req.traverse('/')
+ from zExceptions.unauthorized import Unauthorized
+
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac'] = credentials
+ req.traverse('/')
try:
raise Unauthorized
except:
- self.req.response.exception()
- self.assertEqual(self.req.response.status, 401)
+ req.response.exception()
+ self.assertEqual(req.response.status, 401)
-
def testRedirectOnUnauthorized(self):
# Redirect already-authenticated users to the unauthorized
# handler page if that's what the sysadmin really wants.
- self.root.cookie_authentication.unauth_page = 'login_form'
- self.req.cookies['__ac'] = self.credentials
- self.assertRaises(Redirect, self.req.traverse, '/protected')
+ from Products.CMFCore.CookieCrumbler import Redirect
+ root, cc, req, credentials = self._makeSite()
+ cc.unauth_page = 'login_form'
+ req.cookies['__ac'] = credentials
+ self.assertRaises(Redirect, req.traverse, '/protected')
def testLoginRatherThanResume(self):
# When the user presents both a session resume and new
# credentials, choose the new credentials (so that it's
# possible to log in without logging out)
- self.req.cookies['__ac_name'] = 'isaac'
- self.req.cookies['__ac_password'] = 'pass-w'
- self.req.cookies['__ac'] = self.credentials
- self.req.traverse('/')
+ root, cc, req, credentials = self._makeSite()
+ req.cookies['__ac_name'] = 'isaac'
+ req.cookies['__ac_password'] = 'pass-w'
+ req.cookies['__ac'] = credentials
+ req.traverse('/')
- self.assert_(self.req.has_key('AUTHENTICATED_USER'))
- self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ self.failUnless(req.has_key('AUTHENTICATED_USER'))
+ self.assertEqual(req['AUTHENTICATED_USER'].getUserName(),
'isaac')
-
def testCreateForms(self):
# Verify the factory creates the login forms.
- if CookieCrumbler.__module__.find('CMFCore') >= 0:
+ from Products.CMFCore.CookieCrumbler import manage_addCC
+
+ if 'CMFCore' in self._getTargetClass().__module__:
# This test is disabled in CMFCore.
return
- self.root._delObject('cookie_authentication')
- manage_addCC(self.root, 'login', create_forms=1)
- ids = self.root.login.objectIds()
+
+ root, cc, req, credentials = self._makeSite()
+ root._delObject('cookie_authentication')
+ manage_addCC(root, 'login', create_forms=1)
+ ids = root.login.objectIds()
ids.sort()
self.assertEqual(tuple(ids), (
'index_html', 'logged_in', 'logged_out', 'login_form',
'standard_login_footer', 'standard_login_header'))
+ def test_before_traverse_hooks(self):
+ from OFS.Folder import Folder
+ container = Folder()
+ cc = self._makeOne()
+ cc._setId(self._CC_ID)
+
+ marker = []
+ bt_before = getattr(container, '__before_traverse__', marker)
+ self.failUnless(bt_before is marker)
+
+ container._setObject(self._CC_ID, cc)
+
+ bt_added = getattr(container, '__before_traverse__')
+ self.assertEqual(len(bt_added.items()), 1)
+ k, v = bt_added.items()[0]
+ self.failUnless(k[1].startswith(self._getTargetClass().meta_type))
+ self.assertEqual(v.name, self._CC_ID)
+
+ container._delObject(self._CC_ID)
+
+ bt_removed = getattr(container, '__before_traverse__')
+ self.assertEqual(len(bt_removed.items()), 1)
+
def test_suite():
- return unittest.makeSuite(CookieCrumblerTests)
+ return unittest.TestSuite((
+ unittest.makeSuite(CookieCrumblerTests),
+ ))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
More information about the CMF-checkins
mailing list