[Checkins] SVN: PluggableAuthService/trunk/ Added protection to security sensitive publishable methods so that they require POST. This should prevent a number of potential XSS type attacks. See http://www.zope.org/Products/Zope/Hotfix-2007-03-20/announcement and https://dev.plone.org/plone/ticket/6310

Alec Mitchell apm13 at columbia.edu
Sun Apr 1 16:14:01 EDT 2007


Log message for revision 73968:
  Added protection to security sensitive publishable methods so that they require POST.  This should prevent a number of potential XSS type attacks.  See http://www.zope.org/Products/Zope/Hotfix-2007-03-20/announcement and https://dev.plone.org/plone/ticket/6310
  

Changed:
  U   PluggableAuthService/trunk/doc/CHANGES.txt
  U   PluggableAuthService/trunk/plugins/DynamicGroupsPlugin.py
  U   PluggableAuthService/trunk/plugins/ZODBGroupManager.py
  U   PluggableAuthService/trunk/plugins/ZODBRoleManager.py
  U   PluggableAuthService/trunk/plugins/ZODBUserManager.py
  U   PluggableAuthService/trunk/plugins/tests/helpers.py
  U   PluggableAuthService/trunk/plugins/tests/test_DynamicGroupsPlugin.py
  U   PluggableAuthService/trunk/plugins/tests/test_ZODBGroupManager.py
  U   PluggableAuthService/trunk/plugins/tests/test_ZODBRoleManager.py
  U   PluggableAuthService/trunk/plugins/tests/test_ZODBUserManager.py
  U   PluggableAuthService/trunk/utils.py

-=-
Modified: PluggableAuthService/trunk/doc/CHANGES.txt
===================================================================
--- PluggableAuthService/trunk/doc/CHANGES.txt	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/doc/CHANGES.txt	2007-04-01 20:13:59 UTC (rev 73968)
@@ -12,6 +12,15 @@
 
     Bugs Fixed
 
+      - Converted to publishable security sensitive methods to only accept
+        POST requests to prevent XSS attacks.  See
+        http://www.zope.org/Products/Zope/Hotfix-2007-03-20/announcement and
+        https://dev.plone.org/plone/ticket/6310
+
+      - Fixed issue in the user search filter where unrecognized keyword
+        arguments were ignored resulting in duplicate search entries.
+        (https://dev.plone.org/plone/ticket/6300)
+
       - Made sure the Extensions.upgrade script does not commit full
         transactions but only sets (optimistic) savepoints. Removed bogus
         Zope 2.7 compatibility in the process.

Modified: PluggableAuthService/trunk/plugins/DynamicGroupsPlugin.py
===================================================================
--- PluggableAuthService/trunk/plugins/DynamicGroupsPlugin.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/DynamicGroupsPlugin.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -40,6 +40,7 @@
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import createViewName
 from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.utils import postonly
 
 class IDynamicGroupsPlugin(Interface):
     """ Marker interface.
@@ -399,7 +400,7 @@
         self.ZCacheable_invalidate(view_name=view_name)
             
     security.declareProtected( ManageGroups, 'removeGroup' )
-    def removeGroup( self, group_id ):
+    def removeGroup( self, group_id, REQUEST=None ):
 
         """ Remove a group definition.
 
@@ -416,6 +417,7 @@
         self.ZCacheable_invalidate(view_name=view_name)
         view_name = createViewName('enumerateGroups', group_id)
         self.ZCacheable_invalidate(view_name=view_name)
+    removeGroup = postonly(removeGroup)
 
     #
     #   ZMI
@@ -491,6 +493,7 @@
     def manage_removeGroups( self
                            , group_ids
                            , RESPONSE=None
+                           , REQUEST=None
                            ):
         """ Remove one or more groups via the ZMI.
         """
@@ -510,6 +513,7 @@
             RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s'
                              % ( self.absolute_url(), message )
                              )
+    manage_removeGroups = postonly(manage_removeGroups)
 
 classImplements( DynamicGroupsPlugin
                , IDynamicGroupsPlugin

Modified: PluggableAuthService/trunk/plugins/ZODBGroupManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/ZODBGroupManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/ZODBGroupManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -33,6 +33,7 @@
 from Products.PluggableAuthService.permissions import ManageGroups
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.utils import postonly
 
 class IZODBGroupManager(Interface):
     """ Marker interface.
@@ -262,7 +263,7 @@
         return result
 
     security.declareProtected( ManageGroups, 'addPrincipalToGroup' )
-    def addPrincipalToGroup( self, principal_id, group_id ):
+    def addPrincipalToGroup( self, principal_id, group_id, REQUEST=None ):
 
         """ Add a principal to a group.
 
@@ -280,9 +281,10 @@
             self._principal_groups[ principal_id ] = new
 
         return not already
+    addPrincipalToGroup = postonly(addPrincipalToGroup)
 
     security.declareProtected( ManageGroups, 'removePrincipalFromGroup' )
-    def removePrincipalFromGroup( self, principal_id, group_id ):
+    def removePrincipalFromGroup( self, principal_id, group_id, REQUEST=None ):
 
         """ Remove a prinicpal from from a group.
 
@@ -304,6 +306,7 @@
             self._principal_groups[ principal_id ] = new
 
         return already
+    removePrincipalFromGroup = postonly(removePrincipalFromGroup)
 
     #
     #   ZMI
@@ -373,6 +376,7 @@
     def manage_removeGroups( self
                            , group_ids
                            , RESPONSE=None
+                           , REQUEST=None
                            ):
         """ Remove one or more groups via the ZMI.
         """
@@ -392,12 +396,14 @@
             RESPONSE.redirect( '%s/manage_groups?manage_tabs_message=%s'
                              % ( self.absolute_url(), message )
                              )
+    manage_removeGroups = postonly(manage_removeGroups)
 
     security.declareProtected( ManageGroups, 'manage_addPrincipalsToGroup' )
     def manage_addPrincipalsToGroup( self
                                    , group_id
                                    , principal_ids
                                    , RESPONSE=None
+                                   , REQUEST=None
                                    ):
         """ Add one or more principals to a group via the ZMI.
         """
@@ -419,6 +425,7 @@
                                + '&manage_tabs_message=%s'
                                ) % ( self.absolute_url(), group_id, message )
                              )
+    manage_addPrincipalsToGroup = postonly(manage_addPrincipalsToGroup)
 
     security.declareProtected( ManageGroups
                              , 'manage_removePrincipalsFromGroup' 
@@ -427,6 +434,7 @@
                                         , group_id
                                         , principal_ids
                                         , RESPONSE=None
+                                        , REQUEST=None
                                         ):
         """ Remove one or more principals from a group via the ZMI.
         """
@@ -448,6 +456,7 @@
                                + '&manage_tabs_message=%s'
                                ) % ( self.absolute_url(), group_id, message )
                              )
+    manage_removePrincipalsFromGroup = postonly(manage_removePrincipalsFromGroup)
 
 classImplements( ZODBGroupManager
                , IZODBGroupManager

Modified: PluggableAuthService/trunk/plugins/ZODBRoleManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/ZODBRoleManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/ZODBRoleManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -35,6 +35,7 @@
 from Products.PluggableAuthService.permissions import ManageUsers
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.utils import postonly
 
 class IZODBRoleManager(Interface):
     """ Marker interface.
@@ -209,7 +210,7 @@
                                        } )
 
     security.declareProtected( ManageUsers, 'removeRole' )
-    def removeRole( self, role_id ):
+    def removeRole( self, role_id, REQUEST=None ):
 
         """ Remove 'role_id' from the list of roles managed by this object.
 
@@ -219,6 +220,7 @@
             self.removeRoleFromPrincipal( role_id, principal_id )
 
         del self._roles[ role_id ]
+    removeRole = postonly(removeRole)
 
     #
     #   Role assignment API
@@ -275,7 +277,7 @@
         return result
 
     security.declareProtected( ManageUsers, 'assignRoleToPrincipal' )
-    def assignRoleToPrincipal( self, role_id, principal_id ):
+    def assignRoleToPrincipal( self, role_id, principal_id, REQUEST=None ):
 
         """ Assign a role to a principal (user or group).
 
@@ -293,9 +295,10 @@
             self._principal_roles[ principal_id ] = new
 
         return not already
+    assignRoleToPrincipal = postonly(assignRoleToPrincipal)
 
     security.declareProtected( ManageUsers, 'removeRoleFromPrincipal' )
-    def removeRoleFromPrincipal( self, role_id, principal_id ):
+    def removeRoleFromPrincipal( self, role_id, principal_id, REQUEST=None ):
 
         """ Remove a role from a principal (user or group).
 
@@ -316,6 +319,7 @@
             self._principal_roles[ principal_id ] = new
 
         return already
+    removeRoleFromPrincipal = postonly(removeRoleFromPrincipal)
 
     #
     #   ZMI
@@ -377,6 +381,7 @@
     def manage_removeRoles( self
                           , role_ids
                           , RESPONSE
+                          , REQUEST=None
                           ):
         """ Remove one or more roles via the ZMI.
         """
@@ -395,12 +400,14 @@
         RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s'
                          % ( self.absolute_url(), message )
                          )
+    manage_removeRoles = postonly(manage_removeRoles)
 
     security.declareProtected( ManageUsers, 'manage_assignRoleToPrincipals' )
     def manage_assignRoleToPrincipals( self
                                      , role_id
                                      , principal_ids
                                      , RESPONSE
+                                     , REQUEST=None
                                      ):
         """ Assign a role to one or more principals via the ZMI.
         """
@@ -421,12 +428,14 @@
                            + '&manage_tabs_message=%s'
                            ) % ( self.absolute_url(), role_id, message )
                          )
+    manage_assignRoleToPrincipals = postonly(manage_assignRoleToPrincipals)
 
     security.declareProtected( ManageUsers, 'manage_removeRoleFromPrincipals' )
     def manage_removeRoleFromPrincipals( self
                                        , role_id
                                        , principal_ids
                                        , RESPONSE
+                                       , REQUEST=None
                                        ):
         """ Remove a role from one or more principals via the ZMI.
         """
@@ -447,6 +456,7 @@
                            + '&manage_tabs_message=%s'
                            ) % ( self.absolute_url(), role_id, message )
                          )
+    manage_removeRoleFromPrincipals = postonly(manage_removeRoleFromPrincipals)
 
 classImplements( ZODBRoleManager
                , IZODBRoleManager

Modified: PluggableAuthService/trunk/plugins/ZODBUserManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/ZODBUserManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/ZODBUserManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -41,6 +41,7 @@
 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
 from Products.PluggableAuthService.utils import classImplements
 from Products.PluggableAuthService.utils import createViewName
+from Products.PluggableAuthService.utils import postonly
 
 class IZODBUserManager(Interface):
     """ Marker interface.
@@ -291,7 +292,7 @@
 
     security.declarePrivate('updateUser')
     def updateUser(self, user_id, login_name):
-        
+
         # The following raises a KeyError if the user_id is invalid
         old_login = self.getLoginForUserId(user_id)
 
@@ -395,6 +396,7 @@
                                  , password
                                  , confirm
                                  , RESPONSE=None
+                                 , REQUEST=None
                                  ):
         """ Update a user's login name / password via the ZMI.
         """
@@ -411,6 +413,7 @@
             RESPONSE.redirect( '%s/manage_users?manage_tabs_message=%s'
                              % ( self.absolute_url(), message )
                              )
+    manage_updateUserPassword = postonly(manage_updateUserPassword)
 
     security.declareProtected( ManageUsers, 'manage_updateUser' )
     def manage_updateUser(self, user_id, login_name, RESPONSE=None):
@@ -434,6 +437,7 @@
     def manage_removeUsers( self
                           , user_ids
                           , RESPONSE=None
+                          , REQUEST=None
                           ):
         """ Remove one or more users via the ZMI.
         """
@@ -453,6 +457,7 @@
             RESPONSE.redirect( '%s/manage_users?manage_tabs_message=%s'
                              % ( self.absolute_url(), message )
                              )
+    manage_removeUsers = postonly(manage_removeUsers)
 
     #
     #   Allow users to change their own login name and password.
@@ -478,6 +483,7 @@
                              , password
                              , confirm
                              , RESPONSE=None
+                             , REQUEST=None
                              ):
         """ Update the current user's password and login name.
         """
@@ -501,6 +507,7 @@
                                '?manage_tabs_message=%s'
                              % ( self.absolute_url(), message )
                              )
+    manage_updatePassword = postonly(manage_updatePassword)
 
 classImplements( ZODBUserManager
                , IZODBUserManager

Modified: PluggableAuthService/trunk/plugins/tests/helpers.py
===================================================================
--- PluggableAuthService/trunk/plugins/tests/helpers.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/tests/helpers.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -47,3 +47,16 @@
     def getGroups( self ):
         return self._groups
 
+
+def makeRequestAndResponse():
+    # the POST checking requires a real HTTPRequest
+    from cStringIO import StringIO
+    from ZPublisher.HTTPRequest import HTTPRequest
+    from ZPublisher.HTTPResponse import HTTPResponse
+
+    res = HTTPResponse()
+    req = HTTPRequest(StringIO(),
+                      {'SERVER_NAME': 'localhost',
+                       'SERVER_PORT': '80'},
+                      res)
+    return req, res

Modified: PluggableAuthService/trunk/plugins/tests/test_DynamicGroupsPlugin.py
===================================================================
--- PluggableAuthService/trunk/plugins/tests/test_DynamicGroupsPlugin.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/tests/test_DynamicGroupsPlugin.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -24,6 +24,9 @@
     import IGroupEnumerationPlugin_conformance
 from Products.PluggableAuthService.tests.utils import _setUpDefaultTraversable
 
+from Products.PluggableAuthService.plugins.tests.helpers \
+     import makeRequestAndResponse
+
 class FauxScript:
 
 
@@ -475,6 +478,28 @@
         self.assertEqual( len( groups ), 1 )
         self.failUnless( 'ggp_effable' in groups )
 
+    def testPOSTProtections(self):
+        from zExceptions import Forbidden
+
+        GROUP_ID = 'testgroup'
+
+        dpg = self._makeOne( 'adding' )
+
+        dpg.addGroup( GROUP_ID, 'python:True', 'title', 'description', True )
+
+        req, res = makeRequestAndResponse()
+
+        req.set('REQUEST_METHOD', 'GET')
+
+        # Fails with a GET
+        # test removeGroup
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, dpg.removeGroup,
+                          GROUP_ID, REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        dpg.removeGroup(GROUP_ID, REQUEST=req)
+
 if __name__ == "__main__":
     unittest.main()
 

Modified: PluggableAuthService/trunk/plugins/tests/test_ZODBGroupManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/tests/test_ZODBGroupManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/tests/test_ZODBGroupManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -20,7 +20,7 @@
     import IGroupsPlugin_conformance
 
 from Products.PluggableAuthService.plugins.tests.helpers \
-     import FauxPAS, FauxSmartPAS, DummyUser
+     import FauxPAS, FauxSmartPAS, DummyUser, makeRequestAndResponse
 
 class DummyGroup:
 
@@ -265,10 +265,63 @@
         groups = zgm.getGroupsForPrincipal( user )
         self.assertEqual( groups, ( 'prefixed_group', ) )
 
+    def testPOSTProtections(self):
+        from zExceptions import Forbidden
+
+        USER_ID = 'testuser'
+        GROUP_ID = 'testgroup'
+
+        zgm = self._makeOne()
+        zgm.prefix = 'prefixed_'
+
+        zgm.addGroup( GROUP_ID )
+        user = DummyUser( USER_ID )
+
+        req, res = makeRequestAndResponse()
+
+        # test addPrincipalToGroup
+        # Fails with a GET
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zgm.addPrincipalToGroup,
+                          USER_ID, GROUP_ID, REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        zgm.addPrincipalToGroup(USER_ID, GROUP_ID, REQUEST=req)
+
+        # test removePrincipalFromGroup
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zgm.removePrincipalFromGroup,
+                          USER_ID, GROUP_ID, REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        zgm.removePrincipalFromGroup(USER_ID, GROUP_ID, REQUEST=req)
+
+        # test manage_addPrincipalsToGroup
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zgm.manage_addPrincipalsToGroup,
+                          GROUP_ID, [USER_ID], REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zgm.manage_addPrincipalsToGroup(GROUP_ID, [USER_ID], REQUEST=req)
+
+        # test manage_removePrincipalsFromGroup
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zgm.manage_removePrincipalsFromGroup,
+                          GROUP_ID, [USER_ID], REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zgm.manage_removePrincipalsFromGroup(GROUP_ID, [USER_ID], REQUEST=req)
+
+        # test manage_removeGroup
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zgm.manage_removeGroups,
+                          [GROUP_ID], REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        zgm.manage_removeGroups([GROUP_ID], REQUEST=req)
+
 if __name__ == "__main__":
     unittest.main()
 
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite( ZODBGroupManagerTests ),
-        ))               
+        ))

Modified: PluggableAuthService/trunk/plugins/tests/test_ZODBRoleManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/tests/test_ZODBRoleManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/tests/test_ZODBRoleManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -22,7 +22,7 @@
     import IRoleAssignerPlugin_conformance
 
 from Products.PluggableAuthService.plugins.tests.helpers \
-     import FauxPAS, FauxSmartPAS, DummyUser
+     import FauxPAS, FauxSmartPAS, DummyUser, makeRequestAndResponse
 
 class ZODBRoleManagerTests( unittest.TestCase
                           , IRolesPlugin_conformance
@@ -471,6 +471,72 @@
 
         self.failIf( 'test' in zrm.getRolesForPrincipal( user ) )
 
+    def testPOSTProtections(self):
+        from zExceptions import Forbidden
+
+        USER_ID = 'testuser'
+        ROLE_ID = 'myrole'
+
+        root = FauxPAS()
+        zrm = self._makeOne( id='remove_existing' ).__of__( root )
+        zrm = self._makeOne()
+        zrm.addRole(ROLE_ID)
+
+        user = DummyUser( USER_ID )
+
+        req, res = makeRequestAndResponse()
+
+        # Fails with a GET
+        # test assignRoleToPrincipal
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.assignRoleToPrincipal,
+                          ROLE_ID, USER_ID, REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.assignRoleToPrincipal(ROLE_ID, USER_ID, REQUEST=req)
+
+        # test removeRoleFromPricipal
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.removeRoleFromPrincipal,
+                          ROLE_ID, USER_ID, REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.removeRoleFromPrincipal(ROLE_ID, USER_ID, REQUEST=req)
+
+        # test removeRole
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.removeRole,
+                          ROLE_ID, REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.removeRole(ROLE_ID, REQUEST=req)
+
+        # Readd the role for the manage_* methods
+        zrm.addRole(ROLE_ID)
+
+        # test manage_assignRoleToPrincipal
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.manage_assignRoleToPrincipals,
+                          ROLE_ID, [USER_ID], RESPONSE=res, REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.manage_assignRoleToPrincipals(ROLE_ID, [USER_ID], RESPONSE=res,
+                                          REQUEST=req)
+
+        # test manage_removeRoleFromPricipal
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.manage_removeRoleFromPrincipals,
+                          ROLE_ID, [USER_ID], RESPONSE=res, REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.manage_removeRoleFromPrincipals(ROLE_ID, [USER_ID], RESPONSE=res,
+                                            REQUEST=req)
+
+        # test manage_removeRoles
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zrm.manage_removeRoles,
+                          [ROLE_ID], RESPONSE=res, REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zrm.manage_removeRoles([ROLE_ID], RESPONSE=res, REQUEST=req)
+
+
+
 if __name__ == "__main__":
     unittest.main()
 

Modified: PluggableAuthService/trunk/plugins/tests/test_ZODBUserManager.py
===================================================================
--- PluggableAuthService/trunk/plugins/tests/test_ZODBUserManager.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/plugins/tests/test_ZODBUserManager.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -21,6 +21,9 @@
 from Products.PluggableAuthService.tests.conformance \
     import IUserAdderPlugin_conformance
 
+from Products.PluggableAuthService.plugins.tests.helpers \
+     import makeRequestAndResponse
+
 class DummyUser:
 
     def __init__( self, id ):
@@ -520,7 +523,42 @@
 
         self.assertEqual(uid_and_info, (USER_ID, USER_ID))
 
+    def testPOSTProtections(self):
+        from AccessControl.AuthEncoding import pw_encrypt
+        from zExceptions import Forbidden
 
+        USER_ID = 'testuser'
+        PASSWORD = 'password'
+
+        ENCRYPTED = pw_encrypt(PASSWORD)
+
+        zum = self._makeOne()
+        zum.addUser(USER_ID, USER_ID, '')
+
+        req, res = makeRequestAndResponse()
+        # test manage_updateUserPassword
+        # Fails with a GET
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zum.manage_updateUserPassword,
+                          USER_ID, PASSWORD, PASSWORD, REQUEST=req)
+        # Works with a POST
+        req.set('REQUEST_METHOD', 'POST')
+        zum.manage_updateUserPassword(USER_ID, PASSWORD, PASSWORD, REQUEST=req)
+
+        # test manage_updatePassword
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zum.manage_updatePassword,
+                          USER_ID, PASSWORD, PASSWORD, REQUEST=req)
+        # XXX: This method is broken
+
+        # test manage_removeUsers
+        req.set('REQUEST_METHOD', 'GET')
+        self.assertRaises(Forbidden, zum.manage_removeUsers,
+                          [USER_ID], REQUEST=req)
+        req.set('REQUEST_METHOD', 'POST')
+        zum.manage_removeUsers([USER_ID], REQUEST=req)
+
+
 if __name__ == "__main__":
     unittest.main()
 

Modified: PluggableAuthService/trunk/utils.py
===================================================================
--- PluggableAuthService/trunk/utils.py	2007-04-01 19:48:42 UTC (rev 73967)
+++ PluggableAuthService/trunk/utils.py	2007-04-01 20:13:59 UTC (rev 73968)
@@ -49,7 +49,18 @@
         normalized_interfaces.append(i)
     return interface.classImplements(class_, *normalized_interfaces)
 
+# postonly protection
+try:
+    # Zope 2.8.9, 2.9.7 and 2.10.3 (and up)
+    from AccessControl.requestmethod import postonly
+except ImportError:
+    try:
+        # Try the hotfix too
+        from Products.Hotfix_20070320 import postonly
+    except:
+        def postonly(callable): return callable
 
+
 product_dir = package_home( globals() )
 product_prefix = os.path.join( os.path.split(product_dir)[:-1] )
 



More information about the Checkins mailing list