summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2017-09-14 10:11:02 -0500
committerJason Madden <jamadden@gmail.com>2017-09-14 10:11:02 -0500
commit34ffae2afa52de6d96ffdfda6409a654f37720e7 (patch)
tree6eb0c6ca73f36696c7daeee683f774f50bedc7f6 /src
parent27f1d8b4358d6651f28e239bbc23a7e38f9cd789 (diff)
downloadzope-security-34ffae2afa52de6d96ffdfda6409a654f37720e7.tar.gz
cross-refs and cleanups for checker and adapter.
Diffstat (limited to 'src')
-rw-r--r--src/zope/security/adapter.py53
-rw-r--r--src/zope/security/checker.py123
2 files changed, 128 insertions, 48 deletions
diff --git a/src/zope/security/adapter.py b/src/zope/security/adapter.py
index f92ac34..e6da570 100644
--- a/src/zope/security/adapter.py
+++ b/src/zope/security/adapter.py
@@ -19,12 +19,13 @@ from zope.security.proxy import removeSecurityProxy
from zope.location import ILocation, LocationProxy
def assertLocation(adapter, parent):
- """Assert locatable adapters.
+ """
+ Assert locatable adapters.
- This function asserts that the adapter get location-proxied if
- it doesn't provide ILocation itself. Further more the returned
- locatable adapter get its parent set if its __parent__ attribute
- is currently None.
+ This function asserts that the adapter get location-proxied if it
+ doesn't provide :class:`zope.location.interfaces.ILocation`
+ itself. Furthermore, the returned locatable adapter get its parent
+ set if its ``__parent__`` attribute is currently None.
"""
# handle none-locatable adapters (A)
if not ILocation.providedBy(adapter):
@@ -42,7 +43,8 @@ def assertLocation(adapter, parent):
class LocatingTrustedAdapterFactory(object):
- """Adapt an adapter factory to provide trusted and (locatable) adapters.
+ """
+ Adapt an adapter factory to provide trusted and (locatable) adapters.
Trusted adapters always adapt unproxied objects. If asked to
adapt any proxied objects, it will unproxy them and then
@@ -50,10 +52,12 @@ class LocatingTrustedAdapterFactory(object):
security-proxied before (N).
Further locating trusted adapters provide a location for protected
- adapters only (S). If such a protected adapter itself does not provide
- ILocation it is wrapped within a location proxy and it parent will
- be set. If the adapter does provide ILocation and it's __parent__ is None,
- we set the __parent__ to the adapter's context:
+ adapters only (S). If such a protected adapter itself does not
+ provide ILocation it is wrapped within a location proxy and it
+ parent will be set. If the adapter does provide
+ :class:`zope.location.interfaces.ILocation` and its
+ ``__parent__`` is None, we set the ``__parent__`` to the adapter's
+ context.
"""
def __init__(self, factory):
self.factory = factory
@@ -83,15 +87,17 @@ class LocatingTrustedAdapterFactory(object):
class TrustedAdapterFactory(LocatingTrustedAdapterFactory):
- """Adapt an adapter factory to provide trusted adapters.
+ """
+ Adapt an adapter factory to provide trusted adapters.
Trusted adapters always adapt unproxied objects. If asked to
adapt any proxied objects, it will unproxy them and then
security-proxy the resulting adapter unless the objects where not
security-proxied before.
- If the adapter does provide ILocation and it's __parent__ is None,
- we set the __parent__ to the adapter's context.
+ If the adapter does provide
+ :class:`zope.location.interfaces.ILocation` and its ``__parent__``
+ is None, we set the ``__parent__`` to the adapter's context.
"""
# do not location-proxy the adapter
@@ -100,17 +106,20 @@ class TrustedAdapterFactory(LocatingTrustedAdapterFactory):
class LocatingUntrustedAdapterFactory(object):
- """Adapt an adapter factory to provide locatable untrusted adapters
+ """
+ Adapt an adapter factory to provide locatable untrusted adapters
Untrusted adapters always adapt proxied objects. If any permission
- other than zope.Public is required, untrusted adapters need a location
- in order that the local authentication mechanism can be inovked
- correctly.
-
- If the adapter does not provide ILocation, we location proxy it and
- set the parent. If the adapter does provide ILocation and
- it's __parent__ is None, we set the __parent__ to the adapter's
- context only:
+ other than :const:`zope.Public
+ <zope.security.interfaces.PUBLIC_PERMISSION_NAME>` is required,
+ untrusted adapters need a location in order that the local
+ authentication mechanism can be inovked correctly.
+
+ If the adapter does not provide
+ :class:`zope.location.interfaces.ILocation`, we location proxy it
+ and set the parent. If the adapter does provide ``ILocation`` and
+ its ``__parent__`` is None, we set the ``__parent__`` to the
+ adapter's context only.
"""
def __init__(self, factory):
diff --git a/src/zope/security/checker.py b/src/zope/security/checker.py
index 7de50d5..7fa2c0c 100644
--- a/src/zope/security/checker.py
+++ b/src/zope/security/checker.py
@@ -11,17 +11,47 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Security Checkers
+"""
+Security Checkers.
+
+This module contains the primary implementations of
+:class:`zope.security.interfaces.IChecker` (:class:`Checker`,
+:class:`MultiChecker`, :func:`NamesChecker`) and
+:class:`zope.security.interfaces.IProxyFactory` (:func:`ProxyFactory`).
+
+It also defines helpers for permission checking (:func:`canAccess`,
+:func:`canWrite`) and getting checkers
+(:func:`getCheckerForInstancesOf`, :func:`selectChecker`).
-You can set the environment variable ZOPE_WATCH_CHECKERS to get additional
-security checker debugging output on the standard error.
+This module is accelerated with a C implementation on CPython by
+default. If the environment variable ``PURE_PYTHON`` is set (to any
+value) before this module is imported, the C extensions will be
+bypassed and the reference Python implementations will be used. This
+can be helpful for debugging and tracing.
-Setting ZOPE_WATCH_CHECKERS to 1 will display messages about unauthorized or
+Debugging Permissions Problems
+==============================
+
+You can set the environment variable ``ZOPE_WATCH_CHECKERS`` before
+this module is imported to get additional security checker debugging
+output on the standard error.
+
+Setting ``ZOPE_WATCH_CHECKERS`` to 1 will display messages about unauthorized or
forbidden attribute access. Setting it to a larger number will also display
messages about granted attribute access.
-Note that the ZOPE_WATCH_CHECKERS mechanism will eventually be
+Note that the ``ZOPE_WATCH_CHECKERS`` mechanism may eventually be
replaced with a more general security auditing mechanism.
+
+.. seealso:: :class:`CheckerLoggingMixin`, :class:`WatchingChecker`, :class:`WatchingCombinedChecker`
+
+API
+===
+
+.. py:data:: CheckerPublic
+
+ The special constant that indicates that no permission
+ checking needs to be done.
"""
import abc
import os
@@ -156,6 +186,13 @@ def canAccess(obj, name):
@implementer(INameBasedChecker)
class CheckerPy(object):
+ """
+ The Python reference implementation of
+ :class:`zope.security.interfaces.INameBasedChecker`.
+
+ Ordinarily there will be no reason to ever explicitly use this class;
+ instead use the class assigned to :class:`Checker`.
+ """
def __init__(self, get_permissions, set_permissions=None):
"""Create a checker
@@ -163,8 +200,8 @@ class CheckerPy(object):
A dictionary must be provided for computing permissions for
names. The dictionary get will be called with attribute names
and must return a permission id, None, or the special marker,
- CheckerPublic. If None is returned, then access to the name is
- forbidden. If CheckerPublic is returned, then access will be
+ :const:`CheckerPublic`. If None is returned, then access to the name is
+ forbidden. If :const:`CheckerPublic` is returned, then access will be
granted without checking a permission.
An optional setattr dictionary may be provided for checking
@@ -295,12 +332,14 @@ class Global(object):
return "%s(%s,%s)" % (self.__class__.__name__,
self.__name__, self.__module__)
-# Marker for public attributes
+
CheckerPublic = Global('CheckerPublic')
CP_HACK_XXX = CheckerPublic
# Now we wrap it in a security proxy so that it retains its
# identity when it needs to be security proxied.
+# XXX: This means that we can't directly document it with
+# sphinx because issubclass() will fail.
d = {}
CheckerPublic = Proxy(CheckerPublic, Checker(d)) # XXX uses CheckerPy
d['__reduce__'] = CheckerPublic
@@ -330,10 +369,15 @@ def NamesChecker(names=(), permission_id=CheckerPublic, **__kw__):
return Checker(data)
def InterfaceChecker(interface, permission_id=CheckerPublic, **__kw__):
+ """
+ Create a :func:`NamesChecker` for all the names defined in the *interface*
+ (a subclass of :class:`zope.interface.Interface`).
+ """
return NamesChecker(interface.names(all=True), permission_id, **__kw__)
def MultiChecker(specs):
- """Create a checker from a sequence of specifications
+ """
+ Create a checker from a sequence of specifications
A specification is:
@@ -346,7 +390,7 @@ def MultiChecker(specs):
All the names in the sequence of names or the interface are
protected by the permission.
- - A dictionoid (having an items method), with items that are
+ - A dictionary (having an items method), with items that are
name/permission-id pairs.
"""
data = {}
@@ -409,8 +453,8 @@ DEFINABLE_TYPES = CLASS_TYPES + (types.ModuleType,)
def defineChecker(type_, checker):
"""Define a checker for a given type of object
- The checker can be a Checker, or a function that, when called with
- an object, returns a Checker.
+ The checker can be a :class:`Checker`, or a function that, when called with
+ an object, returns a :class:`Checker`.
"""
if not isinstance(type_, DEFINABLE_TYPES):
raise TypeError(
@@ -463,16 +507,23 @@ class CombinedChecker(Checker):
The following table describes the result of a combined checker in detail.
- checker1 checker2 CombinedChecker(checker1, checker2)
- ------------------ ------------------ -----------------------------------
- ok anything ok (checker2 is never called)
- Unauthorized ok ok
- Unauthorized Unauthorized Unauthorized
- Unauthorized ForbiddenAttribute Unauthorized
- ForbiddenAttribute ok ok
- ForbiddenAttribute Unauthorized Unauthorized
- ForbiddenAttribute ForbiddenAttribute ForbiddenAttribute
- ------------------ ------------------ -----------------------------------
+ +--------------------+--------------------+-------------------------------------+
+ | checker1 | checker2 | CombinedChecker(checker1, checker2) |
+ +====================+====================+=====================================+
+ | ok | anything | ok (checker 2 never called) |
+ +--------------------+--------------------+-------------------------------------+
+ | Unathorized | ok | ok |
+ +--------------------+--------------------+-------------------------------------+
+ | Unauthorized | Unauthorized | Unauthorized |
+ +--------------------+--------------------+-------------------------------------+
+ | Unauthorized | ForbiddenAttribute | Unauthorized |
+ +--------------------+--------------------+-------------------------------------+
+ | ForbiddenAttribute | ok | ok |
+ +--------------------+--------------------+-------------------------------------+
+ | ForbiddenAttribute | Unauthorized | Unauthorized |
+ +--------------------+--------------------+-------------------------------------+
+ | ForbiddenAttribute | ForbiddenAttribute | ForbiddenAttribute |
+ +--------------------+--------------------+-------------------------------------+
"""
def __init__(self, checker1, checker2):
@@ -509,15 +560,18 @@ class CombinedChecker(Checker):
raise unauthorized_exception
class CheckerLoggingMixin(object):
- """Debugging mixin for checkers.
+ """
+ Debugging mixin for checkers.
Prints verbose debugging information about every performed check to
- sys.stderr.
+ :data:`sys.stderr`.
- If verbosity is set to 1, only displays Unauthorized and Forbidden messages.
- If verbosity is set to a larger number, displays all messages.
"""
+ #: If set to 1 (the default), only displays ``Unauthorized`` and
+ #: ``Forbidden`` messages. If verbosity is set to a larger number,
+ #: displays all messages. Normally this is controlled via the environment
+ #: variable ``ZOPE_WATCH_CHECKERS``.
verbosity = 1
_file = sys.stderr
@@ -586,8 +640,18 @@ class CheckerLoggingMixin(object):
# We have to be careful with the order of inheritance
# here. See https://github.com/zopefoundation/zope.security/issues/8
class WatchingChecker(CheckerLoggingMixin, Checker):
+ """
+ A checker that will perform verbose logging. This will be set
+ as the default when ``ZOPE_WATCH_CHECKERS`` is set when this
+ module is imported.
+ """
verbosity = WATCH_CHECKERS
class WatchingCombinedChecker(CombinedChecker, WatchingChecker):
+ """
+ A checker that will perform verbose logging. This will be set
+ as the default when ``ZOPE_WATCH_CHECKERS`` is set when this
+ module is imported.
+ """
verbosity = WATCH_CHECKERS
if WATCH_CHECKERS: # pragma: no cover
@@ -606,6 +670,13 @@ def _instanceChecker(inst):
return _checkers.get(inst.__class__, _defaultChecker)
def moduleChecker(module):
+ """
+ Return the :class:`zope.security.interfaces.IChecker` defined for the
+ *module*, if any.
+
+ .. seealso:: :func:`zope.security.metaconfigure.protectModule`
+ To define module protections.
+ """
return _checkers.get(module)