summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2020-03-23 08:15:47 -0500
committerJason Madden <jamadden@gmail.com>2020-03-23 08:15:47 -0500
commit027ce52cc45d066ba25809473ba21beb2f8f1bf3 (patch)
tree47d82b9df69f716cfb911c7f119b69082f533d16 /src
parent4382cde773a27d7699c74ad0cda3a9268da9e302 (diff)
downloadzope-security-027ce52cc45d066ba25809473ba21beb2f8f1bf3.tar.gz
Ensure all objects have consistent interface resolution orders.
Fixes #71
Diffstat (limited to 'src')
-rw-r--r--src/zope/security/_compat.py23
-rw-r--r--src/zope/security/checker.py3
-rw-r--r--src/zope/security/zcml.py9
3 files changed, 29 insertions, 6 deletions
diff --git a/src/zope/security/_compat.py b/src/zope/security/_compat.py
index 84ac98d..07b3a02 100644
--- a/src/zope/security/_compat.py
+++ b/src/zope/security/_compat.py
@@ -39,4 +39,25 @@ else: # pragma: no cover
PYTHON2 = False
-_BLANK = u''
+class implementer_if_needed(object):
+ # Helper to make sure we don't redundantly implement interfaces
+ # already inherited. Doing so tends to produce problems with the
+ # C3 order. Even though here we could easily statically determine
+ # if we need the interface or not, this is used for clarity, to
+ # reduce the testing load, and to insulate against changes in
+ # super classes.
+ def __init__(self, *ifaces):
+ self._ifaces = ifaces
+
+ def __call__(self, cls):
+ from zope.interface import implementedBy
+ from zope.interface import implementer
+
+ ifaces_needed = []
+ implemented = implementedBy(cls)
+ ifaces_needed = [
+ iface
+ for iface in self._ifaces
+ if not implemented.isOrExtends(iface)
+ ]
+ return implementer(*ifaces_needed)(cls)
diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py
index 8fbf3ec..2dbc4e1 100644
--- a/src/zope/security/checker.py
+++ b/src/zope/security/checker.py
@@ -82,6 +82,7 @@ from zope.security._definitions import thread_local
from zope.security._compat import CLASS_TYPES
from zope.security._compat import PYTHON2
from zope.security._compat import PURE_PYTHON
+from zope.security._compat import implementer_if_needed
from zope.security.proxy import Proxy
from zope.security.proxy import getChecker
@@ -503,7 +504,7 @@ if _c_available:
_getChecker = _checkers.get
-@implementer(IChecker)
+@implementer_if_needed(IChecker)
class CombinedChecker(Checker):
"""A checker that combines two other checkers in a logical-or fashion.
diff --git a/src/zope/security/zcml.py b/src/zope/security/zcml.py
index 52bcf91..473c06e 100644
--- a/src/zope/security/zcml.py
+++ b/src/zope/security/zcml.py
@@ -18,21 +18,22 @@ __docformat__ = 'restructuredtext'
from zope.configuration.fields import GlobalObject
from zope.configuration.fields import MessageID
from zope.interface import Interface
-from zope.interface import implementer
from zope.schema import Id
from zope.schema.interfaces import IFromUnicode
+from zope.security._compat import implementer_if_needed
from zope.security.permission import checkPermission
from zope.security.management import setSecurityPolicy
from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public
-@implementer(IFromUnicode)
+
+@implementer_if_needed(IFromUnicode)
class Permission(Id):
r"""This field describes a permission.
"""
- def fromUnicode(self, u):
- u = super(Permission, self).fromUnicode(u)
+ def fromUnicode(self, value):
+ u = super(Permission, self).fromUnicode(value)
map = getattr(self.context, 'permission_mapping', {})
return map.get(u, u)