././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1158552 zope_security-7.3/0000755000076500000240000000000014677253221013162 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/.manylinux-install.sh0000755000076500000240000000372314672224647017301 0ustar00jensstaff#!/usr/bin/env bash # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code set -e -x # Running inside docker # Set a cache directory for pip. This was # mounted to be the same as it is outside docker so it # can be persisted. export XDG_CACHE_HOME="/cache" # XXX: This works for macOS, where everything bind-mounted # is seen as owned by root in the container. But when the host is Linux # the actual UIDs come through to the container, triggering # pip to disable the cache when it detects that the owner doesn't match. # The below is an attempt to fix that, taken from bcrypt. It seems to work on # Github Actions. if [ -n "$GITHUB_ACTIONS" ]; then echo Adjusting pip cache permissions mkdir -p $XDG_CACHE_HOME/pip chown -R $(whoami) $XDG_CACHE_HOME fi ls -ld /cache ls -ld /cache/pip # We need some libraries because we build wheels from scratch: yum -y install libffi-devel tox_env_map() { case $1 in *"cp38"*) echo 'py38';; *"cp39"*) echo 'py39';; *"cp310"*) echo 'py310';; *"cp311"*) echo 'py311';; *"cp312"*) echo 'py312';; *"cp313"*) echo 'py313';; *) echo 'py';; esac } # Compile wheels for PYBIN in /opt/python/*/bin; do if \ [[ "${PYBIN}" == *"cp38/"* ]] || \ [[ "${PYBIN}" == *"cp39/"* ]] || \ [[ "${PYBIN}" == *"cp310/"* ]] || \ [[ "${PYBIN}" == *"cp311/"* ]] || \ [[ "${PYBIN}" == *"cp312/"* ]] || \ [[ "${PYBIN}" == *"cp313/"* ]] ; then "${PYBIN}/pip" install -e /io/ "${PYBIN}/pip" wheel /io/ -w wheelhouse/ if [ `uname -m` == 'aarch64' ]; then cd /io/ ${PYBIN}/pip install tox TOXENV=$(tox_env_map "${PYBIN}") ${PYBIN}/tox -e ${TOXENV} cd .. fi rm -rf /io/build /io/*.egg-info fi done # Bundle external shared libraries into the wheels for whl in wheelhouse/zope.security*.whl; do auditwheel repair "$whl" -w /io/wheelhouse/ done ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/.manylinux.sh0000755000076500000240000000077514672224647015641 0ustar00jensstaff#!/usr/bin/env bash # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code set -e -x # Mount the current directory as /io # Mount the pip cache directory as /cache # `pip cache` requires pip 20.1 echo Setting up caching python --version python -mpip --version LCACHE="$(dirname `python -mpip cache dir`)" echo Sharing pip cache at $LCACHE $(ls -ld $LCACHE) docker run --rm -e GITHUB_ACTIONS -v "$(pwd)":/io -v "$LCACHE:/cache" $DOCKER_IMAGE $PRE_CMD /io/.manylinux-install.sh ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/.pre-commit-config.yaml0000644000076500000240000000132214672224647017446 0ustar00jensstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code minimum_pre_commit_version: '3.6' repos: - repo: https://github.com/pycqa/isort rev: "5.13.2" hooks: - id: isort - repo: https://github.com/hhatto/autopep8 rev: "v2.3.1" hooks: - id: autopep8 args: [--in-place, --aggressive, --aggressive] - repo: https://github.com/asottile/pyupgrade rev: v3.17.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/isidentical/teyit rev: 0.4.3 hooks: - id: teyit - repo: https://github.com/PyCQA/flake8 rev: "7.1.1" hooks: - id: flake8 additional_dependencies: - flake8-debugger == 4.1.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/.readthedocs.yaml0000644000076500000240000000123014672224647016412 0ustar00jensstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt - method: pip path: . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878239.0 zope_security-7.3/CHANGES.rst0000644000076500000240000005121114677252137014771 0ustar00jensstaff========= Changes ========= 7.3 (2024-10-02) ---------------- - Respect ``PURE_PYTHON`` environment variable set to ``0``. 7.2 (2024-09-17) ---------------- - Declare final support for Python 3.13. 7.1 (2024-08-16) ---------------- - Allow calling methods of type ```` by default. In particular, Python 3.12 refactored the ``io`` module in such a way as to slightly change the types of some methods, causing ``zope.security`` to no longer consider them callable. See `zope.file#13 `_. 7.0 (2024-05-30) ---------------- - Add preliminary support for Python 3.13 as of 3.13b1. - Drop support for Python 3.7. - Build windows wheels on GHA. 6.2 (2023-10-05) ---------------- - Make ``next()`` on C proxies call ``__next__`` rather than ``next`` (see PEP 3114), and drop support for the Python 2 ``next`` method name from pure-Python proxies. - Drop using ``setup_requires`` due to constant problems on GHA. - Add support for Python 3.12. 6.1 (2023-01-18) ================ - Remove more proxying code for names that no longer exist in Python 3. (`#92 `_) 6.0 (2023-01-16) ================ - Remove proxying code for names that no longer exist in Python 3. (`#92 `_) - Drop support for Python 2.7, 3.5, 3.6. 5.8 (2022-11-30) ================ - The extra ``untrustedpython`` now for Python 3, too, installs ``zope.untrustedpython``. 5.7 (2022-11-17) ================ - Release to rebuild full set of binary wheels. 5.6 (2022-11-16) ================ - Add support for building arm64 wheels on macOS. 5.5 (2022-11-06) ================ - Add support for final release of Python 3.11. 5.4 (2022-09-15) ================ - Disable unsafe math optimizations in C code. See `pull request 89 `_. 5.3 (2022-04-27) ================ - Allow calling bound methods of some built-in objects such as ``().__repr__`` and ``{}.__repr__`` by default. This worked on Python 2, but raised ``ForbiddenAttribute`` on Python 3. See `issue 75 `_. - Remove usage of ``unittest.makeSuite`` as it is deprecated in Python 3.11+. See `issue 83 `_. - Add support for Python 3.11 (as of 3.11.0a7). 5.2 (2022-03-10) ================ - Add support for Python 3.9 and 3.10. 5.1.1 (2020-03-23) ================== - Ensure all objects have consistent interface resolution orders (if all dependencies are up-to-date). See `issue 71 `_. 5.1.0 (2020-02-14) ================== - Let proxied interfaces be iterated on Python 3. This worked on Python 2, but raised ``ForbiddenAttribute`` an Python 3. See `zope.interface issue 141 `_. - Allow to use a common Sphinx version for Python 2 and 3. 5.0.0 (2019-11-11) ================== - Drop support for Python 3.4. - Add support for Python 3.8. - Properly declare dependency on zope.schema >= 4.2.0, introduced in zope.security 4.2.1. - Fix dict item view iteration on PyPy3 7.x. 4.3.1 (2019-01-03) ================== - Fix the decimal.Decimal checker, ``__truediv__`` was missing causing ``ForbiddenAttribute`` on a ``ProxyFactory(Decimal('1')) / 1`` operation 4.3.0 (2018-08-24) ================== - Add the interface ``ISystemPrincipal`` and make ``zope.security.management.system_user`` a regular object that implements this interface. This facilitates providing adapter registrations specifically for the ``system_user``. 4.2.3 (2018-08-09) ================== - Add support for Python 3.7. 4.2.2 (2018-01-11) ================== - Make the pure-Python proxy on Python 2 *not* check permissions for ``__unicode__`` just like the C implementation. Note that ``__str__`` is checked for both implementations on both Python 2 and 3, but if there is no ``__unicode__`` method defined, Python 2's automatic fallback to ``__str__`` is **not** checked when ``unicode`` is called. See `issue 10 `_. 4.2.1 (2017-11-30) ================== - Fix the default values for ``Permission`` fields ``title`` and ``description`` under Python 2. See `issue 48 `_. - Change the ``IPermission.id`` from ``Text`` (unicode) to a ``NativeStringLine``. This matches what ZCML creates and what is usually written in source code. 4.2.0 (2017-09-20) ================== - Fix the extremely rare potential for a crash when the C extensions are in use. See `issue 35 `_. - Fix `issue 7 `_: The pure-Python proxy didn't propagate ``TypeError`` from ``__repr__`` and ``__str__`` like the C implementation did. - Fix `issue 27 `_: iteration of ``zope.interface.providedBy()`` is now allowed by default on all versions of Python. Previously it only worked on Python 2. Note that ``providedBy`` returns unproxied objects for backwards compatibility. - Fix ``__length_hint__`` of proxied iterator objects. Previously it was ignored. - Drop support for Python 3.3. - Enable coveralls.io for coverage measurement and run doctests on all supported Python versions. - Fix `issue 9 `_: iteration of ``itertools.groupby`` objects is now allowed by default. In addition, iteration of all the custom iterator types defined in itertools are also allowed by default. - Simplify the internal ``_compat.py`` module now that we only run on newer Python versions. See `PR 32 `_. - Respect ``PURE_PYTHON`` at runtime. At build time, always try to build the C extensions on supported platforms, ignoring ``PURE_PYTHON``. See `issue 33 `_. - Fix watching checkers (``ZOPE_WATCH_CHECKERS=1``) in pure-Python mode. See `issue 8 `_. - Remove unused internal files from ``tests/``. - Remove ``zope.security.setup``. It was unused and did not work anyway. - Fix the pure-Python proxy on Python 2 letting ``__getslice__`` and ``__setslice__`` fall through to ``__getitem__`` or ``__setitem__``, respectively, if it raised an error. - Fix the pure-Python proxy calling a wrapped ``__getattr__`` or ``__getattribute__`` more than once in situations where the C implementation only called it one time (when it raised an AttributeError). - Reach 100% test coverage and maintain it via automated checks. 4.1.1 (2017-05-17) ================== - Fix `issue 23 `_: iteration of ``collections.OrderedDict`` and its various views is now allowed by default on all versions of Python. - As a further fix for issue 20, iteration of ``BTree`` itself is now allowed by default. 4.1.0 (2017-04-24) ================== - When testing ``PURE_PYTHON`` environments under ``tox``, avoid poisoning the user's global wheel cache. - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix `issue 20 `_: iteration of pure-Python ``BTrees.items()``, and also creating a list from ``BTrees.items()`` on Python 3. The same applies for ``keys()`` and ``values()``. 4.0.3 (2015-06-02) ================== - Fix iteration over security proxies in Python 3 using the pure-Python implementation. 4.0.2 (2015-06-02) ================== - Fix compatibility with ``zope.proxy`` 4.1.5 under PyPy. - Fix the very first call to ``removeSecurityProxy`` returning incorrect results if given a proxy under PyPy. 4.0.1 (2014-03-19) ================== - Add support for Python 3.4. 4.0.0 (2013-07-09) ================== - Update ``boostrap.py`` to version 2.2. - Bugfix: ZOPE_WATCH_CHECKERS=2 used to incorrectly suppress unauthorized/forbidden warnings. - Bugfix: ZOPE_WATCH_CHECKERS=1 used to miss most of the checks. 4.0.0b1 (2013-03-11) ==================== - Add support for PyPy. - Fix extension compilation on windows python 3.x 4.0.0a5 (2013-02-28) ==================== - Undo changes from 4.0.0a4. Instead, ``zope.untrustedpython`` is only included during Python 2 installs. 4.0.0a4 (2013-02-28) ==================== - Remove ``untrustedpython`` extra again, since we do not want to support ``zope.untrustedpython`` in ZTK 2.0. If BBB is really needed, we will create a 3.10.0 release. 4.0.0a3 (2013-02-15) ==================== - Fix test breakage in 4.0.0a2 due to deprecation strategy. 4.0.0a2 (2013-02-15) ==================== - Add back the ``untrustedpython`` extra: now pulls in ``zope.untrustedpython``. Restored deprecated backward-compatible imports for ``zope.security.untrustedpython.{builtins,interpreter,rcompile}`` (the extra and the imports are to be removed in version 4.1). 4.0.0a1 (2013-02-14) ==================== - Add support for Python 3.2 and 3.3. - Bring unit test coverage to 100%. - ``zope.security.untrustedpython`` moved to separate project: ``zope.untrustedpython`` - Convert use of ``assert`` in non-test code to apprpriate error types: - Non-dict's passed to ``Checker.__init__``. - Remove dprecattion of ``zope.security.adapter.TrustedAdapterFactory``. Although it has been marked as deprectaed since before Zope3 3.2, current versions of ``zope.compoent`` still rely on it. - Convert doctests to Sphinx documentation in 'docs'. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Make non-doctest tests fully independent of ``zope.testing``. Two modules, ``zope.security.checker`` and ``zope.security.management``, register cleanups with ``zope.testing`` IFF it is importable, but the tests no longer rely on it. - Enable building extensions without the ``svn:external`` of the ``zope.proxy`` headers into our ``include`` dir. - Bump ``zope.proxy`` dependency to ">= 4.1.0" to enable compilation on Py3k. - Replace deprecated ``zope.component.adapts`` usage with equivalent ``zope.component.adapter`` decorator. - Replace deprecated ``zope.interface.classProvides`` usage with equivalent ``zope.interface.provider`` decorator. - Replace deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Drop support for Python 2.4 and 2.5. - Add test convenience helper ``create_interaction`` and ``with interaction()``. 3.9.0 (2012-12-21) ================== - Pin ``zope.proxy >= 4.1.0`` - Ship with an included ``proxy.h`` header which is compatible with the 4.1.x version ov ``zope.proxy``. 3.8.5 (2012-12-21) ================== - Ship with an included ``proxy.h`` header which is compatible with the supported versions of ``zope.proxy``. 3.8.4 (2012-12-20) ================== - Pin ``zope.proxy >= 3.4.2, <4.1dev`` 3.8.3 (2011-09-24) ================== - Fix a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ================== - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ================== - Fix circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ================== - Add tests for our own ``configure.zcml``. - Add ``zcml`` extra dependencies; run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ================== - Prefer the standard library's ``doctest`` module to the one from ``zope.testing``. - Ensure ``PermissionIdsVocabulary`` directly provides ``IVocabularyFactory``, even though it might be unnecessary because ``IVocabularyFactory`` is provided in ZCML. - Remove the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ================== - Add compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ================== - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ================== - Make ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ================== - Ensure that simple zope.schema's ``VocabularyRegistry`` is used for ``PermissionVocabulary`` tests, because it's replaced implicitly in environments with ``zope.app.schema`` installed that makes that tests fail. - Fix a bug in ``DecoratedSecurityCheckerDescriptor`` which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ================== - Add ``zope.i18nmessageid.Message`` to non-proxied basic types. It's okay, because messages are immutable. Done previously by ``zope.app.security``. - Add ``__name__`` and ``__parent__`` attributes to list of available by default. Done previously by ``zope.app.security``. - Move ``PermissionsVocabulary`` and ``PermissionIdsVocabulary`` vocabularies to the ``zope.security.permission`` module from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like ``zope.View`` and ``zope.ManageContent``, as well as for the special ``zope.Public`` permission. They are placed in a separate ``permissions.zcml`` file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many ``zope.*`` packages. - Add ``addCheckerPublic`` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ================== - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise ``NoInteraction`` when ``zope.security.checkPermission`` is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and ``set`` and ``frozenset`` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ================== - Install decorated security checker support on ``LocationProxy`` from the outside. - Add support to bootstrap on Jython. - Move the ``protectclass`` module from ``zope.app.security`` to this package to reduce the number of dependencies on ``zope.app.security``. - Move the ```` directive implementation from ``zope.app.security`` to this package. - Move the ```` directive implementation from ``zope.app.component`` to this package. 3.5.2 (2008-07-27) ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ================== - Add ``frozenset``, ``set``, ``reversed``, and ``sorted`` to the list of safe builtins. 3.5.0 (2008-03-05) ================== - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) ==================== - Backport a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The ``Py_VISIT`` macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) ==================== - Add dependency on ``zope.thread`` to setup.py; without it, the tests were failing. - Backport a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ================== - Update meta-data. 3.4.0b5 (2007-08-15) ==================== - Fix a circular import in the C implementation. 3.4.0b4 (2007-08-14) ==================== - Improve ugly/brittle ID of ``zope.security.management.system_user``. 3.4.0b3 (2007-08-14) ==================== - Add support for Python 2.5. - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fix inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) ==================== - Bug: Remove stack extraction in ``newInteraction``. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ======= - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ================== - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.2.0 release. - Remove deprecated helper functions, ``proxy.trustedRemoveSecurityProxy`` and ``proxy.getProxiedObject``. - Make handling of ``management.{end,restore}Interaction`` more careful w.r.t. edge cases. - Make behavior of ``canWrite`` consistent with ``canAccess``: if ``canAccess`` does not raise ``ForbiddenAttribute``, then neither will ``canWrite``. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ================== - Add support for use of the new Python 2.4 datatypes, ``set`` and ``frozenset``, within checked code. - Make the C security proxy depend on the ``proxy.h`` header from the ``zope.proxy`` package. - XXX: the spelling of the ``#include`` is bizarre! It seems to be related to ``zpkg``-based builds, and should likely be revisited. For the moment, I have linked in the ``zope.proxy`` package into our own ``include`` directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Update checker to avoid re-proxying objects which have and explicit ``__Security_checker__`` assigned. - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.1.0 release. - Clarify contract of ``IChecker`` to indicate that its ``check*`` methods may raise only ``Forbidden`` or ``Unauthorized`` exceptions. - Add interfaces, (``IPrincipal``, ``IGroupAwarePrincipal``, ``IGroup``, and ``IPermission``) specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ================== - Corresponds to the version of the ``zope.security`` package shipped as part of the Zope X3.0.0 release. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/CONTRIBUTING.md0000644000076500000240000000143714672224647015425 0ustar00jensstaff # Contributing to zopefoundation projects The projects under the zopefoundation GitHub organization are open source and welcome contributions in different forms: * bug reports * code improvements and bug fixes * documentation improvements * pull request reviews For any changes in the repository besides trivial typo fixes you are required to sign the contributor agreement. See https://www.zope.dev/developer/becoming-a-committer.html for details. Please visit our [Developer Guidelines](https://www.zope.dev/developer/guidelines.html) if you'd like to contribute code changes and our [guidelines for reporting bugs](https://www.zope.dev/developer/reporting-bugs.html) if you want to file a bug report. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/COPYRIGHT.txt0000644000076500000240000000004014355021564015261 0ustar00jensstaffZope Foundation and Contributors././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/LICENSE.txt0000644000076500000240000000402614355021564015003 0ustar00jensstaffZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/MANIFEST.in0000644000076500000240000000071214672224647014725 0ustar00jensstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code include *.md include *.rst include *.txt include buildout.cfg include tox.ini include .pre-commit-config.yaml recursive-include docs *.py recursive-include docs *.rst recursive-include docs *.txt recursive-include docs Makefile recursive-include src *.py include *.yaml include *.sh recursive-include docs *.bat recursive-include include *.h recursive-include src *.zcml ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1157749 zope_security-7.3/PKG-INFO0000644000076500000240000006020114677253221014256 0ustar00jensstaffMetadata-Version: 2.1 Name: zope.security Version: 7.3 Summary: Zope Security Framework Home-page: http://github.com/zopefoundation/zope.security Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Project-URL: Documentation, https://zopesecurity.readthedocs.io Project-URL: Issue Tracker, https://github.com/zopefoundation/zope.security/issues Project-URL: Sources, https://github.com/zopefoundation/zope.security Keywords: zope security policy principal permission Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope :: 3 Requires-Python: >=3.8 License-File: LICENSE.txt Requires-Dist: setuptools Requires-Dist: zope.component Requires-Dist: zope.i18nmessageid Requires-Dist: zope.interface Requires-Dist: zope.location Requires-Dist: zope.proxy>=5.2 Requires-Dist: zope.schema>=4.2.0 Provides-Extra: pytz Requires-Dist: pytz; extra == "pytz" Provides-Extra: untrustedpython Requires-Dist: zope.untrustedpython>=5.0.dev0; extra == "untrustedpython" Provides-Extra: zcml Requires-Dist: zope.configuration; extra == "zcml" Provides-Extra: test Requires-Dist: BTrees; extra == "test" Requires-Dist: zope.component; extra == "test" Requires-Dist: zope.configuration; extra == "test" Requires-Dist: zope.location; extra == "test" Requires-Dist: zope.testing; extra == "test" Requires-Dist: zope.testrunner; extra == "test" Provides-Extra: docs Requires-Dist: Sphinx; extra == "docs" Requires-Dist: repoze.sphinx.autointerface; extra == "docs" Requires-Dist: sphinx_rtd_theme; extra == "docs" Requires-Dist: zope.configuration; extra == "docs" Requires-Dist: zope.testing; extra == "docs" =================== ``zope.security`` =================== .. image:: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.security/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.security?branch=master .. image:: https://readthedocs.org/projects/zopesecurity/badge/?version=latest :target: https://zopesecurity.readthedocs.io/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/zope.security.svg :target: https://pypi.python.org/pypi/zope.security/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.security.svg :target: https://pypi.org/project/zope.security/ :alt: Supported Python versions The Security framework provides a generic mechanism to implement security policies on Python objects. Documentation is available at https://zopesecurity.readthedocs.io/ ========= Changes ========= 7.3 (2024-10-02) ---------------- - Respect ``PURE_PYTHON`` environment variable set to ``0``. 7.2 (2024-09-17) ---------------- - Declare final support for Python 3.13. 7.1 (2024-08-16) ---------------- - Allow calling methods of type ```` by default. In particular, Python 3.12 refactored the ``io`` module in such a way as to slightly change the types of some methods, causing ``zope.security`` to no longer consider them callable. See `zope.file#13 `_. 7.0 (2024-05-30) ---------------- - Add preliminary support for Python 3.13 as of 3.13b1. - Drop support for Python 3.7. - Build windows wheels on GHA. 6.2 (2023-10-05) ---------------- - Make ``next()`` on C proxies call ``__next__`` rather than ``next`` (see PEP 3114), and drop support for the Python 2 ``next`` method name from pure-Python proxies. - Drop using ``setup_requires`` due to constant problems on GHA. - Add support for Python 3.12. 6.1 (2023-01-18) ================ - Remove more proxying code for names that no longer exist in Python 3. (`#92 `_) 6.0 (2023-01-16) ================ - Remove proxying code for names that no longer exist in Python 3. (`#92 `_) - Drop support for Python 2.7, 3.5, 3.6. 5.8 (2022-11-30) ================ - The extra ``untrustedpython`` now for Python 3, too, installs ``zope.untrustedpython``. 5.7 (2022-11-17) ================ - Release to rebuild full set of binary wheels. 5.6 (2022-11-16) ================ - Add support for building arm64 wheels on macOS. 5.5 (2022-11-06) ================ - Add support for final release of Python 3.11. 5.4 (2022-09-15) ================ - Disable unsafe math optimizations in C code. See `pull request 89 `_. 5.3 (2022-04-27) ================ - Allow calling bound methods of some built-in objects such as ``().__repr__`` and ``{}.__repr__`` by default. This worked on Python 2, but raised ``ForbiddenAttribute`` on Python 3. See `issue 75 `_. - Remove usage of ``unittest.makeSuite`` as it is deprecated in Python 3.11+. See `issue 83 `_. - Add support for Python 3.11 (as of 3.11.0a7). 5.2 (2022-03-10) ================ - Add support for Python 3.9 and 3.10. 5.1.1 (2020-03-23) ================== - Ensure all objects have consistent interface resolution orders (if all dependencies are up-to-date). See `issue 71 `_. 5.1.0 (2020-02-14) ================== - Let proxied interfaces be iterated on Python 3. This worked on Python 2, but raised ``ForbiddenAttribute`` an Python 3. See `zope.interface issue 141 `_. - Allow to use a common Sphinx version for Python 2 and 3. 5.0.0 (2019-11-11) ================== - Drop support for Python 3.4. - Add support for Python 3.8. - Properly declare dependency on zope.schema >= 4.2.0, introduced in zope.security 4.2.1. - Fix dict item view iteration on PyPy3 7.x. 4.3.1 (2019-01-03) ================== - Fix the decimal.Decimal checker, ``__truediv__`` was missing causing ``ForbiddenAttribute`` on a ``ProxyFactory(Decimal('1')) / 1`` operation 4.3.0 (2018-08-24) ================== - Add the interface ``ISystemPrincipal`` and make ``zope.security.management.system_user`` a regular object that implements this interface. This facilitates providing adapter registrations specifically for the ``system_user``. 4.2.3 (2018-08-09) ================== - Add support for Python 3.7. 4.2.2 (2018-01-11) ================== - Make the pure-Python proxy on Python 2 *not* check permissions for ``__unicode__`` just like the C implementation. Note that ``__str__`` is checked for both implementations on both Python 2 and 3, but if there is no ``__unicode__`` method defined, Python 2's automatic fallback to ``__str__`` is **not** checked when ``unicode`` is called. See `issue 10 `_. 4.2.1 (2017-11-30) ================== - Fix the default values for ``Permission`` fields ``title`` and ``description`` under Python 2. See `issue 48 `_. - Change the ``IPermission.id`` from ``Text`` (unicode) to a ``NativeStringLine``. This matches what ZCML creates and what is usually written in source code. 4.2.0 (2017-09-20) ================== - Fix the extremely rare potential for a crash when the C extensions are in use. See `issue 35 `_. - Fix `issue 7 `_: The pure-Python proxy didn't propagate ``TypeError`` from ``__repr__`` and ``__str__`` like the C implementation did. - Fix `issue 27 `_: iteration of ``zope.interface.providedBy()`` is now allowed by default on all versions of Python. Previously it only worked on Python 2. Note that ``providedBy`` returns unproxied objects for backwards compatibility. - Fix ``__length_hint__`` of proxied iterator objects. Previously it was ignored. - Drop support for Python 3.3. - Enable coveralls.io for coverage measurement and run doctests on all supported Python versions. - Fix `issue 9 `_: iteration of ``itertools.groupby`` objects is now allowed by default. In addition, iteration of all the custom iterator types defined in itertools are also allowed by default. - Simplify the internal ``_compat.py`` module now that we only run on newer Python versions. See `PR 32 `_. - Respect ``PURE_PYTHON`` at runtime. At build time, always try to build the C extensions on supported platforms, ignoring ``PURE_PYTHON``. See `issue 33 `_. - Fix watching checkers (``ZOPE_WATCH_CHECKERS=1``) in pure-Python mode. See `issue 8 `_. - Remove unused internal files from ``tests/``. - Remove ``zope.security.setup``. It was unused and did not work anyway. - Fix the pure-Python proxy on Python 2 letting ``__getslice__`` and ``__setslice__`` fall through to ``__getitem__`` or ``__setitem__``, respectively, if it raised an error. - Fix the pure-Python proxy calling a wrapped ``__getattr__`` or ``__getattribute__`` more than once in situations where the C implementation only called it one time (when it raised an AttributeError). - Reach 100% test coverage and maintain it via automated checks. 4.1.1 (2017-05-17) ================== - Fix `issue 23 `_: iteration of ``collections.OrderedDict`` and its various views is now allowed by default on all versions of Python. - As a further fix for issue 20, iteration of ``BTree`` itself is now allowed by default. 4.1.0 (2017-04-24) ================== - When testing ``PURE_PYTHON`` environments under ``tox``, avoid poisoning the user's global wheel cache. - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix `issue 20 `_: iteration of pure-Python ``BTrees.items()``, and also creating a list from ``BTrees.items()`` on Python 3. The same applies for ``keys()`` and ``values()``. 4.0.3 (2015-06-02) ================== - Fix iteration over security proxies in Python 3 using the pure-Python implementation. 4.0.2 (2015-06-02) ================== - Fix compatibility with ``zope.proxy`` 4.1.5 under PyPy. - Fix the very first call to ``removeSecurityProxy`` returning incorrect results if given a proxy under PyPy. 4.0.1 (2014-03-19) ================== - Add support for Python 3.4. 4.0.0 (2013-07-09) ================== - Update ``boostrap.py`` to version 2.2. - Bugfix: ZOPE_WATCH_CHECKERS=2 used to incorrectly suppress unauthorized/forbidden warnings. - Bugfix: ZOPE_WATCH_CHECKERS=1 used to miss most of the checks. 4.0.0b1 (2013-03-11) ==================== - Add support for PyPy. - Fix extension compilation on windows python 3.x 4.0.0a5 (2013-02-28) ==================== - Undo changes from 4.0.0a4. Instead, ``zope.untrustedpython`` is only included during Python 2 installs. 4.0.0a4 (2013-02-28) ==================== - Remove ``untrustedpython`` extra again, since we do not want to support ``zope.untrustedpython`` in ZTK 2.0. If BBB is really needed, we will create a 3.10.0 release. 4.0.0a3 (2013-02-15) ==================== - Fix test breakage in 4.0.0a2 due to deprecation strategy. 4.0.0a2 (2013-02-15) ==================== - Add back the ``untrustedpython`` extra: now pulls in ``zope.untrustedpython``. Restored deprecated backward-compatible imports for ``zope.security.untrustedpython.{builtins,interpreter,rcompile}`` (the extra and the imports are to be removed in version 4.1). 4.0.0a1 (2013-02-14) ==================== - Add support for Python 3.2 and 3.3. - Bring unit test coverage to 100%. - ``zope.security.untrustedpython`` moved to separate project: ``zope.untrustedpython`` - Convert use of ``assert`` in non-test code to apprpriate error types: - Non-dict's passed to ``Checker.__init__``. - Remove dprecattion of ``zope.security.adapter.TrustedAdapterFactory``. Although it has been marked as deprectaed since before Zope3 3.2, current versions of ``zope.compoent`` still rely on it. - Convert doctests to Sphinx documentation in 'docs'. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Make non-doctest tests fully independent of ``zope.testing``. Two modules, ``zope.security.checker`` and ``zope.security.management``, register cleanups with ``zope.testing`` IFF it is importable, but the tests no longer rely on it. - Enable building extensions without the ``svn:external`` of the ``zope.proxy`` headers into our ``include`` dir. - Bump ``zope.proxy`` dependency to ">= 4.1.0" to enable compilation on Py3k. - Replace deprecated ``zope.component.adapts`` usage with equivalent ``zope.component.adapter`` decorator. - Replace deprecated ``zope.interface.classProvides`` usage with equivalent ``zope.interface.provider`` decorator. - Replace deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Drop support for Python 2.4 and 2.5. - Add test convenience helper ``create_interaction`` and ``with interaction()``. 3.9.0 (2012-12-21) ================== - Pin ``zope.proxy >= 4.1.0`` - Ship with an included ``proxy.h`` header which is compatible with the 4.1.x version ov ``zope.proxy``. 3.8.5 (2012-12-21) ================== - Ship with an included ``proxy.h`` header which is compatible with the supported versions of ``zope.proxy``. 3.8.4 (2012-12-20) ================== - Pin ``zope.proxy >= 3.4.2, <4.1dev`` 3.8.3 (2011-09-24) ================== - Fix a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ================== - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ================== - Fix circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ================== - Add tests for our own ``configure.zcml``. - Add ``zcml`` extra dependencies; run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ================== - Prefer the standard library's ``doctest`` module to the one from ``zope.testing``. - Ensure ``PermissionIdsVocabulary`` directly provides ``IVocabularyFactory``, even though it might be unnecessary because ``IVocabularyFactory`` is provided in ZCML. - Remove the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ================== - Add compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ================== - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ================== - Make ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ================== - Ensure that simple zope.schema's ``VocabularyRegistry`` is used for ``PermissionVocabulary`` tests, because it's replaced implicitly in environments with ``zope.app.schema`` installed that makes that tests fail. - Fix a bug in ``DecoratedSecurityCheckerDescriptor`` which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ================== - Add ``zope.i18nmessageid.Message`` to non-proxied basic types. It's okay, because messages are immutable. Done previously by ``zope.app.security``. - Add ``__name__`` and ``__parent__`` attributes to list of available by default. Done previously by ``zope.app.security``. - Move ``PermissionsVocabulary`` and ``PermissionIdsVocabulary`` vocabularies to the ``zope.security.permission`` module from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like ``zope.View`` and ``zope.ManageContent``, as well as for the special ``zope.Public`` permission. They are placed in a separate ``permissions.zcml`` file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many ``zope.*`` packages. - Add ``addCheckerPublic`` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ================== - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise ``NoInteraction`` when ``zope.security.checkPermission`` is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and ``set`` and ``frozenset`` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ================== - Install decorated security checker support on ``LocationProxy`` from the outside. - Add support to bootstrap on Jython. - Move the ``protectclass`` module from ``zope.app.security`` to this package to reduce the number of dependencies on ``zope.app.security``. - Move the ```` directive implementation from ``zope.app.security`` to this package. - Move the ```` directive implementation from ``zope.app.component`` to this package. 3.5.2 (2008-07-27) ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ================== - Add ``frozenset``, ``set``, ``reversed``, and ``sorted`` to the list of safe builtins. 3.5.0 (2008-03-05) ================== - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) ==================== - Backport a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The ``Py_VISIT`` macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) ==================== - Add dependency on ``zope.thread`` to setup.py; without it, the tests were failing. - Backport a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ================== - Update meta-data. 3.4.0b5 (2007-08-15) ==================== - Fix a circular import in the C implementation. 3.4.0b4 (2007-08-14) ==================== - Improve ugly/brittle ID of ``zope.security.management.system_user``. 3.4.0b3 (2007-08-14) ==================== - Add support for Python 2.5. - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fix inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) ==================== - Bug: Remove stack extraction in ``newInteraction``. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ======= - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ================== - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.2.0 release. - Remove deprecated helper functions, ``proxy.trustedRemoveSecurityProxy`` and ``proxy.getProxiedObject``. - Make handling of ``management.{end,restore}Interaction`` more careful w.r.t. edge cases. - Make behavior of ``canWrite`` consistent with ``canAccess``: if ``canAccess`` does not raise ``ForbiddenAttribute``, then neither will ``canWrite``. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ================== - Add support for use of the new Python 2.4 datatypes, ``set`` and ``frozenset``, within checked code. - Make the C security proxy depend on the ``proxy.h`` header from the ``zope.proxy`` package. - XXX: the spelling of the ``#include`` is bizarre! It seems to be related to ``zpkg``-based builds, and should likely be revisited. For the moment, I have linked in the ``zope.proxy`` package into our own ``include`` directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Update checker to avoid re-proxying objects which have and explicit ``__Security_checker__`` assigned. - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.1.0 release. - Clarify contract of ``IChecker`` to indicate that its ``check*`` methods may raise only ``Forbidden`` or ``Unauthorized`` exceptions. - Add interfaces, (``IPrincipal``, ``IGroupAwarePrincipal``, ``IGroup``, and ``IPermission``) specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ================== - Corresponds to the version of the ``zope.security`` package shipped as part of the Zope X3.0.0 release. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1724678170.0 zope_security-7.3/README.rst0000644000076500000240000000212114663100032014627 0ustar00jensstaff=================== ``zope.security`` =================== .. image:: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.security/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.security?branch=master .. image:: https://readthedocs.org/projects/zopesecurity/badge/?version=latest :target: https://zopesecurity.readthedocs.io/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/zope.security.svg :target: https://pypi.python.org/pypi/zope.security/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.security.svg :target: https://pypi.org/project/zope.security/ :alt: Supported Python versions The Security framework provides a generic mechanism to implement security policies on Python objects. Documentation is available at https://zopesecurity.readthedocs.io/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673880185.0 zope_security-7.3/buildout.cfg0000644000076500000240000000104214361261171015460 0ustar00jensstaff[buildout] develop = . parts = test python coverage-test coverage-report [test] recipe = zc.recipe.testrunner eggs = zope.security [test,zcml,pytz] [python] recipe = zc.recipe.egg eggs = zope.security [test,zcml,pytz,untrustedpython] interpreter = py [coverage-test] recipe = zc.recipe.testrunner eggs = zope.security [test] defaults = ['--coverage', '../../coverage'] [coverage-report] recipe = zc.recipe.egg eggs = z3c.coverage scripts = coverage=coverage-report arguments = ('coverage', 'coverage/report') ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.097886 zope_security-7.3/docs/0000755000076500000240000000000014677253221014112 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/Makefile0000644000076500000240000001272414355021564015554 0ustar00jensstaff# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopesecurity.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopesecurity.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/zopesecurity" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopesecurity" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.092358 zope_security-7.3/docs/_build/0000755000076500000240000000000014677253221015350 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.0981123 zope_security-7.3/docs/_build/doctest/0000755000076500000240000000000014677253221017015 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556663.0 zope_security-7.3/docs/_build/doctest/output.txt0000644000076500000240000000272214672224767021131 0ustar00jensstaffResults of doctest builder run on 2024-09-17 09:04:23 ===================================================== Document: api/permission ------------------------ 1 items passed all tests: 34 tests in default 34 tests in 1 items. 34 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/proxy ------------------- 1 items passed all tests: 11 tests in default 11 tests in 1 items. 11 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/checker --------------------- 1 items passed all tests: 357 tests in default 357 tests in 1 items. 357 passed and 0 failed. Test passed. Document: api/zcml ------------------ 1 items passed all tests: 30 tests in default 30 tests in 1 items. 30 passed and 0 failed. Test passed. Document: api/decorator ----------------------- 1 items passed all tests: 53 tests in default 53 tests in 1 items. 53 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: proxy --------------- 1 items passed all tests: 36 tests in default 36 tests in 1 items. 36 passed and 0 failed. Test passed. Doctest summary =============== 521 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.0924082 zope_security-7.3/docs/_build/html/0000755000076500000240000000000014677253221016314 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.099435 zope_security-7.3/docs/_build/html/_sources/0000755000076500000240000000000014677253221020136 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.101785 zope_security-7.3/docs/_build/html/_sources/api/0000755000076500000240000000000014677253221020707 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/adapter.rst.txt0000644000076500000240000000015614355021564023675 0ustar00jensstaff======================= zope.security.adapter ======================= .. automodule:: zope.security.adapter ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/checker.rst.txt0000644000076500000240000005506114355021564023666 0ustar00jensstaff======================= zope.security.checker ======================= .. currentmodule:: zope.security.checker Module API Documentation ======================== .. automodule:: zope.security.checker API Doctests ============ Protections for Modules ----------------------- The :func:`moduleChecker` API can be used to determine whether a module has been protected: Initially, there's no checker defined for the module: .. doctest:: >>> from zope.security.checker import moduleChecker >>> from zope.security.tests import test_zcml_functest >>> moduleChecker(test_zcml_functest) is None True We can add a checker using :func:`zope.security.metaconfigure.protectModule` (although this is more commonly done using ZCML): .. doctest:: >>> from zope.component import provideUtility >>> from zope.security.metaconfigure import protectModule >>> from zope.security.permission import Permission >>> from zope.security.interfaces import IPermission >>> TEST_PERM = 'zope.security.metaconfigure.test' >>> perm = Permission(TEST_PERM, '') >>> provideUtility(perm, IPermission, TEST_PERM) >>> protectModule(test_zcml_functest, 'foo', TEST_PERM) Now, the checker should exist and have an access dictionary with the name and permission: .. doctest:: >>> def pprint(ob, width=70): ... from pprint import PrettyPrinter ... PrettyPrinter(width=width).pprint(ob) >>> checker = moduleChecker(test_zcml_functest) >>> cdict = checker.get_permissions >>> pprint(cdict) {'foo': 'zope.security.metaconfigure.test'} If we define additional names, they will be added to the dict: >>> protectModule(test_zcml_functest, 'bar', TEST_PERM) >>> protectModule(test_zcml_functest, 'baz', TEST_PERM) >>> pprint(cdict) {'bar': 'zope.security.metaconfigure.test', 'baz': 'zope.security.metaconfigure.test', 'foo': 'zope.security.metaconfigure.test'} The allow directive creates actions for each name defined directly, or via interface: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import Attribute >>> from zope.security.metaconfigure import allow >>> class I1(Interface): ... def x(): pass ... y = Attribute("Y") >>> class I2(I1): ... def a(): pass ... b = Attribute("B") >>> class AContext(object): ... def __init__(self): ... self.actions = [] ... ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is protectModule), ... 'args': args}) ... module='testmodule' >>> context = AContext() >>> allow(context, attributes=['foo', 'bar'], interface=[I1, I2]) >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] The provide directive creates actions for each name defined directly, or via interface: .. doctest:: >>> from zope.security.metaconfigure import require >>> class RContext(object): ... def __init__(self): ... self.actions = [] ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is protectModule), ... 'args': args}) ... module='testmodule' >>> context = RContext() >>> require(context, attributes=['foo', 'bar'], ... interface=[I1, I2], permission='p') >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] Protections for standard objects -------------------------------- .. doctest:: >>> from zope.security.checker import ProxyFactory >>> from zope.security.interfaces import ForbiddenAttribute >>> def check_forbidden_get(object, attr): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_setitem(object, item, value): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... object[item] = value ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_delitem(object, item): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... del object[item] ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_call(callable, *args): # ** ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return callable(*args) # ** ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] Rocks ~~~~~ Rocks are immutable, non-callable objects without interesting methods. They *don't* get proxied. .. doctest:: >>> type(ProxyFactory(object())) is object True >>> type(ProxyFactory(1)) is int True >>> type(ProxyFactory(1.0)) is float True >>> type(ProxyFactory(1j)) is complex True >>> type(ProxyFactory(None)) is type(None) True >>> type(ProxyFactory('xxx')) is str True >>> type(ProxyFactory(True)) is type(True) True Datetime-related instances are rocks, too: .. doctest:: >>> from datetime import timedelta, datetime, date, time, tzinfo >>> type(ProxyFactory( timedelta(1) )) is timedelta True >>> type(ProxyFactory( datetime(2000, 1, 1) )) is datetime True >>> type(ProxyFactory( date(2000, 1, 1) )) is date True >>> type(ProxyFactory( time() )) is time True >>> type(ProxyFactory( tzinfo() )) is tzinfo True >>> try: ... from pytz import UTC ... except ImportError: # pytz checker only if pytz is present. ... True ... else: ... type(ProxyFactory( UTC )) is type(UTC) True dicts ~~~~~ We can do everything we expect to be able to do with proxied dicts. .. doctest:: >>> d = ProxyFactory({'a': 1, 'b': 2}) >>> check_forbidden_get(d, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected 'ForbiddenAttribute: __setitem__' >>> d['a'] 1 >>> len(d) 2 >>> sorted(list(d)) ['a', 'b'] >>> d.get('a') 1 >>> 'a' in d True >>> c = d.copy() >>> check_forbidden_get(c, 'clear') 'ForbiddenAttribute: clear' >>> str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}") True >>> repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}") True >>> def sorted(x): ... x = list(x) ... x.sort() ... return x >>> sorted(d.keys()) ['a', 'b'] >>> sorted(d.values()) [1, 2] >>> sorted(d.items()) [('a', 1), ('b', 2)] Always available (note, that dicts in python-3.x are not orderable, so we are not checking that under python > 2): .. doctest:: >>> d != d False >>> bool(d) True >>> d.__class__ == dict True lists ~~~~~ We can do everything we expect to be able to do with proxied lists. .. doctest:: >>> l = ProxyFactory([1, 2]) >>> check_forbidden_delitem(l, 0) 'ForbiddenAttribute: __delitem__' >>> check_forbidden_setitem(l, 0, 3) 'ForbiddenAttribute: __setitem__' >>> l[0] 1 >>> l[0:1] [1] >>> check_forbidden_setitem(l[:1], 0, 2) 'ForbiddenAttribute: __setitem__' >>> len(l) 2 >>> tuple(l) (1, 2) >>> 1 in l True >>> l.index(2) 1 >>> l.count(2) 1 >>> str(l) '[1, 2]' >>> repr(l) '[1, 2]' >>> l + l [1, 2, 1, 2] Always available: .. doctest:: >>> l < l False >>> l > l False >>> l <= l True >>> l >= l True >>> l == l True >>> l != l False >>> bool(l) True >>> l.__class__ == list True tuples ~~~~~~ We can do everything we expect to be able to do with proxied tuples. .. doctest:: >>> from zope.security.checker import ProxyFactory >>> l = ProxyFactory((1, 2)) >>> l[0] 1 >>> l[0:1] (1,) >>> len(l) 2 >>> list(l) [1, 2] >>> 1 in l True >>> str(l) '(1, 2)' >>> repr(l) '(1, 2)' >>> l + l (1, 2, 1, 2) Always available: .. doctest:: >>> l < l False >>> l > l False >>> l <= l True >>> l >= l True >>> l == l True >>> l != l False >>> bool(l) True >>> l.__class__ == tuple True sets ~~~~ we can do everything we expect to be able to do with proxied sets. .. doctest:: >>> us = set((1, 2)) >>> s = ProxyFactory(us) >>> check_forbidden_get(s, 'add') # Verify that we are protected 'ForbiddenAttribute: add' >>> check_forbidden_get(s, 'remove') # Verify that we are protected 'ForbiddenAttribute: remove' >>> check_forbidden_get(s, 'discard') # Verify that we are protected 'ForbiddenAttribute: discard' >>> check_forbidden_get(s, 'pop') # Verify that we are protected 'ForbiddenAttribute: pop' >>> check_forbidden_get(s, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> len(s) 2 >>> 1 in s True >>> 1 not in s False >>> s.issubset(set((1,2,3))) True >>> s.issuperset(set((1,2,3))) False >>> c = s.union(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | set((2, 3)) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | ProxyFactory(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) | s >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.intersection(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & set((2, 3)) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & ProxyFactory(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) & s >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.difference(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - ProxyFactory(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - set((2, 3)) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) - s >>> sorted(c) [3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.symmetric_difference(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ set((2, 3)) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ ProxyFactory(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) ^ s >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.copy() >>> sorted(c) [1, 2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> str(s) == str(us) True >>> repr(s) == repr(us) True Always available: >>> s < us False >>> s > us False >>> s <= us True >>> s >= us True >>> s == us True >>> s != us False Note that you can't compare proxied sets with other proxied sets due to a limitation in the set comparison functions which won't work with any kind of proxy. .. doctest:: >>> bool(s) True >>> s.__class__ == set True frozensets ~~~~~~~~~~ we can do everything we expect to be able to do with proxied frozensets. .. doctest:: >>> def check_forbidden_get(object, attr): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> from zope.security.checker import ProxyFactory >>> from zope.security.interfaces import ForbiddenAttribute >>> us = frozenset((1, 2)) >>> s = ProxyFactory(us) >>> check_forbidden_get(s, 'add') # Verify that we are protected 'ForbiddenAttribute: add' >>> check_forbidden_get(s, 'remove') # Verify that we are protected 'ForbiddenAttribute: remove' >>> check_forbidden_get(s, 'discard') # Verify that we are protected 'ForbiddenAttribute: discard' >>> check_forbidden_get(s, 'pop') # Verify that we are protected 'ForbiddenAttribute: pop' >>> check_forbidden_get(s, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> len(s) 2 >>> 1 in s True >>> 1 not in s False >>> s.issubset(frozenset((1,2,3))) True >>> s.issuperset(frozenset((1,2,3))) False >>> c = s.union(frozenset((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | frozenset((2, 3)) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) | s >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.intersection(frozenset((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & frozenset((2, 3)) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & ProxyFactory(frozenset((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) & s >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.difference(frozenset((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - frozenset((2, 3)) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) - s >>> sorted(c) [3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.symmetric_difference(frozenset((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ frozenset((2, 3)) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) ^ s >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.copy() >>> sorted(c) [1, 2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> str(s) == str(us) True >>> repr(s) == repr(us) True Always available: >>> s < us False >>> s > us False >>> s <= us True >>> s >= us True >>> s == us True >>> s != us False Note that you can't compare proxied sets with other proxied sets due to a limitation in the frozenset comparison functions which won't work with any kind of proxy. .. doctest:: >>> bool(s) True >>> s.__class__ == frozenset True iterators ~~~~~~~~~ .. doctest:: >>> [a for a in ProxyFactory(iter([1, 2]))] [1, 2] >>> list(ProxyFactory(iter([1, 2]))) [1, 2] >>> list(ProxyFactory(iter((1, 2)))) [1, 2] >>> list(ProxyFactory(iter({1:1, 2:2}))) [1, 2] >>> def f(): ... for i in 1, 2: ... yield i ... >>> list(ProxyFactory(f())) [1, 2] >>> list(ProxyFactory(f)()) [1, 2] We can iterate over custom sequences, too: .. doctest:: >>> class X(object): ... d = 1, 2, 3 ... def __getitem__(self, i): ... return self.d[i] ... >>> x = X() We can iterate over sequences .. doctest:: >>> list(x) [1, 2, 3] >>> from zope.security.checker import NamesChecker >>> from zope.security.checker import ProxyFactory >>> c = NamesChecker(['__getitem__', '__len__']) >>> p = ProxyFactory(x, c) Even if they are proxied .. doctest:: >>> list(p) [1, 2, 3] But if the class has an iter: .. doctest:: >>> X.__iter__ = lambda self: iter(self.d) >>> list(x) [1, 2, 3] We shouldn't be able to iterate if we don't have an assertion: .. doctest:: >>> check_forbidden_call(list, p) 'ForbiddenAttribute: __iter__' New-style classes ~~~~~~~~~~~~~~~~~ .. doctest:: >>> from zope.security.checker import NamesChecker >>> class C(object): ... x = 1 ... y = 2 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = repr(C) >>> C.__module__ == __name__ True >>> len(C.__bases__) 1 >>> len(C.__mro__) 2 Always available: .. doctest:: >>> C == C True >>> C != C False >>> bool(C) True >>> C.__class__ == type True New-style Instances ~~~~~~~~~~~~~~~~~~~ .. doctest:: >>> class C(object): ... x = 1 ... y = 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> c.__class__ == C True Always available: .. doctest:: >>> c == c True >>> c != c False >>> bool(c) True >>> c.__class__ == C True Classic Classes ~~~~~~~~~~~~~~~ .. doctest:: >>> class C: ... x = 1 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = repr(C) >>> C.__module__ == __name__ True Note that these are really only classic on Python 2: >>> import sys >>> len(C.__bases__) == (0 if sys.version_info[0] == 2 else 1) True Always available: .. doctest:: >>> C == C True >>> C != C False >>> bool(C) True Classic Instances ~~~~~~~~~~~~~~~~~ .. doctest:: >>> class C(object): ... x, y = 1, 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> c.__class__ == C True Always available: .. doctest:: >>> c == c True >>> c != c False >>> bool(c) True >>> c.__class__ == C True Interfaces and declarations ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We can still use interfaces though proxies: .. doctest:: >>> from zope.interface import directlyProvides >>> from zope.interface import implementer >>> from zope.interface import provider >>> class I(Interface): ... pass >>> class IN(Interface): ... pass >>> class II(Interface): ... pass >>> @implementer(I) ... @provider(IN) ... class N(object): ... pass >>> n = N() >>> directlyProvides(n, II) >>> N = ProxyFactory(N) >>> n = ProxyFactory(n) >>> I.implementedBy(N) True >>> IN.providedBy(N) True >>> I.providedBy(n) True >>> II.providedBy(n) True Abstract Base Classes ~~~~~~~~~~~~~~~~~~~~~ We work with the ABCMeta meta class: .. doctest:: >>> import abc >>> MyABC = abc.ABCMeta('MyABC', (object,), {}) >>> class Foo(MyABC): pass >>> class Bar(Foo): pass >>> PBar = ProxyFactory(Bar) >>> [c.__name__ for c in PBar.__mro__] ['Bar', 'Foo', 'MyABC', 'object'] >>> check_forbidden_call(PBar) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(PBar, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(PBar) >>> s = repr(PBar) >>> PBar.__module__ == __name__ True >>> len(PBar.__bases__) 1 Always available: .. doctest:: >>> PBar == PBar True >>> PBar != PBar False >>> bool(PBar) True >>> PBar.__class__ == type False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/decorator.rst.txt0000644000076500000240000001175714355021564024250 0ustar00jensstaff========================= zope.security.decorator ========================= .. automodule:: zope.security.decorator API Examples ============ .. currentmodule:: zope.security.decorator .. testsetup:: from zope.component.testing import setUp setUp() To illustrate, we'll create a class that will be proxied: .. doctest:: >>> class Foo(object): ... a = 'a' and a class to proxy it that uses a decorated security checker: .. doctest:: >>> from zope.security.decorator import DecoratedSecurityCheckerDescriptor >>> from zope.proxy import ProxyBase >>> class Wrapper(ProxyBase): ... b = 'b' ... __Security_checker__ = DecoratedSecurityCheckerDescriptor() Next we'll create and register a checker for ``Foo``: .. doctest:: >>> from zope.security.checker import NamesChecker, defineChecker >>> fooChecker = NamesChecker(['a']) >>> defineChecker(Foo, fooChecker) along with a checker for ``Wrapper``: .. doctest:: >>> wrapperChecker = NamesChecker(['b']) >>> defineChecker(Wrapper, wrapperChecker) Using :func:`zope.security.checker.selectChecker`, we can confirm that a ``Foo`` object uses ``fooChecker``: .. doctest:: >>> from zope.security.checker import selectChecker >>> from zope.security.interfaces import ForbiddenAttribute >>> foo = Foo() >>> selectChecker(foo) is fooChecker True >>> fooChecker.check(foo, 'a') >>> try: ... fooChecker.check(foo, 'b') # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('b', <...Foo object ...>) and that a ``Wrapper`` object uses ``wrappeChecker``: .. doctest:: >>> wrapper = Wrapper(foo) >>> selectChecker(wrapper) is wrapperChecker True >>> wrapperChecker.check(wrapper, 'b') >>> try: ... wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('a', <...Foo object ...>) (Note that the object description says `Foo` because the object is a proxy and generally looks and acts like the object it's proxying.) When we access wrapper's ``__Security_checker__`` attribute, we invoke the decorated security checker descriptor. The decorator's job is to make sure checkers from both objects are used when available. In this case, because both objects have checkers, we get a combined checker: .. doctest:: >>> from zope.security.checker import CombinedChecker >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker will work even with security proxied objects. To illustrate, we'll proxify ``foo``: .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> secure_foo = ProxyFactory(foo) >>> secure_foo.a 'a' >>> try: ... secure_foo.b # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('b', <...Foo object ...>) when we wrap the secured ``foo``: .. doctest:: >>> wrapper = Wrapper(secure_foo) we still get a combined checker: .. doctest:: >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker has three other scenarios: - the wrapper has a checker but the proxied object doesn't - the proxied object has a checker but the wrapper doesn't - neither the wrapper nor the proxied object have checkers When the wrapper has a checker but the proxied object doesn't: .. doctest:: >>> from zope.security.checker import NoProxy, _checkers >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is wrapperChecker True the decorator uses only the wrapper checker: .. doctest:: >>> wrapper = Wrapper(foo) >>> wrapper.__Security_checker__ is wrapperChecker True When the proxied object has a checker but the wrapper doesn't: .. doctest:: >>> del _checkers[Wrapper] >>> defineChecker(Wrapper, NoProxy) >>> selectChecker(wrapper) is None True >>> del _checkers[Foo] >>> defineChecker(Foo, fooChecker) >>> selectChecker(foo) is fooChecker True the decorator uses only the proxied object checker: .. doctest:: >>> wrapper.__Security_checker__ is fooChecker True Finally, if neither the wrapper not the proxied have checkers: .. doctest:: >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is None True the decorator doesn't have a checker: .. doctest:: >>> wrapper.__Security_checker__ Traceback (most recent call last): ... AttributeError: 'Foo' has no attribute '__Security_checker__' ``__Security_checker__`` cannot be None, otherwise Checker.proxy blows up: >>> checker.proxy(wrapper) is wrapper True .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/interfaces.rst.txt0000644000076500000240000000017214355021564024376 0ustar00jensstaff========================== zope.security.interfaces ========================== .. automodule:: zope.security.interfaces ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/management.rst.txt0000644000076500000240000000017214355021564024367 0ustar00jensstaff========================== zope.security.management ========================== .. automodule:: zope.security.management ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/metaconfigure.rst.txt0000644000076500000240000000020614355021564025101 0ustar00jensstaff============================= zope.security.metaconfigure ============================= .. automodule:: zope.security.metaconfigure ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/permission.rst.txt0000644000076500000240000000640414355021564024447 0ustar00jensstaff========================== zope.security.permission ========================== .. currentmodule:: zope.security.permission .. testsetup:: from zope.component.testing import setUp setUp() .. autoclass:: zope.security.permission.Permission :members: :member-order: bysource .. autofunction:: zope.security.permission.checkPermission .. doctest:: >>> from zope.security.permission import checkPermission >>> from zope.component import provideUtility >>> from zope.security.interfaces import IPermission >>> from zope.security.permission import Permission >>> x = Permission('x') >>> provideUtility(x, IPermission, 'x') >>> checkPermission(None, 'x') >>> checkPermission(None, 'y') Traceback (most recent call last): ... ValueError: ('Undefined permission ID', 'y') The :data:`zope.security.checker.CheckerPublic` permission always exists: .. doctest:: >>> from zope.security.checker import CheckerPublic >>> checkPermission(None, CheckerPublic) .. autofunction:: zope.security.permission.allPermissions .. doctest:: >>> from zope.security.permission import allPermissions >>> from zope.component import provideUtility >>> y = Permission('y') >>> provideUtility(y, IPermission, 'y') >>> ids = sorted(allPermissions(None)) >>> for perm in sorted(allPermissions(None)): ... print(perm) x y .. autofunction:: zope.security.permission.PermissionsVocabulary To illustrate, we need to register the permissions vocabulary: .. doctest:: >>> from zope.security.permission import PermissionsVocabulary >>> from zope.schema.vocabulary import _clear >>> _clear() >>> from zope.schema.vocabulary import getVocabularyRegistry >>> registry = getVocabularyRegistry() >>> registry.register('Permissions', PermissionsVocabulary) We can now lookup the permissions we created earlier using the vocabulary: .. doctest:: >>> vocab = registry.get(None, 'Permissions') >>> vocab.getTermByToken('x').value is x True >>> vocab.getTermByToken('y').value is y True .. autofunction:: zope.security.permission.PermissionIdsVocabulary To illustrate, we need to register the permission IDs vocabulary: .. doctest:: >>> from zope.security.permission import PermissionIdsVocabulary >>> registry.register('Permission Ids', PermissionIdsVocabulary) We also need to register the special 'zope.Public' permission: >>> provideUtility(Permission('zope.Public'), IPermission, 'zope.Public') We can now lookup these permissions using the vocabulary: .. doctest:: >>> vocab = registry.get(None, 'Permission Ids') The non-public permissions 'x' and 'y' are string values: .. doctest:: >>> print(vocab.getTermByToken('x').value) x >>> print(vocab.getTermByToken('y').value) y However, the public permission value is :data:`~.CheckerPublic`: .. doctest:: >>> vocab.getTermByToken('zope.Public').value is CheckerPublic True and its title is shortened: .. doctest:: >>> print(vocab.getTermByToken('zope.Public').title) Public The terms are sorted by title except for the public permission, which is listed first: .. doctest:: >>> for term in vocab: ... print(term.title) Public x y .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/protectclass.rst.txt0000644000076500000240000000020214355021564024753 0ustar00jensstaff============================ zope.security.protectclass ============================ .. automodule:: zope.security.protectclass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/proxy.rst.txt0000644000076500000240000000175314355021564023442 0ustar00jensstaff===================== zope.security.proxy ===================== .. currentmodule:: zope.security.proxy An introduction to proxies and their uses can be found in :doc:`../proxy`. .. seealso:: :ref:`proxy-known-issues` .. testsetup:: from zope.component.testing import setUp setUp() .. autofunction:: getChecker .. autofunction:: removeSecurityProxy .. autofunction:: getTestProxyItems .. autofunction:: isinstance .. doctest:: >>> from zope.security.proxy import isinstance >>> class C1(object): ... pass >>> c = C1() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True >>> class C2(C1): ... pass >>> c = C2() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True .. autoclass:: Proxy .. autoclass:: ProxyPy .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/simplepolicies.rst.txt0000644000076500000240000000021214355021564025267 0ustar00jensstaff============================== zope.security.simplepolicies ============================== .. automodule:: zope.security.simplepolicies ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/testing.rst.txt0000644000076500000240000000015614355021564023732 0ustar00jensstaff======================= zope.security.testing ======================= .. automodule:: zope.security.testing ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/api/zcml.rst.txt0000644000076500000240000000432214355021564023221 0ustar00jensstaff==================== zope.security.zcml ==================== .. currentmodule:: zope.security.zcml Most users will not directly need to access the contents of this module; they will probably just :ref:`configure via ZCML `. API Reference ============= .. autoclass:: Permission Let's look at an example: .. doctest:: >>> from zope.security.zcml import Permission >>> class FauxContext(object): ... permission_mapping = {'zope.ManageCode':'zope.private'} ... _actions = [] ... def action(self, **kws): ... self._actions.append(kws) >>> context = FauxContext() >>> field = Permission().bind(context) Let's test the fromUnicode method: .. doctest:: >>> field.fromUnicode(u'zope.foo') 'zope.foo' >>> field.fromUnicode(u'zope.ManageCode') 'zope.private' Now let's see whether validation works alright .. doctest:: >>> field._validate('zope.ManageCode') >>> context._actions[0]['args'] (None, 'zope.foo') >>> from zope.schema.interfaces import InvalidId >>> try: ... field._validate('3 foo') ... except InvalidId as e: ... e InvalidId('3 foo') zope.Public is always valid >>> field._validate('zope.Public') .. autointerface:: ISecurityPolicyDirective .. autofunction:: securityPolicy .. autointerface:: IPermissionDirective .. autofunction:: permission .. autointerface:: IRedefinePermission .. autofunction:: redefinePermission .. _via-zcml: Configuring security via ZCML ============================= :mod:`zope.security` provides a ZCML file that configures some utilities and a couple of standard permissions: .. doctest:: >>> from zope.component import getGlobalSiteManager >>> from zope.configuration.xmlconfig import XMLConfig >>> from zope.component.testing import setUp >>> import zope.security >>> setUp() # clear global component registry >>> XMLConfig('permissions.zcml', zope.security)() >>> len(list(getGlobalSiteManager().registeredUtilities())) 7 Clear the current state: .. doctest:: >>> from zope.component.testing import setUp, tearDown >>> tearDown() >>> setUp() >>> XMLConfig('configure.zcml', zope.security)() >>> len(list(getGlobalSiteManager().registeredUtilities())) 10 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/changes.rst.txt0000644000076500000240000000003414355021564023107 0ustar00jensstaff.. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/example.rst.txt0000644000076500000240000001773514355021564023152 0ustar00jensstaff========= Example ========= As an example we take a look at constructing a multi-agent distributed system, and then adding a security layer using the Zope security model onto it. Scenario ======== Our agent simulation consists of autonomous agents that live in various agent homes/sandboxes and perform actions that access services available at their current home. Agents carry around authentication tokens which signify their level of access within any given home. Additionally agents attempt to migrate from home to home randomly. The agent simulation was constructed separately from any security aspects. Now we want to define and integrate a security model into the simulation. The full code for the simulation and the security model is available separately; we present only relevant code snippets here for illustration as we go through the implementation process. For the agent simulation we want to add a security model such that we group agents into two authentication groups, "norse legends", including the principals thor, odin, and loki, and "greek men", including prometheus, archimedes, and thucydides. We associate permissions with access to services and homes. We differentiate the homes such that certain authentication groups only have access to services or the home itself based on the local settings of the home in which they reside. We define the homes/sandboxes - origin - all agents start here, and have access to all services here. - valhalla - only agents in the authentication group 'norse legend' can reside here. - jail - all agents can come here, but only 'norse legend's can leave or access services. Process ======= Loosely we define a process for implementing this security model - mapping permissions onto actions - mapping authentication tokens onto permissions - implementing checkers and security policies that use our authentication tokens and permissions. - binding checkers to our simulation classes - inserting the hooks into the original simulation code to add proxy wrappers to automatically check security. - inserting hooks into the original simulation to register the agents as the active principal in an interaction. Defining a Permission Model =========================== We define the following permissions:: NotAllowed = 'Not Allowed' Public = Checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' and create a dictionary database mapping homes to authentication groups which are linked to associated permissions. Defining and Binding Checkers ============================= :class:`Checkers ` are the foundational unit for the security framework. They define what attributes can be accessed or set on a given instance. They can be used implicitly via Proxy objects, to guard all attribute access automatically or explicitly to check a given access for an operation. Checker construction expects two functions or dictionaries, one is used to map attribute names to permissions for attribute access and another to do the same for setting attributes. We use the following checker factory function:: def PermissionMapChecker(permissions_map={}, setattr_permission_func=NoSetAttr): res = {} for k,v in permissions_map.items(): for iv in v: res[iv]=k return checker.Checker(res.get, setattr_permission_func) time_service_checker = PermissionMapChecker( # permission : [methods] {'AccessTimeService':['getTime']} ) with the NoSetAttr function defined as a lambda which always return the permission ``NotAllowed``. To bind the checkers to the simulation classes we :func:`register ` our checkers with the security model's global checker registry:: import sandbox_simulation from zope.security.checker import defineChecker defineChecker(sandbox_simulation.TimeService, time_service_checker) Defining a Security Policy ========================== We implement our security policy such that it checks the current agent's authentication token against the given permission in the home of the object being accessed. (We extend a simple policy provided by the framework that will track participations for us):: from zope.security.simplepolicies import ParanoidSecurityPolicy @provider(ISecurityPolicy) @implementer(IInteraction) class SimulationSecurityPolicy(ParanoidSecurityPolicy): def checkPermission(self, permission, object): home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if not self.participations: return False for participation in self.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True Since an interaction can have more than one principal, we check that *all* of them are given the necessary permission. This is not really necessary since we only create interactions with a single active principal. There is some additional code present to allow for shortcuts in defining the permission database when defining permissions for all auth groups and all permissions. Integration =========== At this point we have implemented our security model, and we need to integrate it with our simulation model. We do so in three separate steps. First we make it such that agents only access homes that are wrapped in a security proxy. By doing this all access to homes and services (proxies have proxied return values for their methods) is implicitly guarded by our security policy. The second step is that we want to associate the active agent with the security context so the security policy will know which agent's authentication token to validate against. The third step is to set our security policy as the default policy for the Zope security framework. It is possible to create custom security policies at a finer grained than global, but such is left as an exercise for the reader. Interaction Access ================== The :mod:`*default* implementation ` of the interaction management interfaces defines interactions on a per thread basis with a function for an accessor. This model is not appropriate for all systems, as it restricts one to a single active interaction per thread at any given moment. Reimplementing the interaction access methods though is easily doable and is noted here for completeness. Perspectives ============ It's important to keep in mind that there is a lot more that is possible using the security framework than what's been presented here. All of the interactions are interface based, such that if you need to re-implement the semantics to suite your application a new implementation of the interface will be sufficient. Additional possibilities range from restricted interpreters and dynamic loading of untrusted code to non Zope web application security systems. Insert imagination here ;-). Zope Perspective ================ A Zope3 programmer will never commonly need to interact with the low level security framework. Zope3 defines a second security package over top the low level framework and authentication sources and checkers are handled via zcml registration. Still those developing Zope3 will hopefully find this useful as an introduction into the underpinnings of the security framework. Authors ======= - Kapil Thangavelu - Guido Wesdorp - Marius Gedminas ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/hacking.rst.txt0000644000076500000240000002462514355021564023117 0ustar00jensstaff================================= Hacking on :mod:`zope.security` ================================= Getting the Code ================ The main repository for :mod:`zope.security` is in the Zope Foundation Github repository: https://github.com/zopefoundation/zope.security You can get a read-only checkout from there: .. code-block:: sh $ git clone https://github.com/zopefoundation/zope.security.git or fork it and get a writeable checkout of your fork: .. code-block:: sh $ git clone git@github.com/jrandom/zope.security.git The project also mirrors the trunk from the Github repository as a Bazaar branch on Launchpad: https://code.launchpad.net/zope.security You can branch the trunk from there using Bazaar: .. code-block:: sh $ bzr branch lp:zope.security Working in a ``virtualenv`` =========================== Installing ---------- If you use the ``virtualenv`` package to create lightweight Python development environments, you can run the tests using nothing more than the ``python`` binary in a virtualenv. First, create a scratch environment: .. code-block:: sh $ /path/to/virtualenv --no-site-packages /tmp/hack-zope.security Next, get this package registered as a "development egg" in the environment: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install -e .[test] Running the tests ----------------- Then, you can run the tests using the zope.testrunner (or a test runner of your choice): .. code-block:: sh $ /tmp/hack-zope.security/bin/zope-testrunner --test-path=src Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Running: Ran 742 tests with 0 failures, 0 errors, 36 skipped in 0.253 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. If you have the :mod:`coverage` package installed in the virtualenv, you can see how well the tests cover the code: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install coverage ... $ coverage run -m zope.testrunner --test-path=src ... $ coverage report Name Stmts Miss Cover Missing --------------------------------------------------------------- zope/security.py 4 0 100% zope/security/_compat.py 9 0 100% zope/security/_definitions.py 11 0 100% zope/security/adapter.py 45 0 100% zope/security/checker.py 333 0 100% zope/security/decorator.py 33 0 100% zope/security/i18n.py 4 0 100% zope/security/interfaces.py 65 0 100% zope/security/management.py 62 0 100% zope/security/metaconfigure.py 108 0 100% zope/security/metadirectives.py 38 0 100% zope/security/permission.py 46 0 100% zope/security/protectclass.py 39 0 100% zope/security/proxy.py 164 19 88% 55, 86, 97, 119-121, 127-129, 143-144, 153-154, 158-159, 163-164, 298, 330 zope/security/simplepolicies.py 32 0 100% zope/security/zcml.py 43 0 100% --------------------------------------------------------------- TOTAL 1036 19 98% ---------------------------------------------------------------------- Ran 655 tests in 0.000s OK Building the documentation -------------------------- :mod:`zope.security` uses the nifty :mod:`Sphinx` documentation system for building its docs. Using the same virtualenv you set up to run the tests, you can build the docs: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install -e .[docs] ... $ cd docs $ PATH=/tmp/hack-zope.security/bin:$PATH make html sphinx-build -b html -d _build/doctrees . _build/html ... build succeeded. Build finished. The HTML pages are in _build/html. You can also test the code snippets in the documentation: .. code-block:: sh $ PATH=/tmp/hack-zope.security/bin:$PATH make doctest sphinx-build -b doctest -d _build/doctrees . _build/doctest ... running tests... Document: api/zcml ------------------ 1 items passed all tests: 23 tests in default 23 tests in 1 items. 23 passed and 0 failed. Test passed. Document: api/proxy ------------------- 1 items passed all tests: 11 tests in default 11 tests in 1 items. 11 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/permission ------------------------ 1 items passed all tests: 35 tests in default 35 tests in 1 items. 35 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/checker --------------------- 1 items passed all tests: 356 tests in default 356 tests in 1 items. 356 passed and 0 failed. Test passed. Document: api/decorator ----------------------- 1 items passed all tests: 53 tests in default 53 tests in 1 items. 53 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Doctest summary =============== 478 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code Using :mod:`zc.buildout` ======================== Setting up the buildout ----------------------- :mod:`zope.security` ships with its own :file:`buildout.cfg` file and :file:`bootstrap.py` for setting up a development buildout: .. code-block:: sh $ /path/to/python2.6 bootstrap.py ... Generated script '.../bin/buildout' $ bin/buildout Develop: '/home/jrandom/projects/Zope/BTK/security/.' ... Running the tests ----------------- You can now run the tests: .. code-block:: sh $ bin/test --all Running zope.testing.testrunner.layer.UnitTests tests: Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Ran 643 tests with 0 failures and 0 errors in 0.000 seconds. Tearing down left over layers: Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Using :mod:`tox` ================ Running Tests on Multiple Python Versions ----------------------------------------- `tox `_ is a Python-based test automation tool designed to run tests against multiple Python versions. It creates a ``virtualenv`` for each configured version, installs the current package and configured dependencies into each ``virtualenv``, and then runs the configured commands. :mod:`zope.security` configures the following :mod:`tox` environments via its ``tox.ini`` file: - The ``py27``, ``py34``, ``py35``, ``pypy``, etc, environments builds a ``virtualenv`` with the appropriate interpreter, installs :mod:`zope.security` and dependencies, and runs the tests. - The ``py27-pure`` and ``py33-pure`` environments build a ``virtualenv`` with the appropriate interpreter, installs :mod:`zope.security` and dependencies **without compiling C extensions**, and runs the tests via ``python setup.py test -q``. - The ``coverage`` environment builds a ``virtualenv``, runs all the tests under :mod:`coverage`, and prints a report to stdout. - The ``docs`` environment builds a virtualenv and then builds the docs and exercises the doctest snippets. This example requires that you have a working ``python2.7`` on your path, as well as installing ``tox``: .. code-block:: sh $ tox -e py27 GLOB sdist-make: .../zope.security/setup.py py27 sdist-reinst: .../zope.security/.tox/dist/zope.security-4.0.2dev.zip py27 runtests: commands[0] ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... ---------------------------------------------------------------------- Ran 643 tests in 0.000s OK ___________________________________ summary ____________________________________ py26: commands succeeded congratulations :) Running ``tox`` with no arguments runs all the configured environments, including building the docs and testing their snippets: .. code-block:: sh $ tox GLOB sdist-make: .../zope.security/setup.py py26 sdist-reinst: .../zope.security/.tox/dist/zope.security-4.0.2dev.zip py26 runtests: commands[0] ... Doctest summary =============== 478 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code build succeeded. ___________________________________ summary ____________________________________ py26: commands succeeded py27: commands succeeded py27-pure: commands succeeded pypy: commands succeeded py32: commands succeeded py33: commands succeeded py33-pure: commands succeeded py34: commands succeeded coverage: commands succeeded docs: commands succeeded congratulations :) Contributing to :mod:`zope.security` ==================================== Submitting a Bug Report ----------------------- :mod:`zope.security` tracks its bugs on Github: https://github.com/zopefoundation/zope.security/issues Please submit bug reports and feature requests there. Sharing Your Changes -------------------- .. note:: Please ensure that all tests are passing before you submit your code. If possible, your submission should include new tests for new features or bug fixes, although it is possible that you may have tested your new code by updating existing tests. If have made a change you would like to share, the best route is to fork the Githb repository, check out your fork, make your changes on a branch in your fork, and push it. You can then submit a pull request from your branch: https://github.com/zopefoundation/zope.security/pulls ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/index.rst.txt0000644000076500000240000000116714355021564022616 0ustar00jensstaff============================= zope.security Documentation ============================= Narrative Documentation ======================= .. toctree:: :maxdepth: 2 narr example proxy hacking .. toctree:: :maxdepth: 1 changes API Reference ============= .. toctree:: :maxdepth: 2 api/interfaces api/adapter api/checker api/decorator api/management api/permission api/protectclass api/proxy api/simplepolicies api/testing api/metaconfigure api/zcml ==================== Indices and tables ==================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/narr.rst.txt0000644000076500000240000001035614355021564022451 0ustar00jensstaff=========================== Overview and Introduction =========================== .. currentmodule:: zope.security.interfaces The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions =========== Principal --------- A generalization of a concept of a :class:`user `. Further specializations include :class:`groups of users ` and principals that :class:`know what groups they belong to `. All of these principals may interact with the system. Permission ---------- A kind of access, i.e. permission to READ vs. permission to WRITE. Fundamentally the whole security framework is organized around checking permissions on objects. Permissions are represented (and checked) as strings, with the exception of :data:`a constant ` that has the special meaning of "public", i.e., no checking needs to be done. There are :class:`permission objects ` that can be registered as zope.component utilities for validation, introspection, and producing :func:`lists of available permissions ` to help users assign them to objects. Purpose ======= The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and implicit security checks on attribute access for objects. Attribute names are mapped onto permission names when checking access and the implementation of the security check is defined by the security policy, which receives the object, the permission name, and an interaction. Interactions are objects that represent the use of the system by one or more principals. An interaction contains a list of participations, which represents the way a single principal participates in the interaction. An HTTP request is one example of a participation. Its important to keep in mind that the policy provided is just a default, and it can be substituted with one which doesn't care about principals or interactions at all. Framework Components ==================== Low Level Components -------------------- These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. Checkers ~~~~~~~~ A :class:`checker ` is associated with an object kind, and provides the hooks that map attribute checks onto permissions deferring to the security manager (which in turn defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. There are several implementation variants of checkers, such as checkers that grant access based on attribute names. Proxies ~~~~~~~ :class:`Wrappers around Python objects ` that implicitly guard access to their wrapped contents by delegating to their associated checker. Proxies are also viral in nature, in that values returned by proxies are also proxied. High Level Components ===================== Security Management ------------------- Provides accessors for :class:`setting up interactions ` and the :class:`global security policy `. :class:`Interaction ` ------------------------------------------------------------ An :class:`interaction ` represents zero or more principals manipulating or viewing (interacting with) the system. Interactions also provide :func:`a single method ` that accepts the object and the permission of the access being checked and is used to implement the application logic for the security framework. Participation ------------- Stores information about a single principal :class:`participating ` in the :class:`interaction `. Security Policy --------------- A :class:`security policy ` is used to create the interaction that will ultimately be responsible for security checking. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/_build/html/_sources/proxy.rst.txt0000644000076500000240000003454714355021564022700 0ustar00jensstaff============================================= Untrusted Interpreters and Security Proxies ============================================= Untrusted programs are executed by untrusted interpreters. Untrusted interpreters make use of security proxies to prevent un-mediated access to assets. An untrusted interpreter defines an environment for running untrusted programs. All objects within the environment are either: - "safe" objects created internally by the environment or created in the course of executing the untrusted program, or - "basic" objects - security-proxied non-basic objects The environment includes proxied functions for accessing objects outside of the environment. These proxied functions provide the only way to access information outside the environment. Because these functions are proxied, as described below, any access to objects outside the environment is mediated by the target security functions. Safe objects are objects whose operations, except for attribute retrieval, and methods access only information stored within the objects or passed as arguments. Safe objects contained within the interpreter environment can contain only information that is already in the environment or computed directly from information that is included in the environment. For this reason, safe objects created within the environment cannot be used to directly access information outside the environment. Safe objects have some attributes that could (very) indirectly be used to access assets. For this reason, an untrusted interpreter always proxies the results of attribute accesses on a safe objects. Basic objects are safe objects that are used to represent elemental data values such as strings and numbers. Basic objects require a lower level of protection than non-basic objects, as will be described detail in a later section. Security proxies mediate all object operations. Any operation access is checked to see whether a subject is authorized to perform the operation. All operation results other than basic objects are, in turn, security proxied. Security proxies will be described in greater detail in a later section. Any operation on a security proxy that results in a non-basic object is also security proxied. All external resources needed to perform an operation are security proxied. Let's consider the trusted interpreter for evaluating URLs. In operation 1 of the example, the interpreter uses a proxied method for getting the system root object. Because the method is proxied, the result of calling the method and the operation is also proxied. The interpreter has a function for traversing objects. This function is proxied. When traversing an object, the function is passed an object and a name. In operation 2, the function is passed the result of operation 1, which is the proxied root object and the name 'A'. We may traverse an object by invoking an operation on it. For example, we may use an operation to get a sub-object. Because any operation on a proxied object returns a proxied object or a basic object, the result is either a proxied object or a basic object. Traversal may also look up a component. For example, in operation 1, we might look up a presentation component named "A" for the root object. In this case, the external object is not proxied, but, when it is returned from the traversal function, it is proxied (unless it is a a basic object) because the traversal function is proxied, and the result of calling a proxied function is proxied (unless the result is a basic object). Operation 3 proceeds in the same way. When we get to operation 4, we use a function for computing the default presentation of the result of operation 3. As with traversal, the result of getting the default presentation is either a proxied object or a basic object because the function for getting the default presentation is proxied. When we get to the last operation, we have either a proxied object or a basic object. If the result of operation 4 is a basic object, we simply convert it to a string and return it as the result page. If the result of operation 4 is a non-basic object, we invoke a render operation on it and return the result as a string. Note that an untrusted interpreter may or may not provide protection against excessive resource usage. Different interpreters will provide different levels of service with respect to limitations on resource usage. If an untrusted interpreter performs an attribute access, the trusted interpreter must proxy the result unless the result is a basic object. In summary, an untrusted interpreter assures that any access to assets is mediated through security proxies by creating an environment to run untrusted code and making sure that: - The only way to access anything from outside of the environment is to call functions that are proxied in the environment. - Results of any attribute access in the environment are proxied unless the results are basic objects. Security proxies ================ Security proxies are objects that wrap and mediate access to objects. The Python programming language used by Zope defines a set of specific named low-level operations. In addition to operations, Python objects can have attributes, used to represent data and methods. Attributes are accessed using a dot notation. Applications can, and usually do, define methods to provide extended object behaviors. Methods are accessed as attributes through the low-level operation named "__getattribute__". The Python code:: a.b() invokes 2 operations: 1. Use the low-level ``__getattribute__`` operation with the name "b". 2. Use the low-level ``__call__`` operation on the result of the first operation. For all operations except the ``__getattribute__`` and ``__setattribute__`` operations, security proxies have a permission value defined by the permission-declaration subsystem. Two special permission values indicate that access is either forbidden (never allowed) or public (always allowed). For all other permission values, the authorization subsystem is used to decide whether the subject has the permission for the proxied object. If the subject has the permission, then access to the operation is allowed. Otherwise, access is denied. For getting or setting attributes, a proxy has permissions for getting and a permission for setting attribute values for a given attribute name. As described above, these permissions may be one of the two special permission values indicating forbidden or public access, or another permission value that must be checked with the authorization system. For all objects, Zope defines the following operations to be always public: comparison "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" hash "__hash__" boolean value "__nonzero__" class introspection "__class__" interface introspection "__providedBy__", "__implements__" adaptation "__conform__" low-level string representation "__repr__" The result of an operation on a proxied object is a security proxy unless the result is a basic value. Basic objects ============= Basic objects are safe immutable objects that contain only immutable subobjects. Examples of basic objects include: - Strings, - Integers (long and normal), - Floating-point objects, - Date-time objects, - Boolean objects (True and False), and - The special (nil) object, None. Basic objects are safe, so, as described earlier, operations on basic objects, other than attribute access, use only information contained within the objects or information passed to them. For this reason, basic objects cannot be used to access information outside of the untrusted interpreter environment. The decision not to proxy basic objects is largely an optimization. It allows low-level safe computation to be performed without unnecessary overhead, Note that a basic object could contain sensitive information, but such a basic object would need to be obtained by making a call on a proxied object. Therefore, the access to the basic object in the first place is mediated by the security functions. Rationale for mutable safe objects ================================== Some safe objects are not basic. For these objects, we proxy the objects if they originate from outside of the environment. We do this for two reasons: 1. Non-basic objects from outside the environment need to be proxied to prevent unauthorized access to information. 2. We need to prevent un-mediated change of information from outside of the environment. We don't proxy safe objects created within the environment. This is safe to do because such safe objects can contain and provide access to information already in the environment. Sometimes the interpreter or the interpreted program needs to be able to create simple data containers to hold information computed in the course of the program execution. Several safe container types are provided for this purpose. .. _proxy-known-issues: Known Issues With Proxies ========================= Security proxies (proxies in general) are not perfect in Python. There are some things that they cannot transparently proxy. .. _isinstance-and-proxies: isinstance and proxies ---------------------- A proxied object cannot proxy its type (although it does proxy its ``__class__``): .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> class Object(object): ... pass >>> target = Object() >>> target.__class__ >>> type(target) >>> proxy = ProxyFactory(target, None) >>> proxy.__class__ >>> type(proxy) <... 'zope.security...Proxy...'> This means that the builtin :func:`isinstance` may return unexpected results: .. doctest:: >>> isinstance(target, Object) True >>> isinstance(proxy, Object) False There are two workarounds. The safest is to use :func:`zope.security.proxy.isinstance`, which takes specifically this into account (in modules that will be dealing with a number of proxies, it is common to simply place ``from zope.security.proxy import isinstance`` at the top of the file to override the builtin :func:`isinstance`; we won't show that here for clarity): .. doctest:: >>> import zope.security.proxy >>> zope.security.proxy.isinstance(target, Object) True >>> zope.security.proxy.isinstance(proxy, Object) True Alternatively, you can manually remove the security proxy (or indeed, all proxies) with :func:`zope.security.proxy.removeSecurityProxy` or :func:`zope.proxy.removeAllProxies`, respectively, before calling :func:`isinstance`: .. doctest:: >>> from zope.security.proxy import removeSecurityProxy >>> isinstance(removeSecurityProxy(target), Object) True >>> isinstance(removeSecurityProxy(proxy), Object) True issubclass and proxies ---------------------- Security proxies will proxy the return value of ``__class__``: it will be a proxy around the real class of the proxied value. This causes failures with ``issubclass``: .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> class Object(object): ... pass >>> target = Object() >>> target.__class__ is Object True >>> proxy = ProxyFactory(target, None) >>> proxy.__class__ >>> proxy.__class__ is Object False >>> issubclass(proxy.__class__, Object) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class Although the above is a contrived example, using :class:`abstract base classes ` can cause it to arise quite unexpectedly: .. doctest:: >>> try: ... from collections.abc import Mapping ... except ImportError: # PY2 ... from collections import Mapping >>> from abc import ABCMeta >>> isinstance(Mapping, ABCMeta) True >>> isinstance(proxy, Mapping) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class In this case, the workarounds described :ref:`above ` also work: .. doctest:: >>> zope.security.proxy.isinstance(proxy, Mapping) False >>> isinstance(removeSecurityProxy(proxy), Mapping) False .. We need to clean up the caching that ABC does on .. pure-python platforms to make sure that we get where .. we expect to be when we construct LogRecord; otherwise .. the ProxyMetaclass may be in the negative cache, bypassing .. the issubclass() calls we expect. .. doctest:: :hide: >>> if hasattr(ABCMeta, '_abc_invalidation_counter'): ... ABCMeta._abc_invalidation_counter += 1 logging ~~~~~~~ Starting with `Python 2.7.7 `_, the :class:`logging.LogRecord` makes exactly the above ``isinstance`` call: .. doctest:: >>> from logging import LogRecord >>> LogRecord("name", 1, "/path/to/file", 1, ... "The message %s", (proxy,), None) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class `Possible workarounds include `_: - Carefully removing security proxies of objects before passing them to the logging system. - Monkey-patching the logging system to use :func:`zope.security.proxy.isinstance` which does this automatically:: import zope.security.proxy import logging logging.isinstance = zope.security.proxy.isinstance - Using :func:`logging.setLogRecordfactory` to set a custom ``LogRecord`` subclass that unwraps any security proxies before they are given to the super class. Note that this is only available on Python 3. On Python 2, it might be possible to achieve a similar result with a custom :func:`logger class `: .. doctest:: >>> from zope.security.proxy import removeSecurityProxy >>> class UnwrappingLogRecord(LogRecord): ... def __init__(self, name, level, pathname, lineno, ... msg, args, exc_info, *largs, **kwargs): ... args = [removeSecurityProxy(x) for x in args] ... LogRecord.__init__(self, name, level, pathname, ... lineno, msg, args, exc_info, *largs, **kwargs) ... def __repr__(self): ... return '' >>> UnwrappingLogRecord("name", 1, "/path/to/file", 1, ... "The message %s", (proxy,), None) Each specific application will have to determine what solution is correct for its security model. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1037817 zope_security-7.3/docs/api/0000755000076500000240000000000014677253221014663 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/adapter.rst0000644000076500000240000000015614355021564017033 0ustar00jensstaff======================= zope.security.adapter ======================= .. automodule:: zope.security.adapter ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/checker.rst0000644000076500000240000005506114355021564017024 0ustar00jensstaff======================= zope.security.checker ======================= .. currentmodule:: zope.security.checker Module API Documentation ======================== .. automodule:: zope.security.checker API Doctests ============ Protections for Modules ----------------------- The :func:`moduleChecker` API can be used to determine whether a module has been protected: Initially, there's no checker defined for the module: .. doctest:: >>> from zope.security.checker import moduleChecker >>> from zope.security.tests import test_zcml_functest >>> moduleChecker(test_zcml_functest) is None True We can add a checker using :func:`zope.security.metaconfigure.protectModule` (although this is more commonly done using ZCML): .. doctest:: >>> from zope.component import provideUtility >>> from zope.security.metaconfigure import protectModule >>> from zope.security.permission import Permission >>> from zope.security.interfaces import IPermission >>> TEST_PERM = 'zope.security.metaconfigure.test' >>> perm = Permission(TEST_PERM, '') >>> provideUtility(perm, IPermission, TEST_PERM) >>> protectModule(test_zcml_functest, 'foo', TEST_PERM) Now, the checker should exist and have an access dictionary with the name and permission: .. doctest:: >>> def pprint(ob, width=70): ... from pprint import PrettyPrinter ... PrettyPrinter(width=width).pprint(ob) >>> checker = moduleChecker(test_zcml_functest) >>> cdict = checker.get_permissions >>> pprint(cdict) {'foo': 'zope.security.metaconfigure.test'} If we define additional names, they will be added to the dict: >>> protectModule(test_zcml_functest, 'bar', TEST_PERM) >>> protectModule(test_zcml_functest, 'baz', TEST_PERM) >>> pprint(cdict) {'bar': 'zope.security.metaconfigure.test', 'baz': 'zope.security.metaconfigure.test', 'foo': 'zope.security.metaconfigure.test'} The allow directive creates actions for each name defined directly, or via interface: .. doctest:: >>> from zope.interface import Interface >>> from zope.interface import Attribute >>> from zope.security.metaconfigure import allow >>> class I1(Interface): ... def x(): pass ... y = Attribute("Y") >>> class I2(I1): ... def a(): pass ... b = Attribute("B") >>> class AContext(object): ... def __init__(self): ... self.actions = [] ... ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is protectModule), ... 'args': args}) ... module='testmodule' >>> context = AContext() >>> allow(context, attributes=['foo', 'bar'], interface=[I1, I2]) >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'zope.Public'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] The provide directive creates actions for each name defined directly, or via interface: .. doctest:: >>> from zope.security.metaconfigure import require >>> class RContext(object): ... def __init__(self): ... self.actions = [] ... def action(self, discriminator, callable, args): ... self.actions.append( ... {'discriminator': discriminator, ... 'callable': int(callable is protectModule), ... 'args': args}) ... module='testmodule' >>> context = RContext() >>> require(context, attributes=['foo', 'bar'], ... interface=[I1, I2], permission='p') >>> context.actions.sort(key=lambda a: a['discriminator']) >>> pprint(context.actions) [{'args': ('testmodule', 'a', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'a')}, {'args': ('testmodule', 'b', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'b')}, {'args': ('testmodule', 'bar', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'bar')}, {'args': ('testmodule', 'foo', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'foo')}, {'args': ('testmodule', 'x', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'x')}, {'args': ('testmodule', 'y', 'p'), 'callable': 1, 'discriminator': ('http://namespaces.zope.org/zope:module', 'testmodule', 'y')}] Protections for standard objects -------------------------------- .. doctest:: >>> from zope.security.checker import ProxyFactory >>> from zope.security.interfaces import ForbiddenAttribute >>> def check_forbidden_get(object, attr): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_setitem(object, item, value): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... object[item] = value ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_delitem(object, item): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... del object[item] ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> def check_forbidden_call(callable, *args): # ** ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return callable(*args) # ** ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] Rocks ~~~~~ Rocks are immutable, non-callable objects without interesting methods. They *don't* get proxied. .. doctest:: >>> type(ProxyFactory(object())) is object True >>> type(ProxyFactory(1)) is int True >>> type(ProxyFactory(1.0)) is float True >>> type(ProxyFactory(1j)) is complex True >>> type(ProxyFactory(None)) is type(None) True >>> type(ProxyFactory('xxx')) is str True >>> type(ProxyFactory(True)) is type(True) True Datetime-related instances are rocks, too: .. doctest:: >>> from datetime import timedelta, datetime, date, time, tzinfo >>> type(ProxyFactory( timedelta(1) )) is timedelta True >>> type(ProxyFactory( datetime(2000, 1, 1) )) is datetime True >>> type(ProxyFactory( date(2000, 1, 1) )) is date True >>> type(ProxyFactory( time() )) is time True >>> type(ProxyFactory( tzinfo() )) is tzinfo True >>> try: ... from pytz import UTC ... except ImportError: # pytz checker only if pytz is present. ... True ... else: ... type(ProxyFactory( UTC )) is type(UTC) True dicts ~~~~~ We can do everything we expect to be able to do with proxied dicts. .. doctest:: >>> d = ProxyFactory({'a': 1, 'b': 2}) >>> check_forbidden_get(d, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> check_forbidden_setitem(d, 3, 4) # Verify that we are protected 'ForbiddenAttribute: __setitem__' >>> d['a'] 1 >>> len(d) 2 >>> sorted(list(d)) ['a', 'b'] >>> d.get('a') 1 >>> 'a' in d True >>> c = d.copy() >>> check_forbidden_get(c, 'clear') 'ForbiddenAttribute: clear' >>> str(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}") True >>> repr(c) in ("{'a': 1, 'b': 2}", "{'b': 2, 'a': 1}") True >>> def sorted(x): ... x = list(x) ... x.sort() ... return x >>> sorted(d.keys()) ['a', 'b'] >>> sorted(d.values()) [1, 2] >>> sorted(d.items()) [('a', 1), ('b', 2)] Always available (note, that dicts in python-3.x are not orderable, so we are not checking that under python > 2): .. doctest:: >>> d != d False >>> bool(d) True >>> d.__class__ == dict True lists ~~~~~ We can do everything we expect to be able to do with proxied lists. .. doctest:: >>> l = ProxyFactory([1, 2]) >>> check_forbidden_delitem(l, 0) 'ForbiddenAttribute: __delitem__' >>> check_forbidden_setitem(l, 0, 3) 'ForbiddenAttribute: __setitem__' >>> l[0] 1 >>> l[0:1] [1] >>> check_forbidden_setitem(l[:1], 0, 2) 'ForbiddenAttribute: __setitem__' >>> len(l) 2 >>> tuple(l) (1, 2) >>> 1 in l True >>> l.index(2) 1 >>> l.count(2) 1 >>> str(l) '[1, 2]' >>> repr(l) '[1, 2]' >>> l + l [1, 2, 1, 2] Always available: .. doctest:: >>> l < l False >>> l > l False >>> l <= l True >>> l >= l True >>> l == l True >>> l != l False >>> bool(l) True >>> l.__class__ == list True tuples ~~~~~~ We can do everything we expect to be able to do with proxied tuples. .. doctest:: >>> from zope.security.checker import ProxyFactory >>> l = ProxyFactory((1, 2)) >>> l[0] 1 >>> l[0:1] (1,) >>> len(l) 2 >>> list(l) [1, 2] >>> 1 in l True >>> str(l) '(1, 2)' >>> repr(l) '(1, 2)' >>> l + l (1, 2, 1, 2) Always available: .. doctest:: >>> l < l False >>> l > l False >>> l <= l True >>> l >= l True >>> l == l True >>> l != l False >>> bool(l) True >>> l.__class__ == tuple True sets ~~~~ we can do everything we expect to be able to do with proxied sets. .. doctest:: >>> us = set((1, 2)) >>> s = ProxyFactory(us) >>> check_forbidden_get(s, 'add') # Verify that we are protected 'ForbiddenAttribute: add' >>> check_forbidden_get(s, 'remove') # Verify that we are protected 'ForbiddenAttribute: remove' >>> check_forbidden_get(s, 'discard') # Verify that we are protected 'ForbiddenAttribute: discard' >>> check_forbidden_get(s, 'pop') # Verify that we are protected 'ForbiddenAttribute: pop' >>> check_forbidden_get(s, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> len(s) 2 >>> 1 in s True >>> 1 not in s False >>> s.issubset(set((1,2,3))) True >>> s.issuperset(set((1,2,3))) False >>> c = s.union(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | set((2, 3)) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | ProxyFactory(set((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) | s >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.intersection(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & set((2, 3)) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & ProxyFactory(set((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) & s >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.difference(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - ProxyFactory(set((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - set((2, 3)) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) - s >>> sorted(c) [3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.symmetric_difference(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ set((2, 3)) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ ProxyFactory(set((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = set((2, 3)) ^ s >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.copy() >>> sorted(c) [1, 2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> str(s) == str(us) True >>> repr(s) == repr(us) True Always available: >>> s < us False >>> s > us False >>> s <= us True >>> s >= us True >>> s == us True >>> s != us False Note that you can't compare proxied sets with other proxied sets due to a limitation in the set comparison functions which won't work with any kind of proxy. .. doctest:: >>> bool(s) True >>> s.__class__ == set True frozensets ~~~~~~~~~~ we can do everything we expect to be able to do with proxied frozensets. .. doctest:: >>> def check_forbidden_get(object, attr): ... from zope.security.interfaces import ForbiddenAttribute ... try: ... return getattr(object, attr) ... except ForbiddenAttribute as e: ... return 'ForbiddenAttribute: %s' % e.args[0] >>> from zope.security.checker import ProxyFactory >>> from zope.security.interfaces import ForbiddenAttribute >>> us = frozenset((1, 2)) >>> s = ProxyFactory(us) >>> check_forbidden_get(s, 'add') # Verify that we are protected 'ForbiddenAttribute: add' >>> check_forbidden_get(s, 'remove') # Verify that we are protected 'ForbiddenAttribute: remove' >>> check_forbidden_get(s, 'discard') # Verify that we are protected 'ForbiddenAttribute: discard' >>> check_forbidden_get(s, 'pop') # Verify that we are protected 'ForbiddenAttribute: pop' >>> check_forbidden_get(s, 'clear') # Verify that we are protected 'ForbiddenAttribute: clear' >>> len(s) 2 >>> 1 in s True >>> 1 not in s False >>> s.issubset(frozenset((1,2,3))) True >>> s.issuperset(frozenset((1,2,3))) False >>> c = s.union(frozenset((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | frozenset((2, 3)) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s | ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) | s >>> sorted(c) [1, 2, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.intersection(frozenset((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & frozenset((2, 3)) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s & ProxyFactory(frozenset((2, 3))) >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) & s >>> sorted(c) [2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.difference(frozenset((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s - frozenset((2, 3)) >>> sorted(c) [1] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) - s >>> sorted(c) [3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.symmetric_difference(frozenset((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ frozenset((2, 3)) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s ^ ProxyFactory(frozenset((2, 3))) >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = frozenset((2, 3)) ^ s >>> sorted(c) [1, 3] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> c = s.copy() >>> sorted(c) [1, 2] >>> check_forbidden_get(c, 'add') 'ForbiddenAttribute: add' >>> str(s) == str(us) True >>> repr(s) == repr(us) True Always available: >>> s < us False >>> s > us False >>> s <= us True >>> s >= us True >>> s == us True >>> s != us False Note that you can't compare proxied sets with other proxied sets due to a limitation in the frozenset comparison functions which won't work with any kind of proxy. .. doctest:: >>> bool(s) True >>> s.__class__ == frozenset True iterators ~~~~~~~~~ .. doctest:: >>> [a for a in ProxyFactory(iter([1, 2]))] [1, 2] >>> list(ProxyFactory(iter([1, 2]))) [1, 2] >>> list(ProxyFactory(iter((1, 2)))) [1, 2] >>> list(ProxyFactory(iter({1:1, 2:2}))) [1, 2] >>> def f(): ... for i in 1, 2: ... yield i ... >>> list(ProxyFactory(f())) [1, 2] >>> list(ProxyFactory(f)()) [1, 2] We can iterate over custom sequences, too: .. doctest:: >>> class X(object): ... d = 1, 2, 3 ... def __getitem__(self, i): ... return self.d[i] ... >>> x = X() We can iterate over sequences .. doctest:: >>> list(x) [1, 2, 3] >>> from zope.security.checker import NamesChecker >>> from zope.security.checker import ProxyFactory >>> c = NamesChecker(['__getitem__', '__len__']) >>> p = ProxyFactory(x, c) Even if they are proxied .. doctest:: >>> list(p) [1, 2, 3] But if the class has an iter: .. doctest:: >>> X.__iter__ = lambda self: iter(self.d) >>> list(x) [1, 2, 3] We shouldn't be able to iterate if we don't have an assertion: .. doctest:: >>> check_forbidden_call(list, p) 'ForbiddenAttribute: __iter__' New-style classes ~~~~~~~~~~~~~~~~~ .. doctest:: >>> from zope.security.checker import NamesChecker >>> class C(object): ... x = 1 ... y = 2 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = repr(C) >>> C.__module__ == __name__ True >>> len(C.__bases__) 1 >>> len(C.__mro__) 2 Always available: .. doctest:: >>> C == C True >>> C != C False >>> bool(C) True >>> C.__class__ == type True New-style Instances ~~~~~~~~~~~~~~~~~~~ .. doctest:: >>> class C(object): ... x = 1 ... y = 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> c.__class__ == C True Always available: .. doctest:: >>> c == c True >>> c != c False >>> bool(c) True >>> c.__class__ == C True Classic Classes ~~~~~~~~~~~~~~~ .. doctest:: >>> class C: ... x = 1 >>> C = ProxyFactory(C) >>> check_forbidden_call(C) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(C, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(C) >>> s = repr(C) >>> C.__module__ == __name__ True Note that these are really only classic on Python 2: >>> import sys >>> len(C.__bases__) == (0 if sys.version_info[0] == 2 else 1) True Always available: .. doctest:: >>> C == C True >>> C != C False >>> bool(C) True Classic Instances ~~~~~~~~~~~~~~~~~ .. doctest:: >>> class C(object): ... x, y = 1, 2 >>> c = ProxyFactory(C(), NamesChecker(['x'])) >>> check_forbidden_get(c, 'y') 'ForbiddenAttribute: y' >>> check_forbidden_get(c, 'z') 'ForbiddenAttribute: z' >>> c.x 1 >>> c.__class__ == C True Always available: .. doctest:: >>> c == c True >>> c != c False >>> bool(c) True >>> c.__class__ == C True Interfaces and declarations ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We can still use interfaces though proxies: .. doctest:: >>> from zope.interface import directlyProvides >>> from zope.interface import implementer >>> from zope.interface import provider >>> class I(Interface): ... pass >>> class IN(Interface): ... pass >>> class II(Interface): ... pass >>> @implementer(I) ... @provider(IN) ... class N(object): ... pass >>> n = N() >>> directlyProvides(n, II) >>> N = ProxyFactory(N) >>> n = ProxyFactory(n) >>> I.implementedBy(N) True >>> IN.providedBy(N) True >>> I.providedBy(n) True >>> II.providedBy(n) True Abstract Base Classes ~~~~~~~~~~~~~~~~~~~~~ We work with the ABCMeta meta class: .. doctest:: >>> import abc >>> MyABC = abc.ABCMeta('MyABC', (object,), {}) >>> class Foo(MyABC): pass >>> class Bar(Foo): pass >>> PBar = ProxyFactory(Bar) >>> [c.__name__ for c in PBar.__mro__] ['Bar', 'Foo', 'MyABC', 'object'] >>> check_forbidden_call(PBar) 'ForbiddenAttribute: __call__' >>> check_forbidden_get(PBar, '__dict__') 'ForbiddenAttribute: __dict__' >>> s = str(PBar) >>> s = repr(PBar) >>> PBar.__module__ == __name__ True >>> len(PBar.__bases__) 1 Always available: .. doctest:: >>> PBar == PBar True >>> PBar != PBar False >>> bool(PBar) True >>> PBar.__class__ == type False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/decorator.rst0000644000076500000240000001175714355021564017406 0ustar00jensstaff========================= zope.security.decorator ========================= .. automodule:: zope.security.decorator API Examples ============ .. currentmodule:: zope.security.decorator .. testsetup:: from zope.component.testing import setUp setUp() To illustrate, we'll create a class that will be proxied: .. doctest:: >>> class Foo(object): ... a = 'a' and a class to proxy it that uses a decorated security checker: .. doctest:: >>> from zope.security.decorator import DecoratedSecurityCheckerDescriptor >>> from zope.proxy import ProxyBase >>> class Wrapper(ProxyBase): ... b = 'b' ... __Security_checker__ = DecoratedSecurityCheckerDescriptor() Next we'll create and register a checker for ``Foo``: .. doctest:: >>> from zope.security.checker import NamesChecker, defineChecker >>> fooChecker = NamesChecker(['a']) >>> defineChecker(Foo, fooChecker) along with a checker for ``Wrapper``: .. doctest:: >>> wrapperChecker = NamesChecker(['b']) >>> defineChecker(Wrapper, wrapperChecker) Using :func:`zope.security.checker.selectChecker`, we can confirm that a ``Foo`` object uses ``fooChecker``: .. doctest:: >>> from zope.security.checker import selectChecker >>> from zope.security.interfaces import ForbiddenAttribute >>> foo = Foo() >>> selectChecker(foo) is fooChecker True >>> fooChecker.check(foo, 'a') >>> try: ... fooChecker.check(foo, 'b') # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('b', <...Foo object ...>) and that a ``Wrapper`` object uses ``wrappeChecker``: .. doctest:: >>> wrapper = Wrapper(foo) >>> selectChecker(wrapper) is wrapperChecker True >>> wrapperChecker.check(wrapper, 'b') >>> try: ... wrapperChecker.check(wrapper, 'a') # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('a', <...Foo object ...>) (Note that the object description says `Foo` because the object is a proxy and generally looks and acts like the object it's proxying.) When we access wrapper's ``__Security_checker__`` attribute, we invoke the decorated security checker descriptor. The decorator's job is to make sure checkers from both objects are used when available. In this case, because both objects have checkers, we get a combined checker: .. doctest:: >>> from zope.security.checker import CombinedChecker >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker will work even with security proxied objects. To illustrate, we'll proxify ``foo``: .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> secure_foo = ProxyFactory(foo) >>> secure_foo.a 'a' >>> try: ... secure_foo.b # doctest: +ELLIPSIS ... except ForbiddenAttribute as e: ... e ForbiddenAttribute('b', <...Foo object ...>) when we wrap the secured ``foo``: .. doctest:: >>> wrapper = Wrapper(secure_foo) we still get a combined checker: .. doctest:: >>> checker = wrapper.__Security_checker__ >>> type(checker) >>> checker.check(wrapper, 'a') >>> checker.check(wrapper, 'b') The decorator checker has three other scenarios: - the wrapper has a checker but the proxied object doesn't - the proxied object has a checker but the wrapper doesn't - neither the wrapper nor the proxied object have checkers When the wrapper has a checker but the proxied object doesn't: .. doctest:: >>> from zope.security.checker import NoProxy, _checkers >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is wrapperChecker True the decorator uses only the wrapper checker: .. doctest:: >>> wrapper = Wrapper(foo) >>> wrapper.__Security_checker__ is wrapperChecker True When the proxied object has a checker but the wrapper doesn't: .. doctest:: >>> del _checkers[Wrapper] >>> defineChecker(Wrapper, NoProxy) >>> selectChecker(wrapper) is None True >>> del _checkers[Foo] >>> defineChecker(Foo, fooChecker) >>> selectChecker(foo) is fooChecker True the decorator uses only the proxied object checker: .. doctest:: >>> wrapper.__Security_checker__ is fooChecker True Finally, if neither the wrapper not the proxied have checkers: .. doctest:: >>> del _checkers[Foo] >>> defineChecker(Foo, NoProxy) >>> selectChecker(foo) is None True >>> selectChecker(wrapper) is None True the decorator doesn't have a checker: .. doctest:: >>> wrapper.__Security_checker__ Traceback (most recent call last): ... AttributeError: 'Foo' has no attribute '__Security_checker__' ``__Security_checker__`` cannot be None, otherwise Checker.proxy blows up: >>> checker.proxy(wrapper) is wrapper True .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/interfaces.rst0000644000076500000240000000017214355021564017534 0ustar00jensstaff========================== zope.security.interfaces ========================== .. automodule:: zope.security.interfaces ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/management.rst0000644000076500000240000000017214355021564017525 0ustar00jensstaff========================== zope.security.management ========================== .. automodule:: zope.security.management ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/metaconfigure.rst0000644000076500000240000000020614355021564020237 0ustar00jensstaff============================= zope.security.metaconfigure ============================= .. automodule:: zope.security.metaconfigure ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/permission.rst0000644000076500000240000000640414355021564017605 0ustar00jensstaff========================== zope.security.permission ========================== .. currentmodule:: zope.security.permission .. testsetup:: from zope.component.testing import setUp setUp() .. autoclass:: zope.security.permission.Permission :members: :member-order: bysource .. autofunction:: zope.security.permission.checkPermission .. doctest:: >>> from zope.security.permission import checkPermission >>> from zope.component import provideUtility >>> from zope.security.interfaces import IPermission >>> from zope.security.permission import Permission >>> x = Permission('x') >>> provideUtility(x, IPermission, 'x') >>> checkPermission(None, 'x') >>> checkPermission(None, 'y') Traceback (most recent call last): ... ValueError: ('Undefined permission ID', 'y') The :data:`zope.security.checker.CheckerPublic` permission always exists: .. doctest:: >>> from zope.security.checker import CheckerPublic >>> checkPermission(None, CheckerPublic) .. autofunction:: zope.security.permission.allPermissions .. doctest:: >>> from zope.security.permission import allPermissions >>> from zope.component import provideUtility >>> y = Permission('y') >>> provideUtility(y, IPermission, 'y') >>> ids = sorted(allPermissions(None)) >>> for perm in sorted(allPermissions(None)): ... print(perm) x y .. autofunction:: zope.security.permission.PermissionsVocabulary To illustrate, we need to register the permissions vocabulary: .. doctest:: >>> from zope.security.permission import PermissionsVocabulary >>> from zope.schema.vocabulary import _clear >>> _clear() >>> from zope.schema.vocabulary import getVocabularyRegistry >>> registry = getVocabularyRegistry() >>> registry.register('Permissions', PermissionsVocabulary) We can now lookup the permissions we created earlier using the vocabulary: .. doctest:: >>> vocab = registry.get(None, 'Permissions') >>> vocab.getTermByToken('x').value is x True >>> vocab.getTermByToken('y').value is y True .. autofunction:: zope.security.permission.PermissionIdsVocabulary To illustrate, we need to register the permission IDs vocabulary: .. doctest:: >>> from zope.security.permission import PermissionIdsVocabulary >>> registry.register('Permission Ids', PermissionIdsVocabulary) We also need to register the special 'zope.Public' permission: >>> provideUtility(Permission('zope.Public'), IPermission, 'zope.Public') We can now lookup these permissions using the vocabulary: .. doctest:: >>> vocab = registry.get(None, 'Permission Ids') The non-public permissions 'x' and 'y' are string values: .. doctest:: >>> print(vocab.getTermByToken('x').value) x >>> print(vocab.getTermByToken('y').value) y However, the public permission value is :data:`~.CheckerPublic`: .. doctest:: >>> vocab.getTermByToken('zope.Public').value is CheckerPublic True and its title is shortened: .. doctest:: >>> print(vocab.getTermByToken('zope.Public').title) Public The terms are sorted by title except for the public permission, which is listed first: .. doctest:: >>> for term in vocab: ... print(term.title) Public x y .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/protectclass.rst0000644000076500000240000000020214355021564020111 0ustar00jensstaff============================ zope.security.protectclass ============================ .. automodule:: zope.security.protectclass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/proxy.rst0000644000076500000240000000175314355021564016600 0ustar00jensstaff===================== zope.security.proxy ===================== .. currentmodule:: zope.security.proxy An introduction to proxies and their uses can be found in :doc:`../proxy`. .. seealso:: :ref:`proxy-known-issues` .. testsetup:: from zope.component.testing import setUp setUp() .. autofunction:: getChecker .. autofunction:: removeSecurityProxy .. autofunction:: getTestProxyItems .. autofunction:: isinstance .. doctest:: >>> from zope.security.proxy import isinstance >>> class C1(object): ... pass >>> c = C1() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True >>> class C2(C1): ... pass >>> c = C2() >>> isinstance(c, C1) True >>> from zope.security.checker import ProxyFactory >>> isinstance(ProxyFactory(c), C1) True .. autoclass:: Proxy .. autoclass:: ProxyPy .. testcleanup:: from zope.component.testing import tearDown tearDown() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/simplepolicies.rst0000644000076500000240000000021214355021564020425 0ustar00jensstaff============================== zope.security.simplepolicies ============================== .. automodule:: zope.security.simplepolicies ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/testing.rst0000644000076500000240000000015614355021564017070 0ustar00jensstaff======================= zope.security.testing ======================= .. automodule:: zope.security.testing ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/api/zcml.rst0000644000076500000240000000432214355021564016357 0ustar00jensstaff==================== zope.security.zcml ==================== .. currentmodule:: zope.security.zcml Most users will not directly need to access the contents of this module; they will probably just :ref:`configure via ZCML `. API Reference ============= .. autoclass:: Permission Let's look at an example: .. doctest:: >>> from zope.security.zcml import Permission >>> class FauxContext(object): ... permission_mapping = {'zope.ManageCode':'zope.private'} ... _actions = [] ... def action(self, **kws): ... self._actions.append(kws) >>> context = FauxContext() >>> field = Permission().bind(context) Let's test the fromUnicode method: .. doctest:: >>> field.fromUnicode(u'zope.foo') 'zope.foo' >>> field.fromUnicode(u'zope.ManageCode') 'zope.private' Now let's see whether validation works alright .. doctest:: >>> field._validate('zope.ManageCode') >>> context._actions[0]['args'] (None, 'zope.foo') >>> from zope.schema.interfaces import InvalidId >>> try: ... field._validate('3 foo') ... except InvalidId as e: ... e InvalidId('3 foo') zope.Public is always valid >>> field._validate('zope.Public') .. autointerface:: ISecurityPolicyDirective .. autofunction:: securityPolicy .. autointerface:: IPermissionDirective .. autofunction:: permission .. autointerface:: IRedefinePermission .. autofunction:: redefinePermission .. _via-zcml: Configuring security via ZCML ============================= :mod:`zope.security` provides a ZCML file that configures some utilities and a couple of standard permissions: .. doctest:: >>> from zope.component import getGlobalSiteManager >>> from zope.configuration.xmlconfig import XMLConfig >>> from zope.component.testing import setUp >>> import zope.security >>> setUp() # clear global component registry >>> XMLConfig('permissions.zcml', zope.security)() >>> len(list(getGlobalSiteManager().registeredUtilities())) 7 Clear the current state: .. doctest:: >>> from zope.component.testing import setUp, tearDown >>> tearDown() >>> setUp() >>> XMLConfig('configure.zcml', zope.security)() >>> len(list(getGlobalSiteManager().registeredUtilities())) 10 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/changes.rst0000644000076500000240000000003414355021564016245 0ustar00jensstaff.. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1724678151.0 zope_security-7.3/docs/conf.py0000644000076500000240000002214714663100007015403 0ustar00jensstaff# # zope.security documentation build configuration file, created by # sphinx-quickstart on Sun Dec 23 12:19:39 2012. # # This file is execfile()d with the current directory set to its containing # dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out serve # to show the default. import os import sys import pkg_resources sys.path.append(os.path.abspath('../src')) rqmt = pkg_resources.require('zope.security')[0] # We can't have ZOPE_WATCH_CHECKERS set because it interferes # with doctests os.environ['ZOPE_WATCH_CHECKERS'] = '0' # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.extlinks', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'repoze.sphinx.autointerface', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = {'.rst': 'restructuredtext'} # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'zope.security' copyright = '2012, Zope Foundation Contributors ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) # The full version, including alpha/beta/rc tags. release = rqmt.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'zopesecuritydoc' # -- Options for LaTeX output -------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples (source start # file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'zopesecurity.tex', 'zope.security Documentation', 'Zope Foundation Contributors \\textless{}zope-dev@zope.org' '\\textgreater{}', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output -------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'zopesecurity', 'zope.security Documentation', ['Zope Foundation Contributors '], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'zopesecurity', 'zope.security Documentation', 'Zope Foundation Contributors ', 'zopesecurity', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('https://docs.python.org/3/', None), 'zopeinterface': ('https://zopeinterface.readthedocs.io/en/latest', None), 'zopeproxy': ('https://zopeproxy.readthedocs.io/en/latest', None), 'zopeschema': ('https://zopeschema.readthedocs.io/en/latest', None), 'zopelocation': ('https://zopelocation.readthedocs.io/en/latest', None), } extlinks = { 'issue': ( 'https://github.com/zopefoundation/zope.security/issues/%s', 'issue #'), 'pr': ( 'https://github.com/zopefoundation/zope.security/pull/%s', 'pull request #')} autodoc_default_flags = [ 'members', 'show-inheritance', ] autoclass_content = 'both' autodoc_member_order = 'bysource' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/example.rst0000644000076500000240000001773514355021564016310 0ustar00jensstaff========= Example ========= As an example we take a look at constructing a multi-agent distributed system, and then adding a security layer using the Zope security model onto it. Scenario ======== Our agent simulation consists of autonomous agents that live in various agent homes/sandboxes and perform actions that access services available at their current home. Agents carry around authentication tokens which signify their level of access within any given home. Additionally agents attempt to migrate from home to home randomly. The agent simulation was constructed separately from any security aspects. Now we want to define and integrate a security model into the simulation. The full code for the simulation and the security model is available separately; we present only relevant code snippets here for illustration as we go through the implementation process. For the agent simulation we want to add a security model such that we group agents into two authentication groups, "norse legends", including the principals thor, odin, and loki, and "greek men", including prometheus, archimedes, and thucydides. We associate permissions with access to services and homes. We differentiate the homes such that certain authentication groups only have access to services or the home itself based on the local settings of the home in which they reside. We define the homes/sandboxes - origin - all agents start here, and have access to all services here. - valhalla - only agents in the authentication group 'norse legend' can reside here. - jail - all agents can come here, but only 'norse legend's can leave or access services. Process ======= Loosely we define a process for implementing this security model - mapping permissions onto actions - mapping authentication tokens onto permissions - implementing checkers and security policies that use our authentication tokens and permissions. - binding checkers to our simulation classes - inserting the hooks into the original simulation code to add proxy wrappers to automatically check security. - inserting hooks into the original simulation to register the agents as the active principal in an interaction. Defining a Permission Model =========================== We define the following permissions:: NotAllowed = 'Not Allowed' Public = Checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' and create a dictionary database mapping homes to authentication groups which are linked to associated permissions. Defining and Binding Checkers ============================= :class:`Checkers ` are the foundational unit for the security framework. They define what attributes can be accessed or set on a given instance. They can be used implicitly via Proxy objects, to guard all attribute access automatically or explicitly to check a given access for an operation. Checker construction expects two functions or dictionaries, one is used to map attribute names to permissions for attribute access and another to do the same for setting attributes. We use the following checker factory function:: def PermissionMapChecker(permissions_map={}, setattr_permission_func=NoSetAttr): res = {} for k,v in permissions_map.items(): for iv in v: res[iv]=k return checker.Checker(res.get, setattr_permission_func) time_service_checker = PermissionMapChecker( # permission : [methods] {'AccessTimeService':['getTime']} ) with the NoSetAttr function defined as a lambda which always return the permission ``NotAllowed``. To bind the checkers to the simulation classes we :func:`register ` our checkers with the security model's global checker registry:: import sandbox_simulation from zope.security.checker import defineChecker defineChecker(sandbox_simulation.TimeService, time_service_checker) Defining a Security Policy ========================== We implement our security policy such that it checks the current agent's authentication token against the given permission in the home of the object being accessed. (We extend a simple policy provided by the framework that will track participations for us):: from zope.security.simplepolicies import ParanoidSecurityPolicy @provider(ISecurityPolicy) @implementer(IInteraction) class SimulationSecurityPolicy(ParanoidSecurityPolicy): def checkPermission(self, permission, object): home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if not self.participations: return False for participation in self.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True Since an interaction can have more than one principal, we check that *all* of them are given the necessary permission. This is not really necessary since we only create interactions with a single active principal. There is some additional code present to allow for shortcuts in defining the permission database when defining permissions for all auth groups and all permissions. Integration =========== At this point we have implemented our security model, and we need to integrate it with our simulation model. We do so in three separate steps. First we make it such that agents only access homes that are wrapped in a security proxy. By doing this all access to homes and services (proxies have proxied return values for their methods) is implicitly guarded by our security policy. The second step is that we want to associate the active agent with the security context so the security policy will know which agent's authentication token to validate against. The third step is to set our security policy as the default policy for the Zope security framework. It is possible to create custom security policies at a finer grained than global, but such is left as an exercise for the reader. Interaction Access ================== The :mod:`*default* implementation ` of the interaction management interfaces defines interactions on a per thread basis with a function for an accessor. This model is not appropriate for all systems, as it restricts one to a single active interaction per thread at any given moment. Reimplementing the interaction access methods though is easily doable and is noted here for completeness. Perspectives ============ It's important to keep in mind that there is a lot more that is possible using the security framework than what's been presented here. All of the interactions are interface based, such that if you need to re-implement the semantics to suite your application a new implementation of the interface will be sufficient. Additional possibilities range from restricted interpreters and dynamic loading of untrusted code to non Zope web application security systems. Insert imagination here ;-). Zope Perspective ================ A Zope3 programmer will never commonly need to interact with the low level security framework. Zope3 defines a second security package over top the low level framework and authentication sources and checkers are handled via zcml registration. Still those developing Zope3 will hopefully find this useful as an introduction into the underpinnings of the security framework. Authors ======= - Kapil Thangavelu - Guido Wesdorp - Marius Gedminas ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/hacking.rst0000644000076500000240000002462514355021564016255 0ustar00jensstaff================================= Hacking on :mod:`zope.security` ================================= Getting the Code ================ The main repository for :mod:`zope.security` is in the Zope Foundation Github repository: https://github.com/zopefoundation/zope.security You can get a read-only checkout from there: .. code-block:: sh $ git clone https://github.com/zopefoundation/zope.security.git or fork it and get a writeable checkout of your fork: .. code-block:: sh $ git clone git@github.com/jrandom/zope.security.git The project also mirrors the trunk from the Github repository as a Bazaar branch on Launchpad: https://code.launchpad.net/zope.security You can branch the trunk from there using Bazaar: .. code-block:: sh $ bzr branch lp:zope.security Working in a ``virtualenv`` =========================== Installing ---------- If you use the ``virtualenv`` package to create lightweight Python development environments, you can run the tests using nothing more than the ``python`` binary in a virtualenv. First, create a scratch environment: .. code-block:: sh $ /path/to/virtualenv --no-site-packages /tmp/hack-zope.security Next, get this package registered as a "development egg" in the environment: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install -e .[test] Running the tests ----------------- Then, you can run the tests using the zope.testrunner (or a test runner of your choice): .. code-block:: sh $ /tmp/hack-zope.security/bin/zope-testrunner --test-path=src Running zope.testrunner.layer.UnitTests tests: Set up zope.testrunner.layer.UnitTests in 0.000 seconds. Running: Ran 742 tests with 0 failures, 0 errors, 36 skipped in 0.253 seconds. Tearing down left over layers: Tear down zope.testrunner.layer.UnitTests in 0.000 seconds. If you have the :mod:`coverage` package installed in the virtualenv, you can see how well the tests cover the code: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install coverage ... $ coverage run -m zope.testrunner --test-path=src ... $ coverage report Name Stmts Miss Cover Missing --------------------------------------------------------------- zope/security.py 4 0 100% zope/security/_compat.py 9 0 100% zope/security/_definitions.py 11 0 100% zope/security/adapter.py 45 0 100% zope/security/checker.py 333 0 100% zope/security/decorator.py 33 0 100% zope/security/i18n.py 4 0 100% zope/security/interfaces.py 65 0 100% zope/security/management.py 62 0 100% zope/security/metaconfigure.py 108 0 100% zope/security/metadirectives.py 38 0 100% zope/security/permission.py 46 0 100% zope/security/protectclass.py 39 0 100% zope/security/proxy.py 164 19 88% 55, 86, 97, 119-121, 127-129, 143-144, 153-154, 158-159, 163-164, 298, 330 zope/security/simplepolicies.py 32 0 100% zope/security/zcml.py 43 0 100% --------------------------------------------------------------- TOTAL 1036 19 98% ---------------------------------------------------------------------- Ran 655 tests in 0.000s OK Building the documentation -------------------------- :mod:`zope.security` uses the nifty :mod:`Sphinx` documentation system for building its docs. Using the same virtualenv you set up to run the tests, you can build the docs: .. code-block:: sh $ /tmp/hack-zope.security/bin/pip install -e .[docs] ... $ cd docs $ PATH=/tmp/hack-zope.security/bin:$PATH make html sphinx-build -b html -d _build/doctrees . _build/html ... build succeeded. Build finished. The HTML pages are in _build/html. You can also test the code snippets in the documentation: .. code-block:: sh $ PATH=/tmp/hack-zope.security/bin:$PATH make doctest sphinx-build -b doctest -d _build/doctrees . _build/doctest ... running tests... Document: api/zcml ------------------ 1 items passed all tests: 23 tests in default 23 tests in 1 items. 23 passed and 0 failed. Test passed. Document: api/proxy ------------------- 1 items passed all tests: 11 tests in default 11 tests in 1 items. 11 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/permission ------------------------ 1 items passed all tests: 35 tests in default 35 tests in 1 items. 35 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Document: api/checker --------------------- 1 items passed all tests: 356 tests in default 356 tests in 1 items. 356 passed and 0 failed. Test passed. Document: api/decorator ----------------------- 1 items passed all tests: 53 tests in default 53 tests in 1 items. 53 passed and 0 failed. Test passed. 1 items passed all tests: 1 tests in default (cleanup code) 1 tests in 1 items. 1 passed and 0 failed. Test passed. Doctest summary =============== 478 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code Using :mod:`zc.buildout` ======================== Setting up the buildout ----------------------- :mod:`zope.security` ships with its own :file:`buildout.cfg` file and :file:`bootstrap.py` for setting up a development buildout: .. code-block:: sh $ /path/to/python2.6 bootstrap.py ... Generated script '.../bin/buildout' $ bin/buildout Develop: '/home/jrandom/projects/Zope/BTK/security/.' ... Running the tests ----------------- You can now run the tests: .. code-block:: sh $ bin/test --all Running zope.testing.testrunner.layer.UnitTests tests: Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Ran 643 tests with 0 failures and 0 errors in 0.000 seconds. Tearing down left over layers: Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Using :mod:`tox` ================ Running Tests on Multiple Python Versions ----------------------------------------- `tox `_ is a Python-based test automation tool designed to run tests against multiple Python versions. It creates a ``virtualenv`` for each configured version, installs the current package and configured dependencies into each ``virtualenv``, and then runs the configured commands. :mod:`zope.security` configures the following :mod:`tox` environments via its ``tox.ini`` file: - The ``py27``, ``py34``, ``py35``, ``pypy``, etc, environments builds a ``virtualenv`` with the appropriate interpreter, installs :mod:`zope.security` and dependencies, and runs the tests. - The ``py27-pure`` and ``py33-pure`` environments build a ``virtualenv`` with the appropriate interpreter, installs :mod:`zope.security` and dependencies **without compiling C extensions**, and runs the tests via ``python setup.py test -q``. - The ``coverage`` environment builds a ``virtualenv``, runs all the tests under :mod:`coverage`, and prints a report to stdout. - The ``docs`` environment builds a virtualenv and then builds the docs and exercises the doctest snippets. This example requires that you have a working ``python2.7`` on your path, as well as installing ``tox``: .. code-block:: sh $ tox -e py27 GLOB sdist-make: .../zope.security/setup.py py27 sdist-reinst: .../zope.security/.tox/dist/zope.security-4.0.2dev.zip py27 runtests: commands[0] ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... ---------------------------------------------------------------------- Ran 643 tests in 0.000s OK ___________________________________ summary ____________________________________ py26: commands succeeded congratulations :) Running ``tox`` with no arguments runs all the configured environments, including building the docs and testing their snippets: .. code-block:: sh $ tox GLOB sdist-make: .../zope.security/setup.py py26 sdist-reinst: .../zope.security/.tox/dist/zope.security-4.0.2dev.zip py26 runtests: commands[0] ... Doctest summary =============== 478 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code build succeeded. ___________________________________ summary ____________________________________ py26: commands succeeded py27: commands succeeded py27-pure: commands succeeded pypy: commands succeeded py32: commands succeeded py33: commands succeeded py33-pure: commands succeeded py34: commands succeeded coverage: commands succeeded docs: commands succeeded congratulations :) Contributing to :mod:`zope.security` ==================================== Submitting a Bug Report ----------------------- :mod:`zope.security` tracks its bugs on Github: https://github.com/zopefoundation/zope.security/issues Please submit bug reports and feature requests there. Sharing Your Changes -------------------- .. note:: Please ensure that all tests are passing before you submit your code. If possible, your submission should include new tests for new features or bug fixes, although it is possible that you may have tested your new code by updating existing tests. If have made a change you would like to share, the best route is to fork the Githb repository, check out your fork, make your changes on a branch in your fork, and push it. You can then submit a pull request from your branch: https://github.com/zopefoundation/zope.security/pulls ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/index.rst0000644000076500000240000000116714355021564015754 0ustar00jensstaff============================= zope.security Documentation ============================= Narrative Documentation ======================= .. toctree:: :maxdepth: 2 narr example proxy hacking .. toctree:: :maxdepth: 1 changes API Reference ============= .. toctree:: :maxdepth: 2 api/interfaces api/adapter api/checker api/decorator api/management api/permission api/protectclass api/proxy api/simplepolicies api/testing api/metaconfigure api/zcml ==================== Indices and tables ==================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/make.bat0000644000076500000240000001176414355021564015524 0ustar00jensstaff@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopesecurity.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopesecurity.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/narr.rst0000644000076500000240000001035614355021564015607 0ustar00jensstaff=========================== Overview and Introduction =========================== .. currentmodule:: zope.security.interfaces The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions =========== Principal --------- A generalization of a concept of a :class:`user `. Further specializations include :class:`groups of users ` and principals that :class:`know what groups they belong to `. All of these principals may interact with the system. Permission ---------- A kind of access, i.e. permission to READ vs. permission to WRITE. Fundamentally the whole security framework is organized around checking permissions on objects. Permissions are represented (and checked) as strings, with the exception of :data:`a constant ` that has the special meaning of "public", i.e., no checking needs to be done. There are :class:`permission objects ` that can be registered as zope.component utilities for validation, introspection, and producing :func:`lists of available permissions ` to help users assign them to objects. Purpose ======= The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and implicit security checks on attribute access for objects. Attribute names are mapped onto permission names when checking access and the implementation of the security check is defined by the security policy, which receives the object, the permission name, and an interaction. Interactions are objects that represent the use of the system by one or more principals. An interaction contains a list of participations, which represents the way a single principal participates in the interaction. An HTTP request is one example of a participation. Its important to keep in mind that the policy provided is just a default, and it can be substituted with one which doesn't care about principals or interactions at all. Framework Components ==================== Low Level Components -------------------- These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. Checkers ~~~~~~~~ A :class:`checker ` is associated with an object kind, and provides the hooks that map attribute checks onto permissions deferring to the security manager (which in turn defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. There are several implementation variants of checkers, such as checkers that grant access based on attribute names. Proxies ~~~~~~~ :class:`Wrappers around Python objects ` that implicitly guard access to their wrapped contents by delegating to their associated checker. Proxies are also viral in nature, in that values returned by proxies are also proxied. High Level Components ===================== Security Management ------------------- Provides accessors for :class:`setting up interactions ` and the :class:`global security policy `. :class:`Interaction ` ------------------------------------------------------------ An :class:`interaction ` represents zero or more principals manipulating or viewing (interacting with) the system. Interactions also provide :func:`a single method ` that accepts the object and the permission of the access being checked and is used to implement the application logic for the security framework. Participation ------------- Stores information about a single principal :class:`participating ` in the :class:`interaction `. Security Policy --------------- A :class:`security policy ` is used to create the interaction that will ultimately be responsible for security checking. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/docs/proxy.rst0000644000076500000240000003454714355021564016036 0ustar00jensstaff============================================= Untrusted Interpreters and Security Proxies ============================================= Untrusted programs are executed by untrusted interpreters. Untrusted interpreters make use of security proxies to prevent un-mediated access to assets. An untrusted interpreter defines an environment for running untrusted programs. All objects within the environment are either: - "safe" objects created internally by the environment or created in the course of executing the untrusted program, or - "basic" objects - security-proxied non-basic objects The environment includes proxied functions for accessing objects outside of the environment. These proxied functions provide the only way to access information outside the environment. Because these functions are proxied, as described below, any access to objects outside the environment is mediated by the target security functions. Safe objects are objects whose operations, except for attribute retrieval, and methods access only information stored within the objects or passed as arguments. Safe objects contained within the interpreter environment can contain only information that is already in the environment or computed directly from information that is included in the environment. For this reason, safe objects created within the environment cannot be used to directly access information outside the environment. Safe objects have some attributes that could (very) indirectly be used to access assets. For this reason, an untrusted interpreter always proxies the results of attribute accesses on a safe objects. Basic objects are safe objects that are used to represent elemental data values such as strings and numbers. Basic objects require a lower level of protection than non-basic objects, as will be described detail in a later section. Security proxies mediate all object operations. Any operation access is checked to see whether a subject is authorized to perform the operation. All operation results other than basic objects are, in turn, security proxied. Security proxies will be described in greater detail in a later section. Any operation on a security proxy that results in a non-basic object is also security proxied. All external resources needed to perform an operation are security proxied. Let's consider the trusted interpreter for evaluating URLs. In operation 1 of the example, the interpreter uses a proxied method for getting the system root object. Because the method is proxied, the result of calling the method and the operation is also proxied. The interpreter has a function for traversing objects. This function is proxied. When traversing an object, the function is passed an object and a name. In operation 2, the function is passed the result of operation 1, which is the proxied root object and the name 'A'. We may traverse an object by invoking an operation on it. For example, we may use an operation to get a sub-object. Because any operation on a proxied object returns a proxied object or a basic object, the result is either a proxied object or a basic object. Traversal may also look up a component. For example, in operation 1, we might look up a presentation component named "A" for the root object. In this case, the external object is not proxied, but, when it is returned from the traversal function, it is proxied (unless it is a a basic object) because the traversal function is proxied, and the result of calling a proxied function is proxied (unless the result is a basic object). Operation 3 proceeds in the same way. When we get to operation 4, we use a function for computing the default presentation of the result of operation 3. As with traversal, the result of getting the default presentation is either a proxied object or a basic object because the function for getting the default presentation is proxied. When we get to the last operation, we have either a proxied object or a basic object. If the result of operation 4 is a basic object, we simply convert it to a string and return it as the result page. If the result of operation 4 is a non-basic object, we invoke a render operation on it and return the result as a string. Note that an untrusted interpreter may or may not provide protection against excessive resource usage. Different interpreters will provide different levels of service with respect to limitations on resource usage. If an untrusted interpreter performs an attribute access, the trusted interpreter must proxy the result unless the result is a basic object. In summary, an untrusted interpreter assures that any access to assets is mediated through security proxies by creating an environment to run untrusted code and making sure that: - The only way to access anything from outside of the environment is to call functions that are proxied in the environment. - Results of any attribute access in the environment are proxied unless the results are basic objects. Security proxies ================ Security proxies are objects that wrap and mediate access to objects. The Python programming language used by Zope defines a set of specific named low-level operations. In addition to operations, Python objects can have attributes, used to represent data and methods. Attributes are accessed using a dot notation. Applications can, and usually do, define methods to provide extended object behaviors. Methods are accessed as attributes through the low-level operation named "__getattribute__". The Python code:: a.b() invokes 2 operations: 1. Use the low-level ``__getattribute__`` operation with the name "b". 2. Use the low-level ``__call__`` operation on the result of the first operation. For all operations except the ``__getattribute__`` and ``__setattribute__`` operations, security proxies have a permission value defined by the permission-declaration subsystem. Two special permission values indicate that access is either forbidden (never allowed) or public (always allowed). For all other permission values, the authorization subsystem is used to decide whether the subject has the permission for the proxied object. If the subject has the permission, then access to the operation is allowed. Otherwise, access is denied. For getting or setting attributes, a proxy has permissions for getting and a permission for setting attribute values for a given attribute name. As described above, these permissions may be one of the two special permission values indicating forbidden or public access, or another permission value that must be checked with the authorization system. For all objects, Zope defines the following operations to be always public: comparison "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" hash "__hash__" boolean value "__nonzero__" class introspection "__class__" interface introspection "__providedBy__", "__implements__" adaptation "__conform__" low-level string representation "__repr__" The result of an operation on a proxied object is a security proxy unless the result is a basic value. Basic objects ============= Basic objects are safe immutable objects that contain only immutable subobjects. Examples of basic objects include: - Strings, - Integers (long and normal), - Floating-point objects, - Date-time objects, - Boolean objects (True and False), and - The special (nil) object, None. Basic objects are safe, so, as described earlier, operations on basic objects, other than attribute access, use only information contained within the objects or information passed to them. For this reason, basic objects cannot be used to access information outside of the untrusted interpreter environment. The decision not to proxy basic objects is largely an optimization. It allows low-level safe computation to be performed without unnecessary overhead, Note that a basic object could contain sensitive information, but such a basic object would need to be obtained by making a call on a proxied object. Therefore, the access to the basic object in the first place is mediated by the security functions. Rationale for mutable safe objects ================================== Some safe objects are not basic. For these objects, we proxy the objects if they originate from outside of the environment. We do this for two reasons: 1. Non-basic objects from outside the environment need to be proxied to prevent unauthorized access to information. 2. We need to prevent un-mediated change of information from outside of the environment. We don't proxy safe objects created within the environment. This is safe to do because such safe objects can contain and provide access to information already in the environment. Sometimes the interpreter or the interpreted program needs to be able to create simple data containers to hold information computed in the course of the program execution. Several safe container types are provided for this purpose. .. _proxy-known-issues: Known Issues With Proxies ========================= Security proxies (proxies in general) are not perfect in Python. There are some things that they cannot transparently proxy. .. _isinstance-and-proxies: isinstance and proxies ---------------------- A proxied object cannot proxy its type (although it does proxy its ``__class__``): .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> class Object(object): ... pass >>> target = Object() >>> target.__class__ >>> type(target) >>> proxy = ProxyFactory(target, None) >>> proxy.__class__ >>> type(proxy) <... 'zope.security...Proxy...'> This means that the builtin :func:`isinstance` may return unexpected results: .. doctest:: >>> isinstance(target, Object) True >>> isinstance(proxy, Object) False There are two workarounds. The safest is to use :func:`zope.security.proxy.isinstance`, which takes specifically this into account (in modules that will be dealing with a number of proxies, it is common to simply place ``from zope.security.proxy import isinstance`` at the top of the file to override the builtin :func:`isinstance`; we won't show that here for clarity): .. doctest:: >>> import zope.security.proxy >>> zope.security.proxy.isinstance(target, Object) True >>> zope.security.proxy.isinstance(proxy, Object) True Alternatively, you can manually remove the security proxy (or indeed, all proxies) with :func:`zope.security.proxy.removeSecurityProxy` or :func:`zope.proxy.removeAllProxies`, respectively, before calling :func:`isinstance`: .. doctest:: >>> from zope.security.proxy import removeSecurityProxy >>> isinstance(removeSecurityProxy(target), Object) True >>> isinstance(removeSecurityProxy(proxy), Object) True issubclass and proxies ---------------------- Security proxies will proxy the return value of ``__class__``: it will be a proxy around the real class of the proxied value. This causes failures with ``issubclass``: .. doctest:: >>> from zope.security.proxy import ProxyFactory >>> class Object(object): ... pass >>> target = Object() >>> target.__class__ is Object True >>> proxy = ProxyFactory(target, None) >>> proxy.__class__ >>> proxy.__class__ is Object False >>> issubclass(proxy.__class__, Object) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class Although the above is a contrived example, using :class:`abstract base classes ` can cause it to arise quite unexpectedly: .. doctest:: >>> try: ... from collections.abc import Mapping ... except ImportError: # PY2 ... from collections import Mapping >>> from abc import ABCMeta >>> isinstance(Mapping, ABCMeta) True >>> isinstance(proxy, Mapping) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class In this case, the workarounds described :ref:`above ` also work: .. doctest:: >>> zope.security.proxy.isinstance(proxy, Mapping) False >>> isinstance(removeSecurityProxy(proxy), Mapping) False .. We need to clean up the caching that ABC does on .. pure-python platforms to make sure that we get where .. we expect to be when we construct LogRecord; otherwise .. the ProxyMetaclass may be in the negative cache, bypassing .. the issubclass() calls we expect. .. doctest:: :hide: >>> if hasattr(ABCMeta, '_abc_invalidation_counter'): ... ABCMeta._abc_invalidation_counter += 1 logging ~~~~~~~ Starting with `Python 2.7.7 `_, the :class:`logging.LogRecord` makes exactly the above ``isinstance`` call: .. doctest:: >>> from logging import LogRecord >>> LogRecord("name", 1, "/path/to/file", 1, ... "The message %s", (proxy,), None) Traceback (most recent call last): ... TypeError: issubclass() arg 1 must be a class `Possible workarounds include `_: - Carefully removing security proxies of objects before passing them to the logging system. - Monkey-patching the logging system to use :func:`zope.security.proxy.isinstance` which does this automatically:: import zope.security.proxy import logging logging.isinstance = zope.security.proxy.isinstance - Using :func:`logging.setLogRecordfactory` to set a custom ``LogRecord`` subclass that unwraps any security proxies before they are given to the super class. Note that this is only available on Python 3. On Python 2, it might be possible to achieve a similar result with a custom :func:`logger class `: .. doctest:: >>> from zope.security.proxy import removeSecurityProxy >>> class UnwrappingLogRecord(LogRecord): ... def __init__(self, name, level, pathname, lineno, ... msg, args, exc_info, *largs, **kwargs): ... args = [removeSecurityProxy(x) for x in args] ... LogRecord.__init__(self, name, level, pathname, ... lineno, msg, args, exc_info, *largs, **kwargs) ... def __repr__(self): ... return '' >>> UnwrappingLogRecord("name", 1, "/path/to/file", 1, ... "The message %s", (proxy,), None) Each specific application will have to determine what solution is correct for its security model. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1696507664.0 zope_security-7.3/docs/requirements.txt0000644000076500000240000000012214507523420017362 0ustar00jensstaffSphinx repoze.sphinx.autointerface sphinx_rtd_theme>1 docutils<0.19 zope.security ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.0926516 zope_security-7.3/include/0000755000076500000240000000000014677253221014605 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.092704 zope_security-7.3/include/zope.proxy/0000755000076500000240000000000014677253221016742 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.0927525 zope_security-7.3/include/zope.proxy/zope/0000755000076500000240000000000014677253221017717 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1039128 zope_security-7.3/include/zope.proxy/zope/proxy/0000755000076500000240000000000014677253221021100 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1681977856.0 zope_security-7.3/include/zope.proxy/zope/proxy/proxy.h0000644000076500000240000000266114420171000022413 0ustar00jensstaff#ifndef _proxy_H_ #define _proxy_H_ 1 typedef struct { PyObject_HEAD PyObject *proxy_object; } ProxyObject; #define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object) typedef struct { PyTypeObject *proxytype; int (*check)(PyObject *obj); PyObject *(*create)(PyObject *obj); PyObject *(*getobject)(PyObject *proxy); } ProxyInterface; #ifndef PROXY_MODULE /* These are only defined in the public interface, and are not * available within the module implementation. There we use the * classic Python/C API only. */ static ProxyInterface *_proxy_api = NULL; static int Proxy_Import(void) { if (_proxy_api == NULL) { PyObject *m = PyImport_ImportModule("zope.proxy"); if (m != NULL) { PyObject *tmp = PyObject_GetAttrString(m, "_CAPI"); if (tmp != NULL) { if (PyCapsule_CheckExact(tmp)) _proxy_api = (ProxyInterface *) PyCapsule_GetPointer(tmp, NULL); Py_DECREF(tmp); } } } return (_proxy_api == NULL) ? -1 : 0; } #define ProxyType (*_proxy_api->proxytype) #define Proxy_Check(obj) (_proxy_api->check((obj))) #define Proxy_CheckExact(obj) ((obj)->ob_type == ProxyType) #define Proxy_New(obj) (_proxy_api->create((obj))) #define Proxy_GetObject(proxy) (_proxy_api->getobject((proxy))) #endif /* PROXY_MODULE */ #endif /* _proxy_H_ */ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/pyproject.toml0000644000076500000240000000130514672224647016102 0ustar00jensstaff# # Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [build-system] requires = ["setuptools<74"] build-backend = "setuptools.build_meta" [tool.coverage.run] branch = true source = ["zope.security"] relative_files = true [tool.coverage.report] fail_under = 99.5 precision = 2 ignore_errors = true show_missing = true exclude_lines = ["pragma: no cover", "pragma: nocover", "except ImportError:", "raise NotImplementedError", "if __name__ == '__main__':", "self.fail", "raise AssertionError", "raise unittest.Skip"] [tool.coverage.html] directory = "parts/htmlcov" [tool.coverage.paths] source = ["src/", ".tox/*/lib/python*/site-packages/", ".tox/pypy*/site-packages/"] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1160586 zope_security-7.3/setup.cfg0000644000076500000240000000120614677253221015002 0ustar00jensstaff[zest.releaser] create-wheel = no [flake8] doctests = 1 per-file-ignores = src/zope/security/__init__.py: F401 [check-manifest] ignore = .editorconfig .meta.toml docs/_build/html/_sources/* docs/_build/doctest/* docs/_build/*/*/*/*/* docs/_build/*/*/*/* docs/_build/*/*/* docs/_build/*/* docs/_build/* src/coverage.xml [isort] force_single_line = True combine_as_imports = True sections = FUTURE,STDLIB,THIRDPARTY,ZOPE,FIRSTPARTY,LOCALFOLDER known_third_party = docutils, pkg_resources, pytz known_zope = known_first_party = default_section = ZOPE line_length = 79 lines_after_imports = 2 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878266.0 zope_security-7.3/setup.py0000644000076500000240000001341414677252172014703 0ustar00jensstaff############################################################################# # # Copyright (c) 2006 Zope Foundation 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 package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.security package """ import os import platform import sys from distutils.errors import CCompilerError from distutils.errors import DistutilsExecError from distutils.errors import DistutilsPlatformError from setuptools import Extension from setuptools import find_packages from setuptools import setup from setuptools.command.build_ext import build_ext version = '7.3' class optional_build_ext(build_ext): """This class subclasses build_ext and allows the building of C extensions to fail. """ def run(self): try: build_ext.run(self) except DistutilsPlatformError as e: self._unavailable(e) def build_extension(self, ext): try: build_ext.build_extension(self, ext) except (CCompilerError, DistutilsExecError, OSError) as e: self._unavailable(e) def _unavailable(self, e): print('*' * 80) print("""WARNING: An optional code optimization (C extension) could not be compiled. Optimizations for this package will not be available!""") print() print(e) print('*' * 80) here = os.path.abspath(os.path.dirname(__file__)) def read(*rnames): with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: return f.read() codeoptimization = [ Extension( "zope.security._proxy", include_dirs=[os.path.join('include', 'zope.proxy')], sources=[os.path.join('src', 'zope', 'security', "_proxy.c")] ), Extension( "zope.security._zope_security_checker", [os.path.join('src', 'zope', 'security', "_zope_security_checker.c")] ), ] # Jython cannot build the C optimizations, while on PyPy they are # anti-optimizations (the C extension compatibility layer is known-slow, # and defeats JIT opportunities). py_impl = getattr(platform, 'python_implementation', lambda: None) is_pypy = py_impl() == 'PyPy' is_jython = 'java' in sys.platform if is_pypy or is_jython: ext_modules = [] else: ext_modules = codeoptimization TESTS_REQUIRE = [ 'BTrees', 'zope.component', 'zope.configuration', 'zope.location', 'zope.testing', 'zope.testrunner', ] setup(name='zope.security', version=version, author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Security Framework', long_description=( read('README.rst') + '\n\n' + read('CHANGES.rst') ), keywords="zope security policy principal permission", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope :: 3', ], url='http://github.com/zopefoundation/zope.security', project_urls={ 'Documentation': 'https://zopesecurity.readthedocs.io', 'Issue Tracker': ('https://github.com/zopefoundation' '/zope.security/issues'), 'Sources': 'https://github.com/zopefoundation/zope.security', }, license='ZPL 2.1', packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zope'], cmdclass={ 'build_ext': optional_build_ext, }, ext_modules=ext_modules, python_requires='>=3.8', install_requires=[ 'setuptools', 'zope.component', 'zope.i18nmessageid', 'zope.interface', 'zope.location', 'zope.proxy >= 5.2', 'zope.schema >= 4.2.0', ], extras_require={ 'pytz': [ "pytz" ], 'untrustedpython': [ 'zope.untrustedpython >= 5.0.dev0', ], 'zcml': [ 'zope.configuration' ], 'test': TESTS_REQUIRE, 'docs': [ 'Sphinx', 'repoze.sphinx.autointerface', 'sphinx_rtd_theme', 'zope.configuration', 'zope.testing', ], }, include_package_data=True, zip_safe=False, ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.0929222 zope_security-7.3/src/0000755000076500000240000000000014677253221013751 5ustar00jensstaff././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.104031 zope_security-7.3/src/zope/0000755000076500000240000000000014677253221014726 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/__init__.py0000644000076500000240000000007014355021564017030 0ustar00jensstaff__import__('pkg_resources').declare_namespace(__name__) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1100028 zope_security-7.3/src/zope/security/0000755000076500000240000000000014677253221016575 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/__init__.py0000644000076500000240000000176114355021564020707 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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. # ############################################################################## """Base security system """ # We need the injection of DecoratedSecurityCheckerDescriptor into # zope.location's LocationProxy as soon someone uses security proxies by # importing zope.security.proxy: import zope.security.decorator from zope.security.checker import canAccess from zope.security.checker import canWrite from zope.security.management import checkPermission ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878194.0 zope_security-7.3/src/zope/security/_compat.py0000644000076500000240000000323114677252062020572 0ustar00jensstaff############################################################################## # # Copyright (c) 2013 Zope Foundation 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. # ############################################################################## """ Python 2 / 3 compatibility """ import os import platform py_impl = getattr(platform, 'python_implementation', lambda: None) PYPY = py_impl() == 'PyPy' PURE_PYTHON = int(os.environ.get('PURE_PYTHON', PYPY)) class implementer_if_needed: # 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) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/_definitions.py0000644000076500000240000000174514355021564021624 0ustar00jensstaff############################################################################## # # Copyright (c) 2005 Zope Foundation 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. # ############################################################################## """Common definitions to avoid circular imports """ import threading import zope.interface from zope.security import interfaces thread_local = threading.local() @zope.interface.implementer(interfaces.ISystemPrincipal) class SystemUser: id = 'zope.security.management.system_user' title = 'System' description = '' system_user = SystemUser() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1681800803.0 zope_security-7.3/src/zope/security/_proxy.c0000644000076500000240000006410014417437143020261 0ustar00jensstaff/***************************************************************************** * * Copyright (c) 2003, 2004 Zope Foundation 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. * ****************************************************************************** Security Proxy Implementation */ #include #include "zope/proxy/proxy.h" static PyObject *__class__str = 0, *__name__str = 0, *__module__str = 0; #define PyInt_FromLong PyLong_FromLong #define IS_STRING PyUnicode_Check #define MAKE_STRING(name) PyBytes_AS_STRING( \ PyUnicode_AsUTF8String(name)) #define FROM_STRING PyUnicode_FromString #define FROM_STRING_FORMAT PyUnicode_FromFormat #define INTERN PyUnicode_InternFromString #define MOD_ERROR_VAL NULL #define MOD_SUCCESS_VAL(val) val #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #define DECLARE_STRING(N) static PyObject *str_##N DECLARE_STRING(__3pow__); DECLARE_STRING(__bool__); DECLARE_STRING(__call__); DECLARE_STRING(check); DECLARE_STRING(check_getattr); DECLARE_STRING(check_setattr); DECLARE_STRING(__cmp__); DECLARE_STRING(__contains__); DECLARE_STRING(__delitem__); DECLARE_STRING(__getitem__); DECLARE_STRING(__hash__); DECLARE_STRING(__iter__); DECLARE_STRING(__len__); DECLARE_STRING(__next__); DECLARE_STRING(op_abs); DECLARE_STRING(op_add); DECLARE_STRING(op_and); DECLARE_STRING(op_divmod); DECLARE_STRING(op_float); DECLARE_STRING(op_floordiv); DECLARE_STRING(op_iadd); DECLARE_STRING(op_iand); DECLARE_STRING(op_ifloordiv); DECLARE_STRING(op_ilshift); DECLARE_STRING(op_imod); DECLARE_STRING(op_imul); DECLARE_STRING(op_int); DECLARE_STRING(op_invert); DECLARE_STRING(op_ior); DECLARE_STRING(op_ipow); DECLARE_STRING(op_irshift); DECLARE_STRING(op_isub); DECLARE_STRING(op_itruediv); DECLARE_STRING(op_ixor); DECLARE_STRING(op_lshift); DECLARE_STRING(op_mod); DECLARE_STRING(op_mul); DECLARE_STRING(op_neg); DECLARE_STRING(op_or); DECLARE_STRING(op_pos); DECLARE_STRING(op_radd); DECLARE_STRING(op_rand); DECLARE_STRING(op_rdivmod); DECLARE_STRING(op_rfloordiv); DECLARE_STRING(op_rlshift); DECLARE_STRING(op_rmod); DECLARE_STRING(op_rmul); DECLARE_STRING(op_ror); DECLARE_STRING(op_rrshift); DECLARE_STRING(op_rshift); DECLARE_STRING(op_rsub); DECLARE_STRING(op_rtruediv); DECLARE_STRING(op_rxor); DECLARE_STRING(op_sub); DECLARE_STRING(op_truediv); DECLARE_STRING(op_xor); DECLARE_STRING(__pow__); DECLARE_STRING(proxy); DECLARE_STRING(__repr__); DECLARE_STRING(__rpow__); DECLARE_STRING(__setitem__); DECLARE_STRING(__str__); typedef struct { ProxyObject proxy; PyObject *proxy_checker; } SecurityProxy; #define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); } #undef Proxy_Check #define Proxy_Check(proxy) \ PyObject_TypeCheck(proxy, &SecurityProxyType) static PyTypeObject SecurityProxyType; /* * Machinery to call the checker. */ static int check(SecurityProxy *self, PyObject *meth, PyObject *name) { PyObject *r; /* If the checker has __setitem__, we call it's slot rather than calling check or check_getattr. Why? Because calling operator slots is much faster than calling methods and security checks are done so often that speed matters. So we have this hack of using almost-arbitrary operations to represent methods that we call alot. */ if (self->proxy_checker->ob_type->tp_as_mapping != NULL && self->proxy_checker->ob_type->tp_as_mapping->mp_ass_subscript != NULL && meth != str_check_setattr) return self->proxy_checker->ob_type->tp_as_mapping-> mp_ass_subscript(self->proxy_checker, self->proxy.proxy_object, name); r = PyObject_CallMethodObjArgs(self->proxy_checker, meth, self->proxy.proxy_object, name, NULL); if (r == NULL) return -1; Py_DECREF(r); return 0; } /* If the checker has __getitem__, we call it's slot rather than calling proxy. Why? Because calling operator slots is much faster than calling methods and security checks are done so often that speed matters. So we have this hack of using almost-arbitrary operations to represent methods that we call alot. */ #define PROXY_RESULT(self, result) \ if (result != NULL) { \ PyObject *tmp; \ if (self->proxy_checker->ob_type->tp_as_mapping != NULL \ && self->proxy_checker->ob_type->tp_as_mapping->mp_subscript != NULL) \ tmp = self->proxy_checker->ob_type->tp_as_mapping-> \ mp_subscript(self->proxy_checker, result); \ else \ tmp = PyObject_CallMethodObjArgs(self->proxy_checker, str_proxy, \ result, NULL); \ Py_DECREF(result); \ result = tmp; \ } typedef PyObject *(*function1)(PyObject *); static PyObject * check1(SecurityProxy *self, PyObject *opname, function1 operation) { PyObject *result = NULL; if (check(self, str_check, opname) >= 0) { result = operation(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * check2(PyObject *self, PyObject *other, PyObject *opname, PyObject *ropname, binaryfunc operation) { PyObject *result = NULL; if (Proxy_Check(self)) { if (check((SecurityProxy*)self, str_check, opname) >= 0) { result = operation(((SecurityProxy*)self)->proxy.proxy_object, other); PROXY_RESULT(((SecurityProxy*)self), result); } } else if (Proxy_Check(other)) { if (check((SecurityProxy*)other, str_check, ropname) >= 0) { result = operation(self, ((SecurityProxy*)other)->proxy.proxy_object); PROXY_RESULT(((SecurityProxy*)other), result); } } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return result; } static PyObject * check2i(SecurityProxy *self, PyObject *other, PyObject *opname, binaryfunc operation) { PyObject *result = NULL; if (check(self, str_check, opname) >= 0) { result = operation(self->proxy.proxy_object, other); if (result == self->proxy.proxy_object) { /* If the operation was really carried out inplace, don't create a new proxy, but use the old one. */ Py_DECREF(result); Py_INCREF((PyObject *)self); result = (PyObject *)self; } else PROXY_RESULT(self, result); } return result; } #define UNOP(NAME, CALL) \ static PyObject *proxy_##NAME(PyObject *self) \ { return check1((SecurityProxy *)self, str_op_##NAME, CALL); } #define BINOP(NAME, CALL) \ static PyObject *proxy_##NAME(PyObject *self, PyObject *other) \ { return check2(self, other, str_op_##NAME, str_op_r##NAME, CALL); } #define INPLACE(NAME, CALL) \ static PyObject *proxy_i##NAME(PyObject *self, PyObject *other) \ { return check2i((SecurityProxy *)self, other, str_op_i##NAME, CALL); } /* * Slot methods. */ static PyObject * proxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"object", "checker", 0}; SecurityProxy *self; PyObject *object; PyObject *checker; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:_Proxy.__new__", kwlist, &object, &checker)) return NULL; if (checker == Py_None) { PyErr_SetString(PyExc_ValueError, "None passed as proxy checker"); return NULL; } self = (SecurityProxy *)type->tp_alloc(type, 0); if (self == NULL) return NULL; Py_INCREF(object); Py_INCREF(checker); self->proxy.proxy_object = object; self->proxy_checker = checker; return (PyObject *)self; } /* This is needed to avoid calling the base class tp_init, which we don't need. */ static int proxy_init(PyObject *self, PyObject *args, PyObject *kw) { return 0; } static int proxy_clear(SecurityProxy *self) { CLEAR(self->proxy_checker); SecurityProxyType.tp_base->tp_clear((PyObject*)self); return 0; } static void proxy_dealloc(SecurityProxy *self) { PyObject_GC_UnTrack((PyObject*)self); proxy_clear(self); SecurityProxyType.tp_base->tp_dealloc((PyObject*)self); } static int proxy_traverse(SecurityProxy *self, visitproc visit, void *arg) { Py_VISIT(self->proxy.proxy_object); Py_VISIT(self->proxy_checker); return 0; } static PyObject * proxy_richcompare(SecurityProxy* self, PyObject* other, int op) { PyObject *result = NULL; result = PyObject_RichCompare(self->proxy.proxy_object, other, op); if (result == Py_True || result == Py_False) return result; PROXY_RESULT(self, result); return result; } static PyObject * proxy_iter(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___iter__) >= 0) { result = PyObject_GetIter(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * proxy_iternext(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check_getattr, str___next__) >= 0) { result = PyIter_Next(self->proxy.proxy_object); PROXY_RESULT(self, result); } return result; } static PyObject * proxy_getattro(SecurityProxy *self, PyObject *name) { PyObject *result = NULL; if (check(self, str_check_getattr, name) >= 0) { result = PyObject_GetAttr(self->proxy.proxy_object, name); PROXY_RESULT(self, result); } return result; } static int proxy_setattro(SecurityProxy *self, PyObject *name, PyObject *value) { if (check(self, str_check_setattr, name) >= 0) return PyObject_SetAttr(self->proxy.proxy_object, name, value); return -1; } static PyObject * default_repr(PyObject *object) { PyObject *klass, *name = 0, *module = 0, *result = 0; char *sname, *smodule; klass = PyObject_GetAttr(object, __class__str); if (klass == NULL) return NULL; name = PyObject_GetAttr(klass, __name__str); if (name == NULL) goto err; sname = MAKE_STRING(name); if (sname == NULL) goto err; module = PyObject_GetAttr(klass, __module__str); if (module != NULL) { smodule = MAKE_STRING(module); if (smodule == NULL) goto err; result = FROM_STRING_FORMAT("", smodule, sname, object); } else { PyErr_Clear(); result = FROM_STRING_FORMAT("", sname, object); } err: Py_DECREF(klass); Py_XDECREF(name); Py_XDECREF(module); return result; } static PyObject * proxy_str(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___str__) >= 0) { result = PyObject_Str(self->proxy.proxy_object); } else { PyErr_Clear(); result = default_repr(self->proxy.proxy_object); } return result; } static PyObject * proxy_repr(SecurityProxy *self) { PyObject *result = NULL; if (check(self, str_check, str___repr__) >= 0) { result = PyObject_Repr(self->proxy.proxy_object); } else { PyErr_Clear(); result = default_repr(self->proxy.proxy_object); } return result; } static long proxy_hash(SecurityProxy *self) { return PyObject_Hash(self->proxy.proxy_object); } static PyObject * proxy_call(SecurityProxy *self, PyObject *args, PyObject *kwds) { PyObject *result = NULL; if (check(self, str_check, str___call__) >= 0) { result = PyObject_Call(self->proxy.proxy_object, args, kwds); PROXY_RESULT(self, result); } return result; } /* * Number methods. */ #define NUMBER_METHOD(M) \ static PyObject * \ call_##M(PyObject *self) \ { \ PyNumberMethods *nb = self->ob_type->tp_as_number; \ if (nb == NULL || nb->nb_##M == NULL) { \ PyErr_SetString(PyExc_TypeError, \ "object can't be converted to " #M); \ return NULL; \ } \ return nb->nb_##M(self); \ } NUMBER_METHOD(int) NUMBER_METHOD(float) static PyObject * call_ipow(PyObject *self, PyObject *other) { /* PyNumber_InPlacePower has three args. How silly. :-) */ return PyNumber_InPlacePower(self, other, Py_None); } BINOP(add, PyNumber_Add) BINOP(sub, PyNumber_Subtract) BINOP(mul, PyNumber_Multiply) BINOP(mod, PyNumber_Remainder) BINOP(divmod, PyNumber_Divmod) static PyObject * proxy_pow(PyObject *self, PyObject *other, PyObject *modulus) { PyObject *result = NULL; if (Proxy_Check(self)) { if (check((SecurityProxy*)self, str_check, str___pow__) >= 0) { result = PyNumber_Power(((SecurityProxy*)self)->proxy.proxy_object, other, modulus); PROXY_RESULT(((SecurityProxy*)self), result); } } else if (Proxy_Check(other)) { if (check((SecurityProxy*)other, str_check, str___rpow__) >= 0) { result = PyNumber_Power(self, ((SecurityProxy*)other)->proxy.proxy_object, modulus); PROXY_RESULT(((SecurityProxy*)other), result); } } else if (modulus != NULL && Proxy_Check(modulus)) { if (check((SecurityProxy*)modulus, str_check, str___3pow__) >= 0) { result = PyNumber_Power(self, other, ((SecurityProxy*)modulus)->proxy.proxy_object); PROXY_RESULT(((SecurityProxy*)modulus), result); } } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return result; } BINOP(lshift, PyNumber_Lshift) BINOP(rshift, PyNumber_Rshift) BINOP(and, PyNumber_And) BINOP(xor, PyNumber_Xor) BINOP(or, PyNumber_Or) UNOP(neg, PyNumber_Negative) UNOP(pos, PyNumber_Positive) UNOP(abs, PyNumber_Absolute) static int proxy_bool(PyObject *self) { return PyObject_IsTrue(((SecurityProxy*)self)->proxy.proxy_object); } UNOP(invert, PyNumber_Invert) UNOP(int, call_int) UNOP(float, call_float) INPLACE(add, PyNumber_InPlaceAdd) INPLACE(sub, PyNumber_InPlaceSubtract) INPLACE(mul, PyNumber_InPlaceMultiply) INPLACE(mod, PyNumber_InPlaceRemainder) INPLACE(pow, call_ipow) INPLACE(lshift, PyNumber_InPlaceLshift) INPLACE(rshift, PyNumber_InPlaceRshift) INPLACE(and, PyNumber_InPlaceAnd) INPLACE(xor, PyNumber_InPlaceXor) INPLACE(or, PyNumber_InPlaceOr) BINOP(floordiv, PyNumber_FloorDivide) BINOP(truediv, PyNumber_TrueDivide) INPLACE(floordiv, PyNumber_InPlaceFloorDivide) INPLACE(truediv, PyNumber_InPlaceTrueDivide) /* * Sequence methods. */ static Py_ssize_t proxy_length(SecurityProxy *self) { if (check(self, str_check, str___len__) >= 0) return PyObject_Length(self->proxy.proxy_object); return -1; } static PyObject * proxy_length_hint(SecurityProxy *self) { PyObject *result = NULL; result = PyObject_CallMethod(self->proxy.proxy_object, "__length_hint__", NULL); if (result == NULL) { if (PyErr_ExceptionMatches(PyExc_AttributeError)) { PyErr_Clear(); result = Py_NotImplemented; Py_INCREF(result); } } return result; } /* sq_item and sq_ass_item may be called by PySequece_{Get,Set}Item(). */ static PyObject *proxy_getitem(SecurityProxy *, PyObject *); static int proxy_setitem(SecurityProxy *, PyObject *, PyObject *); static PyObject * proxy_igetitem(SecurityProxy *self, Py_ssize_t i) { PyObject *key = PyInt_FromLong(i); PyObject *res = NULL; if (key != NULL) { res = proxy_getitem(self, key); Py_DECREF(key); } return res; } static int proxy_isetitem(SecurityProxy *self, Py_ssize_t i, PyObject *value) { PyObject *key = PyInt_FromLong(i); int res = -1; if (key != NULL) { res = proxy_setitem(self, key, value); Py_DECREF(key); } return res; } static int proxy_contains(SecurityProxy *self, PyObject *value) { if (check(self, str_check, str___contains__) >= 0) return PySequence_Contains(self->proxy.proxy_object, value); return -1; } /* * Mapping methods. */ static PyObject * proxy_getitem(SecurityProxy *self, PyObject *key) { PyObject *result = NULL; if (check(self, str_check, str___getitem__) >= 0) { result = PyObject_GetItem(self->proxy.proxy_object, key); PROXY_RESULT(self, result); } return result; } static int proxy_setitem(SecurityProxy *self, PyObject *key, PyObject *value) { if (value == NULL) { if (check(self, str_check, str___delitem__) >= 0) return PyObject_DelItem(self->proxy.proxy_object, key); } else { if (check(self, str_check, str___setitem__) >= 0) return PyObject_SetItem(self->proxy.proxy_object, key, value); } return -1; } /* * Normal methods. */ static PyNumberMethods proxy_as_number = { proxy_add, /* nb_add */ proxy_sub, /* nb_subtract */ proxy_mul, /* nb_multiply */ proxy_mod, /* nb_remainder */ proxy_divmod, /* nb_divmod */ proxy_pow, /* nb_power */ proxy_neg, /* nb_negative */ proxy_pos, /* nb_positive */ proxy_abs, /* nb_absolute */ proxy_bool, /* nb_bool */ proxy_invert, /* nb_invert */ proxy_lshift, /* nb_lshift */ proxy_rshift, /* nb_rshift */ proxy_and, /* nb_and */ proxy_xor, /* nb_xor */ proxy_or, /* nb_or */ proxy_int, /* nb_int */ 0, /* nb_reserved, formerly nb_long */ proxy_float, /* nb_float */ /* Added in release 2.0 */ /* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */ proxy_iadd, /* nb_inplace_add */ proxy_isub, /* nb_inplace_subtract */ proxy_imul, /* nb_inplace_multiply */ proxy_imod, /* nb_inplace_remainder */ (ternaryfunc)proxy_ipow, /* nb_inplace_power */ proxy_ilshift, /* nb_inplace_lshift */ proxy_irshift, /* nb_inplace_rshift */ proxy_iand, /* nb_inplace_and */ proxy_ixor, /* nb_inplace_xor */ proxy_ior, /* nb_inplace_or */ /* Added in release 2.2 */ /* These require the Py_TPFLAGS_HAVE_CLASS flag */ proxy_floordiv, /* nb_floor_divide */ proxy_truediv, /* nb_true_divide */ proxy_ifloordiv, /* nb_inplace_floor_divide */ proxy_itruediv, /* nb_inplace_true_divide */ }; static PySequenceMethods proxy_as_sequence = { (lenfunc)proxy_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)proxy_igetitem, /* sq_item */ 0, /* sq_slice, unused in PY3 */ (ssizeobjargproc)proxy_isetitem, /* sq_ass_item */ 0, /* sq_ass_slice, unused in PY3 */ (objobjproc)proxy_contains, /* sq_contains */ }; static PyMappingMethods proxy_as_mapping = { (lenfunc)proxy_length, /* mp_length */ (binaryfunc)proxy_getitem, /* mp_subscript */ (objobjargproc)proxy_setitem, /* mp_ass_subscript */ }; static char proxy_doc[] = "\ Security proxy class. Constructor: _Proxy(object, checker)\n\ where 'object' is an arbitrary object, and 'checker' is an object\n\ whose signature is described by the IChecker interface.\n\ A checker should have the following methods:\n\ check(object, operation) # operation is e.g. '__add__' or '__hash__'\n\ check_getattr(object, name)\n\ check_setattr(object, name)\n\ proxy(object)\n\ The check methods should raise an exception if the operation is\n\ disallowed. The proxy method should return a proxy for the object\n\ if one is needed, otherwise the object itself.\n\ "; static PyMethodDef proxy_methods[] = { {"__length_hint__", (PyCFunction)proxy_length_hint, METH_NOARGS, "Guess the length of the object"}, {NULL, NULL} /* sentinel */ }; static PyTypeObject SecurityProxyType = { PyVarObject_HEAD_INIT(NULL, 0) "zope.security._proxy._Proxy", sizeof(SecurityProxy), 0, (destructor)proxy_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved, was tp_compare */ (reprfunc)proxy_repr, /* tp_repr */ &proxy_as_number, /* tp_as_number */ &proxy_as_sequence, /* tp_as_sequence */ &proxy_as_mapping, /* tp_as_mapping */ (hashfunc)proxy_hash, /* tp_hash */ (ternaryfunc)proxy_call, /* tp_call */ (reprfunc)proxy_str, /* tp_str */ (getattrofunc)proxy_getattro, /* tp_getattro */ (setattrofunc)proxy_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ proxy_doc, /* tp_doc */ (traverseproc)proxy_traverse, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)proxy_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)proxy_iter, /* tp_iter */ (iternextfunc)proxy_iternext, /* tp_iternext */ proxy_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ proxy_init, /* tp_init */ 0, /*PyType_GenericAlloc,*/ /* tp_alloc */ proxy_new, /* tp_new */ 0, /*_PyObject_GC_Del,*/ /* tp_free */ }; static PyObject * module_getChecker(PyObject *self, PyObject *arg) { PyObject *result; if (!Proxy_Check(arg)) { PyErr_SetString(PyExc_TypeError, "getChecker argument must be a _Proxy"); return NULL; } result = ((SecurityProxy*)arg)->proxy_checker; Py_INCREF(result); return result; } static PyObject * module_getObject(PyObject *self, PyObject *arg) { PyObject *result; if (!Proxy_Check(arg)) result = arg; else result = ((SecurityProxy*)arg)->proxy.proxy_object; Py_INCREF(result); return result; } static char module___doc__[] = "Security proxy implementation."; static PyMethodDef module_functions[] = { {"getChecker", module_getChecker, METH_O, "get checker from proxy"}, {"getObject", module_getObject, METH_O, "Get the proxied object\n\nReturn the original object if not proxied."}, {NULL} }; MOD_INIT(_proxy) { PyObject *m; MOD_DEF(m, "_proxy", module___doc__, module_functions) if (m == NULL) return MOD_ERROR_VAL; if (Proxy_Import() < 0) return MOD_ERROR_VAL; #define INIT_STRING(S) \ if((str_##S = INTERN(#S)) == NULL) return MOD_ERROR_VAL #define INIT_STRING_OP(S) \ if((str_op_##S = INTERN("__" #S "__")) == NULL) return MOD_ERROR_VAL INIT_STRING(__3pow__); INIT_STRING(__bool__); INIT_STRING(__call__); INIT_STRING(check); INIT_STRING(check_getattr); INIT_STRING(check_setattr); INIT_STRING(__cmp__); INIT_STRING(__contains__); INIT_STRING(__delitem__); INIT_STRING(__getitem__); INIT_STRING(__hash__); INIT_STRING(__iter__); INIT_STRING(__len__); INIT_STRING(__next__); INIT_STRING_OP(abs); INIT_STRING_OP(add); INIT_STRING_OP(and); INIT_STRING_OP(divmod); INIT_STRING_OP(float); INIT_STRING_OP(floordiv); INIT_STRING_OP(iadd); INIT_STRING_OP(iand); INIT_STRING_OP(ifloordiv); INIT_STRING_OP(ilshift); INIT_STRING_OP(imod); INIT_STRING_OP(imul); INIT_STRING_OP(int); INIT_STRING_OP(invert); INIT_STRING_OP(ior); INIT_STRING_OP(ipow); INIT_STRING_OP(irshift); INIT_STRING_OP(isub); INIT_STRING_OP(itruediv); INIT_STRING_OP(ixor); INIT_STRING_OP(lshift); INIT_STRING_OP(mod); INIT_STRING_OP(mul); INIT_STRING_OP(neg); INIT_STRING_OP(or); INIT_STRING_OP(pos); INIT_STRING_OP(radd); INIT_STRING_OP(rand); INIT_STRING_OP(rdivmod); INIT_STRING_OP(rfloordiv); INIT_STRING_OP(rlshift); INIT_STRING_OP(rmod); INIT_STRING_OP(rmul); INIT_STRING_OP(ror); INIT_STRING_OP(rrshift); INIT_STRING_OP(rshift); INIT_STRING_OP(rsub); INIT_STRING_OP(rtruediv); INIT_STRING_OP(rxor); INIT_STRING_OP(sub); INIT_STRING_OP(truediv); INIT_STRING_OP(xor); INIT_STRING(__pow__); INIT_STRING(proxy); INIT_STRING(__repr__); INIT_STRING(__rpow__); INIT_STRING(__setitem__); INIT_STRING(__str__); __class__str = FROM_STRING("__class__"); if (! __class__str) return MOD_ERROR_VAL; __name__str = FROM_STRING("__name__"); if (! __name__str) return MOD_ERROR_VAL; __module__str = FROM_STRING("__module__"); if (! __module__str) return MOD_ERROR_VAL; SecurityProxyType.tp_alloc = PyType_GenericAlloc; SecurityProxyType.tp_free = PyObject_GC_Del; SecurityProxyType.tp_base = &ProxyType; if (PyType_Ready(&SecurityProxyType) < 0) return MOD_ERROR_VAL; Py_INCREF(&SecurityProxyType); PyModule_AddObject(m, "_Proxy", (PyObject *)&SecurityProxyType); return MOD_SUCCESS_VAL(m); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/_zope_security_checker.c0000644000076500000240000004364414355021564023477 0ustar00jensstaff/* Copyright (c) 2004 Zope Foundation 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. */ #include static PyObject *_checkers, *_defaultChecker, *_available_by_default, *NoProxy; static PyObject *Proxy, *thread_local, *CheckerPublic; static PyObject *ForbiddenAttribute, *Unauthorized; #define PyInt_FromLong PyLong_FromLong #define IS_STRING PyUnicode_Check #define MAKE_STRING(name) PyBytes_AS_STRING( \ PyUnicode_AsUTF8String(name)) #define FROM_STRING PyUnicode_FromString #define FROM_STRING_FORMAT PyUnicode_FromFormat #define INTERN PyUnicode_InternFromString #define MOD_ERROR_VAL NULL #define MOD_SUCCESS_VAL(val) val #define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void) #define MOD_DEF(ob, name, doc, methods) \ static struct PyModuleDef moduledef = { \ PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \ ob = PyModule_Create(&moduledef); #define statichere static #define DECLARE_STRING(N) static PyObject *str_##N DECLARE_STRING(checkPermission); DECLARE_STRING(__Security_checker__); DECLARE_STRING(interaction); #define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); } typedef struct { PyObject_HEAD PyObject *getperms, *setperms; } Checker; /* def permission_id(self, name): */ static PyObject * Checker_permission_id(Checker *self, PyObject *name) { /* return self._permission_func(name) */ PyObject *result; if (self->getperms) { result = PyDict_GetItem(self->getperms, name); if (result == NULL) result = Py_None; } else result = Py_None; Py_INCREF(result); return result; } /* def setattr_permission_id(self, name): */ static PyObject * Checker_setattr_permission_id(Checker *self, PyObject *name) { /* return self._setattr_permission_func(name) */ PyObject *result; if (self->setperms) { result = PyDict_GetItem(self->setperms, name); if (result == NULL) result = Py_None; } else result = Py_None; Py_INCREF(result); return result; } static int checkPermission(PyObject *permission, PyObject *object, PyObject *name) { PyObject *interaction, *r; int i; /* if thread_local.interaction.checkPermission(permission, object): */ /* return */ interaction = PyObject_GetAttr(thread_local, str_interaction); if (interaction == NULL) return -1; r = PyObject_CallMethodObjArgs(interaction, str_checkPermission, permission, object, NULL); Py_DECREF(interaction); if (r == NULL) return -1; i = PyObject_IsTrue(r); Py_DECREF(r); if (i < 0) return -1; if (i) return 0; /* else: */ /* __traceback_supplement__ = (TracebackSupplement, object) */ /* raise Unauthorized(object, name, permission) */ r = Py_BuildValue("OOO", object, name, permission); if (r == NULL) return -1; PyErr_SetObject(Unauthorized, r); Py_DECREF(r); return -1; } /* def check(self, object, name): */ /* Note that we have an int version here because we will use it for __setitem__, as described below */ static int Checker_check_int(Checker *self, PyObject *object, PyObject *name) { PyObject *permission=NULL; int operator; /* permission = self._permission_func(name) */ if (self->getperms) permission = PyDict_GetItem(self->getperms, name); /* if permission is not None: */ if (permission != NULL) { /* if permission is CheckerPublic: */ /* return # Public */ if (permission == CheckerPublic) return 0; if (checkPermission(permission, object, name) < 0) return -1; return 0; } operator = (IS_STRING(name) && MAKE_STRING(name)[0] == '_' && MAKE_STRING(name)[1] == '_'); if (operator) { /* elif name in _available_by_default: */ /* return */ int ic = PySequence_Contains(_available_by_default, name); if (ic < 0) return -1; if (ic) return 0; /* if name != '__iter__' or hasattr(object, name): */ /* __traceback_supplement__ = (TracebackSupplement, object) */ /* raise ForbiddenAttribute, (name, object) */ if (strcmp("__iter__", MAKE_STRING(name)) == 0 && ! PyObject_HasAttr(object, name)) /* We want an attr error if we're asked for __iter__ and we don't have it. We'll get one by allowing the access. */ return 0; } { PyObject *args; args = Py_BuildValue("OO", name, object); if (args != NULL) { PyErr_SetObject(ForbiddenAttribute, args); Py_DECREF(args); } return -1; } } /* Here we have the non-int version, implemented using the int version, which is exposed as a method */ static PyObject * Checker_check(Checker *self, PyObject *args) { PyObject *object, *name; if (!PyArg_ParseTuple(args, "OO", &object, &name)) return NULL; if (Checker_check_int(self, object, name) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } /* def check_setattr(self, object, name): */ static PyObject * Checker_check_setattr(Checker *self, PyObject *args) { PyObject *object, *name, *permission=NULL; if (!PyArg_ParseTuple(args, "OO", &object, &name)) return NULL; /* permission = self._permission_func(name) */ if (self->setperms) permission = PyDict_GetItem(self->setperms, name); /* if permission is not None: */ if (permission != NULL) { /* if permission is CheckerPublic: */ /* return # Public */ if (permission != CheckerPublic && checkPermission(permission, object, name) < 0) return NULL; Py_INCREF(Py_None); return Py_None; } /* __traceback_supplement__ = (TracebackSupplement, object) */ /* raise ForbiddenAttribute, (name, object) */ args = Py_BuildValue("OO", name, object); if (args != NULL) { PyErr_SetObject(ForbiddenAttribute, args); Py_DECREF(args); } return NULL; } static PyObject * selectChecker(PyObject *ignored, PyObject *object); /* def proxy(self, value): */ static PyObject * Checker_proxy(Checker *self, PyObject *value) { PyObject *checker, *r; /* if type(value) is Proxy: */ /* return value */ if ((PyObject*)Py_TYPE(value) == Proxy) { Py_INCREF(value); return value; } /* checker = getattr(value, '__Security_checker__', None) */ checker = PyObject_GetAttr(value, str___Security_checker__); /* if checker is None: */ if (checker == NULL) { PyErr_Clear(); /* checker = selectChecker(value) */ checker = selectChecker(NULL, value); if (checker == NULL) return NULL; /* if checker is None: */ /* return value */ if (checker == Py_None) { Py_DECREF(checker); Py_INCREF(value); return value; } } else if (checker == Py_None) { PyObject *errv = Py_BuildValue("sO", "Invalid value, None. " "for security checker", value); if (errv != NULL) { PyErr_SetObject(PyExc_ValueError, errv); Py_DECREF(errv); } return NULL; } r = PyObject_CallFunctionObjArgs(Proxy, value, checker, NULL); Py_DECREF(checker); return r; } /* return Proxy(value, checker) */ static struct PyMethodDef Checker_methods[] = { {"permission_id", (PyCFunction)Checker_permission_id, METH_O, "permission_id(name) -- Return the permission neded to get the name"}, {"setattr_permission_id", (PyCFunction)Checker_setattr_permission_id, METH_O, "setattr_permission_id(name) -- Return the permission neded to set the name" }, {"check_getattr", (PyCFunction)Checker_check, METH_VARARGS, "check_getattr(object, name) -- Check whether a getattr is allowed"}, {"check_setattr", (PyCFunction)Checker_check_setattr, METH_VARARGS, "check_setattr(object, name) -- Check whether a setattr is allowed"}, {"check", (PyCFunction)Checker_check, METH_VARARGS, "check(object, opname) -- Check whether an operation is allowed"}, {"proxy", (PyCFunction)Checker_proxy, METH_O, "proxy(object) -- Security-proxy an object"}, {NULL, NULL} /* sentinel */ }; static int Checker_clear(Checker *self) { CLEAR(self->getperms); CLEAR(self->setperms); return 0; } static void Checker_dealloc(Checker *self) { PyObject_GC_UnTrack((PyObject*)self); Checker_clear(self); Py_TYPE(self)->tp_free((PyObject*)self); } static int Checker_traverse(Checker *self, visitproc visit, void *arg) { if (self->getperms != NULL && visit(self->getperms, arg) < 0) return -1; if (self->setperms != NULL && visit(self->setperms, arg) < 0) return -1; return 0; } static int Checker_init(Checker *self, PyObject *args, PyObject *kwds) { PyObject *getperms, *setperms=NULL; static char *kwlist[] = {"get_permissions", "set_permissions", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "O!|O!:Checker", kwlist, &PyDict_Type, &getperms, &PyDict_Type, &setperms)) return -1; Py_INCREF(getperms); self->getperms = getperms; Py_XINCREF(setperms); self->setperms = setperms; return 0; } static PyObject * Checker_get_get_permissions(Checker *self, void *closure) { if (self->getperms == NULL) { self->getperms = PyDict_New(); if (self->getperms == NULL) return NULL; } Py_INCREF(self->getperms); return self->getperms; } static PyObject * Checker_get_set_permissions(Checker *self, void *closure) { if (self->setperms == NULL) { self->setperms = PyDict_New(); if (self->setperms == NULL) return NULL; } Py_INCREF(self->setperms); return self->setperms; } static PyGetSetDef Checker_getset[] = { {"get_permissions", (getter)Checker_get_get_permissions, NULL, "getattr name to permission dictionary", NULL}, {"set_permissions", (getter)Checker_get_set_permissions, NULL, "setattr name to permission dictionary", NULL}, {NULL} /* Sentinel */ }; /* We create operator aliases for check and proxy. Why? Because calling operator slots is much faster than calling methods and security checks are done so often that speed matters. So we have this hack of using almost-arbitrary operations to represent methods that we call alot. The security proxy implementation participates in the same hack. */ static PyMappingMethods Checker_as_mapping = { /* mp_length */ NULL, /* mp_subscript */ (binaryfunc)Checker_proxy, /* mp_ass_subscript */ (objobjargproc)Checker_check_int, }; static PyTypeObject CheckerType = { PyVarObject_HEAD_INIT(NULL, 0) "zope.security.checker.Checker", sizeof(Checker), 0, /* tp_itemsize */ (destructor)&Checker_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ &Checker_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Security checker", /* tp_doc */ (traverseproc)Checker_traverse, /* tp_traverse */ (inquiry)Checker_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Checker_methods, /* tp_methods */ 0, /* tp_members */ Checker_getset, /* tp_getset */ 0, /* tp_base */ 0, /* internal use */ /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Checker_init, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* Low-level free-mem routine */ /* tp_free */ 0, /* For PyObject_IS_GC */ /* tp_is_gc */ }; /* def selectChecker(object): */ /* """Get a checker for the given object */ /* The appropriate checker is returned or None is returned. If the */ /* return value is None, then object should not be wrapped in a proxy. */ /* """ */ static char selectChecker_doc[] = "Get a checker for the given object\n" "\n" "The appropriate checker is returned or None is returned. If the\n" "return value is None, then object should not be wrapped in a proxy.\n" ; static PyObject * selectChecker(PyObject *ignored, PyObject *object) { PyObject *checker; /* checker = _getChecker(type(object), _defaultChecker) */ checker = PyDict_GetItem(_checkers, (PyObject*)Py_TYPE(object)); if (checker == NULL) checker = _defaultChecker; /* if checker is NoProxy: */ /* return None */ if (checker == NoProxy) { Py_INCREF(Py_None); return Py_None; } /* if checker is _defaultChecker and isinstance(object, Exception): */ /* return None */ if (checker == _defaultChecker && PyObject_IsInstance(object, PyExc_Exception)) { Py_INCREF(Py_None); return Py_None; } /* while not isinstance(checker, Checker): */ /* checker = checker(object) */ /* if checker is NoProxy or checker is None: */ /* return None */ Py_INCREF(checker); while (! PyObject_TypeCheck(checker, &CheckerType)) { PyObject *newchecker; newchecker = PyObject_CallFunctionObjArgs(checker, object, NULL); Py_DECREF(checker); if (newchecker == NULL) return NULL; checker = newchecker; if (checker == NoProxy || checker == Py_None) { Py_DECREF(checker); Py_INCREF(Py_None); return Py_None; } } /* return checker */ return checker; } static char module___doc__[] = "C optimizations for zope.security.checker"; static PyMethodDef module_functions[] = { {"selectChecker", (PyCFunction)selectChecker, METH_O, selectChecker_doc}, {NULL} /* Sentinel */ }; MOD_INIT(_zope_security_checker) { PyObject* m; PyObject* mod; MOD_DEF(mod, "_zope_security_checker", module___doc__, module_functions) if (mod == NULL) { return MOD_ERROR_VAL; } CheckerType.tp_new = PyType_GenericNew; if (PyType_Ready(&CheckerType) < 0) { return MOD_ERROR_VAL; } _defaultChecker = PyObject_CallFunction((PyObject*)&CheckerType, "{}"); if (_defaultChecker == NULL) { return MOD_ERROR_VAL; } #define INIT_STRING(S) \ if((str_##S = INTERN(#S)) == NULL) return MOD_ERROR_VAL INIT_STRING(checkPermission); INIT_STRING(__Security_checker__); INIT_STRING(interaction); if ((_checkers = PyDict_New()) == NULL) { return MOD_ERROR_VAL; } NoProxy = PyObject_CallObject((PyObject*)&PyBaseObject_Type, NULL); if (NoProxy == NULL) { return MOD_ERROR_VAL; } if ((m = PyImport_ImportModule("zope.security._proxy")) == NULL) { return MOD_ERROR_VAL; } if ((Proxy = PyObject_GetAttrString(m, "_Proxy")) == NULL) { return MOD_ERROR_VAL; } Py_DECREF(m); if ((m = PyImport_ImportModule("zope.security._definitions")) == NULL) { return MOD_ERROR_VAL; } thread_local = PyObject_GetAttrString(m, "thread_local"); if (thread_local == NULL) { return MOD_ERROR_VAL; } Py_DECREF(m); if ((m = PyImport_ImportModule("zope.security.interfaces")) == NULL) { return MOD_ERROR_VAL; } ForbiddenAttribute = PyObject_GetAttrString(m, "ForbiddenAttribute"); if (ForbiddenAttribute == NULL) { return MOD_ERROR_VAL; } Unauthorized = PyObject_GetAttrString(m, "Unauthorized"); if (Unauthorized == NULL) { return MOD_ERROR_VAL; } Py_DECREF(m); if ((m = PyImport_ImportModule("zope.security.checker")) == NULL) { return MOD_ERROR_VAL; } CheckerPublic = PyObject_GetAttrString(m, "CheckerPublic"); if (CheckerPublic == NULL) { return MOD_ERROR_VAL; } Py_DECREF(m); if ((_available_by_default = PyList_New(0)) == NULL) { return MOD_ERROR_VAL; } #define EXPORT(N) Py_INCREF(N); PyModule_AddObject(mod, #N, N) EXPORT(_checkers); EXPORT(NoProxy); EXPORT(_defaultChecker); EXPORT(_available_by_default); Py_INCREF(&CheckerType); PyModule_AddObject(mod, "Checker", (PyObject *)&CheckerType); return MOD_SUCCESS_VAL(mod); } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/adapter.py0000644000076500000240000001153314355021564020566 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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. # ############################################################################## """Support for taking security into account in adaptation """ from zope.location import ILocation from zope.location import LocationProxy from zope.security.checker import ProxyFactory from zope.security.proxy import removeSecurityProxy def assertLocation(adapter, parent): """ Assert locatable adapters. 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): locatable = LocationProxy(adapter) locatable.__parent__ = parent return locatable # handle locatable, parentless adapters (B) if adapter.__parent__ is None: adapter.__parent__ = parent return adapter # handle locatable, parentful adapters (C) return adapter class LocatingTrustedAdapterFactory: """ 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 security-proxy the resulting adapter (S) unless the objects where not 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 :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 self.__name__ = factory.__name__ self.__module__ = factory.__module__ # protected methods def _customizeProtected(self, adapter, context): return assertLocation(adapter, context) def _customizeUnprotected(self, adapter, context): if ILocation.providedBy(adapter) and adapter.__parent__ is None: adapter.__parent__ = context return adapter def __call__(self, *args): for arg in args: if removeSecurityProxy(arg) is not arg: args = [removeSecurityProxy(x) for x in args] adapter = self.factory(*args) adapter = self._customizeProtected(adapter, args[0]) return ProxyFactory(adapter) adapter = self.factory(*args) adapter = self._customizeUnprotected(adapter, args[0]) return adapter class TrustedAdapterFactory(LocatingTrustedAdapterFactory): """ 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 :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 def _customizeProtected(self, adapter, context): return self._customizeUnprotected(adapter, context) class LocatingUntrustedAdapterFactory: """ Adapt an adapter factory to provide locatable untrusted adapters Untrusted adapters always adapt proxied objects. If any permission other than :const:`zope.Public ` is required, untrusted adapters need a location in order that the local authentication mechanism can be invoked 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): self.factory = factory self.__name__ = factory.__name__ self.__module__ = factory.__module__ def __call__(self, *args): adapter = self.factory(*args) return assertLocation(adapter, args[0]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878194.0 zope_security-7.3/src/zope/security/checker.py0000644000076500000240000010760414677252062020565 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """ 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`). This module is accelerated with a C implementation on CPython by default. If the environment variable ``PURE_PYTHON`` is set to ``1`` 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. 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 may eventually be replaced with a more general security auditing mechanism. .. seealso:: :class:`CheckerLoggingMixin` .. seealso:: :class:`WatchingChecker` .. seealso:: :class:`WatchingCombinedChecker` API === .. py:data:: CheckerPublic The special constant that indicates that no permission checking needs to be done. .. autofunction:: selectChecker """ import abc import datetime import decimal import io import os import sys import types import weakref import zope.interface.declarations import zope.interface.interface import zope.interface.interfaces from zope.i18nmessageid import Message from zope.interface import Interface from zope.interface import directlyProvides from zope.interface import implementer from zope.interface.interfaces import IDeclaration from zope.interface.interfaces import IInterface from zope.security._compat import PURE_PYTHON from zope.security._compat import implementer_if_needed from zope.security._definitions import thread_local from zope.security.interfaces import ForbiddenAttribute from zope.security.interfaces import IChecker from zope.security.interfaces import INameBasedChecker from zope.security.interfaces import ISecurityProxyFactory from zope.security.interfaces import Unauthorized from zope.security.proxy import Proxy from zope.security.proxy import getChecker try: from zope.exceptions import DuplicationError except ImportError: # pragma: no cover class DuplicationError(Exception): """A duplicate registration was attempted""" WATCH_CHECKERS = 0 if os.environ.get('ZOPE_WATCH_CHECKERS'): # pragma: no cover try: WATCH_CHECKERS = int(os.environ.get('ZOPE_WATCH_CHECKERS')) except ValueError: WATCH_CHECKERS = 1 def ProxyFactory(object, checker=None): """Factory function that creates a proxy for an object The proxy checker is looked up if not provided. """ if isinstance(object, Proxy): if checker is None or checker is getChecker(object): return object else: # We have a proxy, but someone asked us to change its checker. # Let's raise an exception. # # Other reasonable actions would be to either keep the existing # proxy, or to create a new one with the given checker. # The latter might be a security hole though, if untrusted code # can call ProxyFactory. raise TypeError("Tried to use ProxyFactory to change a Proxy's" " checker.") if checker is None: checker = getattr(object, '__Security_checker__', None) if checker is None: checker = selectChecker(object) if checker is None: return object return Proxy(object, checker) directlyProvides(ProxyFactory, ISecurityProxyFactory) # This import represents part of the API for the proxy module from . import proxy # noqa: E402 module level import not at top proxy.ProxyFactory = ProxyFactory def canWrite(obj, name): """Check whether the interaction may write an attribute named name on obj. Convenience method. Rather than using checkPermission in high level code, use canWrite and canAccess to avoid binding code to permissions. """ obj = ProxyFactory(obj) checker = getChecker(obj) try: checker.check_setattr(obj, name) except Unauthorized: return False except ForbiddenAttribute: # we are going to be a bit DWIM-y here: see # http://www.zope.org/Collectors/Zope3-dev/506 # generally, if the check is ForbiddenAttribute we want it to be # raised: it probably indicates a programming or configuration error. # However, we special case a write ForbiddenAttribute when one can # actually read the attribute: this represents a reasonable # configuration of a readonly attribute, and returning False (meaning # "no, you can't write it") is arguably more useful than raising the # exception. try: checker.check_getattr(obj, name) # we'll let *this* ForbiddenAttribute fall through, if any. It # means that both read and write are forbidden. except Unauthorized: pass return False # all other exceptions, other than Unauthorized and ForbiddenAttribute, # should be passed through uncaught, as they indicate programmer error return True def canAccess(obj, name): """Check whether the interaction may access an attribute named name on obj. Convenience method. Rather than using checkPermission in high level code, use canWrite and canAccess to avoid binding code to permissions. """ # access attributes and methods, including, in the current checker # implementation, special names like __getitem__ obj = ProxyFactory(obj) checker = getChecker(obj) try: checker.check_getattr(obj, name) except Unauthorized: return False # if it is Forbidden (or anything else), let it be raised: it probably # indicates a programming or configuration error return True @implementer(INameBasedChecker) class CheckerPy: """ 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 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, :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 set attribute access. """ if not isinstance(get_permissions, dict): raise TypeError('get_permissions must be a dict') self.get_permissions = get_permissions if set_permissions is not None: if not isinstance(set_permissions, dict): raise TypeError('set_permissions must be a dict') else: set_permissions = {} self.set_permissions = set_permissions def permission_id(self, name): 'See INameBasedChecker' return self.get_permissions.get(name) def setattr_permission_id(self, name): 'See INameBasedChecker' if self.set_permissions: return self.set_permissions.get(name) def check_setattr(self, object, name): 'See IChecker' if self.set_permissions: permission = self.set_permissions.get(name) else: permission = None if permission is not None: if permission is CheckerPublic: return # Public if thread_local.interaction.checkPermission(permission, object): return # allowed else: __traceback_supplement__ = (TracebackSupplement, object) raise Unauthorized(object, name, permission) __traceback_supplement__ = (TracebackSupplement, object) raise ForbiddenAttribute(name, object) def check(self, object, name): 'See IChecker' permission = self.get_permissions.get(name) if permission is not None: if permission is CheckerPublic: return # Public if thread_local.interaction.checkPermission(permission, object): return else: __traceback_supplement__ = (TracebackSupplement, object) raise Unauthorized(object, name, permission) elif name in _available_by_default: return if name != '__iter__' or hasattr(object, name): __traceback_supplement__ = (TracebackSupplement, object) raise ForbiddenAttribute(name, object) check_getattr = check # 'See IChecker' def proxy(self, value): 'See IChecker' if isinstance(value, Proxy): return value checker = getattr(value, '__Security_checker__', None) if checker is None: checker = selectChecker(value) if checker is None: return value return Proxy(value, checker) Checker = CheckerPy # in case no C optimizations # Helper class for __traceback_supplement__ class TracebackSupplement: def __init__(self, obj): self.obj = obj def getInfo(self): result = [] try: cls = self.obj.__class__ if hasattr(cls, "__module__"): s = f"{cls.__module__}.{cls.__name__}" else: # pragma: no cover XXX s = str(cls.__name__) result.append(" - class: " + s) except: # pragma: no cover # noqa: E722 do not use bare 'except' pass try: cls = type(self.obj) if hasattr(cls, "__module__"): s = f"{cls.__module__}.{cls.__name__}" else: # pragma: no cover XXX s = str(cls.__name__) result.append(" - type: " + s) except: # pragma: no cover # noqa: E722 do not use bare 'except' pass return "\n".join(result) class Global: """A global object that behaves like a string. We want this to behave as a global, meaning it's pickled by name, rather than value. We need to arrange that it has a suitable __reduce__. """ def __init__(self, name, module=None): if module is None: # pragma: no cover XXX module = sys._getframe(1).f_locals['__name__'] self.__name__ = name self.__module__ = module def __reduce__(self): return self.__name__ def __repr__(self): return "{}({},{})".format(self.__class__.__name__, self.__name__, self.__module__) 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 d['__module__'] = CheckerPublic del d # TODO: It's a bit scary above that we can pickle a proxy if access is # granted to __reduce__. We might want to bother to prevent this in # general and only allow it in this specific case. def NamesChecker(names=(), permission_id=CheckerPublic, **__kw__): """Return a checker that grants access to a set of names. A sequence of names is given as the first argument. If a second argument, permission_id, is given, it is the permission required to access the names. Additional names and permission IDs can be supplied as keyword arguments. """ data = {} data.update(__kw__) for name in names: if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id 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 A specification is: - A two-tuple with: o a sequence of names or an interface o a permission ID All the names in the sequence of names or the interface are protected by the permission. - A dictionary (having an items method), with items that are name/permission-id pairs. """ data = {} for spec in specs: if isinstance(spec, tuple): names, permission_id = spec if IInterface.providedBy(names): names = names.names(all=True) for name in names: if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id else: for name, permission_id in spec.items(): if data.get(name, permission_id) is not permission_id: raise DuplicationError(name) data[name] = permission_id return Checker(data) def selectCheckerPy(object): """Get a checker for the given object The appropriate checker is returned or None is returned. If the return value is None, then object should not be wrapped in a proxy. """ # We need to be careful here. We might have a proxy, in which case # we can't use the type. OTOH, we might not be able to use the # __class__ either, since not everything has one. # TODO: we really need formal proxy introspection # if type(object) is Proxy: # # Is this already a security proxy? # return None checker = _getChecker(type(object), _defaultChecker) # checker = _getChecker(getattr(object, '__class__', type(object)), # _defaultChecker) if checker is NoProxy: return None while not isinstance(checker, Checker): checker = checker(object) if checker is NoProxy or checker is None: return None return checker selectChecker = selectCheckerPy # in case no C optimizations def getCheckerForInstancesOf(class_): return _checkers.get(class_) DEFINABLE_TYPES = (type, types.ModuleType) def defineChecker(type_, checker): """Define a checker for a given type of object 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( 'type_ must be a type, class or module, not a %s' % type_) if type_ in _checkers: raise DuplicationError(type_) _checkers[type_] = checker def undefineChecker(type_): del _checkers[type_] NoProxy = object() # _checkers is a mapping. # # - Keys are types # # - Values are # # o None => rock # o a Checker # o a function returning None or a Checker # _checkers = {} _defaultChecker = Checker({}) _available_by_default = [] # Get optimized versions _c_available = not PURE_PYTHON if _c_available: # pragma: no cover try: import zope.security._zope_security_checker except (ImportError, AttributeError): _c_available = False if _c_available: # pragma: no cover from zope.security._zope_security_checker import Checker from zope.security._zope_security_checker import NoProxy from zope.security._zope_security_checker import _available_by_default from zope.security._zope_security_checker import _checkers from zope.security._zope_security_checker import _defaultChecker from zope.security._zope_security_checker import selectChecker zope.interface.classImplements(Checker, INameBasedChecker) _getChecker = _checkers.get @implementer_if_needed(IChecker) class CombinedChecker(Checker): """A checker that combines two other checkers in a logical-or fashion. The following table describes the result of a combined checker in detail. +--------------------+--------------------+-------------------------------------+ | checker1 | checker2 | CombinedChecker(checker1, checker2) | +====================+====================+=====================================+ | ok | anything | ok (checker 2 never called) | +--------------------+--------------------+-------------------------------------+ | Unauthorized | ok | ok | +--------------------+--------------------+-------------------------------------+ | Unauthorized | Unauthorized | Unauthorized | +--------------------+--------------------+-------------------------------------+ | Unauthorized | ForbiddenAttribute | Unauthorized | +--------------------+--------------------+-------------------------------------+ | ForbiddenAttribute | ok | ok | +--------------------+--------------------+-------------------------------------+ | ForbiddenAttribute | Unauthorized | Unauthorized | +--------------------+--------------------+-------------------------------------+ | ForbiddenAttribute | ForbiddenAttribute | ForbiddenAttribute | +--------------------+--------------------+-------------------------------------+ """ # noqa: E501 line too long def __init__(self, checker1, checker2): """Create a combined checker.""" Checker.__init__(self, checker1.get_permissions, checker1.set_permissions) self._checker2 = checker2 def check(self, object, name): 'See IChecker' try: Checker.check(self, object, name) except ForbiddenAttribute: self._checker2.check(object, name) except Unauthorized as unauthorized_exception: try: self._checker2.check(object, name) except ForbiddenAttribute: raise unauthorized_exception check_getattr = __setitem__ = check def check_setattr(self, object, name): 'See IChecker' try: Checker.check_setattr(self, object, name) except ForbiddenAttribute: self._checker2.check_setattr(object, name) except Unauthorized as unauthorized_exception: try: self._checker2.check_setattr(object, name) except ForbiddenAttribute: raise unauthorized_exception class CheckerLoggingMixin: """ Debugging mixin for checkers. Prints verbose debugging information about every performed check to :data:`sys.stderr`. """ #: 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 def _log(self, msg, verbosity=1): if self.verbosity >= verbosity: self._file.write('%s\n' % msg) def check(self, object, name): try: super().check(object, name) if self.verbosity > 1: if name in _available_by_default: self._log('[CHK] + Always available: %s on %r' % (name, object), 2) else: self._log( f'[CHK] + Granted: {name} on {object!r}', 2) except Unauthorized: self._log( f'[CHK] - Unauthorized: {name} on {object!r}') raise except ForbiddenAttribute: self._log( f'[CHK] - Forbidden: {name} on {object!r}') raise def check_getattr(self, object, name): try: super().check(object, name) if self.verbosity > 1: if name in _available_by_default: self._log( '[CHK] + Always available getattr: %s on %r' % (name, object), 2) else: self._log( '[CHK] + Granted getattr: %s on %r' % (name, object), 2) except Unauthorized: self._log( f'[CHK] - Unauthorized getattr: {name} on {object!r}' ) raise except ForbiddenAttribute: self._log( f'[CHK] - Forbidden getattr: {name} on {object!r}') raise __setitem__ = check_getattr def check_setattr(self, object, name): try: super().check_setattr(object, name) if self.verbosity > 1: self._log( '[CHK] + Granted setattr: {} on {!r}'.format( name, object), 2) except Unauthorized: self._log( '[CHK] - Unauthorized setattr: {} on {!r}'.format( name, object)) raise except ForbiddenAttribute: self._log( f'[CHK] - Forbidden setattr: {name} on {object!r}') raise # 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 # When we make these the default, we also need to be sure # to update the _defaultChecker's type (if it's not the C # extension) so that selectCheckerPy can properly recognize # it as a Checker. # See https://github.com/zopefoundation/zope.security/issues/8 Checker = WatchingChecker CombinedChecker = WatchingCombinedChecker if not _c_available: _defaultChecker.__class__ = Checker 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) _available_by_default[:] = [ '__lt__', '__le__', '__eq__', '__gt__', '__ge__', '__ne__', '__hash__', '__bool__', '__class__', '__providedBy__', '__implements__', '__repr__', '__conform__', '__name__', '__parent__', ] _callableChecker = NamesChecker(['__str__', '__name__', '__call__']) _typeChecker = NamesChecker([ '__str__', '__name__', '__module__', '__bases__', '__mro__', '__implemented__', ]) _namedChecker = NamesChecker(['__name__']) _iteratorChecker = NamesChecker([ 'next', '__next__', '__iter__', '__len__', '__length_hint__', ]) _setChecker = NamesChecker([ '__iter__', '__len__', '__str__', '__contains__', 'copy', 'difference', 'intersection', 'issubset', 'issuperset', 'symmetric_difference', 'union', '__and__', '__or__', '__sub__', '__xor__', '__rand__', '__ror__', '__rsub__', '__rxor__', '__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__', ]) class _BasicTypes(dict): """Basic Types Dictionary Make sure that checkers are really updated, when a new type is added. """ def __setitem__(self, name, value): dict.__setitem__(self, name, value) _checkers[name] = value def __delitem__(self, name): dict.__delitem__(self, name) del _checkers[name] def clear(self): # Make sure you cannot clear the values raise NotImplementedError def update(self, d): dict.update(self, d) _checkers.update(d) _basic_types = { object: NoProxy, int: NoProxy, float: NoProxy, complex: NoProxy, type(None): NoProxy, str: NoProxy, bytes: NoProxy, Message: NoProxy, # Messages are immutable, so it's okay bool: NoProxy, datetime.timedelta: NoProxy, datetime.datetime: NoProxy, datetime.date: NoProxy, datetime.time: NoProxy, datetime.tzinfo: NoProxy, type({}.values()): NoProxy, type({}.keys()): NoProxy, type({}.items()): NoProxy, } try: import pytz except ImportError: # pragma: no cover pass else: _basic_types[type(pytz.UTC)] = NoProxy BasicTypes = _BasicTypes(_basic_types) del _basic_types # Available for tests. Located here so it can be kept in sync with BasicTypes. BasicTypes_examples = { object: object(), int: 65536, float: -1.4142, complex: -1.4142j, type(None): None, bytes: b'abc', bool: True, datetime.timedelta: datetime.timedelta(3), datetime.datetime: datetime.datetime(2003, 1, 1), datetime.date: datetime.date(2003, 1, 1), datetime.time: datetime.time(23, 58), Message: Message('message', domain='hello') } class _Sequence: def __len__(self): raise NotImplementedError() def __getitem__(self, i): raise NotImplementedError() _Declaration_checker = InterfaceChecker( IDeclaration, _implied=CheckerPublic, subscribe=CheckerPublic, unsubscribe=CheckerPublic, __call__=CheckerPublic, ) def f(): # pragma: no cover yield f _default_checkers = { dict: NamesChecker(['__getitem__', '__len__', '__iter__', 'get', 'has_key', 'copy', '__str__', 'keys', 'values', 'items', 'iterkeys', 'iteritems', 'itervalues', '__contains__']), list: NamesChecker(['__getitem__', '__len__', '__iter__', '__contains__', 'index', 'count', '__str__', '__add__', '__radd__', ]), set: _setChecker, frozenset: _setChecker, # XXX: actually decimal.Decimal has more methods, which are unlisted here # so expect ForbiddenAttribute on such decimal.Decimal: NamesChecker(['__bool__', '__cmp__', '__eq__', '__ne__', '__hash__', '__str__', '__neg__', '__pos__', '__abs__', '__add__', '__radd__', '__sub__', '__rsub__', '__mul__', '__rmul__', '__truediv__', '__rtruediv__', '__divmod__', '__rdivmod__', '__mod__', '__rmod__', '__floordiv__', '__rfloordiv__', '__float__', '__int__', '__pow__', '__rpow__', 'adjusted', 'as_tuple', 'compare', 'max', 'min', 'normalize', 'quantize', 'remainder_near', 'same_quantum', 'sqrt', 'to_eng_string', 'to_integral']), # YAGNI: () a rock tuple: NamesChecker(['__getitem__', '__add__', '__radd__', '__contains__', '__len__', '__iter__', '__str__']), Proxy: NoProxy, type(weakref.ref(_Sequence())): NamesChecker(['__call__']), types.FunctionType: _callableChecker, types.MethodType: _callableChecker, types.BuiltinFunctionType: _callableChecker, types.BuiltinMethodType: _callableChecker, # At least on Python 3.5-3.12, types.BuiltinFunctionType and # types.BuiltinMethodType are both , # or PyCFunctionType. However, some builtin methods are instead, or PyCMethodType, which is a subclass of # PyCFunctionType but not identical to it. As of Python 3.12, the io # module makes more use of PyCMethodType, so we can use it to identify # that type. type(io.BytesIO().getbuffer): _callableChecker, # method-wrapper type(().__repr__): _callableChecker, type: _typeChecker, types.ModuleType: lambda module: _checkers.get(module, _namedChecker), type(iter([])): _iteratorChecker, type(iter(())): _iteratorChecker, type(iter({})): _iteratorChecker, type(iter(set())): _iteratorChecker, type(iter(_Sequence())): _iteratorChecker, type(f()): _iteratorChecker, type(Interface): InterfaceChecker( IInterface, __str__=CheckerPublic, _implied=CheckerPublic, subscribe=CheckerPublic, # To iterate, Python calls __len__ as a hint. # AttributeErrors are passed. __len__=CheckerPublic, ), zope.interface.interface.Method: InterfaceChecker( zope.interface.interfaces.IMethod), zope.interface.declarations.ProvidesClass: _Declaration_checker, zope.interface.declarations.ClassProvides: _Declaration_checker, zope.interface.declarations.Implements: _Declaration_checker, zope.interface.declarations.Declaration: _Declaration_checker, abc.ABCMeta: _typeChecker, } def _fixup_dictlike(dict_type): empty_dict = dict_type() populated_dict = dict_type({1: 2}) for dictlike in (empty_dict, populated_dict): for attr in ('__iter__', 'keys', 'items', 'values'): obj = getattr(dictlike, attr)() o_type = type(obj) if o_type not in _default_checkers: _default_checkers[o_type] = _iteratorChecker # PyPy3 has special types for iter({}.items()) etc. iter_type = type(iter(obj)) if iter_type not in _default_checkers: _default_checkers[iter_type] = _iteratorChecker def _fixup_odict(): from collections import OrderedDict # The `_fixup_dictlike` is detected as undefined because it is deleted # later on but this function is called beforehand: _fixup_dictlike(OrderedDict) # noqa: F821 undefined name '_fixup_dictlike' _fixup_odict() del _fixup_odict try: import BTrees # noqa: F401 'BTrees' imported but unused except ImportError: # pragma: no cover pass else: # The C implementation of BTree.items() is its own iterator # and doesn't need any special entries to enable iteration. # But the Python implementation has to call __iter__ to be able # to do iteration. Whitelist it so that they behave the same. # In addition, Python 3 will attempt to call __len__ on iterators # for a length hint, so the C implementations also need to be # added to the _iteratorChecker. The same thing automatically # applies for .keys() and .values() since they return the same type. # We do this here so that all users of zope.security can benefit # without knowing implementation details. # See https://github.com/zopefoundation/zope.security/issues/20 def _fixup_btrees(): import BTrees._base _default_checkers[BTrees._base._TreeItems] = _iteratorChecker for name in ('IF', 'II', 'IO', 'OI', 'OO'): for family_name in ('family32', 'family64'): family = getattr(BTrees, family_name) btree = getattr(family, name).BTree # The `_fixup_dictlike` is detected as undefined because it is # deleted later on but this function is called beforehand: _fixup_dictlike(btree) # noqa: F821 undefined name _fixup_btrees() del _fixup_btrees del _fixup_dictlike def _fixup_zope_interface(): # Make sure the provided and implementedBy objects # can be iterated. # Note that we DO NOT use the _iteratorChecker, but instead # we use NoProxy to be sure that the results (of iteration or not) are not # proxied. On Python 2, these objects are builtin and don't go through the # checking process at all, much like BTrees, so NoProxy is necessary for # compatibility. On Python 3, prior to this, iteration was simply not # allowed. from zope.interface import alsoProvides from zope.interface import providedBy class I1(Interface): pass class I2(Interface): pass @implementer(I1) class Obj: pass o = Obj() # This will be the zope.interface.implementedBy from the class # a zope.interface.declarations.Implements _default_checkers[type(providedBy(o))] = NoProxy alsoProvides(o, I2) # This will be the zope.interface.Provides from the instance _default_checkers[type(providedBy(o))] = NoProxy _fixup_zope_interface() del _fixup_zope_interface def _fixup_itertools(): # itertools.groupby is a built-in custom iterator type introduced # in Python 2.4. It should have the same checker as other built-in # iterators. # Also, itertools._grouper also needs to be exposed as an # iterator. Its type is not exposed by name, but can be accessed # like so: type(list(itertools.groupby([0]))[0][1]) import itertools group = itertools.groupby([0]) type_group = type(group) if type_group not in _default_checkers: _default_checkers[type_group] = _iteratorChecker type_grouper = type(list(group)[0][1]) if type_grouper not in _default_checkers: _default_checkers[type_grouper] = _iteratorChecker # There are also many other custom types in itertools that need the # same treatment. See a similar list in # test_checker.py:test_itertools_checkers def pred(x): return x iterable = (1, 2, 3) pred_iterable = (pred, iterable) for func, args in ( ('count', ()), ('cycle', ((),)), ('dropwhile', pred_iterable), ('islice', (iterable, 2)), ('permutations', (iterable,)), ('product', (iterable,)), ('repeat', (1, 2)), ('starmap', pred_iterable), ('takewhile', pred_iterable), ('tee', (iterable,)), ('zip_longest', (iterable,)), ('accumulate', (iterable,)), ('compress', (iterable, ())), ('combinations', (iterable, 1)), ('combinations_with_replacement', (iterable, 1)), ): func = getattr(itertools, func) result = func(*args) if func == itertools.tee: result = result[0] tresult = type(result) if tresult not in _default_checkers: _default_checkers[tresult] = _iteratorChecker _fixup_itertools() del _fixup_itertools def _clear(): _checkers.clear() _checkers.update(_default_checkers) _checkers.update(BasicTypes) _clear() try: from zope.testing.cleanup import addCleanUp except ImportError: # pragma: no cover pass else: addCleanUp(_clear) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/configure.zcml0000644000076500000240000000117714355021564021447 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/decorator.py0000644000076500000240000000612714355021564021133 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """Decorator support Decorators are proxies that are mostly transparent but that may provide additional features. """ from zope.proxy import ProxyBase from zope.proxy import getProxiedObject from zope.proxy.decorator import SpecificationDecoratorBase from zope.security.checker import CombinedChecker from zope.security.checker import selectChecker from zope.security.proxy import Proxy from zope.security.proxy import getChecker class DecoratedSecurityCheckerDescriptor: """Descriptor for a Decorator that provides a decorated security checker. """ def __get__(self, inst, cls=None): if inst is None: return self else: proxied_object = getProxiedObject(inst) if isinstance(proxied_object, Proxy): checker = getChecker(proxied_object) else: checker = getattr(proxied_object, '__Security_checker__', None) if checker is None: checker = selectChecker(proxied_object) wrapper_checker = selectChecker(inst) if wrapper_checker is None and checker is None: raise AttributeError("%r has no attribute %r" % (proxied_object.__class__.__name__, '__Security_checker__')) elif wrapper_checker is None: return checker elif checker is None: return wrapper_checker else: return CombinedChecker(wrapper_checker, checker) def __set__(self, inst, value): raise TypeError("Can't set __Security_checker__ on a decorated object") class SecurityCheckerDecoratorBase(ProxyBase): """Base class for proxy that provides additional security declarations.""" __Security_checker__ = DecoratedSecurityCheckerDescriptor() class DecoratorBase(SpecificationDecoratorBase, SecurityCheckerDecoratorBase): """Base class for a proxy that provides both additional interfaces and security declarations.""" # zope.location was made independent of security. To work together with # security, we re-inject the DecoratedSecurityCheckerDescriptor onto the # location proxy from here. # This is the only sane place we found for doing it: it kicks in as soon # as someone starts using security proxies. import zope.location.location # noqa: E402 module level import not at top zope.location.location.LocationProxy.__Security_checker__ = ( DecoratedSecurityCheckerDescriptor()) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1727878801.110379 zope_security-7.3/src/zope/security/examples/0000755000076500000240000000000014677253221020413 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1724678151.0 zope_security-7.3/src/zope/security/examples/sandbox.py0000644000076500000240000002106714663100007022415 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """A small sandbox application. """ import random import time from zope.interface import Interface from zope.interface import implementer class IAgent(Interface): """A player/agent in the world. The agent represents an autonomous unit, that lives in various homes/sandboxes and accesses services present at the sandboxes. Agents are imbued with a sense of wanderlust and attempt to find new homes after a few turns of the time generator (think turn based games). """ def action(): """Perform agent's action.""" def setHome(home): """Move to a different home.""" def getHome(): """Return the place where the agent currently lives.""" def getAuthenticationToken(): """Return the authority by which the agent perform actions.""" class IService(Interface): """Marker to designate some form of functionality. Services are available from sandboxes, examples include time service, agent discovery, and sandbox discovery. """ class ISandbox(Interface): """A container for agents to live in and services to be available.""" def getService(service_id): """Get the service having the provided id in this sandbox.""" def getAgents(): """Return a list of agents living in this sandbox.""" def addAgent(agent): """Add a new agent to the sandbox.""" def transportAgent(agent, destination): """Move the specified agent to the destination sandbox.""" class SandboxError(Exception): """A sandbox error is thrown, if any action could not be performed.""" pass class Identity: """Mixin for pretty printing and identity method""" def __init__(self, id, *args, **kw): self.id = id def getId(self): return self.id def __str__(self): return f"<{str(self.__class__.__name__)}> {str(self.id)}" __repr__ = __str__ @implementer(IAgent) class Agent(Identity): def __init__(self, id, home, auth_token, action): """Initialize agent.""" self.id = id self.auth_token = auth_token self.home = home self._action = action def action(self): """See IAgent.""" self._action(self, self.getHome()) def setHome(self, home): """See IAgent.""" self.home = home def getHome(self): """See IAgent.""" return self.home def getAuthenticationToken(self): """See IAgent.""" return self.auth_token @implementer(ISandbox) class Sandbox(Identity): """ see ISandbox doc """ def __init__(self, id, service_factories): self.id = id self._services = {} self._agents = {} for sf in service_factories: self.addService(sf()) def getAgentIds(self): return self._agents.keys() def getAgents(self): return self._agents.values() def getServiceIds(self): return self._services.keys() def getService(self, sid): return self._services.get(sid) def getHome(self): return self def addAgent(self, agent): if agent.getId() not in self._agents \ and IAgent.providedBy(agent): self._agents[agent.getId()] = agent agent.setHome(self) else: raise SandboxError("couldn't add agent %s" % agent) def addService(self, service): if not service.getId() in self._services \ and IService.providedBy(service): self._services[service.getId()] = service service.setHome(self) else: raise SandboxError("couldn't add service %s" % service) def transportAgent(self, agent, destination): if agent.getId() in self._agents \ and destination is not self \ and ISandbox.providedBy(destination): destination.addAgent(agent) del self._agents[agent.getId()] else: raise SandboxError("couldn't transport agent {} to {}".format( agent, destination) ) @implementer(IService) class Service: def getId(self): return self.__class__.__name__ def setHome(self, home): self._home = home def getHome(self): return getattr(self, '_home') class HomeDiscoveryService(Service): """ returns the ids of available agent homes """ def getAvailableHomes(self): return _homes.keys() class AgentDiscoveryService(Service): """ returns the agents available at a given home """ def getLocalAgents(self, home): return home.getAgents() class TimeService(Service): """ returns the local time """ def getTime(self): return time.time() default_service_factories = ( HomeDiscoveryService, AgentDiscoveryService, TimeService ) def action_find_homes(agent, home): home_service = home.getService('HomeDiscoveryService') return home_service.getAvailableHomes() def action_find_neighbors(agent, home): agent_service = home.getService('AgentDiscoveryService') return agent_service.getLocalAgents(home) def action_find_time(agent, home): time_service = home.getService('TimeService') return time_service.getTime() class TimeGenerator: """Represents the passage of time in the agent simulation. each turn represents some discrete unit of time, during which all agents attempt to perform their action. Additionally, all agents are checked to see if they have a desire to move, and if so are transported to a new random home. """ def setupAgent(self, agent): pass def teardownAgent(self, agent): pass def turn(self): global _homes for h in _homes.values(): agents = h.getAgents() for a in agents: self.setupAgent(a) try: a.action() except Exception as e: print('-- Exception --') print('"%s" in "%s" not allow to "%s"' % (a, h, a._action.__name__)) print(e) print() self.teardownAgent(a) agents = filter(WanderLust, agents) for a in agents: self.setupAgent(a) try: home = a.getHome() new_home = GreenerPastures(a) home.transportAgent(a, new_home) except Exception as e: print('-- Exception --') print('moving "%s" from "%s" to "%s"' % (a, h, repr(new_home))) print(e) print() self.teardownAgent(a) def WanderLust(agent): """ is agent ready to move """ if int(random.random() * 100) <= 30: return 1 def GreenerPastures(agent): """ where do they want to go today """ global _homes possible_homes = _homes.keys() possible_homes.remove(agent.getHome().getId()) return _homes.get(random.choice(possible_homes)) # boot strap initial setup. # global list of homes _homes = {} all_homes = ( Sandbox('jail', default_service_factories), Sandbox('origin', default_service_factories), Sandbox('valhalla', default_service_factories) ) origin = all_homes[1] for h in all_homes: _homes[h.getId()] = h agents = [ Agent('odin', None, 'norse legend', action_find_time), Agent('loki', None, 'norse legend', action_find_neighbors), Agent('thor', None, 'norse legend', action_find_homes), Agent('thucydides', None, 'greek men', action_find_time), Agent('archimedes', None, 'greek men', action_find_neighbors), Agent('prometheus', None, 'greek men', action_find_homes), ] for a in agents: origin.addAgent(a) def main(): world = TimeGenerator() for x in range(5): print('world turning') world.turn() for h in _homes.values(): print(h.getId(), h.getAgentIds()) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/examples/sandbox_security.py0000644000076500000240000001424114355021564024350 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """A small, secure sandbox application. This module is responsible of securing the sandbox application and run it in a secure mode. There are several steps that are taken to set up the security 1. map permissions to actions 2. map authentication tokens/principals onto permissions 3. implement checker and security policies that affect 1,2 4. bind checkers to classes/instances 5. proxy wrap as necessary """ import sandbox from zope.interface import implementer from zope.security import checker from zope.security import management from zope.security import simplepolicies from zope.security.interfaces import IParticipation # Define all permissions that will be available NotAllowed = 'Not Allowed' Public = checker.CheckerPublic TransportAgent = 'Transport Agent' AccessServices = 'Access Services' AccessAgents = 'Access Agents' AccessTimeService = 'Access Time Services' AccessAgentService = 'Access Agent Service' AccessHomeService = 'Access Home Service' AddAgent = 'Add Agent' ALL = 'All' def NoSetAttr(name): return NotAllowed class SimulationSecurityDatabase: """Security Database In the database, locations are mapped to authentication tokens to permissions. """ origin = { 'any': [ALL] } jail = { 'norse legend': [TransportAgent, AccessServices, AccessAgentService, AccessHomeService, TransportAgent, AccessAgents], 'any': [AccessTimeService, AddAgent] } valhalla = { 'norse legend': [AddAgent], 'any': [AccessServices, AccessTimeService, AccessAgentService, AccessHomeService, TransportAgent, AccessAgents] } class SimulationSecurityPolicy(simplepolicies.ParanoidSecurityPolicy): """Security Policy during the Simulation. A very simple security policy that is specific to the simulations. """ def checkPermission(self, permission, object): """See zope.security.interfaces.ISecurityPolicy""" home = object.getHome() db = getattr(SimulationSecurityDatabase, home.getId(), None) if db is None: return False allowed = db.get('any', ()) if permission in allowed or ALL in allowed: return True if not self.participations: return False for participation in self.participations: token = participation.principal.getAuthenticationToken() allowed = db.get(token, ()) if permission not in allowed: return False return True @implementer(IParticipation) class AgentParticipation: """Agent Participation during the Simulation. A very simple participation that is specific to the simulations. """ def __init__(self, agent): self.principal = agent self.interaction = None def PermissionMapChecker(permissions_map=None, set_permissions=None): """Create a checker from using the 'permission_map.'""" if permissions_map is None: permissions_map = {} if set_permissions is None: set_permissions = {} res = {} for key, value in permissions_map.items(): for method in value: res[method] = key return checker.Checker(res, set_permissions) ################################# # sandbox security settings sandbox_security = { AccessServices: ['getService', 'addService', 'getServiceIds'], AccessAgents: ['getAgentsIds', 'getAgents'], AddAgent: ['addAgent'], TransportAgent: ['transportAgent'], Public: ['getId', 'getHome'] } sandbox_checker = PermissionMapChecker(sandbox_security) ################################# # service security settings # time service tservice_security = {AccessTimeService: ['getTime']} time_service_checker = PermissionMapChecker(tservice_security) # home service hservice_security = {AccessHomeService: ['getAvailableHomes']} home_service_checker = PermissionMapChecker(hservice_security) # agent service aservice_security = {AccessAgentService: ['getLocalAgents']} agent_service_checker = PermissionMapChecker(aservice_security) def wire_security(): management.setSecurityPolicy(SimulationSecurityPolicy) checker.defineChecker(sandbox.Sandbox, sandbox_checker) checker.defineChecker(sandbox.TimeService, time_service_checker) checker.defineChecker(sandbox.AgentDiscoveryService, agent_service_checker) checker.defineChecker(sandbox.HomeDiscoveryService, home_service_checker) def addAgent(self, agent): if (agent.getId() not in self._agents and sandbox.IAgent.providedBy(agent)): self._agents[agent.getId()] = agent agentChecker = checker.selectChecker(self) wrapped_home = agentChecker.proxy(self) agent.setHome(wrapped_home) else: raise sandbox.SandboxError("couldn't add agent %s" % agent) sandbox.Sandbox.addAgent = addAgent def setupAgent(self, agent): management.newInteraction(AgentParticipation(agent)) sandbox.TimeGenerator.setupAgent = setupAgent def teardownAgent(self, agent): management.endInteraction() sandbox.TimeGenerator.teardownAgent = teardownAgent def GreenerPastures(agent): """ where do they want to go today """ import random _homes = sandbox._homes possible_homes = _homes.keys() possible_homes.remove(agent.getHome().getId()) new_home = _homes.get(random.choice(possible_homes)) return checker.selectChecker(new_home).proxy(new_home) sandbox.GreenerPastures = GreenerPastures if __name__ == '__main__': wire_security() sandbox.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/i18n.py0000644000076500000240000000157414355021564017731 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """Customization of zope.i18n for the Zope application server """ __docformat__ = 'restructuredtext' # import this as _ to create i18n messages in the zope domain from zope.i18nmessageid import MessageFactory ZopeMessageFactory = MessageFactory('zope') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/interfaces.py0000644000076500000240000003267014355021564021276 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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. # ############################################################################## """ Interfaces for security machinery. These can be categorized into a few different groups of related objects. * Exceptions - :class:`IUnauthorized` - :class:`IForbidden` - :class:`IForbiddenAttribute` - :class:`NoInteraction` * Utilities - :class:`ISecurityManagement` - :class:`ISecurityChecking` - :class:`ISecurityProxyFactory` - :class:`IChecker` - :class:`INameBasedChecker` - :class:`ISecurityPolicy` * Principals - :class:`IInteraction` - :class:`IParticipation` - :class:`IInteractionManagement` - :class:`IPrincipal` - :class:`ISystemPrincipal` - :class:`IGroupAwarePrincipal` - :class:`IGroupClosureAwarePrincipal` - :class:`IGroup` - :class:`IMemberGetterGroup` - :class:`IMemberAwareGroup` - :class:`IPermission` Anywhere that an API is documented as accepting a permission, it means the name of the permission, or the special object :class:`zope.security.checker.CheckerPublic`. """ from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer from zope.interface.common.interfaces import IAttributeError from zope.interface.common.interfaces import IException from zope.schema import NativeStringLine from zope.schema import Text from zope.schema import TextLine from zope.security.i18n import ZopeMessageFactory as _ #: The name (id) of the registered :class:`IPermission` utility that signifies #: that the protected attribute is public. #: #: .. versionadded:: 4.2.0 PUBLIC_PERMISSION_NAME = 'zope.Public' class IUnauthorized(IException): """ The action is not authorized. Implemented in :class:`Unauthorized`. """ @implementer(IUnauthorized) class Unauthorized(Exception): """ Some user wasn't allowed to access a resource. Default implementation of :class:`IUnauthorized`. """ class IForbidden(IException): """ A resource cannot be accessed under any circumstances Implemented in :class:`Forbidden`. """ @implementer(IForbidden) class Forbidden(Exception): """ A resource cannot be accessed under any circumstances Default implementation if :class:`IForbidden`. """ class IForbiddenAttribute(IForbidden, IAttributeError): """ An attribute is unavailable because it is forbidden (private). Implemented in :class:`ForbiddenAttribute`. """ @implementer(IForbiddenAttribute) class ForbiddenAttribute(Forbidden, AttributeError): """ An attribute is unavailable because it is forbidden (private). Default implementation of :class:`IForbiddenAttribute`. """ class ISecurityManagement(Interface): """ Public security management API. This is implemented by :mod:`zope.security.management`. """ def getSecurityPolicy(): """Get the system default security policy.""" def setSecurityPolicy(aSecurityPolicy): """Set the system default security policy. This method should only be called by system startup code. It should never, for example, be called during a web request. """ class ISecurityChecking(Interface): """ Public security API. """ def checkPermission(permission, object, interaction=None): """ Return whether security policy allows permission on object. :param str permission: The permission name. :param object: The object being accessed according to the permission. :keyword interaction: An :class:`IInteraction`, providing access to information such as authenticated principals. If it is None, the current interaction is used. """ class ISecurityProxyFactory(Interface): """ A factory for creating security-proxied objects. See :class:`zope.security.checker.ProxyFactory` for the default implementation. """ def __call__(object, checker=None): """ Create a security proxy If a checker (:class:`IChecker`) is given, then use it, otherwise, try to figure out a checker. If the object is already a security proxy, then it will be returned. """ class IChecker(Interface): """ Security-proxy plugin objects that implement low-level checks. The checker is responsible for creating proxies for operation return values, via the ``proxy`` method. There are :meth:`check_getattr` and :meth:`check_setattr` methods for checking getattr and setattr, and a :meth:`check` method for all other operations. The check methods will raise errors if access is not allowed. They return no value. Example (for ``__getitem__``):: checker.check(ob, "__getitem__") return checker.proxy(ob[key]) .. seealso:: :mod:`zope.security.checker` """ def check_getattr(ob, name): """ Check whether attribute access is allowed. If a checker implements ``__setitem__``, then ``__setitem__`` will be called rather than ``check`` to ascertain whether an operation is allowed. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. :raises: :class:`Unauthorized` :raises: :class:`Forbidden` :return: Nothing """ def check_setattr(ob, name): """ Check whether attribute assignment is allowed. If a checker implements ``__setitem__``, then ``__setitem__`` will be called rather than ``check`` to ascertain whether an operation is allowed. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. :raises: :class:`Unauthorized` :raises: :class:`Forbidden` :return: Nothing """ def check(ob, operation): """ Check whether *operation* is allowed. The operation name is the Python special method name, e.g. "__getitem__". May raise Unauthorized or Forbidden. Returns no value. If a checker implements ``__setitem__``, then ``__setitem__`` will be called rather than ``check`` to ascertain whether an operation is allowed. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. :raises: :class:`Unauthorized` :raises: :class:`Forbidden` :return: Nothing """ def proxy(value): """ Return a security proxy for the *value*. If a checker implements ``__getitem__``, then ``__getitem__`` will be called rather than ``proxy`` to proxy the value. This is a hack that allows significantly greater performance due to the fact that low-level operator access is much faster than method access. """ class INameBasedChecker(IChecker): """ Security checker that uses permissions to check attribute access. """ def permission_id(name): """ Return the permission used to check attribute access on *name*. This permission is used by both :meth:`check` and :meth:`check_getattr`. """ def setattr_permission_id(name): """ Return the permission used to check attribute assignment on *name*. This permission is used by :meth:`check_setattr`. """ class ISecurityPolicy(Interface): """ A factory to get :class:`IInteraction` objects. .. seealso:: :mod:`zope.security.simplepolicies` For default implementations. """ def __call__(participation=None): """ Creates and returns a new :class:`IInteraction` for a given request. If *participation* is not None, it is added to the new interaction. """ class IInteraction(Interface): """ A representation of an interaction between some actors and the system. """ participations = Attribute("""An iterable of participations.""") def add(participation): """Add a participation.""" def remove(participation): """Remove a participation.""" def checkPermission(permission, object): """Return whether security context allows permission on object. :param str permission: A permission name :param object: The object being accessed according to the permission :return: Whether the access is allowed or not. :rtype: bool """ class IParticipation(Interface): """ A single participant in an interaction. """ interaction = Attribute("The interaction") principal = Attribute("The authenticated :class:`IPrincipal`") class NoInteraction(Exception): """No interaction started """ class IInteractionManagement(Interface): """ Interaction management API. Every thread has at most one active interaction at a time. .. seealso:: :mod:`zope.security.management` That module provides the default implementation. """ def newInteraction(participation=None): """ Start a new interaction. If *participation* is not None, it is added to the new interaction. Raises an error if the calling thread already has an interaction. """ def queryInteraction(): """ Return the current interaction. Return None if there is no interaction. """ def getInteraction(): """ Return the current interaction. :raise NoInteraction: if there isn't a current interaction. """ def endInteraction(): """ End the current interaction. Does nothing if there is no interaction. """ class IPrincipal(Interface): """ Principals are security artifacts that execute actions in a security environment. The most common examples of principals include user and group objects. It is likely that ``IPrincipal`` objects will have associated views used to list principals in management interfaces. For example, a system in which other meta-data are provided for principals might extend ``IPrincipal`` and register a view for the extended interface that displays the extended information. """ id = TextLine( title=_("Id"), description=_("The unique identification of the principal."), required=True, readonly=True) title = TextLine( title=_("Title"), description=_("The title of the principal. " "This is usually used in the UI."), required=False) description = Text( title=_("Description"), description=_("A detailed description of the principal."), required=False) class ISystemPrincipal(IPrincipal): """ A principal that represents the system (application) itself. Typically a system principal is granted extra capabilities or excluded from certain checks. End users should *not* be able to act as the system principal. Because speed is often a factor, a single instance of a system principal is found at ``zope.security.management.system_user`` and can be compared for by identity (e.g., ``if principal is system_user:``). """ class IGroupAwarePrincipal(IPrincipal): """ Group aware principal interface. Extends ``IPrincipal`` to contain direct group information. """ groups = Attribute( 'An iterable of :class:`IGroup` objects to which the principal' ' directly belongs') class IGroupClosureAwarePrincipal(IGroupAwarePrincipal): """ A group-aware principal that can recursively flatten the membership of groups to return all the groups. """ allGroups = Attribute( "An iterable of the full closure of the principal's groups.") class IGroup(IPrincipal): """ Group of principals """ class IMemberGetterGroup(IGroup): """ A group that can get its members. """ def getMembers(): """Return an iterable of the members of the group""" class IMemberAwareGroup(IMemberGetterGroup): """ A group that can both set and get its members. """ def setMembers(value): """ Set members of group to the principal IDs in the iterable *value*. """ class IPermission(Interface): """A permission object. Note that the ZCML ```` directive restricts the ``id`` to be an identifier (a dotted name or a URI), but this interface allows any native string. """ id = NativeStringLine( title=_("Id"), description=_("Id as which this permission will be known and used."), readonly=True, required=True) title = TextLine( title=_("Title"), description=_("Provides a title for the permission."), required=True) description = Text( title=_("Description"), description=_("Provides a description for the permission."), required=False) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/management.py0000644000076500000240000001177314355021564021270 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """ Default :class:`zope.security.interfaces.ISecurityManagement` and :class:`zope.security.interfaces.IInteractionManagement` implementation. Note that this module itself provides those interfaces. """ from zope.interface import moduleProvides from zope.security._definitions import system_user from zope.security._definitions import thread_local from zope.security.checker import CheckerPublic from zope.security.interfaces import IInteractionManagement from zope.security.interfaces import ISecurityManagement from zope.security.interfaces import NoInteraction from zope.security.simplepolicies import ParanoidSecurityPolicy __all__ = [ 'system_user', 'getSecurityPolicy', 'setSecurityPolicy', 'queryInteraction', 'getInteraction', 'ExistingInteraction', 'newInteraction', 'endInteraction', 'restoreInteraction', 'checkPermission', ] _defaultPolicy = ParanoidSecurityPolicy moduleProvides( ISecurityManagement, IInteractionManagement) # # ISecurityManagement implementation # def getSecurityPolicy(): """Get the system default security policy.""" return _defaultPolicy def setSecurityPolicy(aSecurityPolicy): """Set the system default security policy, and return the previous value. This method should only be called by system startup code. It should never, for example, be called during a web request. """ global _defaultPolicy last, _defaultPolicy = _defaultPolicy, aSecurityPolicy return last # # IInteractionManagement implementation # def queryInteraction(): """Return a current interaction, if there is one.""" return getattr(thread_local, 'interaction', None) def getInteraction(): """Get the current interaction.""" try: return thread_local.interaction except AttributeError: raise NoInteraction class ExistingInteraction(ValueError, AssertionError, # BBB ): """ The exception that :func:`newInteraction` will raise if called during an existing interaction. """ def newInteraction(*participations): """Start a new interaction.""" if queryInteraction() is not None: raise ExistingInteraction("newInteraction called" " while another interaction is active.") thread_local.interaction = getSecurityPolicy()(*participations) def endInteraction(): """End the current interaction.""" try: thread_local.previous_interaction = thread_local.interaction except AttributeError: # if someone does a restore later, it should be restored to not having # an interaction. If there was a previous interaction from a previous # call to endInteraction, it should be removed. try: del thread_local.previous_interaction except AttributeError: pass else: del thread_local.interaction def restoreInteraction(): try: previous = thread_local.previous_interaction except AttributeError: try: del thread_local.interaction except AttributeError: pass else: thread_local.interaction = previous def checkPermission(permission, object, interaction=None): """Return whether security policy allows permission on object. :param str permission: A permission name. :param object: The object being accessed according to the permission. :param interaction: An interaction, providing access to information such as authenticated principals. If it is None, the current interaction is used. :return: A boolean value. ``checkPermission`` is guaranteed to return ``True`` if *permission* is :data:`zope.security.checker.CheckerPublic` or ``None``. :raise NoInteraction: If there is no current interaction and no interaction argument was given. """ if permission is CheckerPublic or permission is None: return True if interaction is None: try: interaction = thread_local.interaction except AttributeError: raise NoInteraction return interaction.checkPermission(permission, object) def _clear(): global _defaultPolicy _defaultPolicy = ParanoidSecurityPolicy try: from zope.testing.cleanup import addCleanUp except ImportError: # pragma: no cover pass else: addCleanUp(_clear) addCleanUp(endInteraction) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/meta.zcml0000644000076500000240000000365414355021564020416 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/metaconfigure.py0000644000076500000240000002017614355021564022001 0ustar00jensstaff############################################################################# # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """ Register class directive. """ __docformat__ = 'restructuredtext' from types import ModuleType from zope.component.factory import Factory from zope.component.interface import provideInterface from zope.component.interfaces import IFactory from zope.component.zcml import utility from zope.configuration.exceptions import ConfigurationError from zope.interface import classImplements from zope.schema.interfaces import IField from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import defineChecker from zope.security.checker import moduleChecker from zope.security.interfaces import PUBLIC_PERMISSION_NAME as PublicPermission from zope.security.protectclass import protectLikeUnto from zope.security.protectclass import protectName from zope.security.protectclass import protectSetAttribute def dottedName(klass): if klass is None: return 'None' return klass.__module__ + '.' + klass.__name__ class ProtectionDeclarationException(Exception): """Security-protection-specific exceptions.""" pass class ClassDirective: def __init__(self, _context, class_): self.__id = dottedName(class_) # this would barf on a module, anyway self.__class = class_ if isinstance(self.__class, ModuleType): # pragma: no cover raise ConfigurationError('Content class attribute must be a class') self.__context = _context def implements(self, _context, interface): for interface in interface: _context.action( discriminator=( 'ContentDirective', self.__class, object()), callable=classImplements, args=(self.__class, interface), ) _context.action( discriminator=None, callable=provideInterface, args=(interface.__module__ + '.' + interface.getName(), interface) ) def require(self, _context, permission=None, attributes=None, interface=None, like_class=None, set_attributes=None, set_schema=None): """Require a permission to access a specific aspect""" if like_class: self.__mimic(_context, like_class) if not (interface or attributes or set_attributes or set_schema): if like_class: return raise ConfigurationError("Nothing required") if not permission: raise ConfigurationError("No permission specified") if interface: for i in interface: if i: self.__protectByInterface(i, permission) if attributes: self.__protectNames(attributes, permission) if set_attributes: self.__protectSetAttributes(set_attributes, permission) if set_schema: for s in set_schema: self.__protectSetSchema(s, permission) def __mimic(self, _context, class_): """Base security requirements on those of the given class""" _context.action( discriminator=('mimic', self.__class, object()), callable=protectLikeUnto, args=(self.__class, class_), ) def allow(self, _context, attributes=None, interface=None): """Like require, but with permission_id zope.Public""" return self.require(_context, PublicPermission, attributes, interface) def __protectByInterface(self, interface, permission_id): "Set a permission on names in an interface." for n, d in sorted(interface.namesAndDescriptions(1)): self.__protectName(n, permission_id) self.__context.action( discriminator=None, callable=provideInterface, args=(interface.__module__ + '.' + interface.getName(), interface) ) def __protectName(self, name, permission_id): "Set a permission on a particular name." self.__context.action( discriminator=('protectName', self.__class, name), callable=protectName, args=(self.__class, name, permission_id) ) def __protectNames(self, names, permission_id): "Set a permission on a bunch of names." for name in names: self.__protectName(name, permission_id) def __protectSetAttributes(self, names, permission_id): "Set a permission on a bunch of names." for name in names: self.__context.action( discriminator=('protectSetAttribute', self.__class, name), callable=protectSetAttribute, args=(self.__class, name, permission_id) ) def __protectSetSchema(self, schema, permission_id): "Set a permission on a bunch of names." _context = self.__context for name in sorted(schema): field = schema[name] if IField.providedBy(field) and not field.readonly: _context.action( discriminator=('protectSetAttribute', self.__class, name), callable=protectSetAttribute, args=(self.__class, name, permission_id) ) _context.action( discriminator=None, callable=provideInterface, args=(schema.__module__ + '.' + schema.getName(), schema) ) def __call__(self): "Handle empty/simple declaration." return () def factory(self, _context, id=None, title="", description=''): """Register a zmi factory for this class""" id = id or self.__id factoryObj = Factory(self.__class, title, description) # note factories are all in one pile, utilities and content, # so addable names must also act as if they were all in the # same namespace, despite the utilities/content division utility(_context, IFactory, factoryObj, permission=PublicPermission, name=id) def protectModule(module, name, permission): """Set up a module checker to require a permission to access a name If there isn't a checker for the module, create one. """ checker = moduleChecker(module) if checker is None: checker = Checker({}, {}) defineChecker(module, checker) if permission == PublicPermission: # Translate public permission to CheckerPublic permission = CheckerPublic # We know a dictionary get method was used because we set it protections = checker.get_permissions protections[name] = permission def _names(attributes, interfaces): seen = {} for name in attributes: if name not in seen: seen[name] = 1 yield name for interface in interfaces: for name in interface: if name not in seen: seen[name] = 1 yield name def allow(context, attributes=(), interface=()): for name in _names(attributes, interface): context.action( discriminator=('http://namespaces.zope.org/zope:module', context.module, name), callable=protectModule, args=(context.module, name, PublicPermission), ) def require(context, permission, attributes=(), interface=()): for name in _names(attributes, interface): context.action( discriminator=('http://namespaces.zope.org/zope:module', context.module, name), callable=protectModule, args=(context.module, name, permission), ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/metadirectives.py0000644000076500000240000001531614355021564022161 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """Component architecture related 'zope' ZCML namespace directive interfaces """ __docformat__ = 'restructuredtext' import zope.configuration.fields import zope.interface import zope.schema from zope.configuration.fields import GlobalInterface from zope.configuration.fields import GlobalObject from zope.configuration.fields import PythonIdentifier from zope.configuration.fields import Tokens from zope.interface import Interface import zope.security.zcml from zope.security.i18n import ZopeMessageFactory as _ from zope.security.zcml import Permission class IClassDirective(zope.interface.Interface): """Make statements about a class""" class_ = zope.configuration.fields.GlobalObject( title=_("Class"), required=True ) class IImplementsSubdirective(zope.interface.Interface): """Declare that the class given by the content directive's class attribute implements a given interface """ interface = zope.configuration.fields.Tokens( title=_("One or more interfaces"), required=True, value_type=zope.configuration.fields.GlobalInterface() ) class IRequireSubdirective(zope.interface.Interface): """Indicate that the a specified list of names or the names in a given Interface require a given permission for access. """ permission = zope.security.zcml.Permission( title=_("Permission"), description=_(""" Specifies the permission by id that will be required to access or mutate the attributes and methods specified."""), required=False, ) attributes = zope.configuration.fields.Tokens( title=_("Attributes and methods"), description=_("This is a list of attributes and methods" " that can be accessed."), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) set_attributes = zope.configuration.fields.Tokens( title=_("Attributes that can be set"), description=_("This is a list of attributes that can be" " modified/mutated."), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) interface = zope.configuration.fields.Tokens( title=_("Interfaces"), description=_("The listed interfaces' methods and attributes" " can be accessed."), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) set_schema = zope.configuration.fields.Tokens( title=_("The attributes specified by the schema can be set"), description=_("The listed schemas' properties can be" " modified/mutated."), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) like_class = zope.configuration.fields.GlobalObject( title=_("Configure like this class"), description=_(""" This argument says that this content class should be configured in the same way the specified class' security is. If this argument is specified, no other argument can be used."""), required=False, ) class IAllowSubdirective(zope.interface.Interface): """ Declare a part of the class to be publicly viewable (that is, requires the zope.Public permission). Only one of the following two attributes may be used. """ attributes = zope.configuration.fields.Tokens( title=_("Attributes"), required=False, value_type=zope.configuration.fields.PythonIdentifier(), ) interface = zope.configuration.fields.Tokens( title=_("Interface"), required=False, value_type=zope.configuration.fields.GlobalInterface(), ) class IFactorySubdirective(zope.interface.Interface): """Specify the factory used to create this content object""" id = zope.schema.Id( title=_("ID"), description=_(""" the identifier for this factory in the ZMI factory identification scheme. If not given, defaults to the literal string given as the content directive's 'class' attribute."""), required=False, ) title = zope.configuration.fields.MessageID( title=_("Title"), description=_("Text suitable for use in the 'add content' menu" " of a management interface"), required=False, ) description = zope.configuration.fields.MessageID( title=_("Description"), description=_("Longer narrative description of what this" " factory does"), required=False, ) class IModule(Interface): """Group security declarations about a module""" module = GlobalObject( title="Module", description="Pointer to the module object.", required=True) class IAllow(Interface): """Allow access to selected module attributes Access is unconditionally allowed to any names provided directly in the attributes attribute or to any names defined by interfaces listed in the interface attribute. """ attributes = Tokens( title="Attributes", description="The attributes to provide access to.", value_type=PythonIdentifier(), required=False) interface = Tokens( title="Interface", description=("Interfaces whos names to provide access to. Access " "will be provided to all of the names defined by the " "interface(s). Multiple interfaces can be supplied."), value_type=GlobalInterface(), required=False) class IRequire(Interface): """Require a permission to access selected module attributes The given permission is required to access any names provided directly in the attributes attribute or any names defined by interfaces listed in the interface attribute. """ attributes = Tokens( title="Attributes", description="The attributes to require permission for.", value_type=PythonIdentifier(), required=False) permission = Permission( title="Permission ID", description="The ID of the permission to require.") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/permission.py0000644000076500000240000000671314355021564021342 0ustar00jensstaff############################################################################## # # Copyright (c) 2002 Zope Foundation 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. # ############################################################################## """Permissions """ __docformat__ = "reStructuredText" import operator from zope.component import getUtilitiesFor from zope.component import queryUtility from zope.interface import directlyProvides from zope.interface import implementer from zope.schema.interfaces import IVocabularyFactory from zope.schema.vocabulary import SimpleTerm from zope.schema.vocabulary import SimpleVocabulary from zope.security.checker import CheckerPublic from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public from zope.security.interfaces import IPermission @implementer(IPermission) class Permission: """ Default implementation of :class:`zope.security.interfaces.IPermission`. """ def __init__(self, id, title="", description=""): self.id = id self.title = title self.description = description def checkPermission(context, permission_id): """ Check whether a given permission object exists in the provided context as a utility. """ if permission_id is CheckerPublic: return if not queryUtility(IPermission, permission_id, context=context): raise ValueError("Undefined permission ID", permission_id) def allPermissions(context=None): """ Get the IDs of all defined permission object utilities. """ for name, _permission in getUtilitiesFor(IPermission, context): if name != zope_Public: yield name def PermissionsVocabulary(context=None): """ A vocabulary of permission IDs. Term values are permissions, while term tokens are permission IDs. """ terms = [] for name, permission in getUtilitiesFor(IPermission, context): terms.append(SimpleTerm(permission, name)) return SimpleVocabulary(terms) directlyProvides(PermissionsVocabulary, IVocabularyFactory) def PermissionIdsVocabulary(context=None): """ A vocabulary of permission IDs. Term values are the permission ID strings except for :data:`zope.Public `, which is the global permission :data:`zope.security.checker.CheckerPublic`. Term titles are the permission ID strings except for :data:`zope.Public `, which is shortened to 'Public'. Terms are sorted by title except for 'Public', which always appears as the first term. """ terms = [] has_public = False for name, _permission in getUtilitiesFor(IPermission, context): if name == zope_Public: has_public = True else: terms.append(SimpleTerm(name, name, name)) terms = sorted(terms, key=operator.attrgetter('title')) if has_public: terms.insert(0, SimpleTerm(CheckerPublic, zope_Public, 'Public')) return SimpleVocabulary(terms) directlyProvides(PermissionIdsVocabulary, IVocabularyFactory) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/permissions.zcml0000644000076500000240000000177314355021564022043 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/protectclass.py0000644000076500000240000000666014355021564021661 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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 instance attributes """ from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import defineChecker from zope.security.checker import getCheckerForInstancesOf from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public 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 the given 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] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1681800803.0 zope_security-7.3/src/zope/security/proxy.py0000644000076500000240000003276714417437143020346 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """ Helper functions for proxies. .. seealso:: :ref:`proxy-known-issues` """ import functools import sys from zope.proxy import PyProxyBase from zope.security._compat import PURE_PYTHON def _check_name(meth, wrap_result=True): name = meth.__name__ def _wrapper(self, *args, **kw): wrapped = super(PyProxyBase, self).__getattribute__('_wrapped') checker = super(PyProxyBase, self).__getattribute__('_checker') checker.check(wrapped, name) res = meth(self, *args, **kw) if not wrap_result: return res return checker.proxy(res) return functools.update_wrapper(_wrapper, meth) def _check_name_inplace(meth): name = meth.__name__ def _wrapper(self, *args, **kw): wrapped = super(PyProxyBase, self).__getattribute__('_wrapped') checker = super(PyProxyBase, self).__getattribute__('_checker') checker.check(wrapped, name) w_meth = getattr(wrapped, name, None) if w_meth is not None: # The proxy object cannot change; we are modifying in place. self._wrapped = w_meth(*args, **kw) return self x_name = '__%s__' % name[3:-2] return ProxyPy(getattr(wrapped, x_name)(*args, **kw), checker) return functools.update_wrapper(_wrapper, meth) def _fmt_address(obj): # Try to replicate PyString_FromString("%p", obj), which actually uses # the platform sprintf(buf, "%p", obj), which we cannot access from Python # directly (and ctypes seems like overkill). if sys.platform != 'win32': return '0x%0x' % id(obj) if sys.maxsize < 2**32: # pragma: no cover return '0x%08X' % id(obj) return '0x%016X' % id(obj) # pragma: no cover class ProxyPy(PyProxyBase): """ The pure-Python reference implementation of a security proxy. This should normally not be created directly, instead use the :func:`~.ProxyFactory`. You can choose to use this implementation instead of the C implementation by default by setting the ``PURE_PYTHON`` environment variable before :mod:`zope.security` is imported. """ __slots__ = ('_wrapped', '_checker') def __new__(cls, value, checker): inst = super().__new__(cls) inst._wrapped = value inst._checker = checker return inst def __init__(self, value, checker): if checker is None: raise ValueError('checker may now be None') self._wrapped = value self._checker = checker # Attribute protocol def __getattribute__(self, name): if name in ('_wrapped', '_checker'): # Only allow _wrapped and _checker to be accessed from inside. if sys._getframe(1).f_locals.get('self') is not self: raise AttributeError(name) wrapped = super().__getattribute__('_wrapped') if name == '_wrapped': return wrapped checker = super().__getattribute__('_checker') if name == '_checker': return checker if name not in ('__cmp__', '__hash__', '__bool__', '__lt__', '__le__', '__eq__', '__ne__', '__ge__', '__gt__'): checker.check_getattr(wrapped, name) if name in ('__reduce__', '__reduce_ex__'): # The superclass specifically denies access to __reduce__ # and __reduce__ex__, not letting proxies be pickled. But # for backwards compatibility, we need to be able to # pickle proxies. See checker:Global for an example. val = getattr(wrapped, name) elif name == '__module__': # The superclass deals with descriptors found in the type # of this object just like the Python language spec states, letting # them have precedence over things found in the instance. This # normally makes us a better proxy implementation. However, the # C version of this code in _proxy doesn't take that same care and # instead uses the generic object attribute access methods directly # on the wrapped object. This is a behaviour difference; so far, # it's only been noticed for the __module__ attribute, which # checker:Global wants to override but couldn't because this # object's type's __module__ would get in the way. That broke # pickling, and checker:Global can't return anything more # sophisticated than a str (a tuple) because it gets proxied and # breaks pickling again. Our solution is to match the C version for # this one attribute. val = getattr(wrapped, name) else: val = super().__getattribute__(name) return checker.proxy(val) def __getattr__(self, name): # We only get here if __getattribute__ has already raised an # AttributeError (we have to implement this because the super # class does). We expect that we will also raise that same # error, one way or another---either it will be forbidden by # the checker or it won't exist. However, if the underlying # object is playing games in *its* # __getattribute__/__getattr__, and we call getattr() on it, # (maybe there are threads involved), we might actually # succeed this time. # The C implementation *does not* do two checks; it only does # one check, and raises either the ForbiddenAttribute or the # underlying AttributeError, *without* invoking any defined # __getattribute__/__getattr__ more than once. So we # explicitly do the same. The consequence is that we lose a # good stack trace if the object implemented its own methods # but we're consistent. We would provide a better error # message or even subclass of AttributeError, but that's liable to # break (doc)tests. wrapped = super().__getattribute__('_wrapped') checker = super().__getattribute__('_checker') checker.check_getattr(wrapped, name) raise AttributeError(name) def __setattr__(self, name, value): if name in ('_wrapped', '_checker'): return super().__setattr__(name, value) wrapped = super().__getattribute__('_wrapped') checker = super().__getattribute__('_checker') checker.check_setattr(wrapped, name) setattr(wrapped, name, value) def __delattr__(self, name): if name in ('_wrapped', '_checker'): raise AttributeError() wrapped = super().__getattribute__('_wrapped') checker = super().__getattribute__('_checker') checker.check_setattr(wrapped, name) delattr(wrapped, name) def __lt__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped < other def __le__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped <= other def __eq__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped == other def __ne__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped != other def __ge__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped >= other def __gt__(self, other): # no check wrapped = super().__getattribute__('_wrapped') return wrapped > other def __hash__(self): # no check wrapped = super().__getattribute__('_wrapped') return hash(wrapped) def __bool__(self): # no check wrapped = super().__getattribute__('_wrapped') return bool(wrapped) def __length_hint__(self): # no check wrapped = super().__getattribute__('_wrapped') try: hint = wrapped.__length_hint__ except AttributeError: return NotImplemented else: return hint() def __str__(self): try: return _check_name(PyProxyBase.__str__)(self) # The C implementation catches almost all exceptions; the # exception is a TypeError that's raised when the repr returns # the wrong type of object. except TypeError: raise except: # noqa: E722 do not use bare 'except' # The C implementation catches all exceptions. wrapped = super().__getattribute__('_wrapped') return ''.format( wrapped.__class__.__module__, wrapped.__class__.__name__, _fmt_address(wrapped)) def __repr__(self): try: return _check_name(PyProxyBase.__repr__)(self) # The C implementation catches almost all exceptions; the # exception is a TypeError that's raised when the repr returns # the wrong type of object. except TypeError: raise except: # noqa: E722 do not use bare 'except' wrapped = super().__getattribute__('_wrapped') return ''.format( wrapped.__class__.__module__, wrapped.__class__.__name__, _fmt_address(wrapped)) for name in ['__call__', # '__repr__', # '__str__', # '__unicode__', # Unchecked in C proxy '__reduce__', '__reduce_ex__', # '__lt__', # Unchecked in C proxy (rich comparison) # '__le__', # Unchecked in C proxy (rich comparison) # '__eq__', # Unchecked in C proxy (rich comparison) # '__ne__', # Unchecked in C proxy (rich comparison) # '__ge__', # Unchecked in C proxy (rich comparison) # '__gt__', # Unchecked in C proxy (rich comparison) # '__bool__', # Unchecked in C proxy (rich comparison) # '__hash__', # Unchecked in C proxy (rich comparison) # '__cmp__', # Unchecked in C proxy '__getitem__', '__setitem__', '__delitem__', '__iter__', '__next__', '__contains__', '__neg__', '__pos__', '__abs__', '__invert__', '__complex__', '__int__', '__float__', '__index__', '__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', '__mod__', '__divmod__', '__pow__', '__radd__', '__rsub__', '__rmul__', '__rtruediv__', '__rfloordiv__', '__rmod__', '__rdivmod__', '__rpow__', '__lshift__', '__rshift__', '__and__', '__xor__', '__or__', '__rlshift__', '__rrshift__', '__rand__', '__rxor__', '__ror__', ]: meth = getattr(PyProxyBase, name) setattr(ProxyPy, name, _check_name(meth)) for name in ( '__len__', ): meth = getattr(PyProxyBase, name) setattr(ProxyPy, name, _check_name(meth, False)) for name in ['__iadd__', '__isub__', '__imul__', '__itruediv__', '__ifloordiv__', '__imod__', '__ilshift__', '__irshift__', '__iand__', '__ixor__', '__ior__', '__ipow__', ]: meth = getattr(PyProxyBase, name) setattr(ProxyPy, name, _check_name_inplace(meth)) def getCheckerPy(proxy): return super(ProxyPy, proxy).__getattribute__('_checker') _builtin_isinstance = sys.modules['builtins'].isinstance def getObjectPy(proxy): if not _builtin_isinstance(proxy, ProxyPy): return proxy return super(ProxyPy, proxy).__getattribute__('_wrapped') _c_available = not PURE_PYTHON if _c_available: # pragma: no cover try: from zope.security._proxy import _Proxy except (ImportError, AttributeError): _c_available = False getChecker = getCheckerPy getObject = getObjectPy Proxy = ProxyPy if _c_available: # pragma: no cover from zope.security._proxy import getChecker from zope.security._proxy import getObject Proxy = _Proxy removeSecurityProxy = getObject def getTestProxyItems(proxy): """Return a sorted sequence of checker names and permissions for testing """ checker = getChecker(proxy) return sorted(checker.get_permissions.items()) def isinstance(object, cls): """Test whether an *object* is an instance of a type. This works even if the object is security proxied. """ # The removeSecurityProxy call is OK here because it is *only* # being used for isinstance return _builtin_isinstance(removeSecurityProxy(object), cls) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/simplepolicies.py0000644000076500000240000000527614355021564022176 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """ Simple :class:`zope.security.interfaces.ISecurityPolicy` implementations. As a reminder, ``ISecurityPolicy`` objects are factories for producing :class:`zope.security.interfaces.IInteraction` objects. That means that the classes themselves are implementations of ``ISecurityPolicy``. """ import zope.interface from zope.security._definitions import system_user from zope.security.checker import CheckerPublic from zope.security.interfaces import IInteraction from zope.security.interfaces import ISecurityPolicy @zope.interface.implementer(IInteraction) @zope.interface.provider(ISecurityPolicy) class ParanoidSecurityPolicy: """ Prohibit all access by any non-system principal, unless the item is :data:`public `. This means that if there are no participations (and hence no principals), then access is allowed. """ def __init__(self, *participations): self.participations = [] for participation in participations: self.add(participation) def add(self, participation): if participation.interaction is not None: raise ValueError("%r already belongs to an interaction" % participation) participation.interaction = self self.participations.append(participation) def remove(self, participation): if participation.interaction is not self: raise ValueError("%r does not belong to this interaction" % participation) self.participations.remove(participation) participation.interaction = None def checkPermission(self, permission, object): if permission is CheckerPublic: return True users = [p.principal for p in self.participations if p.principal is not system_user] return not users @zope.interface.provider(ISecurityPolicy) class PermissiveSecurityPolicy(ParanoidSecurityPolicy): """ Allow all access. """ def checkPermission(self, permission, object): return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/testing.py0000644000076500000240000000621114355021564020620 0ustar00jensstaff############################################################################## # # Copyright (c) 2004-2011 Zope Foundation 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. # ############################################################################## """ Testing support code. This module provides some helper/stub objects for setting up interactions. """ import contextlib import zope.security.management from zope import component from zope import interface from zope.security import interfaces from zope.security.interfaces import PUBLIC_PERMISSION_NAME from zope.security.permission import Permission @interface.implementer(interfaces.IPrincipal) class Principal: """ A trivial implementation of :class:`zope.security.interfaces.IPrincipal`. """ def __init__(self, id, title=None, description='', groups=None): self.id = id self.title = title or id self.description = description if groups is not None: self.groups = groups interface.directlyProvides(self, interfaces.IGroupAwarePrincipal) @interface.implementer(interfaces.IParticipation) class Participation: """ A trivial implementation of :class:`zope.security.interfaces.IParticipation`. """ def __init__(self, principal): self.principal = principal self.interaction = None def addCheckerPublic(): """ Add the CheckerPublic permission as :data:`zope.Public `. """ perm = Permission( PUBLIC_PERMISSION_NAME, 'Public', """Special permission used for resources that are always public The public permission is effectively an optimization, sine it allows security computation to be bypassed. """ ) gsm = component.getGlobalSiteManager() gsm.registerUtility(perm, interfaces.IPermission, perm.id) return perm def create_interaction(principal_id, **kw): """ Create a new interaction for the given principal ID, make it the :func:`current interaction `, and return the :class:`Principal` object. """ principal = Principal(principal_id, **kw) participation = Participation(principal) zope.security.management.newInteraction(participation) return principal @contextlib.contextmanager def interaction(principal_id, **kw): """ A context manager for running an interaction for the given principal ID. """ if zope.security.management.queryInteraction(): # There already is an interaction. Great. Leave it alone. yield else: principal = create_interaction(principal_id, **kw) try: yield principal finally: zope.security.management.endInteraction() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1147478 zope_security-7.3/src/zope/security/tests/0000755000076500000240000000000014677253221017737 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/__init__.py0000644000076500000240000000111514355021564022042 0ustar00jensstaffimport io class QuietWatchingChecker: # zope.testrunner does not support setUp/tearDownModule, # so we use a mixin class to make sure we don't flood stderr # with pointless printing when testing watching checkers def setUp(self): from zope.security import checker self.__old_file = checker.CheckerLoggingMixin._file checker.CheckerLoggingMixin._file = ( io.StringIO() if bytes is not str else io.BytesIO()) def tearDown(self): from zope.security import checker checker.CheckerLoggingMixin._file = self.__old_file ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/exampleclass.py0000644000076500000240000000153014355021564022765 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """Example test classes """ from zope.interface import Interface class ExampleClass: pass class IExample(Interface): pass class IExample2(Interface): pass class IExampleContainer(Interface): pass ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/module.py0000644000076500000240000000303014355021564021566 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. """ from zope.interface import Interface from zope.schema import Text class I(Interface): # noqa: E742 ambiguous class definition 'I' def m1(): """m1""" def m2(): """m2""" class I2(I): def m4(): """m4""" class I3(Interface): def m3(): """m3""" class I4(Interface): def m2(): """m2""" class S(Interface): foo = Text() bar = Text() baro = Text(readonly=True) class S2(Interface): foo2 = Text() bar2 = Text() template_bracket = """ %s """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/redefineperms.zcml0000644000076500000240000000130314355021564023447 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_adapter.py0000644000076500000240000004426314672224636023004 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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. # ############################################################################## import unittest from zope.interface import directlyProvides from zope.interface import implementer from zope.location import ILocation from zope.location import LocationProxy from zope.proxy import getProxiedObject # pylint:disable=attribute-defined-outside-init,protected-access class Test_assertLocation(unittest.TestCase): def _callFUT(self, adapter, parent): from zope.security.adapter import assertLocation return assertLocation(adapter, parent) def test_w_non_ILocation(self): class _NotAdapter: pass adapter = _NotAdapter() parent = object() returned = self._callFUT(adapter, parent) self.assertIsInstance(returned, LocationProxy) self.assertIs(getProxiedObject(returned), adapter) self.assertIs(returned.__parent__, parent) def test_w_ILocation_no_parent(self): @implementer(ILocation) class _Adapter: __parent__ = None adapter = _Adapter() parent = object() returned = self._callFUT(adapter, parent) self.assertIs(returned, adapter) self.assertIs(returned.__parent__, parent) def test_w_ILocation_w_parent(self): parent = object() @implementer(ILocation) class _Adapter: __parent__ = parent adapter = _Adapter() new_parent = object() returned = self._callFUT(adapter, new_parent) self.assertIs(returned, adapter) self.assertIs(returned.__parent__, parent) class LocatingTrustedAdapterFactoryTests(unittest.TestCase): def _getTargetClass(self): from zope.security.adapter import LocatingTrustedAdapterFactory return LocatingTrustedAdapterFactory def _makeOne(self, factory): return self._getTargetClass()(factory) def _makeFactory(self): class _Factory: __name__ = 'testing' __module__ = 'zope.security.tests.test_adapter' _called_with = () def __call__(self, *args): self._called_with = args return self return _Factory() def test_ctor(self): factory = self._makeFactory() ltaf = self._makeOne(factory) self.assertIs(ltaf.factory, factory) self.assertEqual(ltaf.__name__, 'testing') self.assertEqual(ltaf.__module__, 'zope.security.tests.test_adapter') def test__call__w_non_ILocation_non_spacesuit(self): factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() before = factory.__dict__.copy() returned = ltaf(adapter) self.assertIs(returned, factory) after = {k: v for k, v in returned.__dict__.items() if k != '_called_with'} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_non_spacesuit_multiple_args(self): factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() extra = object() before = factory.__dict__.copy() returned = ltaf(adapter, extra) self.assertIs(returned, factory) after = {k: v for k, v in returned.__dict__.items() if k != '_called_with'} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_existing_parent_non_spacesuit(self): factory = self._makeFactory() parent = factory.__parent__ = object() directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() returned = ltaf(adapter) self.assertIs(returned, factory) self.assertIs(returned.__parent__, parent) def test__call__w_ILocation_wo_existing_parent_non_spacesuit(self): factory = self._makeFactory() factory.__parent__ = None directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() returned = ltaf(adapter) self.assertIs(returned, factory) self.assertIs(returned.__parent__, adapter) def test__call__w_non_ILocation_w_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() proxy = ProxyFactory(adapter) before = factory.__dict__.copy() returned = ltaf(proxy) self.assertIsNot(returned, factory) ploc = removeSecurityProxy(returned) self.assertIs(ploc.__parent__, adapter) unwrapped = getProxiedObject(ploc) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_w_spacesuit_multiple_args(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() extra = object() proxy = ProxyFactory(adapter) before = factory.__dict__.copy() returned = ltaf(proxy, extra) self.assertIsNot(returned, factory) ploc = removeSecurityProxy(returned) self.assertIs(ploc.__parent__, adapter) unwrapped = getProxiedObject(ploc) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_multiple_args_extra_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass class _Extra: pass adapter = _NotAdapter() extra = _Extra() proxy = ProxyFactory(extra) before = factory.__dict__.copy() returned = ltaf(adapter, proxy) self.assertIsNot(returned, factory) ploc = removeSecurityProxy(returned) self.assertIs(ploc.__parent__, adapter) unwrapped = getProxiedObject(ploc) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import getObject from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() factory.__parent__ = factory.__name__ = None directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _Adapter: pass adapter = _Adapter() proxy = ProxyFactory(adapter) before = {k: v for k, v in factory.__dict__.items() if k not in ('_called_with', '__parent__')} returned = ltaf(proxy) self.assertIsNot(returned, factory) ploc = removeSecurityProxy(returned) self.assertIs(ploc.__parent__, adapter) unwrapped = getObject(ploc) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with', '__parent__')} self.assertEqual(factory._called_with, (adapter,)) self.assertIs(factory.__parent__, adapter) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_spacesuit_w_existing_parent(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import getObject from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() factory.__name__ = None factory.__parent__ = parent = object() directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _Adapter: pass adapter = _Adapter() proxy = ProxyFactory(adapter) before = {k: v for k, v in factory.__dict__.items() if k not in ('_called_with', '__parent__')} returned = ltaf(proxy) self.assertIsNot(returned, factory) ploc = removeSecurityProxy(returned) self.assertIs(ploc.__parent__, parent) unwrapped = getObject(ploc) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with', '__parent__')} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs class TrustedAdapterFactoryTests(unittest.TestCase): def _getTargetClass(self): from zope.security.adapter import TrustedAdapterFactory return TrustedAdapterFactory def _makeOne(self, factory): return self._getTargetClass()(factory) def _makeFactory(self): class _Factory: __name__ = 'testing' __module__ = 'zope.security.tests.test_adapter' def __call__(self, *args): self._called_with = args return self return _Factory() def test__call__w_non_ILocation_w_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() proxy = ProxyFactory(adapter) before = factory.__dict__.copy() returned = ltaf(proxy) self.assertIsNot(returned, factory) unwrapped = removeSecurityProxy(returned) self.assertNotIn('__parent__', unwrapped.__dict__) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_w_spacesuit_multiple_args(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() extra = object() proxy = ProxyFactory(adapter) before = factory.__dict__.copy() returned = ltaf(proxy, extra) self.assertIsNot(returned, factory) unwrapped = removeSecurityProxy(returned) self.assertNotIn('__parent__', unwrapped.__dict__) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_multiple_args_extra_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass class _Extra: pass adapter = _NotAdapter() extra = _Extra() proxy = ProxyFactory(extra) before = factory.__dict__.copy() returned = ltaf(adapter, proxy) self.assertIsNot(returned, factory) unwrapped = removeSecurityProxy(returned) self.assertNotIn('__parent__', unwrapped.__dict__) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with',)} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_spacesuit(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() factory.__parent__ = factory.__name__ = None directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _Adapter: pass adapter = _Adapter() proxy = ProxyFactory(adapter) before = {k: v for k, v in factory.__dict__.items() if k not in ('_called_with', '__parent__')} returned = ltaf(proxy) self.assertIsNot(returned, factory) unwrapped = removeSecurityProxy(returned) self.assertIs(unwrapped.__parent__, adapter) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with', '__parent__')} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_spacesuit_w_existing_parent(self): from zope.security.proxy import ProxyFactory from zope.security.proxy import removeSecurityProxy factory = self._makeFactory() factory.__name__ = None factory.__parent__ = parent = object() directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _Adapter: pass adapter = _Adapter() proxy = ProxyFactory(adapter) before = {k: v for k, v in factory.__dict__.items() if k not in ('_called_with', '__parent__')} returned = ltaf(proxy) self.assertIsNot(returned, factory) unwrapped = removeSecurityProxy(returned) self.assertIs(unwrapped.__parent__, parent) self.assertIs(unwrapped, factory) after = {k: v for k, v in unwrapped.__dict__.items() if k not in ('_called_with', '__parent__')} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs class LocatingUntrustedAdapterFactoryTests(unittest.TestCase): def _getTargetClass(self): from zope.security.adapter import LocatingUntrustedAdapterFactory return LocatingUntrustedAdapterFactory def _makeOne(self, factory): return self._getTargetClass()(factory) def _makeFactory(self): class _Factory: __name__ = 'testing' __module__ = 'zope.security.tests.test_adapter' _called_with = () def __call__(self, *args): self._called_with = args return self return _Factory() def test_ctor(self): factory = self._makeFactory() ltaf = self._makeOne(factory) self.assertIs(ltaf.factory, factory) self.assertEqual(ltaf.__name__, 'testing') self.assertEqual(ltaf.__module__, 'zope.security.tests.test_adapter') def test__call__w_non_ILocation(self): factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() before = factory.__dict__.copy() returned = ltaf(adapter) self.assertIsNot(returned, factory) unwrapped = getProxiedObject(returned) self.assertIs(unwrapped, factory) after = {k: v for k, v in returned.__dict__.items() if k != '_called_with'} self.assertEqual(factory._called_with, (adapter,)) self.assertEqual(after, before) # no added attrs def test__call__w_non_ILocation_multiple_args(self): factory = self._makeFactory() ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() extra = object() before = factory.__dict__.copy() returned = ltaf(adapter, extra) self.assertIsNot(returned, factory) unwrapped = getProxiedObject(returned) self.assertIs(unwrapped, factory) after = {k: v for k, v in returned.__dict__.items() if k != '_called_with'} self.assertEqual(factory._called_with, (adapter, extra)) self.assertEqual(after, before) # no added attrs def test__call__w_ILocation_w_existing_parent(self): factory = self._makeFactory() parent = factory.__parent__ = object() directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() returned = ltaf(adapter) self.assertIs(returned, factory) self.assertIs(returned.__parent__, parent) def test__call__w_ILocation_wo_existing_parent(self): factory = self._makeFactory() factory.__parent__ = None directlyProvides(factory, ILocation) ltaf = self._makeOne(factory) class _NotAdapter: pass adapter = _NotAdapter() returned = ltaf(adapter) self.assertIs(returned, factory) self.assertIs(returned.__parent__, adapter) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_checker.py0000644000076500000240000025261214672224636022767 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """Tests for zope.security.checker """ import unittest from zope.security import checker as sec_checker from zope.security.tests import QuietWatchingChecker # pylint:disable=protected-access,inherit-non-class,no-method-argument,old-style-class # pylint:disable=blacklisted-name,no-init class Test_ProxyFactory(unittest.TestCase): def _callFUT(self, obj, checker=None): from zope.security.checker import ProxyFactory return ProxyFactory(obj, checker) def test_w_already_proxied_no_checker(self): from zope.security.proxy import Proxy from zope.security.proxy import getChecker obj = object() def _check(*x): raise AssertionError("Never called") proxy = Proxy(obj, _check) returned = self._callFUT(proxy, None) self.assertIs(returned, proxy) self.assertIs(getChecker(returned), _check) def test_w_already_proxied_same_checker(self): from zope.security.proxy import Proxy from zope.security.proxy import getChecker obj = object() def _check(*x): raise AssertionError("Never called") proxy = Proxy(obj, _check) returned = self._callFUT(proxy, _check) self.assertIs(returned, proxy) self.assertIs(getChecker(returned), _check) def test_w_already_proxied_different_checker(self): from zope.security.proxy import Proxy obj = object() def _check(*x): raise AssertionError("Never called") proxy = Proxy(obj, _check) def _sneaky(*x): raise AssertionError("Never called") self.assertRaises(TypeError, self._callFUT, proxy, _sneaky) def test_w_explicit_checker(self): from zope.security.proxy import getChecker obj = object() def _check(*x): raise AssertionError("Never called") returned = self._callFUT(obj, _check) self.assertIsNot(returned, obj) self.assertIs(getChecker(returned), _check) def test_no_checker_no_dunder_no_select(self): obj = object() returned = self._callFUT(obj) self.assertIs(returned, obj) def test_no_checker_w_dunder(self): from zope.security.proxy import getChecker from zope.security.proxy import getObject _check = object() # don't use a func, due to bound method class _WithChecker: __Security_checker__ = _check obj = _WithChecker() returned = self._callFUT(obj) self.assertIsNot(returned, obj) self.assertIs(getObject(returned), obj) self.assertIs(getChecker(returned), _check) def test_no_checker_no_dunder_w_select(self): from zope.security.checker import Checker from zope.security.checker import _checkers from zope.security.checker import _clear from zope.security.proxy import getChecker from zope.security.proxy import getObject class _Obj: pass obj = _Obj() _checker = Checker({}) def _check(*args): return _checker _checkers[_Obj] = _check try: returned = self._callFUT(obj) self.assertIsNot(returned, obj) self.assertIs(getObject(returned), obj) self.assertIs(getChecker(returned), _checker) finally: _clear() class Test_canWrite(unittest.TestCase): def _callFUT(self, obj, name): from zope.security.checker import canWrite return canWrite(obj, name) def _makeChecker(self, ch_get=None, ch_set=None): class _Checker: def check_getattr(self, obj, name): if ch_get is not None: raise ch_get() def check_setattr(self, obj, name): if ch_set is not None: raise ch_set() return _Checker() def test_ok(self): from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker()) self.assertTrue(self._callFUT(proxy, 'whatever')) def test_w_setattr_unauth(self): from zope.security.interfaces import Unauthorized from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_set=Unauthorized)) self.assertFalse(self._callFUT(proxy, 'whatever')) def test_w_setattr_forbidden_getattr_allowed(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_set=ForbiddenAttribute)) self.assertFalse(self._callFUT(proxy, 'whatever')) def test_w_setattr_forbidden_getattr_unauth(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.interfaces import Unauthorized from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_get=Unauthorized, ch_set=ForbiddenAttribute)) self.assertFalse(self._callFUT(proxy, 'whatever')) def test_w_setattr_forbidden_getattr_forbidden(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_get=ForbiddenAttribute, ch_set=ForbiddenAttribute)) self.assertRaises(ForbiddenAttribute, self._callFUT, proxy, 'whatever') class Test_canAccess(unittest.TestCase): def _callFUT(self, obj, name): from zope.security.checker import canAccess return canAccess(obj, name) def _makeChecker(self, ch_get=None): class _Checker: def check_getattr(self, obj, name): if ch_get is not None: raise ch_get() return _Checker() def test_ok(self): from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker()) self.assertTrue(self._callFUT(proxy, 'whatever')) def test_w_getattr_unauth(self): from zope.security.interfaces import Unauthorized from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_get=Unauthorized)) self.assertFalse(self._callFUT(proxy, 'whatever')) def test_w_setattr_forbidden_getattr_allowed(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.proxy import Proxy obj = object() proxy = Proxy(obj, self._makeChecker(ch_get=ForbiddenAttribute)) self.assertRaises(ForbiddenAttribute, self._callFUT, proxy, 'whatever') _marker = object() class CheckerTestsBase(QuietWatchingChecker): # pylint:disable=no-member def _getTargetClass(self): raise NotImplementedError("Subclasses must define") def _makeOne(self, get_permissions=_marker, set_permissions=_marker): if get_permissions is _marker: get_permissions = {} if set_permissions is _marker: return self._getTargetClass()(get_permissions) return self._getTargetClass()(get_permissions, set_permissions) def test_class_conforms_to_IChecker(self): from zope.interface.verify import verifyClass from zope.security.interfaces import IChecker verifyClass(IChecker, self._getTargetClass()) def test_instance_conforms_to_IChecker(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IChecker verifyObject(IChecker, self._makeOne()) def test_ctor_w_non_dict_get_permissions(self): self.assertRaises(TypeError, self._makeOne, object()) def test_ctor_w_non_dict_set_permissions(self): self.assertRaises(TypeError, self._makeOne, {}, object()) def test_permission_id_miss(self): checker = self._makeOne() self.assertIsNone(checker.permission_id('nonesuch')) def test_permission_id_hit(self): checker = self._makeOne({'name': 'PERMISSION'}) self.assertEqual(checker.permission_id('name'), 'PERMISSION') def test_setattr_permission_id_miss_none_set(self): checker = self._makeOne() self.assertIsNone(checker.setattr_permission_id('nonesuch')) def test_setattr_permission_id_miss(self): checker = self._makeOne(set_permissions={'name': 'PERMISSION'}) self.assertIsNone(checker.setattr_permission_id('nonesuch')) def test_setattr_permission_id_hit(self): checker = self._makeOne(set_permissions={'name': 'PERMISSION'}) self.assertEqual(checker.setattr_permission_id('name'), 'PERMISSION') def test_check_setattr_miss_none_set(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne() obj = object() self.assertRaises(ForbiddenAttribute, checker.check_setattr, obj, 'nonesuch') def test_check_setattr_miss(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne(set_permissions={'name': 'PERMISSION'}) obj = object() self.assertRaises(ForbiddenAttribute, checker.check_setattr, obj, 'nonesuch') def test_check_setattr_public(self): from zope.security.checker import CheckerPublic checker = self._makeOne(set_permissions={'name': CheckerPublic}) obj = object() self.assertEqual(checker.check_setattr(obj, 'name'), None) def test_check_setattr_w_interaction_allows(self): from zope.security._definitions import thread_local class _Interaction: def checkPermission(self, obj, perm): return True checker = self._makeOne(set_permissions={'name': 'view'}) obj = object() thread_local.interaction = _Interaction() try: self.assertEqual(checker.check_setattr(obj, 'name'), None) finally: del thread_local.interaction def test_check_setattr_w_interaction_denies(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False checker = self._makeOne(set_permissions={'name': 'view'}) obj = object() thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, checker.check_setattr, obj, 'name') finally: del thread_local.interaction def test_check_miss(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne() obj = object() self.assertRaises(ForbiddenAttribute, checker.check, obj, 'nonesuch') def test_check_available_by_default(self): checker = self._makeOne() obj = object() self.assertEqual(checker.check(obj, '__repr__'), None) def test_check_public(self): from zope.security.checker import CheckerPublic checker = self._makeOne({'name': CheckerPublic}) obj = object() self.assertEqual(checker.check(obj, 'name'), None) def test_check_non_public_w_interaction_allows(self): from zope.security._definitions import thread_local class _Interaction: def checkPermission(self, obj, perm): return True checker = self._makeOne({'name': 'view'}) obj = object() thread_local.interaction = _Interaction() try: self.assertEqual(checker.check(obj, 'name'), None) finally: del thread_local.interaction def test_check_non_public_w_interaction_denies(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False checker = self._makeOne({'name': 'view'}) obj = object() thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, checker.check, obj, 'name') finally: del thread_local.interaction def test_proxy_already_proxied(self): from zope.security.proxy import Proxy from zope.security.proxy import getChecker obj = object() def _check(*x): raise AssertionError("Never called") proxy = Proxy(obj, _check) checker = self._makeOne({'name': 'view'}) returned = checker.proxy(proxy) self.assertIs(returned, proxy) self.assertIs(getChecker(returned), _check) def test_proxy_no_dunder_no_select(self): obj = object() checker = self._makeOne() returned = checker.proxy(obj) self.assertIs(returned, obj) def test_proxy_no_checker_w_dunder(self): from zope.security.proxy import getChecker from zope.security.proxy import getObject _check = object() # don't use a func, due to bound method class _WithChecker: __Security_checker__ = _check obj = _WithChecker() checker = self._makeOne() returned = checker.proxy(obj) self.assertIsNot(returned, obj) self.assertIs(getObject(returned), obj) self.assertIs(getChecker(returned), _check) def test_proxy_no_checker_no_dunder_w_select(self): from zope.security.checker import Checker from zope.security.checker import _checkers from zope.security.checker import _clear from zope.security.proxy import getChecker from zope.security.proxy import getObject class _Obj: pass obj = _Obj() _checker = Checker({}) def _check(*args): return _checker _checkers[_Obj] = _check try: checker = self._makeOne() returned = checker.proxy(obj) self.assertIsNot(returned, obj) self.assertIs(getObject(returned), obj) self.assertIs(getChecker(returned), _checker) finally: _clear() def test_Decimal_operations(self): from decimal import Decimal from decimal import DecimalTuple from zope.security.checker import _default_checkers from zope.security.proxy import Proxy checker = _default_checkers[Decimal] value = Decimal('1.1') proxy = Proxy(value, checker) self.assertTrue(proxy) self.assertLess(proxy, Decimal('2')) self.assertEqual(proxy, Decimal('1.1')) self.assertGreater(proxy, Decimal('1')) self.assertNotEqual(proxy, Decimal('1')) self.assertEqual(-proxy, Decimal('-1.1')) self.assertEqual(proxy + Decimal('1.1'), Decimal('2.2')) self.assertEqual(Decimal('1.1') + proxy, Decimal('2.2')) self.assertEqual(proxy - Decimal('1'), Decimal('0.1')) self.assertEqual(Decimal('2.1') - proxy, Decimal('1')) self.assertEqual(proxy * 1, Decimal('1.1')) self.assertEqual(1 * proxy, Decimal('1.1')) self.assertEqual(proxy * Decimal('1.1'), Decimal('1.21')) self.assertEqual(Decimal('1.1') * proxy, Decimal('1.21')) self.assertEqual(proxy / 1, Decimal('1.1')) self.assertEqual(proxy / Decimal('1'), Decimal('1.1')) self.assertEqual(proxy // 1, Decimal('1')) self.assertEqual(proxy // Decimal('1'), Decimal('1')) self.assertEqual(float(proxy), 1.1) self.assertEqual(int(proxy), 1) self.assertEqual(proxy ** 2, Decimal('1.21')) self.assertEqual(1 ** proxy, Decimal('1')) self.assertEqual(proxy.adjusted(), 0) self.assertEqual( proxy.as_tuple(), DecimalTuple(sign=0, digits=(1, 1), exponent=-1)) self.assertEqual(proxy.compare(Decimal('1.1')), Decimal('0')) self.assertEqual(proxy.normalize(), Decimal('1.1')) self.assertEqual(proxy.quantize(Decimal('1.10')), Decimal('1.10')) def _check_iteration_of_dict_like(self, dict_like): from zope.security.checker import _default_checkers from zope.security.proxy import Proxy checker = _default_checkers[dict] proxy = Proxy(dict_like, checker) # empty self.assertEqual([], list(proxy.items())) self.assertEqual([], list(proxy.keys())) self.assertEqual([], list(proxy.values())) self.assertEqual([], list(proxy)) # With an object dict_like[1] = 2 self.assertEqual([(1, 2)], list(proxy.items())) self.assertEqual([1], list(proxy.keys())) self.assertEqual([1], list(proxy)) self.assertEqual([2], list(proxy.values())) def test_iteration_of_btree_items_keys_values(self): # iteration of BTree.items() is allowed by default. import BTrees for name in ('IF', 'II', 'IO', 'OI', 'OO'): for family_name in ('family32', 'family64'): family = getattr(BTrees, family_name) btree = getattr(family, name).BTree() self._check_iteration_of_dict_like(btree) def test_iteration_of_odict_items_keys_values(self): # iteration of OrderedDict.items() is allowed by default. from collections import OrderedDict odict = OrderedDict() self._check_iteration_of_dict_like(odict) def test_iteration_of_dict_items_keys_values(self): # iteration of regular dict is allowed by default self._check_iteration_of_dict_like(dict()) def test_iteration_of_interface_implementedBy(self): # Iteration of implementedBy is allowed by default # See https://github.com/zopefoundation/zope.security/issues/27 from zope.interface import Interface from zope.interface import implementer from zope.interface import providedBy from zope.security.checker import Checker from zope.security.proxy import Proxy class I1(Interface): pass @implementer(I1) class Obj: pass o = Obj() checker = Checker({}) proxy = Proxy(o, checker) # Since the object itself doesn't have any interfaces, # the providedBy will return the implementedBy of the class provided_by = list(providedBy(proxy)) self.assertEqual(provided_by, [I1]) def test_iteration_of_interface_providesBy(self): # Iteration of zope.interface.Provides is allowed by default # See https://github.com/zopefoundation/zope.security/issues/27 from zope.interface import Interface from zope.interface import alsoProvides from zope.interface import implementer from zope.interface import providedBy from zope.security.checker import Checker from zope.security.proxy import Proxy class I1(Interface): pass class I2(Interface): pass @implementer(I1) class Obj: pass o = Obj() alsoProvides(o, I2) checker = Checker({}) proxy = Proxy(o, checker) # Since the object has its own interfaces, provided # by will return a zope.interface.Provides object provided_by = list(providedBy(proxy)) self.assertEqual(provided_by, [I2, I1]) def test_iteration_with_length_hint(self): # PEP 424 implemented officially in Python 3.4 and # unofficially before in cPython and PyPy that allows for a # __length_hint__ method to be defined on iterators. It should # be allowed by default. See # https://github.com/zopefoundation/zope.security/issues/27 from zope.security.checker import _iteratorChecker from zope.security.proxy import Proxy class Iter: __Security_checker__ = _iteratorChecker items = (0, 1, 2) index = 0 hint = len(items) hint_called = False def __iter__(self): return self def __next__(self): try: return self.items[self.index] except IndexError: raise StopIteration() finally: self.index += 1 def __length_hint__(self): self.hint_called = True return self.hint # The hint is called on raw objects i = Iter() list(i) self.assertTrue(i.hint_called, "__length_hint__ should be called") # The hint is called when we proxy the root object i = Iter() proxy = Proxy(i, _iteratorChecker) self.assertEqual(list(proxy), [0, 1, 2]) self.assertTrue(i.hint_called, "__length_hint__ should be called") # The hint is called when we proxy its iterator i = Iter() it = iter(i) proxy = Proxy(it, _iteratorChecker) self.assertEqual(list(proxy), [0, 1, 2]) self.assertTrue(i.hint_called, "__length_hint__ should be called") def test_iteration_of_itertools_groupby(self): # itertools.groupby is a custom iterator type. # The groups it returns are also custom. from itertools import groupby from zope.security.checker import ProxyFactory group = groupby([0]) list_group = list(group) self.assertEqual(1, len(list_group)) self.assertEqual(0, list_group[0][0]) proxy = ProxyFactory(groupby([0])) list_group = list(proxy) self.assertEqual(1, len(list_group)) self.assertEqual(0, list_group[0][0]) # Note that groupby docs say: "The returned group is itself an # iterator that shares the underlying iterable with groupby(). # Because the source is shared, when the groupby() object is # advanced, the previous group is no longer visible." # For a one-item list, this doesn't make a difference on CPython, # but it does on PyPy (if we use list(group), the list_group[0][0] is # empty); probably this has to do with GC proxy = ProxyFactory(groupby([0])) _key, subiter = next(proxy) self.assertEqual([0], list(subiter)) class TestCheckerPy(CheckerTestsBase, unittest.TestCase): def _getTargetClass(self): return sec_checker.CheckerPy class TestChecker(CheckerTestsBase, unittest.TestCase): def _getTargetClass(self): return sec_checker.Checker @unittest.skipIf(sec_checker.Checker is sec_checker.WatchingChecker, "WatchingChecker is the default") class TestWatchingChecker(TestChecker): def _getTargetClass(self): return sec_checker.WatchingChecker class TestTracebackSupplement(unittest.TestCase): def _getTargetClass(self): from zope.security.checker import TracebackSupplement return TracebackSupplement def _makeOne(self, obj): return self._getTargetClass()(obj) def test_getInfo_builtin_types(self): for val, typ in [('', 'str'), (0, 'int'), (1.0, 'float'), ((), 'tuple'), ([], 'list'), ({}, 'dict'), ]: tbs = self._makeOne(val) self.assertEqual(tbs.getInfo().splitlines(), [f' - class: builtins.{typ}', f' - type: builtins.{typ}', ]) def test_getInfo_newstyle_instance(self): class C: pass tbs = self._makeOne(C()) self.assertEqual(tbs.getInfo().splitlines(), [' - class: %s.C' % self.__class__.__module__, ' - type: %s.C' % self.__class__.__module__, ]) def test_getInfo_classic_instance(self): class C: pass tbs = self._makeOne(C()) lines = tbs.getInfo().splitlines() self.assertEqual(lines[0], ' - class: %s.C' % self.__class__.__module__) self.assertEqual(lines[1], ' - type: %s.C' % self.__class__.__module__) class TestGlobal(unittest.TestCase): def _getTargetClass(self): from zope.security.checker import Global return Global def _makeOne(self, name, module): return self._getTargetClass()(name, module) def test_ctor_name_and_module(self): glob = self._makeOne('foo', 'bar.baz') self.assertEqual(glob.__name__, 'foo') self.assertEqual(glob.__module__, 'bar.baz') def test___reduce__(self): glob = self._makeOne('foo', 'bar.baz') self.assertEqual(glob.__reduce__(), 'foo') def test___repr__(self): glob = self._makeOne('foo', 'bar.baz') self.assertEqual(repr(glob), 'Global(foo,bar.baz)') class Test_NamesChecker(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.security.checker import NamesChecker return NamesChecker(*args, **kw) def test_empty_names_no_kw(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IChecker checker = self._callFUT() verifyObject(IChecker, checker) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_names_no_kw(self): from zope.security.checker import CheckerPublic checker = self._callFUT(('foo', 'bar', 'baz')) self.assertIs(checker.permission_id('foo'), CheckerPublic) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIs(checker.permission_id('baz'), CheckerPublic) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_names_no_kw_explicit_permission(self): other_perm = object() checker = self._callFUT(('foo', 'bar', 'baz'), permission_id=other_perm) self.assertIs(checker.permission_id('foo'), other_perm) self.assertIs(checker.permission_id('bar'), other_perm) self.assertIs(checker.permission_id('baz'), other_perm) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_names_w_kw_no_clash(self): from zope.security.checker import CheckerPublic other_perm = object() checker = self._callFUT(('foo', 'bar', 'baz'), bam=other_perm) self.assertIs(checker.permission_id('foo'), CheckerPublic) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIs(checker.permission_id('baz'), CheckerPublic) self.assertIs(checker.permission_id('bam'), other_perm) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_names_w_kw_w_clash(self): from zope.security.checker import DuplicationError other_perm = object() self.assertRaises(DuplicationError, self._callFUT, ('foo',), foo=other_perm) class Test_InterfaceChecker(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.security.checker import InterfaceChecker return InterfaceChecker(*args, **kw) def test_simple_iface_wo_kw(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import CheckerPublic class IFoo(Interface): bar = Attribute('Bar') checker = self._callFUT(IFoo) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIsNone(checker.permission_id('nonesuch')) def test_simple_iface_w_explicit_permission(self): from zope.interface import Attribute from zope.interface import Interface class IFoo(Interface): bar = Attribute('Bar') other_perm = object() checker = self._callFUT(IFoo, other_perm) self.assertIs(checker.permission_id('bar'), other_perm) def test_simple_iface_w_kw(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import CheckerPublic class IFoo(Interface): bar = Attribute('Bar') other_perm = object() checker = self._callFUT(IFoo, baz=other_perm) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIs(checker.permission_id('baz'), other_perm) self.assertIsNone(checker.permission_id('nonesuch')) def test_derived_iface(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import CheckerPublic class IFoo(Interface): bar = Attribute('Bar') class IBar(IFoo): baz = Attribute('Baz') checker = self._callFUT(IBar) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIs(checker.permission_id('baz'), CheckerPublic) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_clash(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import DuplicationError class IFoo(Interface): bar = Attribute('Bar') bam = Attribute('Bam') other_perm = object() self.assertRaises(DuplicationError, self._callFUT, IFoo, bar=other_perm) class Test_MultiChecker(unittest.TestCase): def _callFUT(self, specs): from zope.security.checker import MultiChecker return MultiChecker(specs) def test_empty(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IChecker checker = self._callFUT([]) verifyObject(IChecker, checker) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_spec_as_names(self): from zope.security.checker import CheckerPublic checker = self._callFUT([(('foo', 'bar', 'baz'), CheckerPublic)]) self.assertIs(checker.permission_id('foo'), CheckerPublic) self.assertIs(checker.permission_id('bar'), CheckerPublic) self.assertIs(checker.permission_id('baz'), CheckerPublic) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_spec_as_iface(self): from zope.interface import Attribute from zope.interface import Interface class IFoo(Interface): bar = Attribute('Bar') other_perm = object() checker = self._callFUT([(IFoo, other_perm)]) self.assertIs(checker.permission_id('bar'), other_perm) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_spec_as_names_and_iface(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import CheckerPublic class IFoo(Interface): bar = Attribute('Bar') other_perm = object() checker = self._callFUT([(IFoo, other_perm), (('foo', 'baz'), CheckerPublic)]) self.assertIs(checker.permission_id('foo'), CheckerPublic) self.assertIs(checker.permission_id('bar'), other_perm) self.assertIs(checker.permission_id('baz'), CheckerPublic) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_spec_as_names_and_iface_clash(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.checker import CheckerPublic from zope.security.checker import DuplicationError class IFoo(Interface): bar = Attribute('Bar') other_perm = object() self.assertRaises(DuplicationError, self._callFUT, [(IFoo, other_perm), (('foo', 'bar'), CheckerPublic)]) def test_w_spec_as_mapping(self): from zope.security.checker import CheckerPublic other_perm = object() spec = {'foo': CheckerPublic, 'bar': other_perm, } checker = self._callFUT([spec]) self.assertIs(checker.permission_id('foo'), CheckerPublic) self.assertIs(checker.permission_id('bar'), other_perm) self.assertIsNone(checker.permission_id('nonesuch')) def test_w_spec_as_names_and_mapping_clash(self): from zope.security.checker import CheckerPublic from zope.security.checker import DuplicationError other_perm = object() spec = { 'foo': other_perm, } self.assertRaises(DuplicationError, self._callFUT, [(('foo', 'bar'), CheckerPublic), spec]) class _SelectCheckerBase: # pylint:disable=no-member def _callFUT(self, obj): raise NotImplementedError("Subclass responsibility") def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def test_w_basic_types_NoProxy(self): import datetime from zope.i18nmessageid import Message msg = Message('msg') for obj in [object(), 42, 3.14, None, 'text', b'binary', msg, True, datetime.timedelta(1), datetime.datetime.now(), datetime.date.today(), datetime.datetime.now().time(), datetime.tzinfo(), ]: self.assertIsNone(self._callFUT(obj)) def test_w_checker_inst(self): from zope.security.checker import Checker from zope.security.checker import _checkers class Foo: pass checker = _checkers[Foo] = Checker({}) self.assertIs(self._callFUT(Foo()), checker) def test_w_factory_returning_checker(self): from zope.security.checker import Checker from zope.security.checker import _checkers class Foo: pass checker = Checker({}) def _factory(obj): return checker _checkers[Foo] = _factory self.assertIs(self._callFUT(Foo()), checker) def test_w_factory_returning_NoProxy(self): from zope.security.checker import NoProxy from zope.security.checker import _checkers class Foo: pass def _factory(obj): return NoProxy _checkers[Foo] = _factory self.assertIsNone(self._callFUT(Foo())) def test_w_factory_returning_None(self): from zope.security.checker import _checkers class Foo: pass def _factory(obj): pass _checkers[Foo] = _factory self.assertIsNone(self._callFUT(Foo())) def test_w_factory_factory(self): from zope.security.checker import Checker from zope.security.checker import _checkers class Foo: pass checker = Checker({}) def _factory(obj): return checker def _factory_factory(obj): return _factory _checkers[Foo] = _factory_factory self.assertIs(self._callFUT(Foo()), checker) def test_itertools_checkers(self): import itertools from zope.security.checker import _iteratorChecker def pred(x): return x iterable = (1, 2, 3) pred_iterable = (pred, iterable) for func, args in ( ('count', ()), ('cycle', ((),)), ('dropwhile', pred_iterable), ('islice', (iterable, 2)), ('permutations', (iterable,)), ('product', (iterable,)), ('repeat', (1, 2)), ('starmap', pred_iterable), ('takewhile', pred_iterable), ('tee', (iterable,)), ('zip_longest', (iterable,)), ('accumulate', (iterable,)), ('compress', (iterable, ())), ('combinations', (iterable, 1)), ('combinations_with_replacement', (iterable, 1)), ): func = getattr(itertools, func) __traceback_info__ = func result = func(*args) if func == itertools.tee: result = result[0] self.assertIs(self._callFUT(result), _iteratorChecker) class Test_selectCheckerPy(_SelectCheckerBase, unittest.TestCase): def _callFUT(self, obj): from zope.security.checker import selectCheckerPy return selectCheckerPy(obj) @unittest.skipIf(sec_checker.selectChecker is sec_checker.selectCheckerPy, "Pure Python") class Test_selectChecker(_SelectCheckerBase, unittest.TestCase): def _callFUT(self, obj): # pragma: no cover from zope.security.checker import selectChecker return selectChecker(obj) class Test_getCheckerForInstancesOf(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, obj): from zope.security.checker import getCheckerForInstancesOf return getCheckerForInstancesOf(obj) def test_miss(self): class Unknown: pass self.assertIsNone(self._callFUT(Unknown)) def test_hit(self): from zope.security.checker import _checkers class Foo: pass checker = _checkers[Foo] = object() self.assertIs(self._callFUT(Foo), checker) class Test_defineChecker(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, type_, checker): from zope.security.checker import defineChecker return defineChecker(type_, checker) def test_w_wrong_type(self): checker = object() for obj in [object(), 42, 3.14, None, 'text', b'binary', True, ]: self.assertRaises(TypeError, self._callFUT, obj, checker) def test_w_duplicate(self): from zope.exceptions import DuplicationError from zope.security.checker import _checkers class Foo: pass checker1, checker2 = object(), object() _checkers[Foo] = checker1 self.assertRaises(DuplicationError, self._callFUT, Foo, checker2) def test_w_newstyle_class(self): from zope.security.checker import _checkers checker = object() class Foo: pass self._callFUT(Foo, checker) self.assertIs(_checkers[Foo], checker) def test_w_module(self): import zope.interface from zope.security.checker import _checkers checker = object() self._callFUT(zope.interface, checker) self.assertIs(_checkers[zope.interface], checker) def test_w_oldstyle_class(self): from zope.security.checker import _checkers checker = object() class Foo: pass self._callFUT(Foo, checker) self.assertIs(_checkers[Foo], checker) class Test_undefineChecker(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, type_): from zope.security.checker import undefineChecker return undefineChecker(type_) def test_miss(self): class Foo: pass self.assertRaises(KeyError, self._callFUT, Foo) def test_hit(self): from zope.security.checker import _checkers class Foo: pass _checkers[Foo] = object() self._callFUT(Foo) self.assertNotIn(Foo, _checkers) class TestCombinedChecker(QuietWatchingChecker, unittest.TestCase): def _getTargetClass(self): from zope.security.checker import CombinedChecker return CombinedChecker def _makeOne(self, checker1=None, checker2=None): if checker1 is None: checker1 = self._makeOther() if checker2 is None: checker1 = self._makeOther() return self._getTargetClass()(checker1, checker2) def _makeOther(self, get_permissions=None, set_permissions=None): from zope.security.checker import Checker if get_permissions is None: get_permissions = {} if set_permissions is None: set_permissions = {} return Checker(get_permissions, set_permissions) def test_class_conforms_to_IChecker(self): from zope.interface.verify import verifyClass from zope.security.interfaces import IChecker verifyClass(IChecker, self._getTargetClass()) def test_instance_conforms_to_IChecker(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IChecker verifyObject(IChecker, self._makeOne()) def test_check_lhs_ok_rhs_not_called(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic class _NeverCalled(Checker): def check(self, object, name): # pylint:disable=redefined-builtin raise AssertionError("Never called") lhs = self._makeOther({'name': CheckerPublic}) rhs = _NeverCalled({}) combined = self._makeOne(lhs, rhs) combined.check(object(), 'name') # no raise def test_check_lhs_unauth_rhs_ok(self): from zope.security._definitions import thread_local from zope.security.checker import CheckerPublic class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther({'name': 'view'}) # unauth rhs = self._makeOther({'name': CheckerPublic}) combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: combined.check(object(), 'name') # no raise finally: del thread_local.interaction def test_check_lhs_unauth_rhs_forbidden(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther({'name': 'view'}) # unauth rhs = self._makeOther() # forbidden combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check, object(), 'name') finally: del thread_local.interaction def test_check_lhs_unauth_rhs_unauth(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther({'name': 'view'}) # unauth rhs = self._makeOther({'name': 'inspect'}) combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check, object(), 'name') finally: del thread_local.interaction def test_check_lhs_forbidden_rhs_ok(self): from zope.security.checker import CheckerPublic lhs = self._makeOther() # forbidden rhs = self._makeOther({'name': CheckerPublic}) combined = self._makeOne(lhs, rhs) combined.check(object(), 'name') # no raise def test_check_lhs_forbidden_rhs_forbidden(self): from zope.security.interfaces import Forbidden lhs = self._makeOther() # forbidden rhs = self._makeOther() # forbidden combined = self._makeOne(lhs, rhs) self.assertRaises(Forbidden, combined.check, object(), 'name') def test_check_lhs_forbidden_rhs_unauth(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther() # Forbidden rhs = self._makeOther({'name': 'inspect'}) combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check, object(), 'name') finally: del thread_local.interaction def test_check_setattr_lhs_ok_rhs_not_called(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic class _NeverCalled(Checker): def check_setattr(self, object, name): raise AssertionError("Never called") lhs = self._makeOther(set_permissions={'name': CheckerPublic}) rhs = _NeverCalled({}) combined = self._makeOne(lhs, rhs) combined.check_setattr(object(), 'name') # no raise def test_check_setattr_lhs_unauth_rhs_ok(self): from zope.security._definitions import thread_local from zope.security.checker import CheckerPublic class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther(set_permissions={'name': 'update'}) # unauth rhs = self._makeOther(set_permissions={'name': CheckerPublic}) combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: combined.check_setattr(object(), 'name') # no raise finally: del thread_local.interaction def test_check_setattr_lhs_unauth_rhs_forbidden(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther(set_permissions={'name': 'view'}) # unauth rhs = self._makeOther() # forbidden combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check_setattr, object(), 'name') finally: del thread_local.interaction def test_check_setattr_lhs_unauth_rhs_unauth(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther(set_permissions={'name': 'view'}) # unauth rhs = self._makeOther(set_permissions={'name': 'inspect'}) # unauth combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check_setattr, object(), 'name') finally: del thread_local.interaction def test_check_setattr_lhs_forbidden_rhs_ok(self): from zope.security.checker import CheckerPublic lhs = self._makeOther() # forbidden rhs = self._makeOther(set_permissions={'name': CheckerPublic}) combined = self._makeOne(lhs, rhs) combined.check_setattr(object(), 'name') # no raise def test_check_setattr_lhs_forbidden_rhs_forbidden(self): from zope.security.interfaces import Forbidden lhs = self._makeOther() # forbidden rhs = self._makeOther() # forbidden combined = self._makeOne(lhs, rhs) self.assertRaises(Forbidden, combined.check_setattr, object(), 'name') def test_check_setattr_lhs_forbidden_rhs_unauth(self): from zope.security._definitions import thread_local from zope.security.interfaces import Unauthorized class _Interaction: def checkPermission(self, obj, perm): return False lhs = self._makeOther() # forbidden rhs = self._makeOther(set_permissions={'name': 'inspect'}) # unauth combined = self._makeOne(lhs, rhs) thread_local.interaction = _Interaction() try: self.assertRaises(Unauthorized, combined.check_setattr, object(), 'name') finally: del thread_local.interaction @unittest.skipIf( sec_checker.WatchingCombinedChecker is sec_checker.CombinedChecker, "WatchingCombinedChecker is the default") class TestWatchingCombinedChecker(TestCombinedChecker): def _getTargetClass(self): return sec_checker.WatchingCombinedChecker class TestCheckerLoggingMixin(unittest.TestCase): def _getTargetClass(self): from zope.security.checker import CheckerLoggingMixin return CheckerLoggingMixin def _makeOne(self, raising=None): class _Checker: def __init__(self, raising, stream): self._file = stream self._raising = raising def check(self, obj, name): if self._raising: raise self._raising check_getattr = check_setattr = check class _Derived(self._getTargetClass(), _Checker): pass return _Derived(raising, self._makeStream()) def _makeStream(self): class _Stream(list): def write(self, msg): self.append(msg) return _Stream() def _makeObject(self): class _Object: def __repr__(self): return 'TESTING' return _Object() def test_check_ok_normal_verbosity(self): checker = self._makeOne() checker.check(self._makeObject(), 'name') self.assertEqual(len(checker._file), 0) def test_check_ok_raised_verbosity_available_by_default(self): checker = self._makeOne() checker.verbosity = 2 checker.check(self._makeObject(), '__name__') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] + Always available: __name__ on TESTING\n') def test_check_ok_raised_verbosity_normal_name(self): checker = self._makeOne() checker.verbosity = 2 checker.check(self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] + Granted: name on TESTING\n') def test_check_unauthorized(self): from zope.security.interfaces import Unauthorized checker = self._makeOne(Unauthorized) self.assertRaises(Unauthorized, checker.check, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Unauthorized: name on TESTING\n') def test_check_unauthorized_raised_verbosity(self): from zope.security.interfaces import Unauthorized checker = self._makeOne(Unauthorized) checker.verbosity = 2 self.assertRaises(Unauthorized, checker.check, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Unauthorized: name on TESTING\n') def test_check_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne(ForbiddenAttribute) self.assertRaises(ForbiddenAttribute, checker.check, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Forbidden: name on TESTING\n') def test_check_getattr_ok_normal_verbosity(self): checker = self._makeOne() checker.check(self._makeObject(), 'name') self.assertEqual(len(checker._file), 0) def test_check_getattr_ok_raised_verbosity_available_by_default(self): checker = self._makeOne() checker.verbosity = 2 checker.check_getattr(self._makeObject(), '__name__') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] + Always available getattr: ' '__name__ on TESTING\n') def test_check_getattr_ok_raised_verbosity_normal_name(self): checker = self._makeOne() checker.verbosity = 2 checker.check_getattr(self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] + Granted getattr: name on TESTING\n') def test_check_getattr_unauthorized(self): from zope.security.interfaces import Unauthorized checker = self._makeOne(Unauthorized) self.assertRaises(Unauthorized, checker.check_getattr, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Unauthorized getattr: name on TESTING\n') def test_check_getattr_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne(ForbiddenAttribute) self.assertRaises(ForbiddenAttribute, checker.check_getattr, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Forbidden getattr: name on TESTING\n') def test_check_setattr_ok_normal_verbosity(self): checker = self._makeOne() checker.check_setattr(self._makeObject(), 'name') self.assertEqual(len(checker._file), 0) def test_check_setattr_ok_raised_verbosity_normal_name(self): checker = self._makeOne() checker.verbosity = 2 checker.check_setattr(self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] + Granted setattr: name on TESTING\n') def test_check_setattr_unauthorized(self): from zope.security.interfaces import Unauthorized checker = self._makeOne(Unauthorized) self.assertRaises(Unauthorized, checker.check_setattr, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Unauthorized setattr: name on TESTING\n') def test_check_setattr_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute checker = self._makeOne(ForbiddenAttribute) self.assertRaises(ForbiddenAttribute, checker.check_setattr, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Forbidden setattr: name on TESTING\n') def test_check_setitem_unauthorized(self): # __setitem__ is an alias for check_getattr, used for speed reasons # (AFAIU calling tp_setitem from C is much faster than calling a # method by name). from zope.security.interfaces import Unauthorized checker = self._makeOne(Unauthorized) self.assertRaises(Unauthorized, checker.__setitem__, self._makeObject(), 'name') self.assertEqual(len(checker._file), 1) self.assertEqual(checker._file[0], '[CHK] - Unauthorized getattr: name on TESTING\n') class Test__instanceChecker(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, type_): from zope.security.checker import _instanceChecker return _instanceChecker(type_) def test_miss(self): from zope.security.checker import _defaultChecker class Foo: pass self.assertIs(self._callFUT(Foo()), _defaultChecker) def test_hit(self): from zope.security.checker import _checkers class Foo: pass checker = _checkers[Foo] = object() self.assertIs(self._callFUT(Foo()), checker) class Test_moduleChecker(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, type_): from zope.security.checker import moduleChecker return moduleChecker(type_) def test_miss(self): from zope.interface import verify self.assertIsNone(self._callFUT(verify)) def test_hit(self): from zope.interface import verify from zope.security.checker import _checkers checker = _checkers[verify] = object() self.assertIs(self._callFUT(verify), checker) # Pre-geddon tests start here class TestSecurityPolicy(QuietWatchingChecker, unittest.TestCase): def setUp(self): super().setUp() from zope.security.management import newInteraction from zope.security.management import setSecurityPolicy sec_checker._clear() self.__oldpolicy = setSecurityPolicy(self._makeSecurityPolicy()) newInteraction() def tearDown(self): super().tearDown() from zope.security.management import endInteraction from zope.security.management import setSecurityPolicy endInteraction() setSecurityPolicy(self.__oldpolicy) sec_checker._clear() def _get_old_class_type(self): # Py3 has no ClassType and no old-style classes import types old_type = getattr(types, 'ClassType', type) self.assertIs(old_type, type) return old_type def _makeSecurityPolicy(self): from zope.interface import implementer from zope.security.interfaces import ISecurityPolicy @implementer(ISecurityPolicy) class SecurityPolicy: def checkPermission(self, permission, _object): return permission == 'test_allowed' return SecurityPolicy def test_defineChecker_class(self): from zope.security.checker import NamesChecker from zope.security.checker import defineChecker class NewStyleClass: pass self.assertIsInstance(NewStyleClass, type) defineChecker(NewStyleClass, NamesChecker()) def test_defineChecker_module(self): import zope.security from zope.security.checker import NamesChecker from zope.security.checker import defineChecker defineChecker(zope.security, NamesChecker()) def test_defineChecker_error(self): from zope.security.checker import NamesChecker from zope.security.checker import defineChecker not_a_type = object() self.assertRaises(TypeError, defineChecker, not_a_type, NamesChecker()) def _makeClasses(self): old_type = self._get_old_class_type() class OldInst: a = 1 def b(self): raise AssertionError("Never called") c = 2 def gete(self): raise AssertionError("Never called") e = property(gete) def __getitem__(self, x): raise AssertionError("Never called") def __setitem__(self, x, v): raise AssertionError("Never called") self.assertIsInstance(OldInst, old_type) class NewInst(OldInst): # This is not needed, but left in to show the change of metaclass # __metaclass__ = type def gete(self): raise AssertionError("Never called") def sete(self, v): raise AssertionError("Never called") e = property(gete, sete) self.assertIsInstance(NewInst, type) return OldInst, NewInst # check_getattr cases: # # - no attribute there # - method # - allow and disallow by permission def test_check_getattr(self): # pylint:disable=attribute-defined-outside-init from zope.security.checker import CheckerPublic from zope.security.checker import NamesChecker from zope.security.interfaces import Forbidden from zope.security.interfaces import Unauthorized OldInst, NewInst = self._makeClasses() oldinst = OldInst() oldinst.d = OldInst() newinst = NewInst() newinst.d = NewInst() for inst in oldinst, newinst: checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'perm') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'a') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'b') self.assertRaises(Unauthorized, checker.check_getattr, inst, 'c') self.assertRaises(Unauthorized, checker.check, inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'test_allowed') checker.check_getattr(inst, 'a') checker.check_getattr(inst, 'b') checker.check_getattr(inst, 'c') checker.check(inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') checker = NamesChecker(['a', 'b', 'c', '__getitem__'], CheckerPublic) checker.check_getattr(inst, 'a') checker.check_getattr(inst, 'b') checker.check_getattr(inst, 'c') checker.check(inst, '__getitem__') self.assertRaises(Forbidden, checker.check, inst, '__setitem__') self.assertRaises(Forbidden, checker.check_getattr, inst, 'd') self.assertRaises(Forbidden, checker.check_getattr, inst, 'e') self.assertRaises(Forbidden, checker.check_getattr, inst, 'f') def test_check_setattr(self): # pylint:disable=attribute-defined-outside-init from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.interfaces import Forbidden from zope.security.interfaces import Unauthorized OldInst, NewInst = self._makeClasses() oldinst = OldInst() oldinst.d = OldInst() newinst = NewInst() newinst.d = NewInst() for inst in oldinst, newinst: checker = Checker({}, {'a': 'perm', 'z': 'perm'}) self.assertRaises(Unauthorized, checker.check_setattr, inst, 'a') self.assertRaises(Unauthorized, checker.check_setattr, inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'c') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': 'test_allowed', 'z': 'test_allowed'}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') checker = Checker({}, {'a': CheckerPublic, 'z': CheckerPublic}) checker.check_setattr(inst, 'a') checker.check_setattr(inst, 'z') self.assertRaises(Forbidden, checker.check_setattr, inst, 'd') self.assertRaises(Forbidden, checker.check_setattr, inst, 'e') self.assertRaises(Forbidden, checker.check_setattr, inst, 'f') def test_proxy(self): from zope.security.checker import BasicTypes_examples from zope.security.checker import CheckerPublic from zope.security.checker import NamesChecker from zope.security.proxy import getChecker from zope.security.proxy import removeSecurityProxy OldInst, NewInst = self._makeClasses() checker = NamesChecker(()) rocks = tuple(BasicTypes_examples.values()) for rock in rocks: proxy = checker.proxy(rock) self.assertIs(proxy, rock, (rock, type(proxy))) for class_ in OldInst, NewInst: inst = class_() for ob in inst, class_: proxy = checker.proxy(ob) self.assertIs(removeSecurityProxy(proxy), ob) checker = getChecker(proxy) if ob is inst: self.assertEqual(checker.permission_id('__str__'), None) else: self.assertEqual(checker.permission_id('__str__'), CheckerPublic) # No longer doing anything special for transparent proxies. # A proxy needs to provide its own security checker. def test_iteration(self): from zope.security.checker import ProxyFactory from zope.security.checker import selectChecker for i in ((1,), [1]): _iter = iter(i) proxy = ProxyFactory(_iter, selectChecker(_iter)) self.assertEqual(next(proxy), 1) def testLayeredProxies(self): # Test that a Proxy will not be re-proxied. from zope.security.checker import Checker from zope.security.checker import NamesChecker from zope.security.proxy import Proxy from zope.security.proxy import getObject class Base: __Security_checker__ = NamesChecker(['__Security_checker__']) base = Base() checker = Checker({}) # base is not proxied, so we expect a proxy proxy1 = checker.proxy(base) self.assertIs(type(proxy1), Proxy) self.assertIs(getObject(proxy1), base) # proxy is a proxy, so we don't expect to get another proxy2 = checker.proxy(proxy1) self.assertIs(proxy2, proxy1) self.assertIs(getObject(proxy2), base) def testMultiChecker(self): from zope.interface import Interface class I1(Interface): def f1(): "f1" def f2(): "f2" class I2(I1): def f3(): "f3" def f4(): "f4" class I3(Interface): def g(): "g" from zope.exceptions import DuplicationError from zope.security.checker import MultiChecker self.assertRaises(DuplicationError, MultiChecker, [(I1, 'p1'), (I2, 'p2')]) self.assertRaises(DuplicationError, MultiChecker, [(I1, 'p1'), {'f2': 'p2'}]) MultiChecker([(I1, 'p1'), (I2, 'p1')]) checker = MultiChecker([ (I2, 'p1'), {'a': 'p3'}, (I3, 'p2'), (('x', 'y', 'z'), 'p4'), ]) self.assertEqual(checker.permission_id('f1'), 'p1') self.assertEqual(checker.permission_id('f2'), 'p1') self.assertEqual(checker.permission_id('f3'), 'p1') self.assertEqual(checker.permission_id('f4'), 'p1') self.assertEqual(checker.permission_id('g'), 'p2') self.assertEqual(checker.permission_id('a'), 'p3') self.assertEqual(checker.permission_id('x'), 'p4') self.assertEqual(checker.permission_id('y'), 'p4') self.assertEqual(checker.permission_id('z'), 'p4') self.assertEqual(checker.permission_id('zzz'), None) def testAlwaysAvailable(self): from zope.security.checker import NamesChecker checker = NamesChecker(()) class C: pass self.assertEqual(checker.check(C, '__hash__'), None) self.assertEqual(checker.check(C, '__bool__'), None) self.assertEqual(checker.check(C, '__class__'), None) self.assertEqual(checker.check(C, '__implements__'), None) self.assertEqual(checker.check(C, '__lt__'), None) self.assertEqual(checker.check(C, '__le__'), None) self.assertEqual(checker.check(C, '__gt__'), None) self.assertEqual(checker.check(C, '__ge__'), None) self.assertEqual(checker.check(C, '__eq__'), None) self.assertEqual(checker.check(C, '__ne__'), None) self.assertEqual(checker.check(C, '__name__'), None) self.assertEqual(checker.check(C, '__parent__'), None) def test_setattr(self): from zope.security.checker import NamesChecker from zope.security.interfaces import Forbidden OldInst, NewInst = self._makeClasses() checker = NamesChecker(['a', 'b', 'c', '__getitem__'], 'test_allowed') for inst in NewInst(), OldInst(): self.assertRaises(Forbidden, checker.check_setattr, inst, 'a') self.assertRaises(Forbidden, checker.check_setattr, inst, 'z') # TODO: write a test to see that # Checker.check/check_setattr handle permission # values that evaluate to False def test_ProxyFactory(self): # pylint:disable=attribute-defined-outside-init from zope.security.checker import NamesChecker from zope.security.checker import ProxyFactory from zope.security.checker import _defaultChecker from zope.security.checker import defineChecker from zope.security.proxy import Proxy from zope.security.proxy import getChecker class SomeClass: pass checker = NamesChecker() specific_checker = NamesChecker() checker_as_magic_attr = NamesChecker() obj = SomeClass() proxy = ProxyFactory(obj) self.assertIs(type(proxy), Proxy) self.assertIs(getChecker(proxy), _defaultChecker) defineChecker(SomeClass, checker) proxy = ProxyFactory(obj) self.assertIs(type(proxy), Proxy) self.assertIs(getChecker(proxy), checker) obj.__Security_checker__ = checker_as_magic_attr proxy = ProxyFactory(obj) self.assertIs(type(proxy), Proxy) self.assertIs(getChecker(proxy), checker_as_magic_attr) proxy = ProxyFactory(obj, specific_checker) self.assertIs(type(proxy), Proxy) self.assertIs(getChecker(proxy), specific_checker) def test_define_and_undefineChecker(self): from zope.security.checker import NamesChecker from zope.security.checker import defineChecker from zope.security.checker import undefineChecker class SomeClass: pass obj = SomeClass() checker = NamesChecker() from zope.security.checker import _defaultChecker from zope.security.checker import selectChecker self.assertIs(selectChecker(obj), _defaultChecker) defineChecker(SomeClass, checker) self.assertIs(selectChecker(obj), checker) undefineChecker(SomeClass) self.assertIs(selectChecker(obj), _defaultChecker) def test_ProxyFactory_using_proxy(self): from zope.security.checker import NamesChecker from zope.security.checker import ProxyFactory class SomeClass: pass obj = SomeClass() checker = NamesChecker() proxy1 = ProxyFactory(obj) proxy2 = ProxyFactory(proxy1) self.assertIs(proxy1, proxy2) # Trying to change the checker on a proxy. self.assertRaises(TypeError, ProxyFactory, proxy1, checker) # Setting exactly the same checker as the proxy already has. proxy1 = ProxyFactory(obj, checker) proxy2 = ProxyFactory(proxy1, checker) self.assertIs(proxy1, proxy2) def test_canWrite_canAccess(self): # the canWrite and canAccess functions are conveniences. Often code # wants to check if a certain option is open to a user before # presenting it. If the code relies on a certain permission, the # Zope 3 goal of keeping knowledge of security assertions out of the # code and only in the zcml assertions is broken. Instead, ask if the # current user canAccess or canWrite some pertinent aspect of the # object. canAccess is used for both read access on an attribute # and call access to methods. # For example, consider this humble pair of class and object. from zope.security.checker import Checker from zope.security.checker import canAccess from zope.security.checker import canWrite from zope.security.checker import defineChecker from zope.security.interfaces import Forbidden class SomeClass: pass obj = SomeClass() # We will establish a checker for the class. This is the standard # name-based checker, and works by specifying two dicts, one for read # and one for write. Each item in the dictionary should be an # attribute name and the permission required to read or write it. # For these tests, the SecurityPolicy defined at the top of this file # is in place. It is a stub. Normally, the security policy would # have knowledge of interactions and participants, and would determine # on the basis of the participants and the object if a certain # permission were authorized. This stub simply says that the # 'test_allowed' permission is authorized and nothing else is, for any # object you pass it. # Therefore, according to the checker created here, the current # 'interaction' (as stubbed out in the security policy) will be allowed # to access and write foo, and access bar. The interaction is # unauthorized for accessing baz and writing bar. Any other access or # write is not merely unauthorized but forbidden--including write # access for baz. checker = Checker( {'foo': 'test_allowed', # these are the read settings 'bar': 'test_allowed', 'baz': 'you_will_not_have_this_permission'}, {'foo': 'test_allowed', # these are the write settings 'bar': 'you_will_not_have_this_permission', 'bing': 'you_will_not_have_this_permission'}) defineChecker(SomeClass, checker) # so, our hapless interaction may write and access foo... self.assertTrue(canWrite(obj, 'foo')) self.assertTrue(canAccess(obj, 'foo')) # ...may access, but not write, bar... self.assertTrue(not canWrite(obj, 'bar')) self.assertTrue(canAccess(obj, 'bar')) # ...and may access baz. self.assertTrue(not canAccess(obj, 'baz')) # there are no security assertions for writing or reading shazam, so # checking these actually raises Forbidden. The rationale behind # exposing the Forbidden exception is primarily that it is usually # indicative of programming or configuration errors. self.assertRaises(Forbidden, canAccess, obj, 'shazam') self.assertRaises(Forbidden, canWrite, obj, 'shazam') # However, we special-case canWrite when an attribute has a Read # setting but no Write setting. Consider the 'baz' attribute from the # checker above: it is readonly. All users are forbidden to write # it. This is a very reasonable configuration. Therefore, canWrite # will hide the Forbidden exception if and only if there is a # setting for accessing the attribute. self.assertTrue(not canWrite(obj, 'baz')) # The reverse is not true at the moment: an unusal case like the # write-only 'bing' attribute will return a boolean for canWrite, # but canRead will simply raise a Forbidden exception, without checking # write settings. self.assertTrue(not canWrite(obj, 'bing')) self.assertRaises(Forbidden, canAccess, obj, 'bing') class TestCheckerPublic(unittest.TestCase): def test_that_pickling_CheckerPublic_retains_identity(self): import pickle from zope.security.checker import CheckerPublic self.assertIs(pickle.loads(pickle.dumps(CheckerPublic)), CheckerPublic) def test_that_CheckerPublic_identity_works_even_when_proxied(self): from zope.security.checker import CheckerPublic from zope.security.checker import ProxyFactory self.assertIs(ProxyFactory(CheckerPublic), CheckerPublic) class TestMixinDecoratedChecker(unittest.TestCase): policy = None _oldpolicy = None interaction = None obj = None def decoratedSetUp(self): from zope.security.management import getInteraction from zope.security.management import newInteraction from zope.security.management import setSecurityPolicy self.policy = self._makeSecurityPolicy() self._oldpolicy = setSecurityPolicy(self.policy) newInteraction() self.interaction = getInteraction() self.obj = object() def decoratedTearDown(self): from zope.security.management import endInteraction from zope.security.management import setSecurityPolicy endInteraction() setSecurityPolicy(self._oldpolicy) del self.policy del self._oldpolicy del self.obj del self.interaction def _makeSecurityPolicy(self): from zope.interface import implementer from zope.security.interfaces import ISecurityPolicy @implementer(ISecurityPolicy) class RecordedSecurityPolicy: def __init__(self): self._checked = [] self.permissions = {} def checkPermission(self, permission, _obj): self._checked.append(permission) return self.permissions.get(permission, True) def checkChecked(self, checked): res = self._checked == checked self._checked = [] return res return RecordedSecurityPolicy def check_checking_impl(self, checker): from zope.security.interfaces import ForbiddenAttribute o = self.obj checker.check_getattr(o, 'both_get_set') self.assertTrue(self.interaction.checkChecked(['dc_get_permission'])) checker.check_getattr(o, 'c_only') self.assertTrue(self.interaction.checkChecked(['get_permission'])) checker.check_getattr(o, 'd_only') self.assertTrue(self.interaction.checkChecked(['dc_get_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_getattr, o, 'completely_different_attr') self.assertTrue(self.interaction.checkChecked([])) checker.check(o, '__str__') self.assertTrue(self.interaction.checkChecked(['get_permission'])) checker.check_setattr(o, 'both_get_set') self.assertTrue(self.interaction.checkChecked(['dc_set_permission'])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'c_only') self.assertTrue(self.interaction.checkChecked([])) self.assertRaises(ForbiddenAttribute, checker.check_setattr, o, 'd_only') self.assertTrue(self.interaction.checkChecked([])) @property def originalChecker(self): from zope.security.checker import NamesChecker return NamesChecker(['both_get_set', 'c_only', '__str__'], 'get_permission') decorationSetMap = {'both_get_set': 'dc_set_permission'} decorationGetMap = {'both_get_set': 'dc_get_permission', 'd_only': 'dc_get_permission'} @property def overridingChecker(self): from zope.security.checker import Checker return Checker(self.decorationGetMap, self.decorationSetMap) class TestCombinedCheckerMixin(QuietWatchingChecker, TestMixinDecoratedChecker, unittest.TestCase): def setUp(self): super().setUp() self.decoratedSetUp() def tearDown(self): self.decoratedTearDown() super().tearDown() def test_checking(self): from zope.security.checker import CombinedChecker from zope.security.interfaces import Unauthorized cc = CombinedChecker(self.overridingChecker, self.originalChecker) self.check_checking_impl(cc) # When a permission is not authorized by the security policy, # the policy is queried twice per check_getattr -- once for each # checker. self.interaction.permissions['dc_get_permission'] = False cc.check_getattr(self.obj, 'both_get_set') self.assertTrue( self.interaction.checkChecked(['dc_get_permission', 'get_permission']) ) # This should raise Unauthorized instead of ForbiddenAttribute, since # access can be granted if you e.g. login with different credentials. self.assertRaises(Unauthorized, cc.check_getattr, self.obj, 'd_only') self.assertRaises(Unauthorized, cc.check, self.obj, 'd_only') def test_interface(self): from zope.interface.verify import verifyObject from zope.security.checker import CombinedChecker from zope.security.interfaces import IChecker dc = CombinedChecker(self.overridingChecker, self.originalChecker) verifyObject(IChecker, dc) class TestBasicTypes(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def test___setitem__(self): from zope.security.checker import BasicTypes from zope.security.checker import _checkers class Foo: pass checker = object() BasicTypes[Foo] = checker self.assertIs(BasicTypes[Foo], checker) self.assertIs(_checkers[Foo], checker) def test___delitem__(self): from zope.security.checker import BasicTypes from zope.security.checker import _checkers class Foo: pass checker = object() BasicTypes[Foo] = checker del BasicTypes[Foo] self.assertNotIn(Foo, BasicTypes) self.assertNotIn(Foo, _checkers) def test_clear(self): from zope.security.checker import BasicTypes self.assertRaises(NotImplementedError, BasicTypes.clear) def test_update(self): from zope.security.checker import BasicTypes from zope.security.checker import _checkers class Foo: pass checker = object() BasicTypes.update({Foo: checker}) self.assertIs(BasicTypes[Foo], checker) self.assertIs(_checkers[Foo], checker) def test(self): from zope.security.checker import BasicTypes from zope.security.checker import NoProxy from zope.security.checker import _checkers from zope.security.checker import _clear class MyType: pass class MyType2: pass # When an item is added to the basic types, it should also be added to # the list of checkers. BasicTypes[MyType] = NoProxy self.assertIn(MyType, _checkers) # If we clear the checkers, the type should still be there _clear() self.assertIn(MyType, BasicTypes) self.assertIn(MyType, _checkers) # Now delete the type from the dictionary, will also delete it from # the checkers del BasicTypes[MyType] self.assertNotIn(MyType, BasicTypes) self.assertNotIn(MyType, _checkers) # The quick way of adding new types is using update BasicTypes.update({MyType: NoProxy, MyType2: NoProxy}) self.assertIn(MyType, BasicTypes) self.assertIn(MyType2, BasicTypes) self.assertIn(MyType, _checkers) self.assertIn(MyType2, _checkers) # Let's remove the two new types del BasicTypes[MyType] del BasicTypes[MyType2] # Of course, BasicTypes is a full dictionary. This dictionary is by # default filled with several entries: keys = BasicTypes.keys() self.assertIn(bool, keys) self.assertIn(int, keys) self.assertIn(float, keys) self.assertIn(str, keys) self.assertIn(object, keys) # ... # Finally, the ``clear()`` method has been deactivated to avoid # unwanted deletions. self.assertRaises(NotImplementedError, BasicTypes.clear) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/test_compile_flags.py0000644000076500000240000000253514355021564024155 0ustar00jensstaff############################################################################## # # Copyright (c) 2022 Zope Foundation 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 # ############################################################################## import struct import unittest import zope.security.checker # noqa: try to load a C module for side effects import zope.security.proxy # noqa: try to load a C module for side effects class TestFloatingPoint(unittest.TestCase): def test_no_fast_math_optimization(self): # Building with -Ofast enables -ffast-math, which sets certain FPU # flags that can cause breakage elsewhere. A library such as BTrees # has no business changing global FPU flags for the entire process. zero_bits = struct.unpack("!Q", struct.pack("!d", 0.0))[0] next_up = zero_bits + 1 smallest_subnormal = struct.unpack("!d", struct.pack("!Q", next_up))[0] self.assertNotEqual(smallest_subnormal, 0.0) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_decorator.py0000644000076500000240000001366314672224636023346 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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 zope.security.decorator """ import unittest from zope.security.tests import QuietWatchingChecker class DecoratedSecurityCheckerDescriptorTests(QuietWatchingChecker, unittest.TestCase): def setUp(self): super().setUp() from zope.security.checker import _clear _clear() def tearDown(self): super().tearDown() from zope.security.checker import _clear _clear() def _getTargetClass(self): from zope.security.decorator import DecoratedSecurityCheckerDescriptor return DecoratedSecurityCheckerDescriptor def _makeOne(self): return self._getTargetClass()() def test_neither_wrapper_nor_object_has_checker(self): from zope.proxy import ProxyBase from zope.security.checker import NoProxy from zope.security.checker import defineChecker class Foo: a = 'a' defineChecker(Foo, NoProxy) foo = Foo() class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() defineChecker(Wrapper, NoProxy) wrapper = Wrapper(foo) self.assertRaises(AttributeError, getattr, wrapper, '__Security_checker__') def test_both_wrapper_and_object_have_checkers_not_security_proxied(self): from zope.proxy import ProxyBase from zope.security.checker import CombinedChecker from zope.security.checker import NamesChecker from zope.security.checker import defineChecker from zope.security.interfaces import ForbiddenAttribute class Foo: a = 'a' fooChecker = NamesChecker(['a']) # a is public defineChecker(Foo, fooChecker) foo = Foo() fooChecker.check(foo, 'a') # no raise self.assertRaises(ForbiddenAttribute, fooChecker.check, foo, 'b') class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() wrapperChecker = NamesChecker(['b']) # b is public defineChecker(Wrapper, wrapperChecker) wrapper = Wrapper(foo) self.assertRaises(ForbiddenAttribute, wrapperChecker.check, foo, 'a') wrapperChecker.check(foo, 'b') # no raise checker = wrapper.__Security_checker__ self.assertIsInstance(checker, CombinedChecker) checker.check(wrapper, 'a') # no raise checker.check(wrapper, 'b') # no raise def test_only_wrapper_has_checker(self): from zope.proxy import ProxyBase from zope.security.checker import NamesChecker from zope.security.checker import NoProxy from zope.security.checker import defineChecker class Foo: a = 'a' foo = Foo() defineChecker(Foo, NoProxy) class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() wrapperChecker = NamesChecker(['b']) # b is public defineChecker(Wrapper, wrapperChecker) wrapper = Wrapper(foo) self.assertIs(wrapper.__Security_checker__, wrapperChecker) def test_only_object_has_checker(self): from zope.proxy import ProxyBase from zope.security.checker import NamesChecker from zope.security.checker import NoProxy from zope.security.checker import defineChecker class Foo: a = 'a' fooChecker = NamesChecker(['a']) # a is public defineChecker(Foo, fooChecker) foo = Foo() class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() defineChecker(Wrapper, NoProxy) wrapper = Wrapper(foo) self.assertIs(wrapper.__Security_checker__, fooChecker) def test_both_wrapper_and_object_have_checkers_security_proxied(self): from zope.proxy import ProxyBase from zope.security.checker import CombinedChecker from zope.security.checker import NamesChecker from zope.security.checker import defineChecker from zope.security.proxy import ProxyFactory class Foo: a = 'a' fooChecker = NamesChecker(['a']) # a is public defineChecker(Foo, fooChecker) foo = Foo() f_sec = ProxyFactory(foo) class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() wrapperChecker = NamesChecker(['b']) # b is public defineChecker(Wrapper, wrapperChecker) w_sec = Wrapper(f_sec) checker = w_sec.__Security_checker__ self.assertIsInstance(checker, CombinedChecker) checker.check(w_sec, 'a') # no raise checker.check(w_sec, 'b') # no raise def test_cannot_overwrite(self): from zope.proxy import ProxyBase from zope.security.checker import NoProxy from zope.security.checker import defineChecker class Foo: a = 'a' defineChecker(Foo, NoProxy) foo = Foo() class Wrapper(ProxyBase): b = 'b' __Security_checker__ = self._makeOne() wrapper = Wrapper(foo) def _try(): wrapper.__Security_checker__ = None self.assertRaises(TypeError, _try) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/test_location.py0000644000076500000240000000321314355021564023153 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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 location support """ import unittest from zope.location.location import LocationProxy from zope.security.tests import QuietWatchingChecker class LocationSecurityProxyTests(QuietWatchingChecker, unittest.TestCase): def test_locationproxy_security(self): from zope.security.checker import NamesChecker from zope.security.checker import defineChecker from zope.security.proxy import ProxyFactory class Unlocated: a = 'a' unlocated = Unlocated() located = LocationProxy(unlocated) # define a checker for the unlocated object, which will also be # used by the security proxy as the LocationProxy defines # __Security_checker__: unlocatedChecker = NamesChecker(['a']) defineChecker(Unlocated, unlocatedChecker) secure_located = ProxyFactory(located) self.assertEqual(secure_located.a, 'a') def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_management.py0000644000076500000240000001632614672224636023477 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """ Unit tests for zope.security.management """ import unittest class Test(unittest.TestCase): def setUp(self): self._cleanUp() def tearDown(self): self._cleanUp() def _cleanUp(self): from zope.security.management import _clear from zope.security.management import endInteraction _clear() endInteraction() def test_import(self): from zope.interface.verify import verifyObject from zope.security import management from zope.security.interfaces import IInteractionManagement from zope.security.interfaces import ISecurityManagement verifyObject(ISecurityManagement, management) verifyObject(IInteractionManagement, management) def test_securityPolicy(self): from zope.security.management import getSecurityPolicy from zope.security.management import setSecurityPolicy from zope.security.simplepolicies import PermissiveSecurityPolicy policy = PermissiveSecurityPolicy setSecurityPolicy(policy) self.assertIs(getSecurityPolicy(), policy) def test_getInteraction_none_present(self): from zope.security.interfaces import NoInteraction from zope.security.management import getInteraction self.assertRaises(NoInteraction, getInteraction) def test_queryInteraction_none_present(self): from zope.security.management import queryInteraction self.assertEqual(queryInteraction(), None) def test_newInteraction(self): from zope.security.management import newInteraction from zope.security.management import queryInteraction newInteraction() interaction = queryInteraction() self.assertIsNotNone(interaction) def test_newInteraction_repeated_without_end(self): from zope.security.management import ExistingInteraction from zope.security.management import newInteraction newInteraction() self.assertRaises(ExistingInteraction, newInteraction) def test_endInteraction(self): from zope.security.management import endInteraction from zope.security.management import newInteraction from zope.security.management import queryInteraction newInteraction() endInteraction() self.assertEqual(queryInteraction(), None) def test_endInteraction_repeated(self): from zope.security.management import endInteraction from zope.security.management import newInteraction from zope.security.management import queryInteraction newInteraction() queryInteraction() endInteraction() self.assertEqual(queryInteraction(), None) endInteraction() self.assertEqual(queryInteraction(), None) def test_restoreInteraction_after_end(self): from zope.security.management import endInteraction from zope.security.management import newInteraction from zope.security.management import queryInteraction from zope.security.management import restoreInteraction newInteraction() interaction = queryInteraction() endInteraction() restoreInteraction() self.assertIs(interaction, queryInteraction()) def test_restoreInteraction_after_new(self): from zope.security.management import newInteraction from zope.security.management import queryInteraction from zope.security.management import restoreInteraction newInteraction() self.assertIsNotNone(queryInteraction()) restoreInteraction() # restore to no interaction self.assertIsNone(queryInteraction()) def test_restoreInteraction_after_neither(self): from zope.security._definitions import thread_local from zope.security.management import queryInteraction from zope.security.management import restoreInteraction try: del thread_local.interaction except AttributeError: pass try: del thread_local.previous_interaction except AttributeError: pass restoreInteraction() self.assertIsNone(queryInteraction()) def test_checkPermission_w_no_interaction(self): from zope.security.interfaces import NoInteraction from zope.security.management import checkPermission permission = 'zope.Test' obj = object() self.assertRaises(NoInteraction, checkPermission, permission, obj) def test_checkPermission_w_interaction(self): from zope.security.management import checkPermission from zope.security.management import newInteraction from zope.security.management import queryInteraction from zope.security.management import setSecurityPolicy permission = 'zope.Test' obj = object() class PolicyStub: def checkPermission(s, p, o,): self.assertIs(p, permission) self.assertIs(o, obj) self.assertTrue(s is queryInteraction() or s is interaction) return s is interaction setSecurityPolicy(PolicyStub) newInteraction() interaction = queryInteraction() self.assertEqual(checkPermission(permission, obj), True) def test_checkPermission_forbidden_policy(self): from zope.security import checkPermission from zope.security.checker import CheckerPublic from zope.security.management import newInteraction from zope.security.management import setSecurityPolicy obj = object() class ForbiddenPolicyStub: def checkPermission(s, p, o): return False setSecurityPolicy(ForbiddenPolicyStub) newInteraction() self.assertEqual(checkPermission('zope.Test', obj), False) self.assertEqual(checkPermission(None, obj), True) self.assertEqual(checkPermission(CheckerPublic, obj), True) def test_system_user(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IPrincipal from zope.security.interfaces import ISystemPrincipal from zope.security.management import system_user self.assertEqual(system_user.id, 'zope.security.management.system_user') self.assertEqual(system_user.title, 'System') for name in 'id', 'title', 'description': self.assertIsInstance(getattr(system_user, name), str) verifyObject(IPrincipal, system_user) verifyObject(ISystemPrincipal, system_user) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_metaconfigure.py0000644000076500000240000007311714672224636024214 0ustar00jensstaff############################################################################## # # Copyright (c) 2012 Zope Foundation 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 ZCML directives """ import unittest from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public class Test_dottedName(unittest.TestCase): def _callFUT(self, obj): from zope.security.metaconfigure import dottedName return dottedName(obj) def test_dottted_name_w_None(self): self.assertEqual(self._callFUT(None), 'None') def test_dottted_name_w_class(self): self.assertEqual(self._callFUT(Test_dottedName), 'zope.security.tests.test_metaconfigure.' + 'Test_dottedName') class ClassDirectiveTests(unittest.TestCase): def _getTargetClass(self): from zope.security.metaconfigure import ClassDirective return ClassDirective def _makeOne(self, _context, class_): return self._getTargetClass()(_context, class_) # def test_ctor_non_class(self): TODO needs better guard in __init__ def test_implements_empty(self): context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.implements(context, []) self.assertEqual(len(context._actions), 0) def test_implements_single_interface(self): from zope.component.interface import provideInterface from zope.interface import Interface from zope.interface import classImplements class IFoo(Interface): pass context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.implements(context, [IFoo]) self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'][:2], ('ContentDirective', Foo, )) # 3rd is object() self.assertIs(context._actions[0]['callable'], classImplements) self.assertEqual(context._actions[0]['args'], (Foo, IFoo)) self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_implements_multiple_interfaces(self): from zope.component.interface import provideInterface from zope.interface import Interface from zope.interface import classImplements class IFoo(Interface): pass class IBar(Interface): pass context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.implements(context, [IFoo, IBar]) self.assertEqual(len(context._actions), 4) self.assertEqual(context._actions[0]['discriminator'][:2], ('ContentDirective', Foo, )) # 3rd is object() self.assertIs(context._actions[0]['callable'], classImplements) self.assertEqual(context._actions[0]['args'], (Foo, IFoo)) self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) self.assertEqual(context._actions[2]['discriminator'][:2], ('ContentDirective', Foo, )) # 3rd is object() self.assertIs(context._actions[2]['callable'], classImplements) self.assertEqual(context._actions[2]['args'], (Foo, IBar)) self.assertIsNone(context._actions[3]['discriminator']) self.assertIs(context._actions[3]['callable'], provideInterface) self.assertEqual(context._actions[3]['args'], ('zope.security.tests.test_metaconfigure.IBar', IBar)) def test_require_only_like_class(self): from zope.security.protectclass import protectLikeUnto class Bar: pass context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, like_class=Bar) self.assertEqual(len(context._actions), 1) self.assertEqual(context._actions[0]['discriminator'][:2], ('mimic', Foo, )) # 3rd is object() self.assertIs(context._actions[0]['callable'], protectLikeUnto) self.assertEqual(context._actions[0]['args'], (Foo, Bar)) def test_require_only_permission(self): from zope.configuration.exceptions import ConfigurationError context = DummyZCMLContext() directive = self._makeOne(context, Foo) self.assertRaises(ConfigurationError, directive.require, context, permission='testing') def test_require_no_like_class_wo_permission(self): from zope.configuration.exceptions import ConfigurationError context = DummyZCMLContext() directive = self._makeOne(context, Foo) with self.assertRaises(ConfigurationError): directive.require(context, attributes=('foo', 'bar')) def test_require_w_single_interface(self): from zope.component.interface import provideInterface from zope.interface import Attribute from zope.interface import Interface from zope.security.protectclass import protectName class IFoo(Interface): bar = Attribute("Bar") baz = Attribute("Baz") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', interface=[IFoo]) self.assertEqual(len(context._actions), 3) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', 'testing')) self.assertEqual(context._actions[1]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectName) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', 'testing')) self.assertIsNone(context._actions[2]['discriminator']) self.assertIs(context._actions[2]['callable'], provideInterface) self.assertEqual(context._actions[2]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_require_w_multiple_interfaces(self): from zope.component.interface import provideInterface from zope.interface import Attribute from zope.interface import Interface from zope.security.protectclass import protectName class IFoo(Interface): bar = Attribute("Bar") class IBar(Interface): baz = Attribute("Baz") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', interface=[IFoo, IBar]) self.assertEqual(len(context._actions), 4) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', 'testing')) self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) self.assertEqual(context._actions[2]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[2]['callable'], protectName) self.assertEqual(context._actions[2]['args'], (Foo, 'baz', 'testing')) self.assertIsNone(context._actions[3]['discriminator']) self.assertIs(context._actions[3]['callable'], provideInterface) self.assertEqual(context._actions[3]['args'], ('zope.security.tests.test_metaconfigure.IBar', IBar)) def test_require_w_attributes(self): from zope.security.protectclass import protectName context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', attributes=['bar', 'baz']) self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', 'testing')) self.assertEqual(context._actions[1]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectName) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', 'testing')) def test_require_w_set_attributes(self): from zope.security.protectclass import protectSetAttribute context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', set_attributes=['bar', 'baz']) self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('protectSetAttribute', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectSetAttribute) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', 'testing')) self.assertEqual(context._actions[1]['discriminator'], ('protectSetAttribute', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectSetAttribute) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', 'testing')) def test_require_w_set_schema_normal_fields(self): from zope.component.interface import provideInterface from zope.interface import Interface from zope.schema import Field from zope.security.protectclass import protectSetAttribute class IFoo(Interface): bar = Field("Bar") baz = Field("Baz") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', set_schema=[IFoo]) self.assertEqual(len(context._actions), 3) self.assertEqual(context._actions[0]['discriminator'], ('protectSetAttribute', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectSetAttribute) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', 'testing')) self.assertEqual(context._actions[1]['discriminator'], ('protectSetAttribute', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectSetAttribute) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', 'testing')) self.assertIsNone(context._actions[2]['discriminator']) self.assertIs(context._actions[2]['callable'], provideInterface) self.assertEqual(context._actions[2]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_require_w_set_schema_ignores_non_fields(self): from zope.component.interface import provideInterface from zope.interface import Attribute from zope.interface import Interface class IFoo(Interface): bar = Attribute("Bar") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', set_schema=[IFoo]) self.assertEqual(len(context._actions), 1) self.assertIsNone(context._actions[0]['discriminator']) self.assertIs(context._actions[0]['callable'], provideInterface) self.assertEqual(context._actions[0]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_require_w_set_schema_ignores_readonly_fields(self): from zope.component.interface import provideInterface from zope.interface import Interface from zope.schema import Field class IFoo(Interface): bar = Field("Bar", readonly=True) context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.require(context, permission='testing', set_schema=[IFoo]) self.assertEqual(len(context._actions), 1) self.assertIsNone(context._actions[0]['discriminator']) self.assertIs(context._actions[0]['callable'], provideInterface) self.assertEqual(context._actions[0]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_allow_no_attributes_or_interface(self): from zope.configuration.exceptions import ConfigurationError context = DummyZCMLContext() directive = self._makeOne(context, Foo) self.assertRaises(ConfigurationError, directive.allow, context) def test_allow_w_single_interface(self): from zope.component.interface import provideInterface from zope.interface import Attribute from zope.interface import Interface from zope.security.protectclass import protectName class IFoo(Interface): bar = Attribute("Bar") baz = Attribute("Baz") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.allow(context, interface=[IFoo]) self.assertEqual(len(context._actions), 3) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', zope_Public)) self.assertEqual(context._actions[1]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectName) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', zope_Public)) self.assertIsNone(context._actions[2]['discriminator']) self.assertIs(context._actions[2]['callable'], provideInterface) self.assertEqual(context._actions[2]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) def test_allow_w_multiple_interfaces(self): from zope.component.interface import provideInterface from zope.interface import Attribute from zope.interface import Interface from zope.security.protectclass import protectName class IFoo(Interface): bar = Attribute("Bar") class IBar(Interface): baz = Attribute("Baz") context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.allow(context, interface=[IFoo, IBar]) self.assertEqual(len(context._actions), 4) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', zope_Public)) self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('zope.security.tests.test_metaconfigure.IFoo', IFoo)) self.assertEqual(context._actions[2]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[2]['callable'], protectName) self.assertEqual(context._actions[2]['args'], (Foo, 'baz', zope_Public)) self.assertIsNone(context._actions[3]['discriminator']) self.assertIs(context._actions[3]['callable'], provideInterface) self.assertEqual(context._actions[3]['args'], ('zope.security.tests.test_metaconfigure.IBar', IBar)) def test_allow_w_attributes(self): from zope.security.protectclass import protectName context = DummyZCMLContext() directive = self._makeOne(context, Foo) directive.allow(context, attributes=['bar', 'baz']) self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('protectName', Foo, 'bar')) self.assertIs(context._actions[0]['callable'], protectName) self.assertEqual(context._actions[0]['args'], (Foo, 'bar', zope_Public)) self.assertEqual(context._actions[1]['discriminator'], ('protectName', Foo, 'baz')) self.assertIs(context._actions[1]['callable'], protectName) self.assertEqual(context._actions[1]['args'], (Foo, 'baz', zope_Public)) def test___call__(self): context = DummyZCMLContext() directive = self._makeOne(context, Foo) self.assertEqual(directive(), ()) def test_factory_wo_explicit_id(self): from zope.component.interface import provideInterface from zope.component.interfaces import IFactory from zope.component.zcml import handler context = DummyZCMLContext() context.info = 'INFO' directive = self._makeOne(context, Foo) directive.factory(context, title='TITLE', description='DESCRIPTION') self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('utility', IFactory, 'zope.security.tests.test_metaconfigure.Foo')) self.assertIs(context._actions[0]['callable'], handler) args = context._actions[0]['args'] self.assertEqual(args[0], 'registerUtility') factory = args[1] self.assertEqual(factory._callable, Foo) self.assertEqual(factory.title, 'TITLE') self.assertEqual(factory.description, 'DESCRIPTION') self.assertEqual(args[2], IFactory) self.assertEqual(args[3], 'zope.security.tests.test_metaconfigure.Foo') self.assertEqual(args[4], 'INFO') self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('', IFactory)) def test_factory_w_explicit_id(self): from zope.component.interface import provideInterface from zope.component.interfaces import IFactory from zope.component.zcml import handler context = DummyZCMLContext() context.info = 'INFO' directive = self._makeOne(context, Foo) directive.factory(context, id='test_id') self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('utility', IFactory, 'test_id')) self.assertIs(context._actions[0]['callable'], handler) args = context._actions[0]['args'] self.assertEqual(args[0], 'registerUtility') factory = args[1] self.assertEqual(factory._callable, Foo) self.assertEqual(args[2], IFactory) self.assertEqual(args[3], 'test_id') self.assertEqual(args[4], 'INFO') self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('', IFactory)) class Foo: pass class Test_protectModule(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, module, name, permission): from zope.security.metaconfigure import protectModule return protectModule(module, name, permission) def test_check_wo_existing_module_checker(self): from zope.security import tests as module from zope.security.checker import _checkers perm = object() self._callFUT(module, 'name', perm) checker = _checkers[module] self.assertIs(checker.get_permissions['name'], perm) def test_check_w_existing_module_checker_zope_Public(self): from zope.security import tests as module from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import _checkers before = _checkers[module] = Checker({'other': CheckerPublic}) self._callFUT(module, 'name', zope_Public) checker = _checkers[module] self.assertIs(checker, before) self.assertIs(checker.get_permissions['name'], CheckerPublic) class Test_allow(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, context, attributes=None, interface=None): from zope.security.metaconfigure import allow if interface is None: if attributes is None: return allow(context) return allow(context, attributes) if attributes is None: return allow(context, interface=interface) return allow(context, attributes, interface) def test_empty(self): context = DummyZCMLContext() self._callFUT(context) self.assertEqual(len(context._actions), 0) def test_w_attributes(self): from zope.security.metaconfigure import protectModule ATTRS = ['foo', 'bar'] context = DummyZCMLContext() context.module = 'testing' self._callFUT(context, ATTRS) self.assertEqual(len(context._actions), len(ATTRS)) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'foo')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'foo', zope_Public)) self.assertEqual(context._actions[1]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[1]['callable'], protectModule) self.assertEqual(context._actions[1]['args'], ('testing', 'bar', zope_Public)) def test_w_interface(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.metaconfigure import protectModule class IFoo(Interface): bar = Attribute('Bar') context = DummyZCMLContext() context.module = 'testing' self._callFUT(context, interface=[IFoo]) self.assertEqual(len(context._actions), 1) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'bar', zope_Public)) def test_w_both(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.metaconfigure import protectModule class IFoo(Interface): bar = Attribute('Bar') baz = Attribute('Baz') ATTRS = ['foo', 'bar'] context = DummyZCMLContext() context.module = 'testing' self._callFUT(context, ATTRS, [IFoo]) self.assertEqual(len(context._actions), 3) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'foo')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'foo', zope_Public)) self.assertEqual(context._actions[1]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[1]['callable'], protectModule) self.assertEqual(context._actions[1]['args'], ('testing', 'bar', zope_Public)) self.assertEqual(context._actions[2]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'baz')) self.assertIs(context._actions[2]['callable'], protectModule) self.assertEqual(context._actions[2]['args'], ('testing', 'baz', zope_Public)) class Test_requre(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, context, permission, attributes=None, interface=None): from zope.security.metaconfigure import require if interface is None: if attributes is None: return require(context, permission) return require(context, permission, attributes) if attributes is None: return require(context, permission, interface=interface) return require(context, permission, attributes, interface) def test_empty(self): context = DummyZCMLContext() context.module = 'testing' perm = object() self._callFUT(context, perm) self.assertEqual(len(context._actions), 0) def test_w_attributes(self): from zope.security.metaconfigure import protectModule ATTRS = ['foo', 'bar'] context = DummyZCMLContext() context.module = 'testing' perm = object() self._callFUT(context, perm, ATTRS) self.assertEqual(len(context._actions), len(ATTRS)) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'foo')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'foo', perm)) self.assertEqual(context._actions[1]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[1]['callable'], protectModule) self.assertEqual(context._actions[1]['args'], ('testing', 'bar', perm)) def test_w_interface(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.metaconfigure import protectModule class IFoo(Interface): bar = Attribute('Bar') context = DummyZCMLContext() context.module = 'testing' perm = object() self._callFUT(context, perm, interface=[IFoo]) self.assertEqual(len(context._actions), 1) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'bar', perm)) def test_w_both(self): from zope.interface import Attribute from zope.interface import Interface from zope.security.metaconfigure import protectModule class IFoo(Interface): bar = Attribute('Bar') baz = Attribute('Baz') ATTRS = ['foo', 'bar'] context = DummyZCMLContext() context.module = 'testing' perm = object() self._callFUT(context, perm, ATTRS, [IFoo]) self.assertEqual(len(context._actions), 3) self.assertEqual(context._actions[0]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'foo')) self.assertIs(context._actions[0]['callable'], protectModule) self.assertEqual(context._actions[0]['args'], ('testing', 'foo', perm)) self.assertEqual(context._actions[1]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'bar')) self.assertIs(context._actions[1]['callable'], protectModule) self.assertEqual(context._actions[1]['args'], ('testing', 'bar', perm)) self.assertEqual(context._actions[2]['discriminator'], ('http://namespaces.zope.org/zope:module', 'testing', 'baz')) self.assertIs(context._actions[2]['callable'], protectModule) self.assertEqual(context._actions[2]['args'], ('testing', 'baz', perm)) class DummyZCMLContext: def __init__(self): self._actions = [] def action(self, **kw): self._actions.append(kw.copy()) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_permission.py0000644000076500000240000001717414672224636023555 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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 permissions """ import unittest from zope.component.testing import PlacelessSetup from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public class PermissionTests(unittest.TestCase): def _getTargetClass(self): from zope.security.permission import Permission return Permission def _makeOne(self, id, *args): klass = self._getTargetClass() return klass(id, *args) def test_class_conforms_to_IPermission(self): from zope.interface.verify import verifyClass from zope.security.interfaces import IPermission verifyClass(IPermission, self._getTargetClass()) def test_instance_conforms_to_IPermission(self): from zope.interface.verify import verifyObject from zope.schema import getValidationErrors from zope.security.interfaces import IPermission verifyObject(IPermission, self._makeOne('testing')) self.assertEqual([], getValidationErrors(IPermission, self._makeOne('testing'))) def test_ctor_only_id(self): permission = self._makeOne('testing') self.assertEqual(permission.id, 'testing') self.assertEqual(permission.title, '') self.assertEqual(permission.description, '') def test_ctor_w_title_and_description(self): permission = self._makeOne('testing', 'TITLE', 'DESCRIPTION') self.assertEqual(permission.id, 'testing') self.assertEqual(permission.title, 'TITLE') self.assertEqual(permission.description, 'DESCRIPTION') class Test_checkPermission(PlacelessSetup, unittest.TestCase): def _callFUT(self, context, permission_id): from zope.security.permission import checkPermission return checkPermission(context, permission_id) def test_w_CheckerPublic(self): from zope.security.checker import CheckerPublic self._callFUT(None, CheckerPublic) # no raise def test_miss(self): self.assertRaises(ValueError, self._callFUT, None, 'nonesuch') def test_hit(self): from zope.component import provideUtility from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') self._callFUT(None, 'testing') # no raise class Test_allPermissions(PlacelessSetup, unittest.TestCase): def _callFUT(self): from zope.security.permission import allPermissions return allPermissions() def test_empty(self): self.assertEqual(list(self._callFUT()), []) def test_w_registration(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') self.assertEqual(list(self._callFUT()), ['testing']) def test_skips_zope_Public(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.checker import CheckerPublic from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') provideUtility(CheckerPublic, IPermission, zope_Public) self.assertEqual(list(self._callFUT()), ['testing']) class Test_PermissionsVocabulary(PlacelessSetup, unittest.TestCase): def _callFUT(self): from zope.security.permission import PermissionsVocabulary return PermissionsVocabulary() def test_empty(self): from zope.schema.vocabulary import SimpleVocabulary vocabulary = self._callFUT() self.assertIsInstance(vocabulary, SimpleVocabulary) self.assertEqual(list(vocabulary), []) def test_w_registration(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') vocabulary = self._callFUT() self.assertEqual([x.token for x in vocabulary], ['testing']) def test_includes_zope_Public(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.checker import CheckerPublic from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') provideUtility(CheckerPublic, IPermission, zope_Public) vocabulary = self._callFUT() self.assertEqual(sorted([x.token for x in vocabulary]), ['testing', zope_Public]) def test_zcml_valid(self): from zope.configuration import xmlconfig from zope.interface.verify import verifyObject from zope.schema import getValidationErrors import zope.security from zope.security.interfaces import IPermission xmlconfig.file('configure.zcml', zope.security) vocabulary = self._callFUT() vocabulary = sorted(vocabulary, key=lambda term: term.token) self.assertEqual(6, len(vocabulary)) for term in vocabulary: p = term.value __traceback_info__ = term.token, p verifyObject(IPermission, p) self.assertEqual([], getValidationErrors(IPermission, p)) class Test_PermissionIdsVocabulary(PlacelessSetup, unittest.TestCase): def _callFUT(self): from zope.security.permission import PermissionIdsVocabulary return PermissionIdsVocabulary() def test_empty(self): from zope.schema.vocabulary import SimpleVocabulary vocabulary = self._callFUT() self.assertIsInstance(vocabulary, SimpleVocabulary) self.assertEqual(list(vocabulary), []) def test_w_registration(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') vocabulary = self._callFUT() self.assertEqual([x.value for x in vocabulary], ['testing']) self.assertEqual([x.token for x in vocabulary], ['testing']) def test_includes_zope_Public(self): self.assertEqual(list(self._callFUT()), []) from zope.component import provideUtility from zope.security.checker import CheckerPublic from zope.security.interfaces import IPermission permission = object() provideUtility(permission, IPermission, 'testing') provideUtility(CheckerPublic, IPermission, zope_Public) vocabulary = self._callFUT() self.assertEqual([x.value for x in vocabulary], [CheckerPublic, 'testing']) self.assertEqual([x.token for x in vocabulary], [zope_Public, 'testing']) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_protectclass.py0000644000076500000240000001261214672224636024063 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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 """ import unittest from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public class Test_protectName(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, class_, name, permission): from zope.security.protectclass import protectName return protectName(class_, name, permission) def test_wo_existing_checker_w_zope_Public(self): from zope.security.checker import CheckerPublic from zope.security.checker import _checkers self._callFUT(Foo, 'bar', zope_Public) self.assertIs(_checkers[Foo].get_permissions['bar'], CheckerPublic) def test_w_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import _checkers checker = _checkers[Foo] = Checker({}) permission = object() self._callFUT(Foo, 'bar', permission) self.assertIs(_checkers[Foo], checker) self.assertIs(checker.get_permissions['bar'], permission) class Test_protectSetAttribute(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, class_, name, permission): from zope.security.protectclass import protectSetAttribute return protectSetAttribute(class_, name, permission) def test_wo_existing_checker_w_zope_Public(self): from zope.security.checker import CheckerPublic from zope.security.checker import _checkers self._callFUT(Foo, 'bar', zope_Public) self.assertIs(_checkers[Foo].set_permissions['bar'], CheckerPublic) def test_w_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import _checkers checker = _checkers[Foo] = Checker({}) permission = object() self._callFUT(Foo, 'bar', permission) self.assertIs(_checkers[Foo], checker) self.assertIs(checker.set_permissions['bar'], permission) class Test_protectLikeUnto(unittest.TestCase): def setUp(self): from zope.security.checker import _clear _clear() def tearDown(self): from zope.security.checker import _clear _clear() def _callFUT(self, class_, like_unto): from zope.security.protectclass import protectLikeUnto return protectLikeUnto(class_, like_unto) def test_wo_existing_like_unto_checker(self): from zope.security.checker import _checkers self.assertNotIn(Foo, _checkers) self._callFUT(Bar, Foo) self.assertNotIn(Foo, _checkers) self.assertNotIn(Bar, _checkers) def test_w_existing_like_unto_checker_wo_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import _checkers from zope.security.checker import defineChecker permission = object() foo_checker = Checker({'bar': CheckerPublic}, {'bar': permission}) defineChecker(Foo, foo_checker) self._callFUT(Bar, Foo) bar_checker = _checkers[Bar] self.assertEqual(bar_checker.get_permissions, foo_checker.get_permissions) self.assertEqual(bar_checker.set_permissions, foo_checker.set_permissions) def test_w_existing_like_unto_checker_w_existing_checker(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.checker import _checkers from zope.security.checker import defineChecker permission1, permission2 = object(), object() foo_checker = Checker({'bar': CheckerPublic}, {'bar': permission2}) defineChecker(Foo, foo_checker) bar_checker = Checker({'bar': permission1, 'baz': CheckerPublic}, {}) defineChecker(Bar, bar_checker) self._callFUT(Bar, Foo) bar_checker = _checkers[Bar] self.assertEqual(bar_checker.get_permissions, {'bar': CheckerPublic, 'baz': CheckerPublic}) self.assertEqual(bar_checker.set_permissions, foo_checker.set_permissions) class Foo: bar = 'Bar' class Bar(Foo): baz = 'Baz' def test_suite(): return unittest.TestSuite(( unittest.defaultTestLoader.loadTestsFromTestCase(Test_protectName), unittest.defaultTestLoader.loadTestsFromTestCase( Test_protectSetAttribute), unittest.defaultTestLoader.loadTestsFromTestCase(Test_protectLikeUnto), )) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_proxy.py0000644000076500000240000020001714672224636022534 0ustar00jensstaff############################################################################## # # Copyright (c) 2003 Zope Foundation 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. # ############################################################################## """Security proxy tests """ import io import os import unittest from zope.security._compat import PURE_PYTHON # pylint:disable=protected-access,eval-used,too-many-lines,too-many-public-methods class AbstractProxyTestBase: # pylint:disable=no-member,blacklisted-name def _getTargetClass(self): raise NotImplementedError("Subclass responsibility") def _makeOne(self, obj, checker): return self._getTargetClass()(obj, checker) def test_ctor_w_checker_None(self): self.assertRaises(ValueError, self._makeOne, object(), None) def test___getattr___w_checker_ok(self): class Foo: bar = 'Bar' target = Foo() checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy.bar, 'Bar') self.assertEqual(getattr(proxy, 'bar'), 'Bar') self.assertEqual(checker._checked, 'bar') self.assertEqual(checker._proxied, 'Bar') def test___getattr___w_checker_unauthorized(self): from zope.security.interfaces import Unauthorized class Foo: bar = 'Bar' target = Foo() checker = DummyChecker(Unauthorized) proxy = self._makeOne(target, checker) self.assertRaises(Unauthorized, getattr, proxy, 'bar') self.assertEqual(checker._checked, 'bar') def test___getattr___w_checker_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute class Foo: bar = 'Bar' target = Foo() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): getattr(proxy, 'bar') self.assertEqual(checker._checked, 'bar') def test__getattr__w_checker_ok_dynamic_attribute_called_once(self): class Dynamic: count = 0 def __getattr__(self, name): self.count += 1 if self.count == 1: # Called from __getattribute__ raise AttributeError(name) raise AssertionError("We should not be called more than once") target = Dynamic() checker = DummyChecker() proxy = self._makeOne(target, checker) with self.assertRaisesRegex(AttributeError, "name"): getattr(proxy, 'name') self.assertEqual(1, target.count) def test___setattr___w_checker_ok(self): class Foo: bar = 'Bar' target = Foo() checker = DummyChecker() proxy = self._makeOne(target, checker) proxy.bar = 'Baz' self.assertEqual(target.bar, 'Baz') self.assertEqual(checker._checked, 'bar') self.assertEqual(checker._proxied, None) def test___setattr___w_checker_unauthorized(self): from zope.security.interfaces import Unauthorized class Foo: bar = 'Bar' target = Foo() checker = DummyChecker(Unauthorized) proxy = self._makeOne(target, checker) self.assertRaises(Unauthorized, setattr, proxy, 'bar', 'Baz') self.assertEqual(checker._checked, 'bar') def test___setattr___w_checker_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute class Foo: bar = 'Bar' target = Foo() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, setattr, proxy, 'bar', 'Baz') self.assertEqual(checker._checked, 'bar') def test___delattr___w_checker_ok(self): class Foo: bar = None target = Foo() target.bar = 'Bar' checker = DummyChecker() proxy = self._makeOne(target, checker) del proxy.bar self.assertEqual(target.bar, None) self.assertEqual(checker._checked, 'bar') self.assertEqual(checker._proxied, None) def test___delattr___w_checker_unauthorized(self): from zope.security.interfaces import Unauthorized class Foo: def __init__(self): self.bar = 'Bar' target = Foo() checker = DummyChecker(Unauthorized) proxy = self._makeOne(target, checker) self.assertRaises(Unauthorized, delattr, proxy, 'bar') self.assertEqual(target.bar, 'Bar') self.assertEqual(checker._checked, 'bar') def test___delattr___w_checker_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute class Foo: def __init__(self): self.bar = 'Bar' target = Foo() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, delattr, proxy, 'bar') self.assertEqual(target.bar, 'Bar') self.assertEqual(checker._checked, 'bar') def test___str___checker_allows_str(self): target = object() checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(str(proxy), str(target)) def test___str___checker_forbids_str(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.proxy import _fmt_address target = object() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) address = _fmt_address(target) self.assertEqual(str(proxy), '' % address) def test___str___fails_return(self): from zope.security.interfaces import ForbiddenAttribute class CustomStr: def __str__(self): "" # Docstring, not a return target = CustomStr() checker = DummyChecker(ForbiddenAttribute, allowed=('__str__')) proxy = self._makeOne(target, checker) with self.assertRaises(TypeError): str(target) with self.assertRaises(TypeError): str(proxy) def test___repr___checker_allows_str(self): target = object() checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(repr(proxy), repr(target)) def test___repr___checker_forbids_str(self): from zope.security.interfaces import ForbiddenAttribute from zope.security.proxy import _fmt_address target = object() checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) address = _fmt_address(target) self.assertEqual(repr(proxy), '' % address) def test___str___falls_through_to_repr_when_both_allowed(self): from zope.security.interfaces import ForbiddenAttribute class CustomRepr: def __repr__(self): return "" target = CustomRepr() checker = DummyChecker( ForbiddenAttribute, allowed=("__str__", '__repr__')) proxy = self._makeOne(target, checker) self.assertEqual(repr(proxy), "") self.assertEqual(str(target), "") self.assertEqual(str(proxy), str(target)) def test___str___doesnot_fall_through_to_repr_when_str_not_allowed(self): from zope.security.interfaces import ForbiddenAttribute class CustomRepr: def __repr__(self): return "" target = CustomRepr() checker = DummyChecker(ForbiddenAttribute, allowed=('__repr__')) proxy = self._makeOne(target, checker) self.assertEqual(repr(proxy), "") self.assertEqual(str(target), "") self.assertIn("" target = CustomRepr() checker = DummyChecker(ForbiddenAttribute, allowed=('__str__')) proxy = self._makeOne(target, checker) self.assertEqual(str(target), "") self.assertEqual(str(proxy), str(target)) self.assertIn("" # Docstring, not a return target = CustomRepr() checker = DummyChecker(ForbiddenAttribute, allowed=('__repr__')) proxy = self._makeOne(target, checker) with self.assertRaises(TypeError): repr(target) with self.assertRaises(TypeError): repr(proxy) def test__le__(self): target = 1 checker = object() # checker not consulted proxy = self._makeOne(target, checker) self.assertLessEqual(proxy, 1) def test__ne__(self): target = 1 checker = object() # checker not consulted proxy = self._makeOne(target, checker) self.assertEqual(proxy, 1) def test__ge__(self): target = 1 checker = object() # checker not consulted proxy = self._makeOne(target, checker) self.assertGreaterEqual(proxy, 1) def test__gt__(self): target = 1 checker = object() # checker not consulted proxy = self._makeOne(target, checker) self.assertGreater(proxy, 0) def test___hash___w_self(self): target = object() checker = object() # checker not consulted proxy = self._makeOne(target, checker) self.assertEqual(hash(proxy), hash(target)) def test___call___w_checker_ok(self): class Foo: def __call__(self): return 'Bar' target = Foo() checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy(), 'Bar') self.assertEqual(checker._checked, '__call__') self.assertEqual(checker._proxied, 'Bar') def test___call___w_checker_unauthorized(self): from zope.security.interfaces import Unauthorized class Foo: def __call__(self): raise AssertionError("Never called") target = Foo() checker = DummyChecker(Unauthorized, ['__name__', '__str__']) proxy = self._makeOne(target, checker) self.assertRaises(Unauthorized, proxy) self.assertEqual(checker._checked, '__call__') def test___call___w_checker_forbidden_attribute(self): from zope.security.interfaces import ForbiddenAttribute class Foo: def __call__(self): raise AssertionError("Never called") target = Foo() checker = DummyChecker(ForbiddenAttribute, ['__str__']) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, proxy) self.assertEqual(checker._checked, '__call__') def test___int___w_checker_allows(self): target = 3.0 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(int(proxy), int(target)) self.assertEqual(checker._checked, '__int__') def test___int___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3.0 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, int, proxy) self.assertEqual(checker._checked, '__int__') def test___float___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(float(proxy), float(target)) self.assertEqual(checker._checked, '__float__') def test___float___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, float, proxy) self.assertEqual(checker._checked, '__float__') def test___add___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy + 2, target + 2) self.assertEqual(checker._checked, '__add__') def test___add___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy + 2) self.assertEqual(checker._checked, '__add__') def test___sub___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy - 2, target - 2) self.assertEqual(checker._checked, '__sub__') def test___sub___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy - 2) self.assertEqual(checker._checked, '__sub__') def test___mul___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy * 2, target * 2) self.assertEqual(checker._checked, '__mul__') def test___mul___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy * 2) self.assertEqual(checker._checked, '__mul__') def test___truediv___w_checker_allows(self): target = 3.0 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy / 2, target / 2) self.assertEqual(checker._checked, '__truediv__') def test___truediv___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3.0 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy / 2) self.assertEqual(checker._checked, '__truediv__') def test___floordiv___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy // 2, target // 2) self.assertEqual(checker._checked, '__floordiv__') def test___floordiv___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy // 2) self.assertEqual(checker._checked, '__floordiv__') def test___mod___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy % 2, target % 2) self.assertEqual(checker._checked, '__mod__') def test___mod___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy % 2) self.assertEqual(checker._checked, '__mod__') def test___divmod___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(divmod(proxy, 2), divmod(target, 2)) self.assertEqual(checker._checked, '__divmod__') def test___divmod___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: divmod(proxy, 2)) self.assertEqual(checker._checked, '__divmod__') def test___pow___w_x_proxied_allowed(self): x, y, z = 3, 4, 7 checker = DummyChecker() proxy = self._makeOne(x, checker) self.assertEqual(pow(proxy, y, z), pow(x, y, z)) self.assertEqual(checker._checked, '__pow__') def test___pow___w_x_proxied_forbidden(self): from zope.security.interfaces import ForbiddenAttribute y, z = 4, 7 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(y, checker) self.assertRaises(ForbiddenAttribute, lambda: pow(proxy, y, z)) self.assertEqual(checker._checked, '__pow__') def test___pow___w_y_proxied_allowed(self): x, y = 3, 4 checker = DummyChecker() proxy = self._makeOne(y, checker) self.assertEqual(pow(x, proxy), pow(x, y)) self.assertEqual(checker._checked, '__rpow__') def test___pow___w_y_proxied_forbidden(self): from zope.security.interfaces import ForbiddenAttribute x, y = 3, 4 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(y, checker) self.assertRaises(ForbiddenAttribute, lambda: pow(x, proxy)) self.assertEqual(checker._checked, '__rpow__') def test___neg___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(-proxy, -target) self.assertEqual(checker._checked, '__neg__') def test___neg___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: -proxy) self.assertEqual(checker._checked, '__neg__') def test___pos___w_checker_allows(self): target = -3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(+proxy, +target) self.assertEqual(checker._checked, '__pos__') def test___pos___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = -3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: +proxy) self.assertEqual(checker._checked, '__pos__') def test___abs___w_checker_allows(self): target = -3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(abs(proxy), abs(target)) self.assertEqual(checker._checked, '__abs__') def test___abs___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = -3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, abs, proxy) self.assertEqual(checker._checked, '__abs__') def test___bool___(self): target = 12 checker = object() proxy = self._makeOne(target, checker) self.assertEqual(bool(proxy), bool(target)) def test___invert___w_checker_allows(self): target = 47 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(~proxy, ~target) self.assertEqual(checker._checked, '__invert__') def test___invert___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 47 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: ~proxy) self.assertEqual(checker._checked, '__invert__') def test___lshift___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy << 2, target << 2) self.assertEqual(checker._checked, '__lshift__') def test___lshift___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy << 2) self.assertEqual(checker._checked, '__lshift__') def test___rshift___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy >> 2, target >> 2) self.assertEqual(checker._checked, '__rshift__') def test___rshift___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy >> 2) self.assertEqual(checker._checked, '__rshift__') def test___and___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy & 2, target & 2) self.assertEqual(checker._checked, '__and__') def test___and___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy & 2) self.assertEqual(checker._checked, '__and__') def test___xor___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy ^ 2, target ^ 2) self.assertEqual(checker._checked, '__xor__') def test___xor___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy ^ 2) self.assertEqual(checker._checked, '__xor__') def test___or___w_checker_allows(self): target = 3 checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy | 2, target | 2) self.assertEqual(checker._checked, '__or__') def test___or___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy | 2) self.assertEqual(checker._checked, '__or__') def test___iadd___not_inplace_checker_allows(self): target = 3 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy += 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 6) self.assertEqual(checker._checked, '__iadd__') def test___iadd___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __iadd__(self, rhs): self.value += rhs return self target = Foo(3) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy += 3 self.assertIs(proxy, before) self.assertEqual(target.value, 6) self.assertEqual(checker._checked, '__iadd__') def test___iadd___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy += 3 self.assertEqual(checker._checked, '__iadd__') def test___isub___not_inplace_checker_allows(self): target = 3 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy -= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 0) self.assertEqual(checker._checked, '__isub__') def test___isub___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __isub__(self, rhs): self.value -= rhs return self target = Foo(3) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy -= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 0) self.assertEqual(checker._checked, '__isub__') def test___isub___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy -= 3 self.assertEqual(checker._checked, '__isub__') def test___imul___not_inplace_checker_allows(self): target = 3 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy *= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 9) self.assertEqual(checker._checked, '__imul__') def test___imul___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __imul__(self, rhs): self.value *= rhs return self target = Foo(3) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy *= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 9) self.assertEqual(checker._checked, '__imul__') def test___imul___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 3 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy *= 3 self.assertEqual(checker._checked, '__imul__') def test___itruediv___not_inplace_checker_allows(self): target = 6 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy /= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 2) self.assertEqual(checker._checked, '__itruediv__') def test___itruediv___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __itruediv__(self, rhs): self.value /= rhs return self target = Foo(6) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy /= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 2) self.assertEqual(checker._checked, '__itruediv__') def test___itruediv___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 6 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy /= 3 self.assertEqual(checker._checked, '__itruediv__') def test___ifloordiv___not_inplace_checker_allows(self): target = 6 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy //= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 2) self.assertEqual(checker._checked, '__ifloordiv__') def test___ifloordiv___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __ifloordiv__(self, rhs): self.value //= rhs return self target = Foo(6) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy //= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 2) self.assertEqual(checker._checked, '__ifloordiv__') def test___ifloordiv___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 6 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy //= 3 self.assertEqual(checker._checked, '__ifloordiv__') def test___imod___not_inplace_checker_allows(self): target = 6 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy %= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 0) self.assertEqual(checker._checked, '__imod__') def test___imod___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __imod__(self, rhs): self.value %= rhs return self target = Foo(6) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy %= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 0) self.assertEqual(checker._checked, '__imod__') def test___imod___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 6 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy %= 3 self.assertEqual(checker._checked, '__imod__') def test___ipow___not_inplace_checker_allows(self): target = 2 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy **= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 8) self.assertEqual(checker._checked, '__ipow__') def test___ipow___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __ipow__(self, rhs): self.value **= rhs return self target = Foo(2) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy **= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 8) self.assertEqual(checker._checked, '__ipow__') def test___ipow___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 2 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy **= 3 self.assertEqual(checker._checked, '__ipow__') def test___ilshift___not_inplace_checker_allows(self): target = 2 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy <<= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 16) self.assertEqual(checker._checked, '__ilshift__') def test___ilshift___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __ilshift__(self, rhs): self.value <<= rhs return self target = Foo(2) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy <<= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 16) self.assertEqual(checker._checked, '__ilshift__') def test___ilshift___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 2 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy <<= 3 self.assertEqual(checker._checked, '__ilshift__') def test___irshift___not_inplace_checker_allows(self): target = 16 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy >>= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 2) self.assertEqual(checker._checked, '__irshift__') def test___irshift___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __irshift__(self, rhs): self.value >>= rhs return self target = Foo(16) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy >>= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 2) self.assertEqual(checker._checked, '__irshift__') def test___irshift___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 16 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy >>= 3 self.assertEqual(checker._checked, '__irshift__') def test___iand___not_inplace_checker_allows(self): target = 7 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy &= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 3) self.assertEqual(checker._checked, '__iand__') def test___iand___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __iand__(self, rhs): self.value &= rhs return self target = Foo(7) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy &= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 3) self.assertEqual(checker._checked, '__iand__') def test___iand___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 7 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy &= 3 self.assertEqual(checker._checked, '__iand__') def test___ixor___not_inplace_checker_allows(self): target = 7 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy ^= 3 self.assertIsNot(proxy, before) self.assertEqual(checker._checked, '__ixor__') self.assertEqual(proxy, 4) def test___ixor___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __ixor__(self, rhs): self.value ^= rhs return self target = Foo(7) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy ^= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 4) self.assertEqual(checker._checked, '__ixor__') def test___ixor___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 7 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy ^= 3 self.assertEqual(checker._checked, '__ixor__') def test___ior___not_inplace_checker_allows(self): target = 6 checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy |= 3 self.assertIsNot(proxy, before) self.assertEqual(proxy, 7) self.assertEqual(checker._checked, '__ior__') def test___ior___inplace_checker_allows(self): class Foo: def __init__(self, value): self.value = value def __ior__(self, rhs): self.value |= rhs return self target = Foo(6) checker = DummyChecker() proxy = before = self._makeOne(target, checker) proxy |= 3 self.assertIs(proxy, before) self.assertEqual(target.value, 7) self.assertEqual(checker._checked, '__ior__') def test___ior___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = 6 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) with self.assertRaises(ForbiddenAttribute): proxy |= 3 self.assertEqual(checker._checked, '__ior__') def test___len___w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(len(proxy), len(target)) self.assertEqual(checker._checked, '__len__') def test___len___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = [0, 1, 2] checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, len, proxy) self.assertEqual(checker._checked, '__len__') def test__length_hint_w_checker_allows(self): target = iter([0, 1, 2]) checker = DummyChecker() proxy = self._makeOne(target, checker) hint = object.__getattribute__(proxy, '__length_hint__') self.assertEqual(3, hint()) def test__length_hint_dne(self): target = object() checker = DummyChecker() proxy = self._makeOne(target, checker) hint = object.__getattribute__(proxy, '__length_hint__') self.assertEqual(NotImplemented, hint()) def test___contains___hit_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertIn(1, proxy) self.assertEqual(checker._checked, '__contains__') def test___contains___miss_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertNotIn(4, proxy) self.assertEqual(checker._checked, '__contains__') def test___contains___w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = [0, 1, 2] checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: 0 in proxy) self.assertEqual(checker._checked, '__contains__') def test___getitem___sequence_hit_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy[1], 1) self.assertEqual(checker._checked, '__getitem__') def test___getitem___sequence_miss_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertRaises(IndexError, lambda: proxy[4]) self.assertEqual(checker._checked, '__getitem__') def test___getitem___sequence_w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = [0, 1, 2] checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy[0]) self.assertEqual(checker._checked, '__getitem__') def test___setitem___sequence_hit_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) proxy[1] = 7 self.assertEqual(target[1], 7) self.assertEqual(checker._checked, '__setitem__') def test___setitem___sequence_miss_w_checker_allows(self): target = [0, 1, 2] checker = DummyChecker() proxy = self._makeOne(target, checker) def _try(): proxy[4] = 7 self.assertRaises(IndexError, _try) self.assertEqual(checker._checked, '__setitem__') def test___setitem___sequence_w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = [0, 1, 2] checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) def _try(): proxy[4] = 7 self.assertRaises(ForbiddenAttribute, _try) self.assertEqual(checker._checked, '__setitem__') def test___getslice___error_propagates(self): class Missing(Exception): pass class Get: def __getitem__(self, x): raise Missing('__getitem__') target = Get() checker = DummyChecker() proxy = self._makeOne(target, checker) with self.assertRaisesRegex(Missing, '__getitem__'): proxy[1:2] self.assertEqual(checker._checked, '__getitem__') def test___getslice___dne_uses_getitem(self): class Missing(Exception): pass class Get: def __getitem__(self, x): raise Missing('__getitem__') target = Get() checker = DummyChecker() proxy = self._makeOne(target, checker) with self.assertRaisesRegex(Missing, '__getitem__'): proxy[1:2] self.assertEqual(checker._checked, '__getitem__') def test___setslice___error_propagates(self): class Missing(Exception): pass class Set: def __setitem__(self, k, v): raise Missing('__setitem__') target = Set() checker = DummyChecker() proxy = self._makeOne(target, checker) with self.assertRaisesRegex(Missing, '__setitem__'): proxy[1:2] = 1 self.assertEqual(checker._checked, '__setitem__') def test___setslice___dne_uses_setitem(self): class Missing(Exception): pass class Set: def __setitem__(self, k, v): raise Missing('__setitem__') target = Set() checker = DummyChecker() proxy = self._makeOne(target, checker) with self.assertRaisesRegex(Missing, '__setitem__'): proxy[1:2] = 1 self.assertEqual(checker._checked, '__setitem__') def test___getitem___mapping_hit_w_checker_allows(self): target = {'a': 0, 'b': 1, 'c': 2} checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertEqual(proxy['b'], 1) self.assertEqual(checker._checked, '__getitem__') def test___getitem___mapping_miss_w_checker_allows(self): target = {'a': 0, 'b': 1, 'c': 2} checker = DummyChecker() proxy = self._makeOne(target, checker) self.assertRaises(KeyError, lambda: proxy['d']) self.assertEqual(checker._checked, '__getitem__') def test___getitem___mapping_w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = {'a': 0, 'b': 1, 'c': 2} checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) self.assertRaises(ForbiddenAttribute, lambda: proxy['b']) self.assertEqual(checker._checked, '__getitem__') def test___setitem___mapping_hit_w_checker_allows(self): target = {'a': 0, 'b': 1, 'c': 2} checker = DummyChecker() proxy = self._makeOne(target, checker) proxy['a'] = 7 self.assertEqual(target['a'], 7) self.assertEqual(checker._checked, '__setitem__') def test___setitem___mapping_w_checker_forbids(self): from zope.security.interfaces import ForbiddenAttribute target = {'a': 0, 'b': 1, 'c': 2} checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(target, checker) def _try(): proxy['a'] = 7 self.assertRaises(ForbiddenAttribute, _try) self.assertEqual(checker._checked, '__setitem__') binops = [ "x+y", "x-y", "x*y", "x/y", "divmod(x, y)", "x**y", "x//y", "x<>y", "x&y", "x|y", "x^y", ] def test_binops(self): from zope.security.proxy import removeSecurityProxy checker = DummyChecker() for expr in self.binops: first = 1 for x in [1, self._makeOne(1, checker)]: for y in [2, self._makeOne(2, checker)]: if first: z = eval(expr) first = 0 else: self.assertEqual( removeSecurityProxy(eval(expr)), z, f"x={x!r}; y={y!r}; expr={expr!r}") @unittest.skipIf(PURE_PYTHON, "Needs C extension") class ProxyCTests(AbstractProxyTestBase, unittest.TestCase): def _getTargetClass(self): # pragma: no cover from zope.security.proxy import _Proxy return _Proxy class ProxyPyTests(AbstractProxyTestBase, unittest.TestCase): def _getTargetClass(self): from zope.security.proxy import ProxyPy return ProxyPy def test_wrapper_checker_unaccessible(self): # Can't access '_wrapped' / '_checker' in C version target = object() checker = object() proxy = self._makeOne(target, checker) self.assertRaises(AttributeError, getattr, proxy, '_wrapped') self.assertRaises(AttributeError, getattr, proxy, '_checker') def test_access_checker_from_subclass(self): target = object() checker = DummyChecker() class Sub(self._getTargetClass()): def get_checker(self): return self._checker sub = Sub(target, checker) self.assertIs(checker, sub.get_checker()) def test_ctor_w_checker(self): from zope.security.proxy import getCheckerPy from zope.security.proxy import getObjectPy # Can't access '_wrapped' / '_checker' in C version target = object() checker = object() proxy = self._makeOne(target, checker) self.assertIs(getObjectPy(proxy), target) self.assertIs(getCheckerPy(proxy), checker) def test___delattr___w__wrapped(self): target = object() checker = object() proxy = self._makeOne(target, checker) def test(): del proxy._wrapped self.assertRaises(AttributeError, test) def test___delattr___w__checker(self): target = object() checker = object() proxy = self._makeOne(target, checker) def test(): del proxy._checker self.assertRaises(AttributeError, test) def test___pow___w_z_proxied_allowed(self): x, y, z = 3, 4, 7 checker = DummyChecker() proxy = self._makeOne(z, checker) # Disabled, because a PyProxy cannot lie about its type, so # pow(i, j, proxy(k)) will fail with a TypeError self.assertRaises(TypeError, pow, (x, y, proxy)) def test___pow___w_z_proxied_forbidden(self): from zope.security.interfaces import ForbiddenAttribute x, y, z = 3, 4, 7 checker = DummyChecker(ForbiddenAttribute) proxy = self._makeOne(z, checker) # Disabled, because a PyProxy cannot lie about its type, so # pow(i, j, proxy(k)) will fail with a TypeError self.assertRaises(TypeError, pow, (x, y, proxy)) def test_getObjectPy_initial_conditions(self): # Once upon a time, we dynamically set _builtin_isinstance # in z.s.proxy.isinstance itself. And at that time getObjectPy # (aka removeSecurityProxy) called z.s.proxy.isinstance if # _builtin_isinstance was not set...which recursively calls # getObjectPy. The net result was that the very first call # to getObjectPy would falsely return the proxy object if passed # a proxy, not the wrapped object! # This test makes sure we're not dynamically setting that attribute # any more. import zope.security.proxy target = object() checker = object() proxy = self._makeOne(target, checker) orig_builtin_isinstance = zope.security.proxy._builtin_isinstance zope.security.proxy._builtin_isinstance = None try: self.assertRaises( TypeError, zope.security.proxy.getObjectPy, proxy) finally: zope.security.proxy._builtin_isinstance = orig_builtin_isinstance def test_getObjectPy_other_object(self): # If it's not a proxy, return it from zope.security.proxy import getObjectPy self.assertIs(self, getObjectPy(self)) def test_get_reduce(self): class Reduce: def __reduce__(self): return 1 def __reduce_ex__(self, prot): return prot reduce_ = Reduce() proxy = self._makeOne(reduce_, DummyChecker()) self.assertEqual(1, proxy.__reduce__()) self.assertEqual(2, proxy.__reduce_ex__(2)) def test__module__(self): class WithModule: __module__ = 'foo' module = WithModule() proxy = self._makeOne(module, DummyChecker()) self.assertEqual(WithModule.__module__, proxy.__module__) class DummyChecker: _proxied = _checked = None def __init__(self, raising=None, allowed=()): self._raising = raising self._allowed = allowed def check(self, target, name): self._checked = name if name not in self._allowed: if self._raising is not None: raise self._raising(name) check_getattr = check_setattr = check def proxy(self, value): self._proxied = value return value class Test_getTestProxyItems(unittest.TestCase): def _callFUT(self, proxy): from zope.security.proxy import getTestProxyItems return getTestProxyItems(proxy) def test_w_empty_checker(self): from zope.security.checker import Checker from zope.security.proxy import Proxy target = object() proxy = Proxy(target, Checker({})) self.assertEqual(self._callFUT(proxy), []) def test_w_non_empty_checker(self): from zope.security.checker import Checker from zope.security.checker import CheckerPublic from zope.security.proxy import Proxy target = object() permission = object() proxy = Proxy(target, Checker({'foo': CheckerPublic, 'bar': permission})) self.assertEqual(self._callFUT(proxy), [('bar', permission), ('foo', CheckerPublic)]) class Test_isinstance(unittest.TestCase): def _callFUT(self, object, cls): from zope.security.proxy import isinstance return isinstance(object, cls) def test_w_unproxied_object(self): class Foo: pass target = Foo() self.assertTrue(self._callFUT(target, Foo)) self.assertFalse(self._callFUT(target, int)) def test_w_proxied_object(self): from zope.security.checker import Checker from zope.security.proxy import Proxy class Foo: pass target = Foo() proxy = Proxy(target, Checker({})) self.assertTrue(self._callFUT(proxy, Foo)) self.assertFalse(self._callFUT(proxy, int)) # pre-geddon class Checker: ok = 1 unproxied_types = {str, } def check_getattr(self, _object, name): if name in ("__class__", "__name__", "__module__"): return if not self.ok or name not in ("__next__", "foo"): raise RuntimeError def check_setattr(self, _object, name): if name != "foo": raise RuntimeError def check(self, _object, _opname): if not self.ok: raise RuntimeError def proxy(self, value): from zope.security.proxy import ProxyFactory if type(value) in self.unproxied_types: return value return ProxyFactory(value, self) class Something: def __init__(self): self.foo = [1, 2, 3] def __getitem__(self, key): return self.foo[key] def __setitem__(self, key, value): self.foo[key] = value def __delitem__(self, key): del self.foo[key] def __call__(self, arg): return 42 def __eq__(self, other): return self is other def __hash__(self): return 42 def __iter__(self): return self def __next__(self): return 42 # Infinite sequence def __len__(self): return 42 def __contains__(self, x): return x == 42 class ProxyFactoryTests(unittest.TestCase): def setUp(self): from zope.security.proxy import ProxyFactory self.x = Something() self.c = Checker() self.p = ProxyFactory(self.x, self.c) def shouldFail(self, *args): self.c.ok = 0 self.assertRaises(RuntimeError, *args) self.c.ok = 1 def testDerivation(self): from zope.proxy import ProxyBase self.assertIsInstance(self.p, ProxyBase) def testStr(self): from zope.security.proxy import ProxyFactory self.assertEqual(str(self.p), str(self.x)) x = Something() c = Checker() c.ok = 0 p = ProxyFactory(x, c) s = str(p) self.assertTrue(s.startswith( ">y", "x&y", "x|y", "x^y", ] def test_binops(self): from zope.security.proxy import removeSecurityProxy P = self.c.proxy for expr in self.binops: first = 1 for x in [1, P(1)]: for y in [2, P(2)]: if first: z = eval(expr) first = 0 else: self.assertEqual( removeSecurityProxy(eval(expr)), z, f"x={x!r}; y={y!r}; expr={expr!r}") self.shouldFail(self._make_eval(expr, locals()), x, y) def test_inplace(self): # TODO: should test all inplace operators... from zope.security.proxy import removeSecurityProxy P = self.c.proxy pa = P(1) pa += 2 self.assertEqual(removeSecurityProxy(pa), 3) a = [1, 2, 3] pa = qa = P(a) pa += [4, 5, 6] self.assertIs(pa, qa) self.assertEqual(a, [1, 2, 3, 4, 5, 6]) def doit(): pa = P(1) pa += 2 self.shouldFail(doit) pa = P(2) pa **= 2 self.assertEqual(removeSecurityProxy(pa), 4) def doit2(): pa = P(2) pa **= 2 self.shouldFail(doit2) def test_iterate_interface(self): # This used to work on Python 2, but fail on Python 3. # See https://github.com/zopefoundation/zope.interface/issues/141 from zope.interface import Interface from zope.security.proxy import ProxyFactory class IFoo(Interface): def x(): """A method""" proxy = ProxyFactory(IFoo) self.assertEqual(list(IFoo), ['x']) self.assertEqual(list(proxy), list(IFoo)) def test_method_wrapper(self): from zope.security.proxy import ProxyFactory self.assertEqual(ProxyFactory({}).__repr__(), '{}') def test_builtin_method(self): from zope.security.proxy import ProxyFactory self.assertEqual(ProxyFactory(io.FileIO(os.devnull, 'rb').read)(), b'') def test_using_mapping_slots_hack(): """The security proxy will use mapping slots, on the checker to go faster If a checker implements normally, a checkers's check and check_getattr methods are used to check operator and attribute access: >>> from zope.security.proxy import ProxyFactory >>> log = [] >>> def dump(): ... out = '\\n'.join(log) ... del log[:] ... return out >>> class Checker(object): ... def check(self, object, name): ... log.append(('check %s' % name)) ... def check_getattr(self, object, name): ... log.append(('check_getattr %s' % name)) ... def proxy(self, object): ... return 1 >>> def f(): ... pass >>> p = ProxyFactory(f, Checker()) >>> p.__name__ 1 >>> dump() 'check_getattr __name__' >>> p() 1 >>> dump() 'check __call__' But, if the checker has a __setitem__ method: >>> def __setitem__(self, object, name): ... log.append(('__setitem__ %s' % name)) >>> Checker.__setitem__ = __setitem__ It will be used rather than either check or check_getattr: >>> p.__name__ 1 >>> dump() '__setitem__ __name__' >>> p() 1 >>> dump() '__setitem__ __call__' If a checker has a __getitem__ method: >>> def __getitem__(self, object): ... return 2 >>> Checker.__getitem__ = __getitem__ It will be used rather than it's proxy method: >>> p.__name__ 2 >>> dump() '__setitem__ __name__' >>> p() 2 >>> dump() '__setitem__ __call__' """ class LocationProxySecurityCheckerTests(unittest.TestCase): def test_LocationProxy_gets_a_security_checker_when_importing_z_security( self): # Regression test for a problem introduced in 3.8.1 and fixed in # 3.8.3. For details see change log. import sys from importlib import reload as _reload from zope.location.location import LocationProxy import zope.security # This attribute is set when zope.security.decorator is imported, to # show that it will be set too, if zope.security.proxy is imported # we set it to a different value at first: del LocationProxy.__Security_checker__ self.assertFalse( hasattr(LocationProxy, '__Security_checker__')) # After deleting zope.security.decorator and reloading # zope.security.proxy the attribute is set again: del sys.modules["zope.security.decorator"] _reload(zope.security) self.assertTrue( hasattr(LocationProxy, '__Security_checker__')) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_simpleinteraction.py0000644000076500000240000000533414672224636025111 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation 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. # ############################################################################## """Unit tests for zope.security.simpleinteraction. """ import unittest class RequestStub: def __init__(self, principal=None): self.principal = principal self.interaction = None class TestInteraction(unittest.TestCase): def test(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IInteraction from zope.security.simplepolicies import ParanoidSecurityPolicy interaction = ParanoidSecurityPolicy() verifyObject(IInteraction, interaction) def test_add(self): from zope.security.simplepolicies import ParanoidSecurityPolicy rq = RequestStub() interaction = ParanoidSecurityPolicy() interaction.add(rq) self.assertIn(rq, interaction.participations) self.assertIs(rq.interaction, interaction) # rq already added self.assertRaises(ValueError, interaction.add, rq) interaction2 = ParanoidSecurityPolicy() self.assertRaises(ValueError, interaction2.add, rq) def test_remove(self): from zope.security.simplepolicies import ParanoidSecurityPolicy rq = RequestStub() interaction = ParanoidSecurityPolicy() self.assertRaises(ValueError, interaction.remove, rq) interaction.add(rq) interaction.remove(rq) self.assertNotIn(rq, interaction.participations) self.assertIsNone(rq.interaction) def testCreateInteraction(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IInteraction from zope.security.simplepolicies import ParanoidSecurityPolicy i1 = ParanoidSecurityPolicy() verifyObject(IInteraction, i1) self.assertEqual(list(i1.participations), []) user = object() request = RequestStub(user) i2 = ParanoidSecurityPolicy(request) verifyObject(IInteraction, i2) self.assertEqual(list(i2.participations), [request]) def test_suite(): return unittest.TestSuite(( unittest.defaultTestLoader.loadTestsFromTestCase(TestInteraction), )) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_simplepolicies.py0000644000076500000240000001134514672224636024400 0ustar00jensstaff############################################################################## # # Copyright (c) 2013 Zope Foundation 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. # ############################################################################## import unittest class ConformsToIInteraction: def _getTargetClass(self): raise NotImplementedError("Subclass responsibility") def _makeOne(self, *participations): return self._getTargetClass()(*participations) def test_class_conforms_to_IInteraction(self): from zope.interface.verify import verifyClass from zope.security.interfaces import IInteraction verifyClass(IInteraction, self._getTargetClass()) def test_instance_conforms_to_IInteraction(self): from zope.interface.verify import verifyObject from zope.security.interfaces import IInteraction verifyObject(IInteraction, self._makeOne()) class ParanoidSecurityPolicyTests(unittest.TestCase, ConformsToIInteraction, ): def _getTargetClass(self): from zope.security.simplepolicies import ParanoidSecurityPolicy return ParanoidSecurityPolicy def test_ctor_no_participations(self): policy = self._makeOne() self.assertEqual(policy.participations, []) def test_ctor_w_participations(self): class Participation: interaction = None p1, p2, p3 = Participation(), Participation(), Participation() policy = self._makeOne(p1, p2, p3) self.assertEqual(policy.participations, [p1, p2, p3]) self.assertIs(p1.interaction, policy) self.assertIs(p2.interaction, policy) self.assertIs(p3.interaction, policy) def test_add_w_foreign_participation(self): class Participation: interaction = object() policy = self._makeOne() self.assertRaises(ValueError, policy.add, Participation()) def test_remove_w_foreign_participation(self): class Participation: interaction = object() policy = self._makeOne() self.assertRaises(ValueError, policy.remove, Participation()) def test_remove(self): class Participation: interaction = None p1, p2, p3 = Participation(), Participation(), Participation() policy = self._makeOne(p1, p2, p3) policy.remove(p2) self.assertEqual(policy.participations, [p1, p3]) self.assertIs(p1.interaction, policy) self.assertIsNone(p2.interaction) self.assertIs(p3.interaction, policy) def test_checkPermission_w_public(self): from zope.security.checker import CheckerPublic policy = self._makeOne() target = object() self.assertTrue(policy.checkPermission(CheckerPublic, target)) def test_checkPermission_w_non_public_only_system_user(self): from zope.security._definitions import system_user class Participation: interaction = None principal = system_user policy = self._makeOne(Participation()) permission = object() target = object() self.assertTrue(policy.checkPermission(permission, target)) def test_checkPermission_w_non_public_other_user(self): class Participation: interaction = None principal = object() policy = self._makeOne(Participation()) permission = object() target = object() self.assertFalse(policy.checkPermission(permission, target)) def test_checkPermission_w_no_participations(self): # The permission and object don't matter: if there are no # participations, access is allowed. policy = self._makeOne() self.assertTrue(policy.checkPermission(None, None)) self.assertTrue(policy.checkPermission(self, self)) class PermissiveSecurityPolicyTests(unittest.TestCase, ConformsToIInteraction): def _getTargetClass(self): from zope.security.simplepolicies import PermissiveSecurityPolicy return PermissiveSecurityPolicy def test_checkPermission_w_public(self): policy = self._makeOne() permission = object() target = object() self.assertTrue(policy.checkPermission(permission, target)) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/tests/test_testing.py0000644000076500000240000000531014355021564023020 0ustar00jensstaff############################################################################# # # Copyright (c) 2011 Zope Foundation 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. # ############################################################################## import unittest from zope.testing.cleanup import CleanUp from zope.security import testing from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public class TestTestingFunctions(CleanUp, unittest.TestCase): def test_create_interaction_should_return_principal(self): from zope.security.management import getInteraction principal = testing.create_interaction( 'foo', groups=['bar'], description='desc') ix = getInteraction() participation = ix.participations[0] self.assertEqual('foo', participation.principal.id) self.assertEqual(principal.groups, participation.principal.groups) self.assertEqual('desc', participation.principal.description) def test_usable_as_contextmanager(self): from zope.security.management import getInteraction from zope.security.management import queryInteraction with testing.interaction('foo'): ix = getInteraction() participation = ix.participations[0] self.assertEqual('foo', participation.principal.id) # Nesting doesn't change anything with testing.interaction('baz'): ix = getInteraction() participation = ix.participations[0] self.assertEqual('foo', participation.principal.id) self.assertFalse(queryInteraction()) def test_contextmanager_ends_interaction_on_exception(self): from zope.security.management import queryInteraction class MyError(Exception): pass with self.assertRaises(MyError): with testing.interaction('foo'): raise MyError() self.assertFalse(queryInteraction()) def test_addCheckerPublic(self): from zope import component from zope.security.interfaces import IPermission perm = testing.addCheckerPublic() utility = component.getUtility(IPermission, name=zope_Public) self.assertIs(perm, utility) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556574.0 zope_security-7.3/src/zope/security/tests/test_zcml.py0000644000076500000240000001747014672224636022331 0ustar00jensstaff############################################################################## # # Copyright (c) 2013 Zope Foundation 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. # ############################################################################## import unittest from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public class ConformsToIFromUnicode: def test_class_conforms_to_IFromUnicode(self): from zope.interface.verify import verifyClass from zope.schema.interfaces import IFromUnicode verifyClass(IFromUnicode, self._getTargetClass()) def test_instance_conforms_to_IFromUnicode(self): from zope.interface.verify import verifyObject from zope.schema.interfaces import IFromUnicode verifyObject(IFromUnicode, self._makeOne()) class PermissionTests(unittest.TestCase, ConformsToIFromUnicode, ): def _getTargetClass(self): from zope.security.zcml import Permission return Permission def _makeOne(self, context=None): if context is None: context = DummyZCMLContext() permission = self._getTargetClass()() permission.context = context return permission def test_fromUnicode_miss(self): permission = self._makeOne() self.assertEqual(permission.fromUnicode('nonesuch.permission'), 'nonesuch.permission') def test_fromUnicode_hit(self): permission = self._makeOne() p_obj = object() permission.context.permission_mapping = {'extant.permission': p_obj} self.assertIs(permission.fromUnicode('extant.permission'), p_obj) def test__validate_w_public(self): context = DummyZCMLContext() permission = self._makeOne(context) permission._validate(zope_Public) self.assertEqual(len(context._actions), 0) def test__validate_w_non_public(self): from zope.security.permission import checkPermission context = DummyZCMLContext() permission = self._makeOne(context) permission._validate('a.permission') self.assertEqual(len(context._actions), 1) self.assertEqual(context._actions[0]['discriminator'], None) self.assertEqual(context._actions[0]['callable'], checkPermission) self.assertEqual(context._actions[0]['args'], (None, 'a.permission')) class Test_securityPolicy(unittest.TestCase): def _callFUT(self, _context, component): from zope.security.zcml import securityPolicy return securityPolicy(_context, component) def test_it(self): from zope.security.management import setSecurityPolicy context = DummyZCMLContext() component = object() self._callFUT(context, component) self.assertEqual(len(context._actions), 1) self.assertEqual(context._actions[0]['discriminator'], 'defaultPolicy') self.assertEqual(context._actions[0]['callable'], setSecurityPolicy) self.assertEqual(context._actions[0]['args'], (component,)) class Test_permission(unittest.TestCase): def _callFUT(self, _context, id, title, description=None): from zope.security.zcml import permission if description is None: return permission(_context, id, title) return permission(_context, id, title, description) def test_wo_description(self): from zope.component.interface import provideInterface from zope.component.zcml import handler from zope.security.interfaces import IPermission context = DummyZCMLContext() context.info = 'INFO' self._callFUT(context, 'a.permission', 'TITLE') self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('utility', IPermission, 'a.permission')) self.assertEqual(context._actions[0]['callable'], handler) args = context._actions[0]['args'] self.assertEqual(args[0], 'registerUtility') permission = args[1] self.assertEqual(permission.id, 'a.permission') self.assertEqual(permission.title, 'TITLE') self.assertEqual(permission.description, '') self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('', IPermission)) def test_w_description(self): from zope.component.interface import provideInterface from zope.component.zcml import handler from zope.security.interfaces import IPermission context = DummyZCMLContext() context.info = 'INFO' self._callFUT(context, 'a.permission', 'TITLE', 'DESCRIPTION') self.assertEqual(len(context._actions), 2) self.assertEqual(context._actions[0]['discriminator'], ('utility', IPermission, 'a.permission')) self.assertEqual(context._actions[0]['callable'], handler) args = context._actions[0]['args'] self.assertEqual(args[0], 'registerUtility') permission = args[1] self.assertEqual(permission.id, 'a.permission') self.assertEqual(permission.title, 'TITLE') self.assertEqual(permission.description, 'DESCRIPTION') self.assertIsNone(context._actions[1]['discriminator']) self.assertIs(context._actions[1]['callable'], provideInterface) self.assertEqual(context._actions[1]['args'], ('', IPermission)) class Test_redefinePermission(unittest.TestCase): def _callFUT(self, _context, from_, to): from zope.security.zcml import redefinePermission return redefinePermission(_context, from_, to) def test_wo_existing_mapping(self): z_context = DummyZCMLContext() class Context: pass context = z_context.context = Context() after = object() self._callFUT(z_context, 'before.permission', after) self.assertIs(context.permission_mapping['before.permission'], after) def test_w_existing_mapping_wo_existing_key(self): z_context = DummyZCMLContext() class Context: pass context = z_context.context = Context() mapping = context.permission_mapping = {} after = object() self._callFUT(z_context, 'before.permission', after) self.assertIs(context.permission_mapping, mapping) self.assertIs(context.permission_mapping['before.permission'], after) def test_w_existing_mapping_w_existing_key(self): z_context = DummyZCMLContext() class Context: pass context = z_context.context = Context() mapping = context.permission_mapping = {} mapping['before.permission'] = object() after = object() self._callFUT(z_context, 'before.permission', after) self.assertIs(context.permission_mapping, mapping) self.assertIs(context.permission_mapping['before.permission'], after) class DummyZCMLContext: def __init__(self): self._actions = [] def action(self, **kw): self._actions.append(kw) def test_suite(): return unittest.TestSuite(( unittest.defaultTestLoader.loadTestsFromTestCase(PermissionTests), unittest.defaultTestLoader.loadTestsFromTestCase(Test_securityPolicy), unittest.defaultTestLoader.loadTestsFromTestCase(Test_permission), unittest.defaultTestLoader.loadTestsFromTestCase( Test_redefinePermission), )) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1724678151.0 zope_security-7.3/src/zope/security/tests/test_zcml_functest.py0000644000076500000240000005236414663100007024226 0ustar00jensstaff############################################################################## # # Copyright (c) 2001, 2002, 2003, 2012 Zope Foundation 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. # ############################################################################## """Directives Tests """ import io import unittest def configfile(s): return io.StringIO(""" %s """ % s) class TestClassDirective(unittest.TestCase): def setUp(self): from zope.security.tests.exampleclass import ExampleClass try: del ExampleClass.__implements__ except AttributeError: pass from zope.component.testing import setUp setUp() def tearDown(self): from zope.security.tests.exampleclass import ExampleClass try: del ExampleClass.__implements__ except AttributeError: pass from zope.component.testing import tearDown tearDown() def _meta(self): from zope.configuration.xmlconfig import XMLConfig import zope.security XMLConfig('meta.zcml', zope.security)() def testEmptyDirective(self): from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) def testImplements(self): from zope.component.interface import queryInterface from zope.configuration.xmlconfig import xmlconfig from zope.security.tests.exampleclass import ExampleClass from zope.security.tests.exampleclass import IExample self._meta() self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), None) f = configfile(""" """) xmlconfig(f) self.assertTrue(IExample.implementedBy(ExampleClass)) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), IExample) def testMulImplements(self): from zope.component.interface import queryInterface from zope.configuration.xmlconfig import xmlconfig from zope.security.tests.exampleclass import ExampleClass from zope.security.tests.exampleclass import IExample from zope.security.tests.exampleclass import IExample2 self._meta() self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), None) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample2"), None) f = configfile(""" """) xmlconfig(f) self.assertTrue(IExample.implementedBy(ExampleClass)) self.assertTrue(IExample2.implementedBy(ExampleClass)) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample"), IExample) self.assertEqual(queryInterface( "zope.security.tests.exampleclass.IExample2"), IExample2) def testRequire(self): from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) def testAllow(self): from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) def testMimic(self): from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) class TestFactorySubdirective(unittest.TestCase): def setUp(self): from zope.component.testing import setUp setUp() def tearDown(self): from zope.component.testing import tearDown tearDown() def _meta(self): from zope.configuration.xmlconfig import XMLConfig import zope.security XMLConfig('meta.zcml', zope.security)() def testFactory(self): from zope.component import getUtility from zope.component.interfaces import IFactory from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) factory = getUtility(IFactory, 'test.Example') self.assertEqual(factory.title, "Example content") self.assertEqual(factory.description, "Example description") def testFactoryNoId(self): from zope.component import getUtility from zope.component.interfaces import IFactory from zope.configuration.xmlconfig import xmlconfig from zope.interface.interfaces import ComponentLookupError self._meta() f = configfile(""" """) xmlconfig(f) self.assertRaises(ComponentLookupError, getUtility, IFactory, 'Example') factory = getUtility( IFactory, 'zope.security.tests.exampleclass.ExampleClass') self.assertEqual(factory.title, "Example content") self.assertEqual(factory.description, "Example description") def testFactoryPublicPermission(self): from zope.component import getUtility from zope.component.interfaces import IFactory from zope.configuration.xmlconfig import xmlconfig self._meta() f = configfile(""" """) xmlconfig(f) factory = getUtility(IFactory, 'test.Example') self.assertTrue(hasattr(factory, '__Security_checker__')) template = """ %s """ class TestFactoryDirective(unittest.TestCase): def setUp(self): from zope.component.testing import setUp setUp() def tearDown(self): from zope.component.testing import tearDown tearDown() def meta(self): from zope.configuration.xmlconfig import XMLConfig import zope.security XMLConfig('meta.zcml', zope.security)() def testFactory(self): from zope.component import createObject from zope.configuration.xmlconfig import xmlconfig from zope.security import proxy from zope.security.tests import exampleclass self.meta() f = configfile(''' ''') xmlconfig(f) obj = createObject('test.Example') self.assertTrue(proxy.isinstance(obj, exampleclass.ExampleClass)) def _pfx(name): from zope.security.tests import module return module.__name__ + '.' + name def defineDirectives(): from zope.configuration.xmlconfig import XMLConfig from zope.configuration.xmlconfig import xmlconfig import zope.security XMLConfig('meta.zcml', zope.security)() xmlconfig(io.StringIO(""" """)) NOTSET = () P1 = "zope.Extravagant" P2 = "zope.Paltry" class TestRequireDirective(unittest.TestCase): def setUp(self): from zope.component.testing import setUp from zope.interface import implementer from zope.security.tests import module setUp() defineDirectives() class B: def m1(self): raise AssertionError("Never called") def m2(self): raise AssertionError("Never called") @implementer(module.I) class C(B): def m3(self): raise AssertionError("Never called") def m4(self): raise AssertionError("Never called") module.test_base = B module.test_class = C module.test_instance = C() self.assertState() def tearDown(self): from zope.security.tests import module module.test_class = None from zope.component.testing import tearDown tearDown() def assertState(self, m1P=NOTSET, m2P=NOTSET, m3P=NOTSET): # Verify that class, instance, and methods have expected permissions from zope.security.checker import selectChecker from zope.security.tests import module checker = selectChecker(module.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 assertDeclaration(self, declaration, **state): from zope.security.tests import module apply_declaration(module.template_bracket % declaration) self.assertState(**state) # "testSimple*" exercises tags that do NOT have children. This mode # inherently sets the instances as well as the class attributes. def test_wo_any_attributes(self): from zope.configuration.exceptions import ConfigurationError from zope.security.tests import module declaration = (''' ''' % (_pfx("test_class"), P1)) self.assertRaises(ConfigurationError, apply_declaration, module.template_bracket % declaration) # "testSimple*" exercises tags that do NOT have children. This mode # inherently sets the instances as well as the class attributes. def testSimpleMethodsPlural(self): declaration = (''' ''' % (_pfx("test_class"), P1)) self.assertDeclaration(declaration, m1P=P1, m3P=P1) def test_set_attributes(self): from zope.security.checker import selectChecker from zope.security.tests import module declaration = (''' ''' % (_pfx("test_class"), P1)) apply_declaration(module.template_bracket % declaration) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), P1) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), P1) def test_set_schema(self): from zope.component.interface import queryInterface from zope.security.checker import selectChecker from zope.security.tests import module self.assertEqual(queryInterface(_pfx("S")), None) declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("S"))) apply_declaration(module.template_bracket % declaration) self.assertEqual(queryInterface(_pfx("S")), module.S) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), None) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), None) self.assertEqual(checker.setattr_permission_id('foo'), P1) self.assertEqual(checker.setattr_permission_id('bar'), P1) self.assertEqual(checker.setattr_permission_id('baro'), None) def test_multiple_set_schema(self): from zope.component.interface import queryInterface from zope.security.checker import selectChecker from zope.security.tests import module self.assertEqual(queryInterface(_pfx("S")), None) self.assertEqual(queryInterface(_pfx("S2")), None) declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("S"), _pfx("S2"))) apply_declaration(module.template_bracket % declaration) self.assertEqual(queryInterface(_pfx("S")), module.S) self.assertEqual(queryInterface(_pfx("S2")), module.S2) checker = selectChecker(module.test_instance) self.assertEqual(checker.setattr_permission_id('m1'), None) self.assertEqual(checker.setattr_permission_id('m2'), None) self.assertEqual(checker.setattr_permission_id('m3'), None) self.assertEqual(checker.setattr_permission_id('foo'), P1) self.assertEqual(checker.setattr_permission_id('bar'), P1) self.assertEqual(checker.setattr_permission_id('foo2'), P1) self.assertEqual(checker.setattr_permission_id('bar2'), P1) self.assertEqual(checker.setattr_permission_id('baro'), None) def testSimpleInterface(self): from zope.component.interface import queryInterface from zope.security.tests import module self.assertEqual(queryInterface(_pfx("I")), None) declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("I"))) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) # Make sure we know about the interfaces self.assertEqual(queryInterface(_pfx("I")), module.I) def testMultipleInterface(self): from zope.component.interface import queryInterface from zope.security.tests import module self.assertEqual(queryInterface(_pfx("I3")), None) self.assertEqual(queryInterface(_pfx("I4")), None) declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("I3"), _pfx("I4"))) self.assertDeclaration(declaration, m3P=P1, m2P=P1) # Make sure we know about the interfaces self.assertEqual(queryInterface(_pfx("I3")), module.I3) self.assertEqual(queryInterface(_pfx("I4")), module.I4) # "testComposite*" exercises tags that DO have children. # "testComposite*TopPerm" exercises tags with permission in containing tag. # "testComposite*ElementPerm" exercises tags w/permission in children. def testCompositeNoPerm(self): # Establish rejection of declarations lacking a permission spec. from zope.configuration.exceptions import ConfigurationError declaration = (''' ''' % (_pfx("test_class"))) with self.assertRaises(ConfigurationError): self.assertDeclaration(declaration) def testCompositeMethodsPluralElementPerm(self): declaration = (''' ''' % (_pfx("test_class"), P1)) self.assertDeclaration(declaration, m1P=P1, m3P=P1) def testCompositeInterfaceTopPerm(self): declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("I"))) self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testSubInterfaces(self): declaration = (''' ''' % (_pfx("test_class"), P1, _pfx("I2"))) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testMimicOnly(self): declaration = (''' '''.format(_pfx("test_base"), P1, _pfx("test_class"), _pfx("test_base"))) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P1) def testMimicAsDefault(self): declaration = ( ''' '''.format( _pfx("test_base"), P1, _pfx("test_class"), _pfx("test_base"), P2)) # m1 and m2 are in the interface, so should be set, and m3 should not: self.assertDeclaration(declaration, m1P=P1, m2P=P2, m3P=P2) def apply_declaration(declaration): '''Apply the xmlconfig machinery.''' from zope.configuration.xmlconfig import xmlconfig return xmlconfig(io.StringIO(declaration)) def make_dummy(): from zope.interface import Interface import zope.security.zcml global IDummy class IDummy(Interface): perm = zope.security.zcml.Permission(title='') perms = [] def dummy(context_, perm): global perms perms.append(perm) class DirectivesTest(unittest.TestCase): def setUp(self): from zope.component.testing import setUp setUp() def tearDown(self): del perms[:] from zope.component.testing import tearDown tearDown() def testRedefinePermission(self): from zope.configuration import xmlconfig from zope.security import tests make_dummy() xmlconfig.file("redefineperms.zcml", tests) self.assertEqual(perms, ['zope.Security']) def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672749940.0 zope_security-7.3/src/zope/security/zcml.py0000644000076500000240000000761514355021564020121 0ustar00jensstaff############################################################################## # # Copyright (c) 2004 Zope Foundation 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. # ############################################################################## """Security related configuration fields. """ __docformat__ = 'restructuredtext' from zope.configuration.fields import GlobalObject from zope.configuration.fields import MessageID from zope.interface import Interface from zope.schema import Id from zope.schema.interfaces import IFromUnicode from zope.security._compat import implementer_if_needed from zope.security.interfaces import PUBLIC_PERMISSION_NAME as zope_Public from zope.security.management import setSecurityPolicy from zope.security.permission import checkPermission @implementer_if_needed(IFromUnicode) class Permission(Id): r"""This field describes a permission. """ def fromUnicode(self, value): u = super().fromUnicode(value) map = getattr(self.context, 'permission_mapping', {}) return map.get(u, u) def _validate(self, value): super()._validate(value) if value != zope_Public: self.context.action( discriminator=None, callable=checkPermission, args=(None, value), # Delay execution till end. This is an # optimization. We don't want to intersperse utility # lookup, done when checking permissions, with utility # definitions. Utility lookup is expensive after # utility definition, as extensive caches have to be # rebuilt. order=9999999, ) class ISecurityPolicyDirective(Interface): """Defines the security policy that will be used for Zope.""" component = GlobalObject( title="Component", description="Pointer to the object that will handle the security.", required=True) def securityPolicy(_context, component): _context.action( discriminator='defaultPolicy', callable=setSecurityPolicy, args=(component,) ) class IPermissionDirective(Interface): """Define a new security object.""" id = Id( title="ID", description="ID as which this object will be known and used.", required=True) title = MessageID( title="Title", description="Provides a title for the object.", required=True) description = MessageID( title="Description", description="Provides a description for the object.", required=False) def permission(_context, id, title, description=''): from zope.component.zcml import utility from zope.security.interfaces import IPermission from zope.security.permission import Permission permission = Permission(id, title, description) utility(_context, IPermission, permission, name=id) class IRedefinePermission(Interface): """Define a permission to replace another permission.""" from_ = Permission( title="Original permission", description="Original permission ID to redefine.", required=True) to = Permission( title="Substituted permission", description="Substituted permission ID.", required=True) def redefinePermission(_context, from_, to): _context = _context.context # check if context has any permission mappings yet if not hasattr(_context, 'permission_mapping'): _context.permission_mapping = {} _context.permission_mapping[from_] = to ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1727878801.1150842 zope_security-7.3/src/zope.security.egg-info/0000755000076500000240000000000014677253221020266 5ustar00jensstaff././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/PKG-INFO0000644000076500000240000006020114677253221021362 0ustar00jensstaffMetadata-Version: 2.1 Name: zope.security Version: 7.3 Summary: Zope Security Framework Home-page: http://github.com/zopefoundation/zope.security Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Project-URL: Documentation, https://zopesecurity.readthedocs.io Project-URL: Issue Tracker, https://github.com/zopefoundation/zope.security/issues Project-URL: Sources, https://github.com/zopefoundation/zope.security Keywords: zope security policy principal permission Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope :: 3 Requires-Python: >=3.8 License-File: LICENSE.txt Requires-Dist: setuptools Requires-Dist: zope.component Requires-Dist: zope.i18nmessageid Requires-Dist: zope.interface Requires-Dist: zope.location Requires-Dist: zope.proxy>=5.2 Requires-Dist: zope.schema>=4.2.0 Provides-Extra: pytz Requires-Dist: pytz; extra == "pytz" Provides-Extra: untrustedpython Requires-Dist: zope.untrustedpython>=5.0.dev0; extra == "untrustedpython" Provides-Extra: zcml Requires-Dist: zope.configuration; extra == "zcml" Provides-Extra: test Requires-Dist: BTrees; extra == "test" Requires-Dist: zope.component; extra == "test" Requires-Dist: zope.configuration; extra == "test" Requires-Dist: zope.location; extra == "test" Requires-Dist: zope.testing; extra == "test" Requires-Dist: zope.testrunner; extra == "test" Provides-Extra: docs Requires-Dist: Sphinx; extra == "docs" Requires-Dist: repoze.sphinx.autointerface; extra == "docs" Requires-Dist: sphinx_rtd_theme; extra == "docs" Requires-Dist: zope.configuration; extra == "docs" Requires-Dist: zope.testing; extra == "docs" =================== ``zope.security`` =================== .. image:: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml/badge.svg :target: https://github.com/zopefoundation/zope.security/actions/workflows/tests.yml .. image:: https://coveralls.io/repos/github/zopefoundation/zope.security/badge.svg?branch=master :target: https://coveralls.io/github/zopefoundation/zope.security?branch=master .. image:: https://readthedocs.org/projects/zopesecurity/badge/?version=latest :target: https://zopesecurity.readthedocs.io/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/zope.security.svg :target: https://pypi.python.org/pypi/zope.security/ :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/zope.security.svg :target: https://pypi.org/project/zope.security/ :alt: Supported Python versions The Security framework provides a generic mechanism to implement security policies on Python objects. Documentation is available at https://zopesecurity.readthedocs.io/ ========= Changes ========= 7.3 (2024-10-02) ---------------- - Respect ``PURE_PYTHON`` environment variable set to ``0``. 7.2 (2024-09-17) ---------------- - Declare final support for Python 3.13. 7.1 (2024-08-16) ---------------- - Allow calling methods of type ```` by default. In particular, Python 3.12 refactored the ``io`` module in such a way as to slightly change the types of some methods, causing ``zope.security`` to no longer consider them callable. See `zope.file#13 `_. 7.0 (2024-05-30) ---------------- - Add preliminary support for Python 3.13 as of 3.13b1. - Drop support for Python 3.7. - Build windows wheels on GHA. 6.2 (2023-10-05) ---------------- - Make ``next()`` on C proxies call ``__next__`` rather than ``next`` (see PEP 3114), and drop support for the Python 2 ``next`` method name from pure-Python proxies. - Drop using ``setup_requires`` due to constant problems on GHA. - Add support for Python 3.12. 6.1 (2023-01-18) ================ - Remove more proxying code for names that no longer exist in Python 3. (`#92 `_) 6.0 (2023-01-16) ================ - Remove proxying code for names that no longer exist in Python 3. (`#92 `_) - Drop support for Python 2.7, 3.5, 3.6. 5.8 (2022-11-30) ================ - The extra ``untrustedpython`` now for Python 3, too, installs ``zope.untrustedpython``. 5.7 (2022-11-17) ================ - Release to rebuild full set of binary wheels. 5.6 (2022-11-16) ================ - Add support for building arm64 wheels on macOS. 5.5 (2022-11-06) ================ - Add support for final release of Python 3.11. 5.4 (2022-09-15) ================ - Disable unsafe math optimizations in C code. See `pull request 89 `_. 5.3 (2022-04-27) ================ - Allow calling bound methods of some built-in objects such as ``().__repr__`` and ``{}.__repr__`` by default. This worked on Python 2, but raised ``ForbiddenAttribute`` on Python 3. See `issue 75 `_. - Remove usage of ``unittest.makeSuite`` as it is deprecated in Python 3.11+. See `issue 83 `_. - Add support for Python 3.11 (as of 3.11.0a7). 5.2 (2022-03-10) ================ - Add support for Python 3.9 and 3.10. 5.1.1 (2020-03-23) ================== - Ensure all objects have consistent interface resolution orders (if all dependencies are up-to-date). See `issue 71 `_. 5.1.0 (2020-02-14) ================== - Let proxied interfaces be iterated on Python 3. This worked on Python 2, but raised ``ForbiddenAttribute`` an Python 3. See `zope.interface issue 141 `_. - Allow to use a common Sphinx version for Python 2 and 3. 5.0.0 (2019-11-11) ================== - Drop support for Python 3.4. - Add support for Python 3.8. - Properly declare dependency on zope.schema >= 4.2.0, introduced in zope.security 4.2.1. - Fix dict item view iteration on PyPy3 7.x. 4.3.1 (2019-01-03) ================== - Fix the decimal.Decimal checker, ``__truediv__`` was missing causing ``ForbiddenAttribute`` on a ``ProxyFactory(Decimal('1')) / 1`` operation 4.3.0 (2018-08-24) ================== - Add the interface ``ISystemPrincipal`` and make ``zope.security.management.system_user`` a regular object that implements this interface. This facilitates providing adapter registrations specifically for the ``system_user``. 4.2.3 (2018-08-09) ================== - Add support for Python 3.7. 4.2.2 (2018-01-11) ================== - Make the pure-Python proxy on Python 2 *not* check permissions for ``__unicode__`` just like the C implementation. Note that ``__str__`` is checked for both implementations on both Python 2 and 3, but if there is no ``__unicode__`` method defined, Python 2's automatic fallback to ``__str__`` is **not** checked when ``unicode`` is called. See `issue 10 `_. 4.2.1 (2017-11-30) ================== - Fix the default values for ``Permission`` fields ``title`` and ``description`` under Python 2. See `issue 48 `_. - Change the ``IPermission.id`` from ``Text`` (unicode) to a ``NativeStringLine``. This matches what ZCML creates and what is usually written in source code. 4.2.0 (2017-09-20) ================== - Fix the extremely rare potential for a crash when the C extensions are in use. See `issue 35 `_. - Fix `issue 7 `_: The pure-Python proxy didn't propagate ``TypeError`` from ``__repr__`` and ``__str__`` like the C implementation did. - Fix `issue 27 `_: iteration of ``zope.interface.providedBy()`` is now allowed by default on all versions of Python. Previously it only worked on Python 2. Note that ``providedBy`` returns unproxied objects for backwards compatibility. - Fix ``__length_hint__`` of proxied iterator objects. Previously it was ignored. - Drop support for Python 3.3. - Enable coveralls.io for coverage measurement and run doctests on all supported Python versions. - Fix `issue 9 `_: iteration of ``itertools.groupby`` objects is now allowed by default. In addition, iteration of all the custom iterator types defined in itertools are also allowed by default. - Simplify the internal ``_compat.py`` module now that we only run on newer Python versions. See `PR 32 `_. - Respect ``PURE_PYTHON`` at runtime. At build time, always try to build the C extensions on supported platforms, ignoring ``PURE_PYTHON``. See `issue 33 `_. - Fix watching checkers (``ZOPE_WATCH_CHECKERS=1``) in pure-Python mode. See `issue 8 `_. - Remove unused internal files from ``tests/``. - Remove ``zope.security.setup``. It was unused and did not work anyway. - Fix the pure-Python proxy on Python 2 letting ``__getslice__`` and ``__setslice__`` fall through to ``__getitem__`` or ``__setitem__``, respectively, if it raised an error. - Fix the pure-Python proxy calling a wrapped ``__getattr__`` or ``__getattribute__`` more than once in situations where the C implementation only called it one time (when it raised an AttributeError). - Reach 100% test coverage and maintain it via automated checks. 4.1.1 (2017-05-17) ================== - Fix `issue 23 `_: iteration of ``collections.OrderedDict`` and its various views is now allowed by default on all versions of Python. - As a further fix for issue 20, iteration of ``BTree`` itself is now allowed by default. 4.1.0 (2017-04-24) ================== - When testing ``PURE_PYTHON`` environments under ``tox``, avoid poisoning the user's global wheel cache. - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix `issue 20 `_: iteration of pure-Python ``BTrees.items()``, and also creating a list from ``BTrees.items()`` on Python 3. The same applies for ``keys()`` and ``values()``. 4.0.3 (2015-06-02) ================== - Fix iteration over security proxies in Python 3 using the pure-Python implementation. 4.0.2 (2015-06-02) ================== - Fix compatibility with ``zope.proxy`` 4.1.5 under PyPy. - Fix the very first call to ``removeSecurityProxy`` returning incorrect results if given a proxy under PyPy. 4.0.1 (2014-03-19) ================== - Add support for Python 3.4. 4.0.0 (2013-07-09) ================== - Update ``boostrap.py`` to version 2.2. - Bugfix: ZOPE_WATCH_CHECKERS=2 used to incorrectly suppress unauthorized/forbidden warnings. - Bugfix: ZOPE_WATCH_CHECKERS=1 used to miss most of the checks. 4.0.0b1 (2013-03-11) ==================== - Add support for PyPy. - Fix extension compilation on windows python 3.x 4.0.0a5 (2013-02-28) ==================== - Undo changes from 4.0.0a4. Instead, ``zope.untrustedpython`` is only included during Python 2 installs. 4.0.0a4 (2013-02-28) ==================== - Remove ``untrustedpython`` extra again, since we do not want to support ``zope.untrustedpython`` in ZTK 2.0. If BBB is really needed, we will create a 3.10.0 release. 4.0.0a3 (2013-02-15) ==================== - Fix test breakage in 4.0.0a2 due to deprecation strategy. 4.0.0a2 (2013-02-15) ==================== - Add back the ``untrustedpython`` extra: now pulls in ``zope.untrustedpython``. Restored deprecated backward-compatible imports for ``zope.security.untrustedpython.{builtins,interpreter,rcompile}`` (the extra and the imports are to be removed in version 4.1). 4.0.0a1 (2013-02-14) ==================== - Add support for Python 3.2 and 3.3. - Bring unit test coverage to 100%. - ``zope.security.untrustedpython`` moved to separate project: ``zope.untrustedpython`` - Convert use of ``assert`` in non-test code to apprpriate error types: - Non-dict's passed to ``Checker.__init__``. - Remove dprecattion of ``zope.security.adapter.TrustedAdapterFactory``. Although it has been marked as deprectaed since before Zope3 3.2, current versions of ``zope.compoent`` still rely on it. - Convert doctests to Sphinx documentation in 'docs'. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Make non-doctest tests fully independent of ``zope.testing``. Two modules, ``zope.security.checker`` and ``zope.security.management``, register cleanups with ``zope.testing`` IFF it is importable, but the tests no longer rely on it. - Enable building extensions without the ``svn:external`` of the ``zope.proxy`` headers into our ``include`` dir. - Bump ``zope.proxy`` dependency to ">= 4.1.0" to enable compilation on Py3k. - Replace deprecated ``zope.component.adapts`` usage with equivalent ``zope.component.adapter`` decorator. - Replace deprecated ``zope.interface.classProvides`` usage with equivalent ``zope.interface.provider`` decorator. - Replace deprecated ``zope.interface.implements`` usage with equivalent ``zope.interface.implementer`` decorator. - Drop support for Python 2.4 and 2.5. - Add test convenience helper ``create_interaction`` and ``with interaction()``. 3.9.0 (2012-12-21) ================== - Pin ``zope.proxy >= 4.1.0`` - Ship with an included ``proxy.h`` header which is compatible with the 4.1.x version ov ``zope.proxy``. 3.8.5 (2012-12-21) ================== - Ship with an included ``proxy.h`` header which is compatible with the supported versions of ``zope.proxy``. 3.8.4 (2012-12-20) ================== - Pin ``zope.proxy >= 3.4.2, <4.1dev`` 3.8.3 (2011-09-24) ================== - Fix a regression introduced in 3.8.1: ``zope.location``\'s LocationProxy did not get a security checker if ``zope.security.decorator`` was not imported manually. Now ``zope.security.decorator`` is imported in ``zope.security.proxy`` without re-introducing the circular import fixed in 3.8.1. 3.8.2 (2011-05-24) ================== - Fix a test that failed on Python 2.7. 3.8.1 (2011-05-03) ================== - Fix circular import beween ``zope.security.decorator`` and ``zope.security.proxy`` which led to an ``ImportError`` when only importing ``zope.security.decorator``. 3.8.0 (2010-12-14) ================== - Add tests for our own ``configure.zcml``. - Add ``zcml`` extra dependencies; run related tests only if ``zope.configuration`` is available. - Run tests related to the ``untrustedpython`` functionality only if ``RestrictedPython`` is available. 3.7.3 (2010-04-30) ================== - Prefer the standard library's ``doctest`` module to the one from ``zope.testing``. - Ensure ``PermissionIdsVocabulary`` directly provides ``IVocabularyFactory``, even though it might be unnecessary because ``IVocabularyFactory`` is provided in ZCML. - Remove the dependency on the zope.exceptions package: zope.security.checker now imports ``DuplicationError`` from zope.exceptions if available, otherwise it defines a package-specific ``DuplicationError`` class which inherits from Exception. 3.7.2 (2009-11-10) ================== - Add compatibility with Python 2.6 abstract base classes. 3.7.1 (2009-08-13) ================== - Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The Py_VISIT macro does the necessary checks, so it is used instead of the previous code. 3.7.0 (2009-05-13) ================== - Make ``pytz`` a soft dependency: the checker for ``pytz.UTC`` is created / tested only if the package is already present. Run ``bin/test_pytz`` to run the tests with ``pytz`` on the path. 3.6.3 (2009-03-23) ================== - Ensure that simple zope.schema's ``VocabularyRegistry`` is used for ``PermissionVocabulary`` tests, because it's replaced implicitly in environments with ``zope.app.schema`` installed that makes that tests fail. - Fix a bug in ``DecoratedSecurityCheckerDescriptor`` which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.6.2 (2009-03-14) ================== - Add ``zope.i18nmessageid.Message`` to non-proxied basic types. It's okay, because messages are immutable. Done previously by ``zope.app.security``. - Add ``__name__`` and ``__parent__`` attributes to list of available by default. Done previously by ``zope.app.security``. - Move ``PermissionsVocabulary`` and ``PermissionIdsVocabulary`` vocabularies to the ``zope.security.permission`` module from the ``zope.app.security`` package. - Add zcml permission definitions for most common and useful permissions, like ``zope.View`` and ``zope.ManageContent``, as well as for the special ``zope.Public`` permission. They are placed in a separate ``permissions.zcml`` file, so it can be easily excluded/redefined. They are selected part of permissions moved from ``zope.app.security`` and used by many ``zope.*`` packages. - Add ``addCheckerPublic`` helper function in ``zope.security.testing`` module that registers the "zope.Public" permission as an IPermission utility. - Add security declarations for the ``zope.security.permisson.Permission`` class. - Improve test coverage. 3.6.1 (2009-03-10) ================== - Use ``from`` imports instead of ``zope.deferred`` to avoid circular import problems, thus drop dependency on ``zope.deferredimport``. - Raise ``NoInteraction`` when ``zope.security.checkPermission`` is called without interaction being active (LP #301565). - Don't define security checkers for deprecated set types from the "sets" module on Python 2.6. It's discouraged to use them and ``set`` and ``frozenset`` built-in types should be used instead. - Change package's mailng list address to zope-dev at zope.org as zope3-dev at zope.org is now retired. - Remove old zpkg-related files. 3.6.0 (2009-01-31) ================== - Install decorated security checker support on ``LocationProxy`` from the outside. - Add support to bootstrap on Jython. - Move the ``protectclass`` module from ``zope.app.security`` to this package to reduce the number of dependencies on ``zope.app.security``. - Move the ```` directive implementation from ``zope.app.security`` to this package. - Move the ```` directive implementation from ``zope.app.component`` to this package. 3.5.2 (2008-07-27) ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.5.1 (2008-06-04) ================== - Add ``frozenset``, ``set``, ``reversed``, and ``sorted`` to the list of safe builtins. 3.5.0 (2008-03-05) ================== - Changed title for ``zope.security.management.system_user`` to be more presentable. 3.4.3 - (2009/11/26) ==================== - Backport a fix made by Gary Poster to the 3.4 branch: Fix for LP bug 181833 (from Gustavo Niemeyer). Before "visiting" a sub-object, a check should be made to ensure the object is still valid. Because garbage collection may involve loops, if you garbage collect an object, it is possible that the actions done on this object may modify the state of other objects. This may cause another round of garbage collection, eventually generating a segfault (see LP bug). The ``Py_VISIT`` macro does the necessary checks, so it is used instead of the previous code. 3.4.2 - (2009/03/23) ==================== - Add dependency on ``zope.thread`` to setup.py; without it, the tests were failing. - Backport a fix made by Albertas Agejevas to the 3.4 branch. He fixed a bug in DecoratedSecurityCheckerDescriptor which made security-wrapping location proxied exception instances throw exceptions on Python 2.5. See https://bugs.launchpad.net/zope3/+bug/251848 3.4.1 - 2008/07/27 ================== - Make C code compatible with Python 2.5 on 64bit architectures. 3.4.0 (2007-10-02) ================== - Update meta-data. 3.4.0b5 (2007-08-15) ==================== - Fix a circular import in the C implementation. 3.4.0b4 (2007-08-14) ==================== - Improve ugly/brittle ID of ``zope.security.management.system_user``. 3.4.0b3 (2007-08-14) ==================== - Add support for Python 2.5. - Bug: ``zope.security.management.system_user`` wasn't a valid principal (didn't provide IPrincipal). - Bug: Fix inclusion of doctest to use the doctest module from ``zope.testing``. Now tests can be run multiple times without breaking. (#98250) 3.4.0b2 (2007-06-15) ==================== - Bug: Remove stack extraction in ``newInteraction``. When using eggs this is an extremly expensive function. The publisher is now more than 10 times faster when using eggs and about twice as fast with a zope trunk checkout. 3.4.0b1 ======= - Temporarily fixed the hidden (and accidental) dependency on zope.testing to become optional. Note: The releases between 3.2.0 and 3.4.0b1 where not tracked as an individual package and have been documented in the Zope 3 changelog. 3.2.0 (2006-01-05) ================== - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.2.0 release. - Remove deprecated helper functions, ``proxy.trustedRemoveSecurityProxy`` and ``proxy.getProxiedObject``. - Make handling of ``management.{end,restore}Interaction`` more careful w.r.t. edge cases. - Make behavior of ``canWrite`` consistent with ``canAccess``: if ``canAccess`` does not raise ``ForbiddenAttribute``, then neither will ``canWrite``. See: http://www.zope.org/Collectors/Zope3-dev/506 - Code style / documentation / test fixes. 3.1.0 (2005-10-03) ================== - Add support for use of the new Python 2.4 datatypes, ``set`` and ``frozenset``, within checked code. - Make the C security proxy depend on the ``proxy.h`` header from the ``zope.proxy`` package. - XXX: the spelling of the ``#include`` is bizarre! It seems to be related to ``zpkg``-based builds, and should likely be revisited. For the moment, I have linked in the ``zope.proxy`` package into our own ``include`` directory. See the subversion checkin: http://svn.zope.org/Zope3/?rev=37882&view=rev - Update checker to avoid re-proxying objects which have and explicit ``__Security_checker__`` assigned. - Corresponds to the verison of the ``zope.security`` package shipped as part of the Zope 3.1.0 release. - Clarify contract of ``IChecker`` to indicate that its ``check*`` methods may raise only ``Forbidden`` or ``Unauthorized`` exceptions. - Add interfaces, (``IPrincipal``, ``IGroupAwarePrincipal``, ``IGroup``, and ``IPermission``) specifying contracts of components in the security framework. - Code style / documentation / test fixes. 3.0.0 (2004-11-07) ================== - Corresponds to the version of the ``zope.security`` package shipped as part of the Zope X3.0.0 release. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/SOURCES.txt0000644000076500000240000000653714677253221022165 0ustar00jensstaff.manylinux-install.sh .manylinux.sh .pre-commit-config.yaml .readthedocs.yaml CHANGES.rst CONTRIBUTING.md COPYRIGHT.txt LICENSE.txt MANIFEST.in README.rst buildout.cfg pyproject.toml setup.cfg setup.py tox.ini docs/Makefile docs/changes.rst docs/conf.py docs/example.rst docs/hacking.rst docs/index.rst docs/make.bat docs/narr.rst docs/proxy.rst docs/requirements.txt docs/_build/doctest/output.txt docs/_build/html/_sources/changes.rst.txt docs/_build/html/_sources/example.rst.txt docs/_build/html/_sources/hacking.rst.txt docs/_build/html/_sources/index.rst.txt docs/_build/html/_sources/narr.rst.txt docs/_build/html/_sources/proxy.rst.txt docs/_build/html/_sources/api/adapter.rst.txt docs/_build/html/_sources/api/checker.rst.txt docs/_build/html/_sources/api/decorator.rst.txt docs/_build/html/_sources/api/interfaces.rst.txt docs/_build/html/_sources/api/management.rst.txt docs/_build/html/_sources/api/metaconfigure.rst.txt docs/_build/html/_sources/api/permission.rst.txt docs/_build/html/_sources/api/protectclass.rst.txt docs/_build/html/_sources/api/proxy.rst.txt docs/_build/html/_sources/api/simplepolicies.rst.txt docs/_build/html/_sources/api/testing.rst.txt docs/_build/html/_sources/api/zcml.rst.txt docs/api/adapter.rst docs/api/checker.rst docs/api/decorator.rst docs/api/interfaces.rst docs/api/management.rst docs/api/metaconfigure.rst docs/api/permission.rst docs/api/protectclass.rst docs/api/proxy.rst docs/api/simplepolicies.rst docs/api/testing.rst docs/api/zcml.rst include/zope.proxy/zope/proxy/proxy.h src/zope/__init__.py src/zope.security.egg-info/PKG-INFO src/zope.security.egg-info/SOURCES.txt src/zope.security.egg-info/dependency_links.txt src/zope.security.egg-info/namespace_packages.txt src/zope.security.egg-info/not-zip-safe src/zope.security.egg-info/requires.txt src/zope.security.egg-info/top_level.txt src/zope/security/__init__.py src/zope/security/_compat.py src/zope/security/_definitions.py src/zope/security/_proxy.c src/zope/security/_zope_security_checker.c src/zope/security/adapter.py src/zope/security/checker.py src/zope/security/configure.zcml src/zope/security/decorator.py src/zope/security/i18n.py src/zope/security/interfaces.py src/zope/security/management.py src/zope/security/meta.zcml src/zope/security/metaconfigure.py src/zope/security/metadirectives.py src/zope/security/permission.py src/zope/security/permissions.zcml src/zope/security/protectclass.py src/zope/security/proxy.py src/zope/security/simplepolicies.py src/zope/security/testing.py src/zope/security/zcml.py src/zope/security/examples/sandbox.py src/zope/security/examples/sandbox_security.py src/zope/security/tests/__init__.py src/zope/security/tests/exampleclass.py src/zope/security/tests/module.py src/zope/security/tests/redefineperms.zcml src/zope/security/tests/test_adapter.py src/zope/security/tests/test_checker.py src/zope/security/tests/test_compile_flags.py src/zope/security/tests/test_decorator.py src/zope/security/tests/test_location.py src/zope/security/tests/test_management.py src/zope/security/tests/test_metaconfigure.py src/zope/security/tests/test_permission.py src/zope/security/tests/test_protectclass.py src/zope/security/tests/test_proxy.py src/zope/security/tests/test_simpleinteraction.py src/zope/security/tests/test_simplepolicies.py src/zope/security/tests/test_testing.py src/zope/security/tests/test_zcml.py src/zope/security/tests/test_zcml_functest.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/dependency_links.txt0000644000076500000240000000000114677253221024334 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/namespace_packages.txt0000644000076500000240000000000514677253221024614 0ustar00jensstaffzope ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1672756842.0 zope_security-7.3/src/zope.security.egg-info/not-zip-safe0000644000076500000240000000000114355037152022510 0ustar00jensstaff ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/requires.txt0000644000076500000240000000057714677253221022677 0ustar00jensstaffsetuptools zope.component zope.i18nmessageid zope.interface zope.location zope.proxy>=5.2 zope.schema>=4.2.0 [docs] Sphinx repoze.sphinx.autointerface sphinx_rtd_theme zope.configuration zope.testing [pytz] pytz [test] BTrees zope.component zope.configuration zope.location zope.testing zope.testrunner [untrustedpython] zope.untrustedpython>=5.0.dev0 [zcml] zope.configuration ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1727878801.0 zope_security-7.3/src/zope.security.egg-info/top_level.txt0000644000076500000240000000000514677253221023013 0ustar00jensstaffzope ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1726556583.0 zope_security-7.3/tox.ini0000644000076500000240000000363414672224647014510 0ustar00jensstaff# Generated from: # https://github.com/zopefoundation/meta/tree/master/config/c-code [tox] minversion = 4.0 envlist = lint py38,py38-pure py39,py39-pure py310,py310-pure py311,py311-pure py312,py312-pure py313,py313-pure pypy3 docs coverage py38-watch, py311-watch [testenv] deps = setuptools <74 setenv = pure: PURE_PYTHON=1 !pure-!pypy3: PURE_PYTHON=0 ZOPE_INTERFACE_STRICT_IRO=1 watch: ZOPE_WATCH_CHECKERS=1 commands = zope-testrunner --test-path=src {posargs:-vc} sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest extras = test docs [testenv:setuptools-latest] basepython = python3 deps = git+https://github.com/pypa/setuptools.git\#egg=setuptools [testenv:coverage] basepython = python3 allowlist_externals = mkdir deps = coverage[toml] setenv = PURE_PYTHON=1 commands = mkdir -p {toxinidir}/parts/htmlcov coverage run -m zope.testrunner --test-path=src {posargs:-vc} coverage html coverage report [testenv:release-check] description = ensure that the distribution is ready to release basepython = python3 skip_install = true deps = setuptools <74 twine build check-manifest check-python-versions >= 0.20.0 wheel commands_pre = commands = check-manifest check-python-versions --only setup.py,tox.ini,.github/workflows/tests.yml python -m build --sdist --no-isolation twine check dist/* [testenv:lint] description = This env runs all linters configured in .pre-commit-config.yaml basepython = python3 skip_install = true deps = pre-commit commands_pre = commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs] basepython = python3 skip_install = false commands_pre = commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest