[Erp5-report] r23321 - in /erp5/tags/version-5.1.2/products/ERP5Type: patches/ tests/
nobody at svn.erp5.org
nobody at svn.erp5.org
Mon Sep 1 15:25:03 CEST 2008
Author: yusei
Date: Mon Sep 1 15:24:47 2008
New Revision: 23321
URL: http://svn.erp5.org?rev=23321&view=rev
Log:
Add cookie crumbler bug fix.
Added:
erp5/tags/version-5.1.2/products/ERP5Type/tests/testCookieCrumbler.py
Modified:
erp5/tags/version-5.1.2/products/ERP5Type/patches/CookieCrumbler.py
erp5/tags/version-5.1.2/products/ERP5Type/tests/ERP5TypeTestCase.py
Modified: erp5/tags/version-5.1.2/products/ERP5Type/patches/CookieCrumbler.py
URL: http://svn.erp5.org/erp5/tags/version-5.1.2/products/ERP5Type/patches/CookieCrumbler.py?rev=23321&r1=23320&r2=23321&view=diff
==============================================================================
--- erp5/tags/version-5.1.2/products/ERP5Type/patches/CookieCrumbler.py (original)
+++ erp5/tags/version-5.1.2/products/ERP5Type/patches/CookieCrumbler.py Mon Sep 1 15:24:47 2008
@@ -26,7 +26,16 @@
"""
from Products.CMFCore.CookieCrumbler import CookieCrumbler
-from urllib import quote
+from Products.CMFCore.CookieCrumbler import CookieCrumblerDisabled
+from urllib import quote, unquote
+from ZPublisher.HTTPRequest import HTTPRequest
+
+ATTEMPT_NONE = 0 # No attempt at authentication
+ATTEMPT_LOGIN = 1 # Attempt to log in
+ATTEMPT_RESUME = 2 # Attempt to resume session
+
+from base64 import encodestring, decodestring
+from DateTime import DateTime
class PatchedCookieCrumbler(CookieCrumbler):
"""
@@ -60,3 +69,86 @@
return None
CookieCrumbler.getLoginURL = getLoginURL
+
+def modifyRequest(self, req, resp):
+ """Copies cookie-supplied credentials to the basic auth fields.
+
+ Returns a flag indicating what the user is trying to do with
+ cookies: ATTEMPT_NONE, ATTEMPT_LOGIN, or ATTEMPT_RESUME. If
+ cookie login is disabled for this request, raises
+ CookieCrumblerDisabled.
+ """
+ if (req.__class__ is not HTTPRequest
+ or not req['REQUEST_METHOD'] in ('HEAD', 'GET', 'PUT', 'POST')
+ or req.environ.has_key('WEBDAV_SOURCE_PORT')):
+ raise CookieCrumblerDisabled
+
+ # attempt may contain information about an earlier attempt to
+ # authenticate using a higher-up cookie crumbler within the
+ # same request.
+ attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
+
+ if attempt == ATTEMPT_NONE:
+ if req._auth:
+ # An auth header was provided and no cookie crumbler
+ # created it. The user must be using basic auth.
+ raise CookieCrumblerDisabled
+
+ if req.has_key(self.pw_cookie) and req.has_key(self.name_cookie):
+ # Attempt to log in and set cookies.
+ attempt = ATTEMPT_LOGIN
+ name = req[self.name_cookie]
+ pw = req[self.pw_cookie]
+ #ac = encodestring('%s:%s' % (name, pw)).rstrip() => changed for remove all newlines
+ ac = encodestring('%s:%s' % (name, pw)).replace('\012','')
+ self._setAuthHeader(ac, req, resp)
+ if req.get(self.persist_cookie, 0):
+ # Persist the user name (but not the pw or session)
+ expires = (DateTime() + 365).toZone('GMT').rfc822()
+ resp.setCookie(self.name_cookie, name,
+ path=self.getCookiePath(),
+ expires=expires)
+ else:
+ # Expire the user name
+ resp.expireCookie(self.name_cookie,
+ path=self.getCookiePath())
+ method = self.getCookieMethod( 'setAuthCookie'
+ , self.defaultSetAuthCookie )
+ method( resp, self.auth_cookie, quote( ac ) )
+ self.delRequestVar(req, self.name_cookie)
+ self.delRequestVar(req, self.pw_cookie)
+
+ elif req.has_key(self.auth_cookie):
+ # Attempt to resume a session if the cookie is valid.
+ # Copy __ac to the auth header.
+ ac = unquote(req[self.auth_cookie])
+ if ac and ac != 'deleted':
+ try:
+ decodestring(ac)
+ except:
+ # Not a valid auth header.
+ pass
+ else:
+ attempt = ATTEMPT_RESUME
+ self._setAuthHeader(ac, req, resp)
+ self.delRequestVar(req, self.auth_cookie)
+ method = self.getCookieMethod(
+ 'twiddleAuthCookie', None)
+ if method is not None:
+ method(resp, self.auth_cookie, quote(ac))
+
+ req._cookie_auth = attempt
+ return attempt
+
+CookieCrumbler.modifyRequest = modifyRequest
+
+
+def credentialsChanged(self, user, name, pw):
+ #ac = encodestring('%s:%s' % (name, pw)).rstrip() => changed for remove all newlines
+ ac = encodestring('%s:%s' % (name, pw)).replace('\012','')
+ method = self.getCookieMethod( 'setAuthCookie'
+ , self.defaultSetAuthCookie )
+ resp = self.REQUEST['RESPONSE']
+ method( resp, self.auth_cookie, quote( ac ) )
+
+CookieCrumbler.credentialsChanged = credentialsChanged
Modified: erp5/tags/version-5.1.2/products/ERP5Type/tests/ERP5TypeTestCase.py
URL: http://svn.erp5.org/erp5/tags/version-5.1.2/products/ERP5Type/tests/ERP5TypeTestCase.py?rev=23321&r1=23320&r2=23321&view=diff
==============================================================================
--- erp5/tags/version-5.1.2/products/ERP5Type/tests/ERP5TypeTestCase.py (original)
+++ erp5/tags/version-5.1.2/products/ERP5Type/tests/ERP5TypeTestCase.py Mon Sep 1 15:24:47 2008
@@ -163,6 +163,7 @@
from cStringIO import StringIO
from urllib import urlretrieve
from glob import glob
+import sys, re, base64
portal_name = 'erp5_portal'
@@ -790,6 +791,100 @@
% title) # run_unit_test depends on this string.
raise
+ def publish(self, path, basic=None, env=None, extra=None,
+ request_method='GET', stdin=None, handle_errors=True):
+ '''Publishes the object at 'path' returning a response object.'''
+
+ from StringIO import StringIO
+ from ZPublisher.Response import Response
+ from ZPublisher.Test import publish_module
+
+ from AccessControl.SecurityManagement import getSecurityManager
+ from AccessControl.SecurityManagement import setSecurityManager
+
+ # Save current security manager
+ sm = getSecurityManager()
+
+ # Commit the sandbox for good measure
+ get_transaction().commit()
+
+ if env is None:
+ env = {}
+ if extra is None:
+ extra = {}
+
+ request = self.app.REQUEST
+
+ env['SERVER_NAME'] = request['SERVER_NAME']
+ env['SERVER_PORT'] = request['SERVER_PORT']
+ env['REQUEST_METHOD'] = request_method
+
+ p = path.split('?')
+ if len(p) == 1:
+ env['PATH_INFO'] = p[0]
+ elif len(p) == 2:
+ [env['PATH_INFO'], env['QUERY_STRING']] = p
+ else:
+ raise TypeError, ''
+
+ if basic:
+ env['HTTP_AUTHORIZATION'] = "Basic %s" % base64.encodestring(basic).replace('\012', '')
+
+ if stdin is None:
+ stdin = StringIO()
+
+ outstream = StringIO()
+ response = Response(stdout=outstream, stderr=sys.stderr)
+
+ publish_module('Zope2',
+ response=response,
+ stdin=stdin,
+ environ=env,
+ extra=extra,
+ debug=not handle_errors,
+ )
+
+ # Restore security manager
+ setSecurityManager(sm)
+
+ return ResponseWrapper(response, outstream, path)
+
+
+class ResponseWrapper:
+ '''Decorates a response object with additional introspective methods.'''
+
+ _bodyre = re.compile('^$^\n(.*)', re.MULTILINE | re.DOTALL)
+
+ def __init__(self, response, outstream, path):
+ self._response = response
+ self._outstream = outstream
+ self._path = path
+
+ def __getattr__(self, name):
+ return getattr(self._response, name)
+
+ def getOutput(self):
+ '''Returns the complete output, headers and all.'''
+ return self._outstream.getvalue()
+
+ def getBody(self):
+ '''Returns the page body, i.e. the output par headers.'''
+ body = self._bodyre.search(self.getOutput())
+ if body is not None:
+ body = body.group(1)
+ return body
+
+ def getPath(self):
+ '''Returns the path used by the request.'''
+ return self._path
+
+ def getHeader(self, name):
+ '''Returns the value of a response header.'''
+ return self.headers.get(name.lower())
+
+ def getCookie(self, name):
+ '''Returns a response cookie.'''
+ return self.cookies.get(name)
class ERP5ReportTestCase(ERP5TypeTestCase):
"""Base class for testing ERP5 Reports
Added: erp5/tags/version-5.1.2/products/ERP5Type/tests/testCookieCrumbler.py
URL: http://svn.erp5.org/erp5/tags/version-5.1.2/products/ERP5Type/tests/testCookieCrumbler.py?rev=23321&view=auto
==============================================================================
--- erp5/tags/version-5.1.2/products/ERP5Type/tests/testCookieCrumbler.py (added)
+++ erp5/tags/version-5.1.2/products/ERP5Type/tests/testCookieCrumbler.py Mon Sep 1 15:24:47 2008
@@ -1,0 +1,101 @@
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import base64
+from cStringIO import StringIO
+import unittest
+import urllib
+
+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
+
+from Products.CMFCore.tests.testCookieCrumbler import makerequest
+from Products.CMFCore.tests.testCookieCrumbler import CookieCrumblerTests
+
+class ERP5CookieCrumblerTests (CookieCrumblerTests):
+ """ Modify original CMFCore Cookie Crumbler unit test to test long login """
+
+ def setUp(self):
+ root = Folder()
+ self.root = root
+ root.isTopLevelPrincipiaApplicationObject = 1 # User folder needs this
+ root.getPhysicalPath = lambda: () # hack
+ root._View_Permission = ('Anonymous',)
+
+ users = UserFolder()
+ users._setId('acl_users')
+ users._doAddUser('abraham', 'pass-w', ('Patriarch',), ())
+ users._doAddUser('isaac', 'pass-w', ('Son',), ())
+ users._doAddUser('abrahammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm',
+ 'pass-wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww',
+ ('Son',), ())
+ root._setObject(users.id, users)
+
+ cc = CookieCrumbler()
+ cc.id = 'cookie_authentication'
+ root._setObject(cc.id, cc)
+ self.cc = getattr(root, cc.id)
+
+ index = DTMLMethod()
+ index.munge('This is the default view')
+ index._setId('index_html')
+ root._setObject(index.getId(), index)
+
+ login = DTMLMethod()
+ login.munge('Please log in first.')
+ login._setId('login_form')
+ root._setObject(login.getId(), login)
+
+ protected = DTMLMethod()
+ protected._View_Permission = ('Manager',)
+ protected.munge('This is the protected view')
+ protected._setId('protected')
+ root._setObject(protected.getId(), protected)
+
+ self.responseOut = StringIO()
+ self.req = makerequest(root, self.responseOut)
+
+ self.credentials = urllib.quote(
+ base64.encodestring('abraham:pass-w').replace('\012', ''))
+
+ def testCookieLongLogin(self):
+ # verify the user and auth cookie get set
+ long_name = 'abrahammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm'
+ long_pass = 'pass-wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'
+ self.req.cookies['__ac_name'] = long_name
+ self.req.cookies['__ac_password'] = long_pass
+ self.req.traverse('/')
+
+ self.assert_(self.req.has_key('AUTHENTICATED_USER'))
+ self.assertEqual(self.req['AUTHENTICATED_USER'].getUserName(),
+ 'abrahammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm')
+ resp = self.req.response
+ self.assert_(resp.cookies.has_key('__ac'))
+ self.credentials = base64.encodestring('%s:%s' % (long_name, long_pass)).replace('\012', '')
+ self.assertEqual(resp.cookies['__ac']['value'],
+ self.credentials)
+ self.assertEqual(resp.cookies['__ac']['path'], '/')
+
+def test_suite():
+ return unittest.makeSuite(ERP5CookieCrumblerTests)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
More information about the Erp5-report
mailing list