diff options
author | Brandon Rhodes <brandon@rhodesmill.org> | 2009-01-28 13:03:16 +0000 |
---|---|---|
committer | Brandon Rhodes <brandon@rhodesmill.org> | 2009-01-28 13:03:16 +0000 |
commit | f6ccc8e575f48e902a14e11a4defc15fa568ffd2 (patch) | |
tree | 0798a44888fd59873f442532a5ba652b8d9eb337 | |
parent | 514ffcb97b9311866d0f8cb227d770650e282573 (diff) | |
download | zope-security-f6ccc8e575f48e902a14e11a4defc15fa568ffd2.tar.gz |
Moved the "protectclass" module, a slender collection of three
commonly-used utility functions, into "zope.security" so that the Zope
modules that use it can stop depending on the much larger (and ZMI-
supporting) "zope.app.security" if they do not need to.
-rw-r--r-- | src/zope/security/protectclass.py | 89 | ||||
-rw-r--r-- | src/zope/security/tests/module.py | 20 | ||||
-rw-r--r-- | src/zope/security/tests/modulehookup.py | 32 | ||||
-rw-r--r-- | src/zope/security/tests/test_protectclass.py | 128 | ||||
-rw-r--r-- | src/zope/security/tests/test_protectsubclass.py | 59 |
5 files changed, 328 insertions, 0 deletions
diff --git a/src/zope/security/protectclass.py b/src/zope/security/protectclass.py new file mode 100644 index 0000000..667b049 --- /dev/null +++ b/src/zope/security/protectclass.py @@ -0,0 +1,89 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Make assertions about permissions needed to access class instances +attributes + +$Id$ +""" +from zope.security.checker import defineChecker, getCheckerForInstancesOf +from zope.security.checker import Checker, CheckerPublic + + +def protectName(class_, name, permission): + """Set a permission on a particular name.""" + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + if permission == 'zope.Public': + # Translate public permission to CheckerPublic + permission = CheckerPublic + + # We know a dictionary was used because we set it + protections = checker.get_permissions + protections[name] = permission + +def protectSetAttribute(class_, name, permission): + """Set a permission on a particular name.""" + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + if permission == 'zope.Public': + # Translate public permission to CheckerPublic + permission = CheckerPublic + + # We know a dictionary was used because we set it + # Note however, that if a checker was created manually + # and the caller used say NamesChecker or MultiChecker, + # then set_permissions may be None here as Checker + # defaults a missing set_permissions parameter to None. + # Jim says this doensn't happens with the C version of the + # checkers because they use a 'shared dummy dict'. + protections = checker.set_permissions + protections[name] = permission + +def protectLikeUnto(class_, like_unto): + """Use the protections from like_unto for class_""" + + unto_checker = getCheckerForInstancesOf(like_unto) + if unto_checker is None: + return + + # We know a dictionary was used because we set it + # Note however, that if a checker was created manually + # and the caller used say NamesChecker or MultiChecker, + # then set_permissions may be None here as Checker + # defaults a missing set_permissions parameter to None. + # Jim says this doensn't happens with the C version of the + # checkers because they use a 'shared dummy dict'. + unto_get_protections = unto_checker.get_permissions + unto_set_protections = unto_checker.set_permissions + + checker = getCheckerForInstancesOf(class_) + if checker is None: + checker = Checker({}, {}) + defineChecker(class_, checker) + + get_protections = checker.get_permissions + for name in unto_get_protections: + get_protections[name] = unto_get_protections[name] + + set_protections = checker.set_permissions + for name in unto_set_protections: + set_protections[name] = unto_set_protections[name] diff --git a/src/zope/security/tests/module.py b/src/zope/security/tests/module.py new file mode 100644 index 0000000..0e29bae --- /dev/null +++ b/src/zope/security/tests/module.py @@ -0,0 +1,20 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""This empty module is for containing objects used in the course of tests. + +(There is a problem with the way the unit tests interact with the modules +being tests, so the objects can't be expected to show up in place.) + +$Id$ +""" diff --git a/src/zope/security/tests/modulehookup.py b/src/zope/security/tests/modulehookup.py new file mode 100644 index 0000000..76aabb0 --- /dev/null +++ b/src/zope/security/tests/modulehookup.py @@ -0,0 +1,32 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Preliminaries to hookup a test suite with the external TestModule. + +This is necessary because the test framework interferes with seeing changes in +the running modules via the module namespace. This enables having some +subject classes, instances, permissions, etc, that don't live in the test +modules, themselves. + +$Id$ +""" +from zope.interface import Interface + +from zope.security.tests import module as TestModule + +class I(Interface): + def m1(): + pass + def m2(): + pass + diff --git a/src/zope/security/tests/test_protectclass.py b/src/zope/security/tests/test_protectclass.py new file mode 100644 index 0000000..5c4792f --- /dev/null +++ b/src/zope/security/tests/test_protectclass.py @@ -0,0 +1,128 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Test handler for 'protectClass' directive + +$Id$ +""" +import unittest +from zope.interface import implements +from zope.security.checker import selectChecker +from zope.security.permission import Permission +from zope import component +from zope.component.testing import PlacelessSetup + +from zope.security.interfaces import IPermission +from zope.security.protectclass import protectName, protectLikeUnto +from zope.security.protectclass import protectSetAttribute +from zope.security.tests.modulehookup import TestModule, I + +NOTSET = [] + +P1 = "extravagant" +P2 = "paltry" + +class Test(PlacelessSetup, unittest.TestCase): + + def setUp(self): + super(Test, self).setUp() + + component.provideUtility(Permission(P1), IPermission, P1) + component.provideUtility(Permission(P2), IPermission, P2) + + class B(object): + def m1(self): + return "m1" + def m2(self): + return "m2" + + class C(B): + implements(I) + def m3(self): + return "m3" + def m4(self): + return "m4" + + TestModule.test_base = B + TestModule.test_class = C + TestModule.test_instance = C() + self.assertState() + + def tearDown(self): + super(Test, self).tearDown() + TestModule.test_class = None + + def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.permission_id('m1'), (m1P or None)) + self.assertEqual(checker.permission_id('m2'), (m2P or None)) + self.assertEqual(checker.permission_id('m3'), (m3P or None)) + + def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) + self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) + self.assertEqual(checker.setattr_permission_id('m3'), (m3P or None)) + + # "testSimple*" exercises tags that do NOT have children. This mode + # inherently sets the instances as well as the class attributes. + + def testSimpleMethodsPlural(self): + protectName(TestModule.test_class, 'm1', P1) + protectName(TestModule.test_class, 'm3', P1) + self.assertState(m1P=P1, m3P=P1) + + def testLikeUntoOnly(self): + protectName(TestModule.test_base, 'm1', P1) + protectName(TestModule.test_base, 'm2', P1) + protectSetAttribute(TestModule.test_base, 'm1', P1) + protectSetAttribute(TestModule.test_base, 'm2', P1) + protectLikeUnto(TestModule.test_class, TestModule.test_base) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertState(m1P=P1, m2P=P1) + self.assertSetattrState(m1P=P1, m2P=P1) + + def assertSetattrState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): + "Verify that class, instance, and methods have expected permissions." + checker = selectChecker(TestModule.test_instance) + self.assertEqual(checker.setattr_permission_id('m1'), (m1P or None)) + self.assertEqual(checker.setattr_permission_id('m2'), (m2P or None)) + self.assertEqual(checker.setattr_permission_id("m3"), (m3P or None)) + + def testSetattr(self): + protectSetAttribute(TestModule.test_class, 'm1', P1) + protectSetAttribute(TestModule.test_class, 'm3', P1) + self.assertSetattrState(m1P=P1, m3P=P1) + + def testLikeUntoAsDefault(self): + protectName(TestModule.test_base, 'm1', P1) + protectName(TestModule.test_base, 'm2', P1) + protectSetAttribute(TestModule.test_base, 'm1', P1) + protectSetAttribute(TestModule.test_base, 'm2', P1) + protectLikeUnto(TestModule.test_class, TestModule.test_base) + protectName(TestModule.test_class, 'm2', P2) + protectName(TestModule.test_class, 'm3', P2) + protectSetAttribute(TestModule.test_class, 'm2', P2) + protectSetAttribute(TestModule.test_class, 'm3', P2) + # m1 and m2 are in the interface, so should be set, and m3 should not: + self.assertState(m1P=P1, m2P=P2, m3P=P2) + self.assertSetattrState(m1P=P1, m2P=P2, m3P=P2) + +def test_suite(): + loader=unittest.TestLoader() + return loader.loadTestsFromTestCase(Test) + +if __name__=='__main__': + unittest.TextTestRunner().run(test_suite()) diff --git a/src/zope/security/tests/test_protectsubclass.py b/src/zope/security/tests/test_protectsubclass.py new file mode 100644 index 0000000..6019249 --- /dev/null +++ b/src/zope/security/tests/test_protectsubclass.py @@ -0,0 +1,59 @@ +############################################################################## +# +# Copyright (c) 2001, 2002 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. +# +############################################################################## +"""Test proper protection of inherited methods + +$Id$ +""" +import unittest +from zope.security.checker import selectChecker +from zope.security.permission import Permission +from zope import component +from zope.component.testing import PlacelessSetup + +from zope.security.interfaces import IPermission +from zope.security.protectclass import protectName + +class Test(PlacelessSetup, unittest.TestCase): + + def testInherited(self): + + class B1(object): + def g(self): return 'B1.g' + + class B2(object): + def h(self): return 'B2.h' + + class S(B1, B2): + pass + + component.provideUtility(Permission('B1', ''), IPermission, 'B1') + component.provideUtility(Permission('S', ''), IPermission, 'S') + protectName(B1, 'g', 'B1') + protectName(S, 'g', 'S') + protectName(S, 'h', 'S') + + self.assertEqual(selectChecker(B1()).permission_id('g'), 'B1') + self.assertEqual(selectChecker(B2()).permission_id('h'), None) + self.assertEqual(selectChecker(S()).permission_id('g'), 'S') + self.assertEqual(selectChecker(S()).permission_id('h'), 'S') + + self.assertEqual(S().g(), 'B1.g') + self.assertEqual(S().h(), 'B2.h') + + +def test_suite(): + return unittest.makeSuite(Test) + +if __name__=='__main__': + unittest.main(defaultTest='test_suite') |