diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/zope/security/adapter.py | 53 | ||||
-rw-r--r-- | src/zope/security/checker.py | 123 |
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) |