From 348287cf29d668e72ee29b5ca47353e3bc0e1b28 Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Mon, 29 Jan 2018 22:42:22 +0100 Subject: [PATCH 01/13] fix startup in python3 --- src/Products/PlonePAS/tools/groupdata.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Products/PlonePAS/tools/groupdata.py b/src/Products/PlonePAS/tools/groupdata.py index 24b9d82c..25c917cf 100644 --- a/src/Products/PlonePAS/tools/groupdata.py +++ b/src/Products/PlonePAS/tools/groupdata.py @@ -514,9 +514,15 @@ def canWriteProperty(self, prop_name): break # shadowed by read-only return 0 +<<<<<<< HEAD canAddToGroup = MemberData.canAddToGroup.__func__ canRemoveFromGroup = MemberData.canRemoveFromGroup.__func__ canAssignRole = MemberData.canAssignRole.__func__ +======= + canAddToGroup = MemberData.canAddToGroup + canRemoveFromGroup = MemberData.canRemoveFromGroup + canAssignRole = MemberData.canAssignRole +>>>>>>> fix startup in python3 # plugin getters From 08a3671abf090c98a86972c2d99fed0ce77ce6c0 Mon Sep 17 00:00:00 2001 From: Pavel Bogdanovic Date: Thu, 22 Mar 2018 12:27:12 +0100 Subject: [PATCH 02/13] Fix pull request: Fix startup issues in python3 --- src/Products/PlonePAS/tools/groupdata.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Products/PlonePAS/tools/groupdata.py b/src/Products/PlonePAS/tools/groupdata.py index 25c917cf..24b9d82c 100644 --- a/src/Products/PlonePAS/tools/groupdata.py +++ b/src/Products/PlonePAS/tools/groupdata.py @@ -514,15 +514,9 @@ def canWriteProperty(self, prop_name): break # shadowed by read-only return 0 -<<<<<<< HEAD canAddToGroup = MemberData.canAddToGroup.__func__ canRemoveFromGroup = MemberData.canRemoveFromGroup.__func__ canAssignRole = MemberData.canAssignRole.__func__ -======= - canAddToGroup = MemberData.canAddToGroup - canRemoveFromGroup = MemberData.canRemoveFromGroup - canAssignRole = MemberData.canAssignRole ->>>>>>> fix startup in python3 # plugin getters From 94f53bfcf928b7f196490ac844bf33e77c596fdc Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Wed, 28 Mar 2018 16:12:24 +0200 Subject: [PATCH 03/13] fix setting the auth-cookie in py3 --- src/Products/PlonePAS/plugins/cookie_handler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Products/PlonePAS/plugins/cookie_handler.py b/src/Products/PlonePAS/plugins/cookie_handler.py index a301a1ed..1e0ec655 100644 --- a/src/Products/PlonePAS/plugins/cookie_handler.py +++ b/src/Products/PlonePAS/plugins/cookie_handler.py @@ -70,7 +70,11 @@ def updateCredentials(self, request, response, login, new_password): setAuthCookie = getattr(self, 'setAuthCookie', None) if setAuthCookie: - cookie_val = encodestring('%s:%s' % (login, new_password)) + cookie_str = b':'.join([ + login.encode('utf-8'), + new_password.encode('utf-8'), + ]) + cookie_val = encodestring(cookie_str) cookie_val = cookie_val.rstrip() setAuthCookie(response, self.cookie_name, quote(cookie_val)) else: From 05bd87c25b81065c831c1d07cf0c1911cff5061b Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Wed, 28 Mar 2018 16:17:52 +0200 Subject: [PATCH 04/13] fix assignment of MemberData-functions during startup in py3 --- src/Products/PlonePAS/tools/groupdata.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Products/PlonePAS/tools/groupdata.py b/src/Products/PlonePAS/tools/groupdata.py index 24b9d82c..cfe9eeb6 100644 --- a/src/Products/PlonePAS/tools/groupdata.py +++ b/src/Products/PlonePAS/tools/groupdata.py @@ -30,6 +30,7 @@ from zope.interface import implementer import logging +import six logger = logging.getLogger('PlonePAS') _marker = object() @@ -514,9 +515,15 @@ def canWriteProperty(self, prop_name): break # shadowed by read-only return 0 - canAddToGroup = MemberData.canAddToGroup.__func__ - canRemoveFromGroup = MemberData.canRemoveFromGroup.__func__ - canAssignRole = MemberData.canAssignRole.__func__ + if six.PY3: + canAddToGroup = MemberData.canAddToGroup + canRemoveFromGroup = MemberData.canRemoveFromGroup + canAssignRole = MemberData.canAssignRole + else: + # in PY2 this is a unbound method + canAddToGroup = MemberData.canAddToGroup.__func__ + canRemoveFromGroup = MemberData.canRemoveFromGroup.__func__ + canAssignRole = MemberData.canAssignRole.__func__ # plugin getters From 8cdb99009fe84a93c79ef3107318b0d6614cd245 Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Fri, 18 May 2018 07:59:50 +0200 Subject: [PATCH 05/13] user properties are text in py3 --- src/Products/PlonePAS/plugins/ufactory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/PlonePAS/plugins/ufactory.py b/src/Products/PlonePAS/plugins/ufactory.py index 6663650b..8edeabea 100644 --- a/src/Products/PlonePAS/plugins/ufactory.py +++ b/src/Products/PlonePAS/plugins/ufactory.py @@ -227,7 +227,7 @@ def getProperty(self, id, default=_marker): for sheet in self.getOrderedPropertySheets(): if sheet.hasProperty(id): value = sheet.getProperty(id) - if isinstance(value, six.text_type): + if six.PY2 and isinstance(value, six.text_type): # XXX Temporarily work around the fact that # property sheets blindly store and return # unicode. This is sub-optimal and should be From 30f9640bad1e61e5d3e7054f8ca018396eae995e Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Wed, 23 May 2018 10:49:55 +0200 Subject: [PATCH 06/13] fix scaling user profile --- src/Products/PlonePAS/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Products/PlonePAS/utils.py b/src/Products/PlonePAS/utils.py index 61af69c8..106ad12f 100644 --- a/src/Products/PlonePAS/utils.py +++ b/src/Products/PlonePAS/utils.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from Products.PlonePAS.config import IMAGE_SCALE_PARAMS from Products.PluggableAuthService.interfaces.plugins import IGroupsPlugin -from six import StringIO +from six import BytesIO from six.moves import urllib import six @@ -159,7 +159,7 @@ def scale_image(image_file, max_size=None, default_format=None): if original_mode == 'P' and format in ('GIF', 'PNG'): image = image.convert('P') # Save - new_file = StringIO() + new_file = BytesIO() image.save(new_file, format, quality=IMAGE_SCALE_PARAMS['quality']) new_file.seek(0) # Return the file data and the new mimetype From 605c3aba93fceb3be5b6a5fc04a4ab4d7c959728 Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Wed, 23 May 2018 11:55:07 +0200 Subject: [PATCH 07/13] fix another case where properties are text in py3 --- src/Products/PlonePAS/tools/memberdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/PlonePAS/tools/memberdata.py b/src/Products/PlonePAS/tools/memberdata.py index 29d2386a..36a9245e 100644 --- a/src/Products/PlonePAS/tools/memberdata.py +++ b/src/Products/PlonePAS/tools/memberdata.py @@ -304,7 +304,7 @@ def getProperty(self, id, default=_marker): if sheet.hasProperty(id): # Return the first one that has the property. value = sheet.getProperty(id) - if isinstance(value, six.text_type): + if six.PY2 and isinstance(value, six.text_type): # XXX Temporarily work around the fact that # property sheets blindly store and return # unicode. This is sub-optimal and should be From fbb6ccfbce26b6687c4d366a093821e2554c199c Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Thu, 14 Jun 2018 00:09:33 +0200 Subject: [PATCH 08/13] migrate all tests away from PloneTestCase fix most tests with py3 --- src/Products/PlonePAS/plugins/ufactory.py | 2 +- src/Products/PlonePAS/testing.py | 52 ++++++ src/Products/PlonePAS/tests/base.py | 3 - src/Products/PlonePAS/tests/cookie_auth.rst | 16 +- src/Products/PlonePAS/tests/dummy.py | 6 +- src/Products/PlonePAS/tests/test_basic_ops.py | 30 ++-- src/Products/PlonePAS/tests/test_doctests.py | 4 +- .../PlonePAS/tests/test_groupdatatool.py | 39 +++-- .../PlonePAS/tests/test_groupstool.py | 46 ++++-- .../PlonePAS/tests/test_memberdatatool.py | 11 +- .../PlonePAS/tests/test_membershiptool.py | 151 +++++++++++------- .../PlonePAS/tests/test_properties.py | 25 ++- .../PlonePAS/tests/test_role_plugin.py | 8 +- src/Products/PlonePAS/tests/test_views.py | 9 +- src/Products/PlonePAS/tools/membership.py | 8 +- 15 files changed, 276 insertions(+), 134 deletions(-) create mode 100644 src/Products/PlonePAS/testing.py delete mode 100644 src/Products/PlonePAS/tests/base.py diff --git a/src/Products/PlonePAS/plugins/ufactory.py b/src/Products/PlonePAS/plugins/ufactory.py index 8edeabea..f0fe7c18 100644 --- a/src/Products/PlonePAS/plugins/ufactory.py +++ b/src/Products/PlonePAS/plugins/ufactory.py @@ -215,7 +215,7 @@ def setProperties(self, properties=None, **kw): continue update = {} - for (key, value) in properties.items(): + for (key, value) in list(properties.items()): if sheet.hasProperty(key): update[key] = value del properties[key] diff --git a/src/Products/PlonePAS/testing.py b/src/Products/PlonePAS/testing.py new file mode 100644 index 00000000..39bcf180 --- /dev/null +++ b/src/Products/PlonePAS/testing.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +from plone.app.contenttypes.testing import PLONE_APP_CONTENTTYPES_FIXTURE +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.app.testing import applyProfile +from plone.app.testing import FunctionalTesting +from plone.app.testing import IntegrationTesting +from plone.app.testing import PloneSandboxLayer +from plone.testing import z2 + +import Products.PlonePAS + + +class ProductsPlonepasLayer(PloneSandboxLayer): + + defaultBases = (PLONE_APP_CONTENTTYPES_FIXTURE,) + + def setUpZope(self, app, configurationContext): + # Load any other ZCML that is required for your tests. + # The z3c.autoinclude feature is disabled in the Plone fixture base + # layer. + self.loadZCML(package=Products.PlonePAS) + z2.installProduct(app, 'Products.PlonePAS') + + def setUpPloneSite(self, portal): + applyProfile(portal, 'Products.PlonePAS:PlonePAS') + # setRoles(portal, TEST_USER_ID, ['Manager']) + from Products.CMFPlone.utils import _createObjectByType + _createObjectByType('Folder', portal, id='Members') + mtool = portal.portal_membership + if not mtool.getMemberareaCreationFlag(): + mtool.setMemberareaCreationFlag() + mtool.createMemberArea(TEST_USER_ID) + if mtool.getMemberareaCreationFlag(): + mtool.setMemberareaCreationFlag() + + _createObjectByType('Folder', portal, id='folder') + + +PRODUCTS_PLONEPAS_FIXTURE = ProductsPlonepasLayer() + + +PRODUCTS_PLONEPAS_INTEGRATION_TESTING = IntegrationTesting( + bases=(PRODUCTS_PLONEPAS_FIXTURE,), + name='ProductsPlonepasLayer:IntegrationTesting', +) + + +PRODUCTS_PLONEPAS_FUNCTIONAL_TESTING = FunctionalTesting( + bases=(PRODUCTS_PLONEPAS_FIXTURE,), + name='ProductsPlonepasLayer:FunctionalTesting', +) diff --git a/src/Products/PlonePAS/tests/base.py b/src/Products/PlonePAS/tests/base.py deleted file mode 100644 index 20038455..00000000 --- a/src/Products/PlonePAS/tests/base.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- -from plone.app.testing.bbb import PloneTestCase as TestCase # noqa -from plone.app.testing.bbb import PloneTestCase as FunctionalTestCase # noqa diff --git a/src/Products/PlonePAS/tests/cookie_auth.rst b/src/Products/PlonePAS/tests/cookie_auth.rst index e5b0e795..ae68c028 100644 --- a/src/Products/PlonePAS/tests/cookie_auth.rst +++ b/src/Products/PlonePAS/tests/cookie_auth.rst @@ -11,13 +11,13 @@ User in Plone Site Plone Site has PAS installed >>> portal = layer['portal'] - >>> print portal.acl_users.meta_type + >>> print(portal.acl_users.meta_type) Pluggable Auth Service User exists in the user folder inside the Plone Site. >>> uf = portal.acl_users - >>> print uf.meta_type + >>> print(uf.meta_type) Pluggable Auth Service >>> user_name, user_password, user_role = ('foo', 'bar', 'Manager') @@ -32,7 +32,7 @@ Login to Plone Site using Basic Auth works. >>> browser = Browser(layer['app']) >>> browser.addHeader('Authorization', 'Basic %s:%s' % (TEST_USER_NAME, TEST_USER_PASSWORD,)) >>> browser.open('%s/manage' % portal.absolute_url()) - >>> print browser.headers + >>> print(browser.headers) Status: 200 ... Make sure cookie plugin is installed and activated. @@ -53,10 +53,10 @@ Make sure cookie plugin is installed and activated. ... pass >>> for active, iface in actives: - ... print iface, + ... print(iface,) ... for id, plugin in active: ... if id == 'credentials_cookie_auth': - ... print True + ... print(True) <...IExtraction...> True <...IChallenge...> True <...ICredentialsUpdate...> <...ICredentialsReset...> @@ -68,7 +68,7 @@ User Exists on the folder containing the Plone Site, which should be a Pluggable Auth Service too. >>> uf = layer['app'].acl_users - >>> print uf.meta_type + >>> print(uf.meta_type) Pluggable Auth Service >>> user_name, user_password, user_role = ('baz', 'bar', 'Manager') @@ -85,7 +85,7 @@ Login directly to containing folder using Basic Auth works. >>> browser = Browser(layer['app']) >>> browser.addHeader('Authorization', 'Basic %s:%s' % (user_name, user_password,)) >>> browser.open('%s/manage' % layer['app'].absolute_url()) - >>> print browser.headers + >>> print(browser.headers) Status: 200 ... Login to Plone Site using Basic Auth works. @@ -93,6 +93,6 @@ Login to Plone Site using Basic Auth works. >>> browser = Browser(layer['app']) >>> browser.addHeader('Authorization', 'Basic %s:%s' % (user_name, user_password,)) >>> browser.open('%s/manage' % portal.absolute_url()) - >>> print browser.headers + >>> print(browser.headers) Status: 200 ... diff --git a/src/Products/PlonePAS/tests/dummy.py b/src/Products/PlonePAS/tests/dummy.py index 5c7a8c36..3dfede87 100644 --- a/src/Products/PlonePAS/tests/dummy.py +++ b/src/Products/PlonePAS/tests/dummy.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from OFS.SimpleItem import SimpleItem -from six import StringIO +from six import BytesIO from ZPublisher.HTTPRequest import FileUpload -TEXT = 'file data' +TEXT = b'file data' class FieldStorage(object): @@ -33,7 +33,7 @@ def __init__(self, filename=None, data=None, headers=None): self.data = data if headers is not None: self.headers = headers - self.file = StringIO(self.data) + self.file = BytesIO(self.data) def seek(self, *args): pass diff --git a/src/Products/PlonePAS/tests/test_basic_ops.py b/src/Products/PlonePAS/tests/test_basic_ops.py index 55188058..220d8960 100644 --- a/src/Products/PlonePAS/tests/test_basic_ops.py +++ b/src/Products/PlonePAS/tests/test_basic_ops.py @@ -1,20 +1,24 @@ # -*- coding: utf-8 -*- -from Products.PlonePAS.tests import base -from Products.PluggableAuthService.PluggableAuthService import \ - _SWALLOWABLE_PLUGIN_EXCEPTIONS -from Products.PluggableAuthService.interfaces.authservice import \ - IPluggableAuthService -from Products.PluggableAuthService.interfaces.events import \ - IPrincipalDeletedEvent +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING +from Products.PluggableAuthService.interfaces.authservice import IPluggableAuthService +from Products.PluggableAuthService.interfaces.events import IPrincipalDeletedEvent from Products.PluggableAuthService.interfaces.plugins import IRolesPlugin +from Products.PluggableAuthService.PluggableAuthService import _SWALLOWABLE_PLUGIN_EXCEPTIONS from zope.component import adapter from zope.component import getGlobalSiteManager +import unittest -class BasicOpsTestCase(base.TestCase): - def afterSetUp(self): - self.loginAsPortalOwner() +class BasicOpsTestCase(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] + setRoles(self.portal, TEST_USER_ID, ['Manager']) self.acl_users = self.portal.acl_users def compareRoles(self, target, user, roles): @@ -32,11 +36,9 @@ def compareRoles(self, target, user, roles): user_roles = list(u.getRoles()) else: user_roles = list(u.getRolesInContext(target)) - actual_roles = filter(lambda x: x not in non_roles, user_roles) - actual_roles.sort() + actual_roles = list(filter(lambda x: x not in non_roles, user_roles)) wished_roles = list(roles) - wished_roles.sort() - if actual_roles == wished_roles: + if sorted(actual_roles) == sorted(wished_roles): return 1 raise RuntimeError("User %s: Whished roles: %s BUT current " "roles: %s" % (user, wished_roles, actual_roles)) diff --git a/src/Products/PlonePAS/tests/test_doctests.py b/src/Products/PlonePAS/tests/test_doctests.py index 9e2c509c..6cf3903e 100644 --- a/src/Products/PlonePAS/tests/test_doctests.py +++ b/src/Products/PlonePAS/tests/test_doctests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from plone.app.testing.bbb import PTC_FUNCTIONAL_TESTING +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_FUNCTIONAL_TESTING from plone.testing import layered import doctest import unittest @@ -14,7 +14,7 @@ def test_suite(): package='Products.PlonePAS.tests', optionflags=doctest.ELLIPSIS ), - layer=PTC_FUNCTIONAL_TESTING + layer=PRODUCTS_PLONEPAS_FUNCTIONAL_TESTING ) ) suite.addTest( diff --git a/src/Products/PlonePAS/tests/test_groupdatatool.py b/src/Products/PlonePAS/tests/test_groupdatatool.py index 343c6e7a..d69da2d9 100644 --- a/src/Products/PlonePAS/tests/test_groupdatatool.py +++ b/src/Products/PlonePAS/tests/test_groupdatatool.py @@ -2,9 +2,12 @@ from AccessControl import Permissions from AccessControl import Unauthorized from Acquisition import aq_parent -from Products.PlonePAS.tests import base from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_NAME +from plone.app.testing import logout +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING + +import unittest def sortTuple(t): @@ -13,9 +16,12 @@ def sortTuple(t): return tuple(l) -class TestGroupDataTool(base.TestCase): +class TestGroupDataTool(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.acl_users = self.portal.acl_users self.groups = self.portal.portal_groups self.groupdata = self.portal.portal_groupdata @@ -35,9 +41,12 @@ def testWrapGroup(self): 'GroupManager') -class TestGroupData(base.TestCase): +class TestGroupData(unittest.TestCase): - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.memberdata = self.portal.portal_memberdata self.acl_users = self.portal.acl_users @@ -73,13 +82,13 @@ def testGroupMembersAreWrapped(self): self.assertEqual(aq_parent(ms[0]).__class__.__name__, 'PluggableAuthService') def testAddMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) g = self.groups.getGroupById('foo') g.addMember(TEST_USER_ID) self.assertEqual(g.getGroupMembers()[0].getId(), TEST_USER_ID) def testRemoveMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) g = self.groups.getGroupById('foo') g.addMember(TEST_USER_ID) g.removeMember(TEST_USER_ID) @@ -121,6 +130,7 @@ def testGetRoles(self): ('Authenticated', 'Member')) def testGetRolesInContext(self): + self.folder = self.portal['folder'] g = self.groups.getGroupById('foo') self.acl_users.userSetGroups(TEST_USER_ID, groupnames=['foo']) user = self.acl_users.getUser(TEST_USER_NAME) @@ -145,19 +155,22 @@ def testHasRole(self): self.assertTrue(g.has_role('Member')) -class TestMethodProtection(base.TestCase): +class TestMethodProtection(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.groups = self.portal.portal_groups self.groups.addGroup('foo') self.groupdata = self.groups.getGroupById('foo') def testAnonAddMember(self): - self.logout() + logout() self.assertRaises(Unauthorized, self.groupdata.addMember, TEST_USER_ID) def testAnonRemoveMember(self): - self.logout() + logout() self.assertRaises(Unauthorized, self.groupdata.removeMember, TEST_USER_ID) @@ -169,10 +182,10 @@ def testMemberRemoveMember(self): TEST_USER_ID) def testManagerAddMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) self.groupdata.addMember(TEST_USER_ID) def testManagerRemoveMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) self.groupdata.addMember(TEST_USER_ID) self.groupdata.removeMember(TEST_USER_ID) diff --git a/src/Products/PlonePAS/tests/test_groupstool.py b/src/Products/PlonePAS/tests/test_groupstool.py index fce58aa7..da81fd0a 100644 --- a/src/Products/PlonePAS/tests/test_groupstool.py +++ b/src/Products/PlonePAS/tests/test_groupstool.py @@ -3,16 +3,20 @@ from AccessControl import Unauthorized from Acquisition import aq_base from Acquisition import aq_parent +from plone.app.testing import setRoles +from plone.app.testing import logout +from plone.app.testing import TEST_USER_ID +from plone.app.testing import TEST_USER_ID from Products.CMFCore.utils import getToolByName from Products.PlonePAS.plugins.group import PloneGroup -from Products.PlonePAS.tests import base +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING from Products.PlonePAS.tools.groupdata import GroupData -from Products.PluggableAuthService.interfaces.events import \ - IGroupDeletedEvent -from plone.app.testing import TEST_USER_ID +from Products.PluggableAuthService.interfaces.events import IGroupDeletedEvent from zope.component import adapter from zope.component import getGlobalSiteManager +import unittest + def sortTuple(t): l = list(t) @@ -20,15 +24,18 @@ def sortTuple(t): return tuple(l) -class GroupsToolTest(base.TestCase): +class TestGroupsTool(unittest.TestCase): - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.gt = getToolByName(self.portal, 'portal_groups') self.gd = getToolByName(self.portal, 'portal_groupdata') self.group_id = 'group1' # Create a new Group - self.loginAsPortalOwner() + setRoles(self.portal, TEST_USER_ID, ['Manager']) self.gt.addGroup( self.group_id, ['Reviewer'], @@ -89,20 +96,23 @@ def test_edit_group(self): self.assertTrue('Reviewers' in group.getGroups()) -class TestMethodProtection(base.TestCase): +class TestMethodProtection(unittest.TestCase): # GroupData has wrong security declarations - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.groups = self.portal.portal_groups self.groups.addGroup('foo') self.groupdata = self.groups.getGroupById('foo') def testAnonAddMember(self): - self.logout() + logout() self.assertRaises(Unauthorized, self.groupdata.addMember, TEST_USER_ID) def testAnonRemoveMember(self): - self.logout() + logout() self.assertRaises(Unauthorized, self.groupdata.removeMember, TEST_USER_ID) @@ -114,21 +124,25 @@ def testMemberRemoveMember(self): TEST_USER_ID) def testManagerAddMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) self.groupdata.addMember(TEST_USER_ID) def testManagerRemoveMember(self): - self.setPermissions([Permissions.manage_users]) + self.portal.manage_role('Member', [Permissions.manage_users]) self.groupdata.addMember(TEST_USER_ID) self.groupdata.removeMember(TEST_USER_ID) -class TestGroupsTool(base.TestCase): +class TestGroupsTool(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.acl_users = self.portal.acl_users self.groups = self.portal.portal_groups + self.folder = self.portal['folder'] if 'auto_group' in self.acl_users: self.acl_users.manage_delObjects(['auto_group']) @@ -262,7 +276,7 @@ def testGetGroupInfo(self): def testGetGroupInfoAsAnonymous(self): self.groups.addGroup('foo', title='Foo', description='Bar') - self.logout() + logout() info = self.groups.restrictedTraverse('getGroupInfo')('foo') self.assertEqual(info.get('title'), 'Foo') self.assertEqual(info.get('description'), 'Bar') diff --git a/src/Products/PlonePAS/tests/test_memberdatatool.py b/src/Products/PlonePAS/tests/test_memberdatatool.py index 440322aa..c4321242 100644 --- a/src/Products/PlonePAS/tests/test_memberdatatool.py +++ b/src/Products/PlonePAS/tests/test_memberdatatool.py @@ -2,13 +2,18 @@ from DateTime import DateTime from OFS.Image import Image from plone.app.testing import TEST_USER_ID as default_user -from Products.PlonePAS.tests import base from Products.PlonePAS.tests import dummy +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING +import unittest -class TestMemberDataTool(base.TestCase): - def afterSetUp(self): +class TestMemberDataTool(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.memberdata = self.portal.portal_memberdata self.membership = self.portal.portal_membership self.membership.memberareaCreationFlag = 0 diff --git a/src/Products/PlonePAS/tests/test_membershiptool.py b/src/Products/PlonePAS/tests/test_membershiptool.py index 5f1ab79f..be4b27c6 100644 --- a/src/Products/PlonePAS/tests/test_membershiptool.py +++ b/src/Products/PlonePAS/tests/test_membershiptool.py @@ -11,25 +11,34 @@ from plone.app.testing import TEST_USER_ID from plone.app.testing import TEST_USER_NAME from plone.app.testing import TEST_USER_PASSWORD +from plone.app.testing import login +from plone.app.testing import logout +from plone.app.testing import setRoles from Products.CMFCore.utils import getToolByName from Products.PlonePAS.browser.member import PASMemberView from Products.PlonePAS.interfaces.membership import IMembershipTool from Products.PlonePAS.plugins.ufactory import PloneUser -from Products.PlonePAS.tests import base +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING from Products.PlonePAS.tests import dummy from Products.PlonePAS.tools.memberdata import MemberData from Products.PlonePAS.tools.membership import MembershipTool from Products.PlonePAS.utils import getGroupsForPrincipal -import six -from six import StringIO +from six import BytesIO from zExceptions import BadRequest +from plone.app.testing import TEST_USER_ID + import os +import six +import unittest -class MembershipToolTest(base.TestCase): +class MembershipToolTest(unittest.TestCase): - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.mt = getToolByName(self.portal, 'portal_membership') self.md = getToolByName(self.portal, 'portal_memberdata') @@ -120,12 +129,16 @@ def test_id_clean(self): cleaned = cleanId(u'abc') self.assertEqual(cleaned, 'abc') self.assertTrue(isinstance(cleaned, str)) - self.assertFalse(isinstance(cleaned, six.text_type)) + if six.PY2: + self.assertFalse(isinstance(cleaned, six.text_type)) + +class MemberAreaTest(unittest.TestCase): -class MemberAreaTest(base.TestCase): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.mt = getToolByName(self.portal, 'portal_membership') self.md = getToolByName(self.portal, 'portal_memberdata') # Enable member-area creation @@ -133,7 +146,7 @@ def afterSetUp(self): # Those are all valid chars in Zope. self.mid = "Member #1 - Houston, TX. ($100)" self.pas = self.portal.acl_users - self.loginAsPortalOwner() + setRoles(self.portal, TEST_USER_ID, ['Manager']) def test_funky_member_ids_1(self): mid = self.mid @@ -167,11 +180,15 @@ def test_memberareaCreationFlag_respected(self): self.assertFalse('bar' in self.portal.Members) -class TestMembershipTool(base.TestCase): +class TestMembershipTool(unittest.TestCase): - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.groups = self.portal.portal_groups + self.folder = self.portal['folder'] def test_interface(self): from zope.interface.verify import verifyClass @@ -232,7 +249,7 @@ def testChangeOwnMemberPortraitWithEmailUsers(self): {'email': 'member2@host.com', 'title': 'Member #2'} ) - self.login(member_id) + login(self.portal, member_id) image = self.makeRealImage() safe_member_id = self.membership._getSafeMemberId(member_id) @@ -247,6 +264,7 @@ def testChangeOwnMemberPortraitWithEmailUsers(self): def testCannotChangeOtherMemberPortrait(self): # A normal member should not be able to change the portrait of # another member. + setRoles(self.portal, TEST_USER_ID, ['Member']) image = self.makeRealImage() self.membership.addMember('joe', 'secret', ['Member'], []) self.assertRaises(Unauthorized, self.membership.changeMemberPortrait, @@ -257,7 +275,7 @@ def testChangeMemberPortraitAsManager(self): # member. image = self.makeRealImage() self.membership.addMember('joe', 'secret', ['Member'], []) - self.setRoles(['Manager']) + setRoles(self.portal, TEST_USER_ID, ['Manager']) # This should not raise Unauthorized: self.membership.changeMemberPortrait(image, 'joe') self.assertEqual(self.membership.getPersonalPortrait('joe').getId(), @@ -282,9 +300,9 @@ def testCannotDeleteOtherPersonalPortrait(self): # another member. image = self.makeRealImage() self.membership.addMember('joe', 'secret', ['Member'], []) - self.setRoles(['Manager']) + setRoles(self.portal, TEST_USER_ID, ['Manager']) self.membership.changeMemberPortrait(image, 'joe') - self.setRoles(['Member']) + setRoles(self.portal, TEST_USER_ID, ['Member']) self.assertRaises(Unauthorized, self.membership.deletePersonalPortrait, 'joe') @@ -293,7 +311,7 @@ def testDeleteOtherPersonalPortraitAsManager(self): # member. image = self.makeRealImage() self.membership.addMember('joe', 'secret', ['Member'], []) - self.setRoles(['Manager']) + setRoles(self.portal, TEST_USER_ID, ['Manager']) self.membership.changeMemberPortrait(image, 'joe') self.membership.deletePersonalPortrait('joe') self.assertEqual( @@ -318,7 +336,7 @@ def testPortraitForNonStandardUserId(self): safe_id = self.membership._getSafeMemberId(user_id) self.assertEqual(safe_id, 'bob--jones-2Btest-40example.org') self.membership.addMember(user_id, 'secret', ['Member'], []) - self.login(user_id) + login(self.portal, user_id) # Should return the default portrait self.assertEqual( @@ -333,7 +351,7 @@ def testPortraitForNonStandardUserId(self): 'Image') # Other users should be able to see your portrait. - self.login(TEST_USER_NAME) + login(self.portal, TEST_USER_NAME) self.assertEqual( self.membership.getPersonalPortrait(user_id).getId(), safe_id) @@ -342,7 +360,7 @@ def testPortraitForNonStandardUserId(self): 'Image') # You can delete your own portrait. - self.login(user_id) + login(self.portal, user_id) self.membership.deletePersonalPortrait(user_id) self.assertEqual( self.membership.getPersonalPortrait(user_id).getId(), @@ -351,7 +369,7 @@ def testPortraitForNonStandardUserId(self): # Managers should be able to change the portrait of another # member and delete it. manager_image = self.makeRealImage() - self.loginAsPortalOwner() + setRoles(self.portal, TEST_USER_ID, ['Manager']) # This should not raise Unauthorized: self.membership.changeMemberPortrait(manager_image, user_id) self.assertEqual(self.membership.getPersonalPortrait(user_id).getId(), @@ -406,7 +424,7 @@ def testSetPassword(self): def testSetPasswordIfAnonymous(self): # Anonymous should not be able to change password - self.logout() + logout() try: self.membership.setPassword('geheim') except BadRequest: @@ -424,9 +442,9 @@ def testSetPasswordAndKeepGroups(self): groups = self.groups groups.addGroup(group2, None, [], []) group = groups.getGroupById(group2) - self.loginAsPortalOwner() + setRoles(self.portal, TEST_USER_ID, ['Manager']) group.addMember(TEST_USER_ID) - self.login(TEST_USER_NAME) # Back to normal + login(self.portal, TEST_USER_NAME) # Back to normal ugroups = self.portal.acl_users.getUserById(TEST_USER_ID).getGroups() self.membership.setPassword('geheim') t_groups = self.portal.acl_users.getUserById(TEST_USER_ID).getGroups() @@ -460,20 +478,20 @@ def testGetAuthenticatedMemberIsWrapped(self): 'PluggableAuthService') def testGetAuthenticatedMemberIfAnonymous(self): - self.logout() + logout() member = self.membership.getAuthenticatedMember() self.assertEqual(member.getUserName(), 'Anonymous User') def testAnonymousMemberIsNotWrapped(self): # Also see http://dev.plone.org/plone/ticket/1851 - self.logout() + logout() member = self.membership.getAuthenticatedMember() self.assertNotEqual(member.__class__.__name__, 'MemberData') self.assertEqual(member.__class__.__name__, 'SpecialUser') def testIsAnonymousUser(self): self.assertFalse(self.membership.isAnonymousUser()) - self.logout() + logout() self.assertTrue(self.membership.isAnonymousUser()) def testWrapUserWrapsBareUser(self): @@ -513,14 +531,14 @@ def testWrapUserWrapsAnonymous(self): def testGetCandidateLocalRoles(self): self.assertEqual(self.membership.getCandidateLocalRoles(self.folder), ('Owner',)) - self.setRoles(['Member', 'Reviewer']) + setRoles(self.portal, TEST_USER_ID, ['Member', 'Reviewer']) self.assertEqual(self.membership.getCandidateLocalRoles(self.folder), ('Owner', 'Reviewer')) def testSetLocalRoles(self): self.assertTrue( 'Owner' in self.folder.get_local_roles_for_userid(TEST_USER_ID)) - self.setRoles(['Member', 'Reviewer']) + setRoles(self.portal, TEST_USER_ID, ['Member', 'Reviewer']) self.membership.setLocalRoles(self.folder, [TEST_USER_ID, 'user2'], 'Reviewer') self.assertEqual(self.folder.get_local_roles_for_userid(TEST_USER_ID), @@ -529,7 +547,7 @@ def testSetLocalRoles(self): ('Reviewer',)) def testDeleteLocalRoles(self): - self.setRoles(['Member', 'Reviewer']) + setRoles(self.portal, TEST_USER_ID, ['Member', 'Reviewer']) self.membership.setLocalRoles(self.folder, ['user2'], 'Reviewer') self.assertEqual(self.folder.get_local_roles_for_userid('user2'), ('Reviewer',)) @@ -630,7 +648,7 @@ def testGetCandidateLocalRolesForAssigned(self): def test_bug4333_delete_user_remove_memberdata(self): # delete user should delete portal_memberdata memberdata = self.portal.portal_memberdata - self.setRoles(['Manager']) + setRoles(self.portal, TEST_USER_ID, ['Manager']) self.addMember('barney', 'Barney Rubble', 'barney@bedrock.com', ['Member'], '2002-01-01') barney = self.membership.getMemberById('barney') @@ -655,7 +673,7 @@ def test_bug4333_delete_user_remove_memberdata(self): def testBogusMemberPortrait(self): # Should change the portrait image - bad_file = dummy.File(data='
This is a lie!!!
', + bad_file = dummy.File(data=b'
This is a lie!!!
', headers={'content_type': 'image/jpeg'}) self.assertRaises(IOError, self.membership.changeMemberPortrait, bad_file, TEST_USER_ID) @@ -668,24 +686,27 @@ def testGetBadMembers(self): bad_file = Image( id=TEST_USER_ID, title='', - file=StringIO('
This is a lie!!!
') + file=BytesIO(b'
This is a lie!!!
') ) # Manually set a bad image using private methods self.portal.portal_memberdata._setPortrait(bad_file, TEST_USER_ID) self.assertEqual(self.membership.getBadMembers(), [TEST_USER_ID]) # Try an empty image - empty_file = Image(id=TEST_USER_ID, title='', file=StringIO('')) + empty_file = Image(id=TEST_USER_ID, title='', file=BytesIO(b'')) self.portal.portal_memberdata._setPortrait(empty_file, TEST_USER_ID) - self.assertEqual(self.membership.getBadMembers(), []) + self.assertEqual(self.membership.getBadMembers(), [TEST_USER_ID]) # And a good image self.membership.changeMemberPortrait(self.makeRealImage(), TEST_USER_ID) self.assertEqual(self.membership.getBadMembers(), []) -class TestCreateMemberarea(base.TestCase): +class TestCreateMemberarea(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.membership.addMember('user2', 'secret', ['Member'], []) @@ -702,7 +723,7 @@ def testCreateMemberarea(self): def testCreatMemberareaUsesCurrentUser(self): if self.membership.memberareaCreationFlag is True: # Should create a memberarea for user2 - self.login('user2') + login(self.portal, 'user2') self.membership.createMemberarea() memberfolder = self.membership.getHomeFolder('user2') self.assertTrue( @@ -765,9 +786,12 @@ def testCreateMemberareaWhenDisabled(self): ) -class TestMemberareaSetup(base.TestCase): +class TestMemberareaSetup(unittest.TestCase): - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.membership.addMember('user2', 'secret', ['Member'], []) self.membership.createMemberarea('user2') @@ -806,9 +830,12 @@ def testHomePageNotExists(self): self.assertFalse('index_html' in self.home) -class TestSearchForMembers(base.TestCase): +class TestSearchForMembers(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.memberdata = self.portal.portal_memberdata self.membership = self.portal.portal_membership # Don't let default_user disturb results @@ -885,10 +912,13 @@ def testSearchByRequestObj(self): len(search(REQUEST=dict(name='jürgen'))), 1) -class TestDefaultUserAndPasswordNotChanged(base.TestCase): +class TestDefaultUserAndPasswordNotChanged(unittest.TestCase): # A test for a silly transaction/persistency bug in PlonePAS - def afterSetUp(self): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership def testDefaultUserAndPasswordUnchanged(self): @@ -900,10 +930,12 @@ def testDefaultUserAndPasswordUnchanged(self): self.assertFalse(self.membership.testCurrentPassword('geheim')) -class TestMethodProtection(base.TestCase): +class TestMethodProtection(unittest.TestCase): # MembershipTool is missing security declarations # http://dev.plone.org/plone/ticket/5432 + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + _unprotected = ( 'changeMemberPortrait', 'deletePersonalPortrait', @@ -911,29 +943,33 @@ class TestMethodProtection(base.TestCase): 'searchForMembers', ) - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership def assertUnprotected(self, object, method): - self.logout() + logout() object.restrictedTraverse(method) def assertProtected(self, object, method): - self.logout() + logout() self.assertRaises(Unauthorized, object.restrictedTraverse, method) for method in _unprotected: - exec "def testUnprotected_%s(self):" \ + exec ("def testUnprotected_%s(self):" \ " self.assertProtected(self.membership, '%s')" \ - % (method, method) + % (method, method)) + + exec ("def testMemberAccessible_%s(self):" \ + " self.membership.restrictedTraverse('%s')" % (method, method)) - exec "def testMemberAccessible_%s(self):" \ - " self.membership.restrictedTraverse('%s')" % (method, method) +class TestMemberInfoView(unittest.TestCase): -class TestMemberInfoView(base.TestCase): + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.membership = self.portal.portal_membership self.view = PASMemberView(self.portal, self.portal.REQUEST) @@ -967,7 +1003,7 @@ def testGetMemberInfoViewForNonMember(self): self.assertEqual(info['name_or_id'], 'charon') def testGetMemberInfoViewForAnonymous(self): - self.logout() + logout() self.assertTrue(self.membership.isAnonymousUser()) info = self.view.info() self.assertEqual(info['username'], 'Anonymous User') @@ -991,18 +1027,17 @@ def testGetMemberInfoViewHomePageBad(self): def testSetGroupsWithUserNameIdDifference(self): pas = self.portal['acl_users'] self.portal.portal_groups.addGroup('Editors', [], []) - self.setGroups(['Editors'], name=TEST_USER_ID) - self.login(TEST_USER_NAME) + self.portal.acl_users.userSetGroups(TEST_USER_ID, groupnames=['Editors']) + login(self.portal, TEST_USER_NAME) user = getSecurityManager().getUser() self.assertTrue( 'Editors' in getGroupsForPrincipal(user, pas['plugins']) ) - self.login() def testSetGroupsWithSameUserNameAndId(self): pas = self.portal['acl_users'] self.portal.portal_groups.addGroup('Editors', [], []) - self.setGroups(['Editors']) + self.portal.acl_users.userSetGroups(TEST_USER_ID, groupnames=['Editors']) user = getSecurityManager().getUser() self.assertTrue( 'Editors' in getGroupsForPrincipal(user, pas['plugins']) diff --git a/src/Products/PlonePAS/tests/test_properties.py b/src/Products/PlonePAS/tests/test_properties.py index 3dab964c..f6d5763c 100644 --- a/src/Products/PlonePAS/tests/test_properties.py +++ b/src/Products/PlonePAS/tests/test_properties.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.app.testing import login from Products.CMFCore.utils import getToolByName from Products.PlonePAS.plugins.property import ZODBMutablePropertyProvider -from Products.PlonePAS.tests import base from Products.PluggableAuthService.interfaces.plugins import \ IUserEnumerationPlugin +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING +import unittest -class PropertiesTest(base.TestCase): + +class PropertiesTest(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] def test_user_properties(self): mt = getToolByName(self.portal, 'portal_membership') @@ -37,7 +47,7 @@ def test_user_properties(self): member = mt.getMemberById('user1') # Set some member properties. Needs to be logged in as the user. - self.login('user1') + login(self.portal, 'user1') member.setMemberProperties({'age': 30, 'fullname': 'User #1 Is Cool', 'email': 'user1@anotherhost.qa'}) @@ -81,7 +91,7 @@ def test_group_properties(self): gt = getToolByName(self.portal, 'portal_groups') gd = getToolByName(self.portal, 'portal_groupdata') - self.loginAsPortalOwner() + setRoles(self.portal, TEST_USER_ID, ['Manager']) # Create a new Group gt.addGroup( @@ -185,9 +195,12 @@ def test_schema_for_mutable_property_provider(self): self.assertEqual(sheet.getProperty('addresses'), ('Here', 'There')) -class PropertySearchTest(base.TestCase): +class PropertySearchTest(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING - def afterSetUp(self): + def setUp(self): + self.portal = self.layer['portal'] self.mt = getToolByName(self.portal, 'portal_membership') self.md = getToolByName(self.portal, 'portal_memberdata') self.gt = getToolByName(self.portal, 'portal_groups') diff --git a/src/Products/PlonePAS/tests/test_role_plugin.py b/src/Products/PlonePAS/tests/test_role_plugin.py index 00ec703d..67710330 100644 --- a/src/Products/PlonePAS/tests/test_role_plugin.py +++ b/src/Products/PlonePAS/tests/test_role_plugin.py @@ -2,7 +2,6 @@ # $Id$ """Tests for Products.PlonePAS.plugins.role.GroupAwareRoleManager""" -from Products.PlonePAS.tests import base from Products.PluggableAuthService.PluggableAuthService import \ _PLUGIN_TYPE_INFO from Products.PluggableAuthService.interfaces.plugins import IGroupsPlugin @@ -13,6 +12,9 @@ makeRequestAndResponse from Products.PluginRegistry.PluginRegistry import PluginRegistry from zope.interface import implementer +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING + +import unittest @implementer(IGroupsPlugin) @@ -22,9 +24,11 @@ def getGroupsForPrincipal(self, principal, request=None): return principal._groups -class GroupAwareRoleManagerTests(base.TestCase): +class GroupAwareRoleManagerTests(unittest.TestCase): """Roles manager that takes care of goup of principal""" + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING + def _getTargetClass(self): from Products.PlonePAS.plugins.role import GroupAwareRoleManager diff --git a/src/Products/PlonePAS/tests/test_views.py b/src/Products/PlonePAS/tests/test_views.py index d0a7f60c..e4c62ca2 100644 --- a/src/Products/PlonePAS/tests/test_views.py +++ b/src/Products/PlonePAS/tests/test_views.py @@ -1,10 +1,15 @@ # -*- encoding: utf-8 -*- -from Products.PlonePAS.tests import base +from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_INTEGRATION_TESTING +import unittest -class TestPASSearchView(base.TestCase): + +class TestPASSearchView(unittest.TestCase): + + layer = PRODUCTS_PLONEPAS_INTEGRATION_TESTING def test_sort(self): + self.portal = self.layer['portal'] pas_search = self.portal.restrictedTraverse('@@pas_search') values = [{'title': u'Sociologie'}, {'title': u'Économie'}, {'title': u'anthropologie'}] diff --git a/src/Products/PlonePAS/tools/membership.py b/src/Products/PlonePAS/tools/membership.py index 58cd8266..cb743a75 100644 --- a/src/Products/PlonePAS/tools/membership.py +++ b/src/Products/PlonePAS/tools/membership.py @@ -32,7 +32,7 @@ from ZODB.POSException import ConflictError from plone.protect.interfaces import IDisableCSRFProtection import six -from six import StringIO +from six import BytesIO from zExceptions import BadRequest from zope import event from zope.component import getUtility @@ -762,7 +762,9 @@ def getBadMembers(self): counter = 1 for member_id in tuple(portraits.keys()): portrait = portraits[member_id] - portrait_data = str(portrait.data) + portrait_data = portrait.data + if six.PY2: + portrait_data = str(portrait.data) if portrait_data == '': continue if not HAS_PIL: @@ -772,7 +774,7 @@ def getBadMembers(self): ) try: import PIL - PIL.Image.open(StringIO(portrait_data)) + PIL.Image.open(BytesIO(portrait_data)) except ConflictError: pass except: From 1acfcb7ce9308f6c7ba044f664284b404246853e Mon Sep 17 00:00:00 2001 From: Philip Bauer Date: Fri, 22 Jun 2018 14:25:05 +0200 Subject: [PATCH 09/13] fix tests for py3 and py2 --- src/Products/PlonePAS/tests/cookie_auth.rst | 8 ++++---- src/Products/PlonePAS/tests/test_doctests.py | 16 ++++++++++++++-- .../PlonePAS/tests/test_membershiptool.py | 2 +- src/Products/PlonePAS/tools/membership.py | 2 +- src/Products/PlonePAS/utils.py | 8 ++++---- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Products/PlonePAS/tests/cookie_auth.rst b/src/Products/PlonePAS/tests/cookie_auth.rst index ae68c028..7405a560 100644 --- a/src/Products/PlonePAS/tests/cookie_auth.rst +++ b/src/Products/PlonePAS/tests/cookie_auth.rst @@ -53,13 +53,13 @@ Make sure cookie plugin is installed and activated. ... pass >>> for active, iface in actives: - ... print(iface,) + ... print(iface) ... for id, plugin in active: ... if id == 'credentials_cookie_auth': ... print(True) - <...IExtraction...> True - <...IChallenge...> True - <...ICredentialsUpdate...> <...ICredentialsReset...> + <...IExtraction...>...True + <...IChallenge...>...True + <...ICredentialsUpdate...>...<...ICredentialsReset...> User in parent folder --------------------- diff --git a/src/Products/PlonePAS/tests/test_doctests.py b/src/Products/PlonePAS/tests/test_doctests.py index 6cf3903e..73647bfd 100644 --- a/src/Products/PlonePAS/tests/test_doctests.py +++ b/src/Products/PlonePAS/tests/test_doctests.py @@ -1,10 +1,20 @@ # -*- coding: utf-8 -*- from Products.PlonePAS.testing import PRODUCTS_PLONEPAS_FUNCTIONAL_TESTING from plone.testing import layered + import doctest +import re +import six import unittest +class Py23DocChecker(doctest.OutputChecker): + def check_output(self, want, got, optionflags): + if six.PY2: + got = re.sub("IOError", "OSError", got) + return doctest.OutputChecker.check_output(self, want, got, optionflags) + + def test_suite(): suite = unittest.TestSuite() suite.addTest( @@ -12,7 +22,8 @@ def test_suite(): doctest.DocFileSuite( 'cookie_auth.rst', package='Products.PlonePAS.tests', - optionflags=doctest.ELLIPSIS + optionflags=doctest.ELLIPSIS, + checker=Py23DocChecker(), ), layer=PRODUCTS_PLONEPAS_FUNCTIONAL_TESTING ) @@ -20,7 +31,8 @@ def test_suite(): suite.addTest( doctest.DocTestSuite( 'Products.PlonePAS.utils', - optionflags=doctest.ELLIPSIS + optionflags=doctest.ELLIPSIS, + checker=Py23DocChecker(), ) ) return suite diff --git a/src/Products/PlonePAS/tests/test_membershiptool.py b/src/Products/PlonePAS/tests/test_membershiptool.py index be4b27c6..42d989c8 100644 --- a/src/Products/PlonePAS/tests/test_membershiptool.py +++ b/src/Products/PlonePAS/tests/test_membershiptool.py @@ -694,7 +694,7 @@ def testGetBadMembers(self): # Try an empty image empty_file = Image(id=TEST_USER_ID, title='', file=BytesIO(b'')) self.portal.portal_memberdata._setPortrait(empty_file, TEST_USER_ID) - self.assertEqual(self.membership.getBadMembers(), [TEST_USER_ID]) + self.assertEqual(self.membership.getBadMembers(), []) # And a good image self.membership.changeMemberPortrait(self.makeRealImage(), TEST_USER_ID) diff --git a/src/Products/PlonePAS/tools/membership.py b/src/Products/PlonePAS/tools/membership.py index cb743a75..7d6fd389 100644 --- a/src/Products/PlonePAS/tools/membership.py +++ b/src/Products/PlonePAS/tools/membership.py @@ -765,7 +765,7 @@ def getBadMembers(self): portrait_data = portrait.data if six.PY2: portrait_data = str(portrait.data) - if portrait_data == '': + if not portrait_data: continue if not HAS_PIL: raise RuntimeError( diff --git a/src/Products/PlonePAS/utils.py b/src/Products/PlonePAS/utils.py index 106ad12f..8544c0e6 100644 --- a/src/Products/PlonePAS/utils.py +++ b/src/Products/PlonePAS/utils.py @@ -69,11 +69,11 @@ def scale_image(image_file, max_size=None, default_format=None): >>> scale_image(invalid, (50, 50)) Traceback (most recent call last): ... - IOError: cannot identify image file... + OSError: cannot identify image file... >>> scale_image(sneaky, (50, 50)) Traceback (most recent call last): ... - IOError: cannot identify image file... + OSError: cannot identify image file... Now that that's out of the way we check on our real images to make sure the format and mode are preserved, that they are scaled, and that they @@ -112,13 +112,13 @@ def scale_image(image_file, max_size=None, default_format=None): width only unless told not to (we need to reset out files before trying again though:: - >>> orig_jpg.seek(0) + >>> _ = orig_jpg.seek(0) >>> new_jpg, mimetype = scale_image(orig_jpg, (70, 100)) >>> img = Image.open(new_jpg) >>> img.size (70, 70) - >>> orig_jpg.seek(0) + >>> _ = orig_jpg.seek(0) >>> new_jpg, mimetype = scale_image(orig_jpg, (70, 50)) >>> img = Image.open(new_jpg) >>> img.size From 75a67a20e383e1d42e43768202b2607e85e0e87f Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Tue, 26 Jun 2018 23:59:36 +0200 Subject: [PATCH 10/13] InitializeClass was moved to AccessControl.class_init --- src/Products/PlonePAS/plugins/autogroup.py | 2 +- src/Products/PlonePAS/plugins/cookie_handler.py | 2 +- src/Products/PlonePAS/plugins/crumbler.py | 2 +- src/Products/PlonePAS/plugins/group.py | 2 +- src/Products/PlonePAS/plugins/local_role.py | 2 +- src/Products/PlonePAS/plugins/passwordpolicy.py | 2 +- src/Products/PlonePAS/plugins/property.py | 2 +- src/Products/PlonePAS/plugins/role.py | 2 +- src/Products/PlonePAS/plugins/ufactory.py | 2 +- src/Products/PlonePAS/plugins/user.py | 2 +- src/Products/PlonePAS/tools/groupdata.py | 2 +- src/Products/PlonePAS/tools/groups.py | 2 +- src/Products/PlonePAS/tools/memberdata.py | 2 +- src/Products/PlonePAS/tools/membership.py | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Products/PlonePAS/plugins/autogroup.py b/src/Products/PlonePAS/plugins/autogroup.py index 8ec0ee3f..5783ef63 100644 --- a/src/Products/PlonePAS/plugins/autogroup.py +++ b/src/Products/PlonePAS/plugins/autogroup.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PlonePAS.interfaces.group import IGroupIntrospection from Products.PluggableAuthService.PropertiedUser import PropertiedUser diff --git a/src/Products/PlonePAS/plugins/cookie_handler.py b/src/Products/PlonePAS/plugins/cookie_handler.py index 1e0ec655..e3103c31 100644 --- a/src/Products/PlonePAS/plugins/cookie_handler.py +++ b/src/Products/PlonePAS/plugins/cookie_handler.py @@ -10,7 +10,7 @@ from AccessControl.SecurityManagement import getSecurityManager from Acquisition import aq_base from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from base64 import encodestring from Products.PluggableAuthService.interfaces.authservice import \ diff --git a/src/Products/PlonePAS/plugins/crumbler.py b/src/Products/PlonePAS/plugins/crumbler.py index 0061e1a3..1c940fc9 100644 --- a/src/Products/PlonePAS/plugins/crumbler.py +++ b/src/Products/PlonePAS/plugins/crumbler.py @@ -7,7 +7,7 @@ """ from AccessControl.SecurityInfo import ClassSecurityInfo from Acquisition import aq_base -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from OFS.Folder import Folder from Products.CMFCore.CookieCrumbler import manage_addCC diff --git a/src/Products/PlonePAS/plugins/group.py b/src/Products/PlonePAS/plugins/group.py index 2bc36bf6..bbbd21ed 100644 --- a/src/Products/PlonePAS/plugins/group.py +++ b/src/Products/PlonePAS/plugins/group.py @@ -5,7 +5,7 @@ """ from AccessControl import ClassSecurityInfo -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from BTrees.OOBTree import OOBTree from BTrees.OOBTree import OOSet diff --git a/src/Products/PlonePAS/plugins/local_role.py b/src/Products/PlonePAS/plugins/local_role.py index f09ef382..93e76b5d 100644 --- a/src/Products/PlonePAS/plugins/local_role.py +++ b/src/Products/PlonePAS/plugins/local_role.py @@ -9,7 +9,7 @@ from AccessControl import ClassSecurityInfo from Acquisition import aq_inner from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from Products.PlonePAS.interfaces.plugins import ILocalRolesPlugin from Products.PluggableAuthService.plugins.LocalRolePlugin \ diff --git a/src/Products/PlonePAS/plugins/passwordpolicy.py b/src/Products/PlonePAS/plugins/passwordpolicy.py index 0b3c29a8..f0e36f89 100644 --- a/src/Products/PlonePAS/plugins/passwordpolicy.py +++ b/src/Products/PlonePAS/plugins/passwordpolicy.py @@ -4,7 +4,7 @@ Mutable Property Provider """ from AccessControl import ClassSecurityInfo -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PluggableAuthService.interfaces.plugins import IValidationPlugin from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin diff --git a/src/Products/PlonePAS/plugins/property.py b/src/Products/PlonePAS/plugins/property.py index 799bd379..430a9dfa 100644 --- a/src/Products/PlonePAS/plugins/property.py +++ b/src/Products/PlonePAS/plugins/property.py @@ -3,7 +3,7 @@ Mutable Property Provider """ from AccessControl import ClassSecurityInfo -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from BTrees.OOBTree import OOBTree from Products.CMFCore.utils import getToolByName diff --git a/src/Products/PlonePAS/plugins/role.py b/src/Products/PlonePAS/plugins/role.py index 8a0b4442..9c836ae0 100644 --- a/src/Products/PlonePAS/plugins/role.py +++ b/src/Products/PlonePAS/plugins/role.py @@ -10,7 +10,7 @@ from Acquisition import aq_get from Acquisition import aq_inner from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from Products.PlonePAS.interfaces.capabilities import IAssignRoleCapability from Products.PlonePAS.utils import getGroupsForPrincipal diff --git a/src/Products/PlonePAS/plugins/ufactory.py b/src/Products/PlonePAS/plugins/ufactory.py index f0fe7c18..9be8b467 100644 --- a/src/Products/PlonePAS/plugins/ufactory.py +++ b/src/Products/PlonePAS/plugins/ufactory.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from AccessControl import ClassSecurityInfo from AccessControl.PermissionRole import _what_not_even_god_should_do -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from Products.PlonePAS.interfaces.plugins import ILocalRolesPlugin from Products.PlonePAS.interfaces.propertysheets import IMutablePropertySheet diff --git a/src/Products/PlonePAS/plugins/user.py b/src/Products/PlonePAS/plugins/user.py index 79ee8f0f..5a4d8ace 100644 --- a/src/Products/PlonePAS/plugins/user.py +++ b/src/Products/PlonePAS/plugins/user.py @@ -5,7 +5,7 @@ from AccessControl import AuthEncoding from AccessControl import ClassSecurityInfo from AccessControl.Permissions import manage_users as ManageUsers -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from Products.PlonePAS.interfaces.capabilities import IDeleteCapability from Products.PlonePAS.interfaces.capabilities import IPasswordSetCapability diff --git a/src/Products/PlonePAS/tools/groupdata.py b/src/Products/PlonePAS/tools/groupdata.py index cfe9eeb6..4da00f87 100644 --- a/src/Products/PlonePAS/tools/groupdata.py +++ b/src/Products/PlonePAS/tools/groupdata.py @@ -7,7 +7,7 @@ from Acquisition import aq_base from Acquisition import aq_inner from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from BTrees.OOBTree import OOBTree from OFS.PropertyManager import PropertyManager from OFS.SimpleItem import SimpleItem diff --git a/src/Products/PlonePAS/tools/groups.py b/src/Products/PlonePAS/tools/groups.py index 2e320c6c..837805af 100644 --- a/src/Products/PlonePAS/tools/groups.py +++ b/src/Products/PlonePAS/tools/groups.py @@ -5,7 +5,7 @@ from Acquisition import aq_base from Acquisition import aq_inner from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from OFS.SimpleItem import SimpleItem from Products.CMFCore.utils import UniqueObject from Products.CMFCore.utils import getToolByName diff --git a/src/Products/PlonePAS/tools/memberdata.py b/src/Products/PlonePAS/tools/memberdata.py index 36a9245e..68cfbb14 100644 --- a/src/Products/PlonePAS/tools/memberdata.py +++ b/src/Products/PlonePAS/tools/memberdata.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from AccessControl import ClassSecurityInfo from AccessControl.requestmethod import postonly -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2 from Products.CMFCore.interfaces import IMember from Products.CMFCore.MemberDataTool import _marker diff --git a/src/Products/PlonePAS/tools/membership.py b/src/Products/PlonePAS/tools/membership.py index 7d6fd389..4f5c2b09 100644 --- a/src/Products/PlonePAS/tools/membership.py +++ b/src/Products/PlonePAS/tools/membership.py @@ -7,7 +7,7 @@ from Acquisition import aq_get from Acquisition import aq_inner from Acquisition import aq_parent -from App.class_init import InitializeClass +from AccessControl.class_init import InitializeClass from App.special_dtml import DTMLFile from DateTime import DateTime from OFS.Image import Image From 7f0dca597812816c2f23f8558eaf4ed737cfc140 Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Wed, 27 Jun 2018 00:15:38 +0200 Subject: [PATCH 11/13] setDefaultRoles is deprecated. addPermission from AccessControl.Permission is used. --- src/Products/PlonePAS/permissions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Products/PlonePAS/permissions.py b/src/Products/PlonePAS/permissions.py index 126932a3..0ab494d1 100644 --- a/src/Products/PlonePAS/permissions.py +++ b/src/Products/PlonePAS/permissions.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -from Products.CMFCore.permissions import setDefaultRoles +from AccessControl.Permission import addPermission AddGroups = 'Add Groups' -setDefaultRoles(AddGroups, ('Manager',)) +addPermission(AddGroups, default_roles=('Manager',)) ManageGroups = 'Manage Groups' -setDefaultRoles(ManageGroups, ('Manager',)) +addPermission(ManageGroups, default_roles=('Manager',)) ViewGroups = 'View Groups' -setDefaultRoles(ViewGroups, ('Manager', 'Owner', 'Member')) +addPermission(ViewGroups, default_roles=('Manager', 'Owner', 'Member')) DeleteGroups = 'Delete Groups' -setDefaultRoles(DeleteGroups, ('Manager', )) +addPermission(DeleteGroups, default_roles=('Manager', )) SetGroupOwnership = 'Set Group Ownership' -setDefaultRoles(SetGroupOwnership, ('Manager', 'Owner')) +addPermission(SetGroupOwnership, default_roles=('Manager', 'Owner')) From bc212075995a9970d74cac64d64cfa364116742a Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Fri, 14 Sep 2018 13:13:16 +0200 Subject: [PATCH 12/13] note changes --- CHANGES.rst | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3e3653f6..f7f37333 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,11 +12,26 @@ Breaking changes: New features: -- Make imports work with python 3. Fix startup. +- Make it work in Python 3: + Make imports work. + Fix startup. + Fix setting the auth-cookie. + Fix assignment of MemberData-functions during startup. + User properties are text. + Fix scaling user profile. + Migrate all tests away from PloneTestCasei. + Fix other tests. [pbauer] Bug fixes: +- InitializeClass was moved to AccessControl.class_init + [jensens] + +- setDefaultRoles is deprecated. + addPermission from AccessControl.Permission is used. + [jensens] + - Removed ``Extensions/Install.py`` which had only backwards compatibility imports. [maurits] From 7c7ac6246c7bcb6ccfd5a9e9f0156ffccf0bea4b Mon Sep 17 00:00:00 2001 From: "Jens W. Klein" Date: Fri, 14 Sep 2018 13:18:39 +0200 Subject: [PATCH 13/13] update metadata --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 009c32f6..5114586f 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ 'setuptools', 'six', 'zope.deprecation', - 'Zope2 >=2.13.22', + 'Zope', ] setup( @@ -35,6 +35,8 @@ "License :: OSI Approved :: Zope Public License", "Programming Language :: Python", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", ], keywords='Zope CMF Plone PAS authentication', author='Kapil Thangavelu, Wichert Akkerman',