AccessControl 7.0__tar.gz → 7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. {AccessControl-7.0 → accesscontrol-7.2}/.manylinux-install.sh +7 -12
  2. accesscontrol-7.2/.pre-commit-config.yaml +28 -0
  3. {AccessControl-7.0 → accesscontrol-7.2}/CHANGES.rst +22 -0
  4. {AccessControl-7.0 → accesscontrol-7.2}/MANIFEST.in +1 -1
  5. {AccessControl-7.0 → accesscontrol-7.2}/PKG-INFO +26 -4
  6. accesscontrol-7.2/pyproject.toml +25 -0
  7. {AccessControl-7.0 → accesscontrol-7.2}/setup.py +4 -4
  8. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/ImplPython.py +5 -1
  9. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/SecurityInfo.py +2 -2
  10. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/ZopeGuards.py +1 -1
  11. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/cAccessControl.c +24 -2
  12. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testClassSecurityInfo.py +5 -5
  13. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testModuleSecurity.py +2 -2
  14. accesscontrol-7.2/src/AccessControl/tests/testPermissionRole.py +170 -0
  15. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testSecurityManager.py +28 -27
  16. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testZopeGuards.py +1 -1
  17. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testZopeSecurityPolicy.py +18 -2
  18. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_tainted.py +75 -57
  19. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_userfolder.py +1 -1
  20. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/userfolder.py +1 -0
  21. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/users.py +1 -1
  22. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/PKG-INFO +26 -4
  23. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/SOURCES.txt +2 -1
  24. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/requires.txt +1 -4
  25. {AccessControl-7.0 → accesscontrol-7.2}/tox.ini +14 -20
  26. AccessControl-7.0/.coveragerc +0 -30
  27. AccessControl-7.0/src/AccessControl/tests/testPermissionRole.py +0 -129
  28. {AccessControl-7.0 → accesscontrol-7.2}/.manylinux.sh +0 -0
  29. {AccessControl-7.0 → accesscontrol-7.2}/CONTRIBUTING.md +0 -0
  30. {AccessControl-7.0 → accesscontrol-7.2}/COPYRIGHT.txt +0 -0
  31. {AccessControl-7.0 → accesscontrol-7.2}/HISTORY.rst +0 -0
  32. {AccessControl-7.0 → accesscontrol-7.2}/LICENSE.txt +0 -0
  33. {AccessControl-7.0 → accesscontrol-7.2}/README.rst +0 -0
  34. {AccessControl-7.0 → accesscontrol-7.2}/buildout.cfg +0 -0
  35. {AccessControl-7.0 → accesscontrol-7.2}/docs/AccessControl.rst +0 -0
  36. {AccessControl-7.0 → accesscontrol-7.2}/docs/requestmethod.rst +0 -0
  37. {AccessControl-7.0 → accesscontrol-7.2}/include/Acquisition/Acquisition.h +0 -0
  38. {AccessControl-7.0 → accesscontrol-7.2}/include/ExtensionClass/ExtensionClass.h +0 -0
  39. {AccessControl-7.0 → accesscontrol-7.2}/include/ExtensionClass/_compat.h +0 -0
  40. {AccessControl-7.0 → accesscontrol-7.2}/setup.cfg +0 -0
  41. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/AuthEncoding.py +0 -0
  42. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/ImplC.py +0 -0
  43. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/Implementation.py +0 -0
  44. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/Owned.py +0 -0
  45. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/Permission.py +0 -0
  46. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/PermissionMapping.py +0 -0
  47. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/PermissionRole.py +0 -0
  48. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/Permissions.py +0 -0
  49. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/Role.py +0 -0
  50. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/SecurityManagement.py +0 -0
  51. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/SecurityManager.py +0 -0
  52. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/SimpleObjectPolicies.py +0 -0
  53. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/SpecialUsers.py +0 -0
  54. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/User.py +0 -0
  55. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/ZopeSecurityPolicy.py +0 -0
  56. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/__init__.py +0 -0
  57. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/_compat.h +0 -0
  58. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/class_init.py +0 -0
  59. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/configure.zcml +0 -0
  60. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/interfaces.py +0 -0
  61. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/logger_wrapper.py +0 -0
  62. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/meta.zcml +0 -0
  63. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/metaconfigure.py +0 -0
  64. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/owner.py +0 -0
  65. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/permissions.zcml +0 -0
  66. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/requestmethod.py +0 -0
  67. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/rolemanager.py +0 -0
  68. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/safe_formatter.py +0 -0
  69. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/security.py +0 -0
  70. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tainted.py +0 -0
  71. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/__init__.py +0 -0
  72. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/actual_python.py +0 -0
  73. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/mixed_module/__init__.py +0 -0
  74. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/mixed_module/submodule/__init__.py +0 -0
  75. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/private_module/__init__.py +0 -0
  76. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/private_module/submodule/__init__.py +0 -0
  77. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/public_module/__init__.py +0 -0
  78. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/public_module/submodule/__init__.py +0 -0
  79. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testImplementation.py +0 -0
  80. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testPermissionMapping.py +0 -0
  81. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/testZCML.py +0 -0
  82. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_owner.py +0 -0
  83. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_requestmethod.py +0 -0
  84. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_rolemanager.py +0 -0
  85. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_safe_formatter.py +0 -0
  86. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_safeiter.py +0 -0
  87. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/tests/test_users.py +0 -0
  88. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl/unauthorized.py +0 -0
  89. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/dependency_links.txt +0 -0
  90. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/entry_points.txt +0 -0
  91. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/not-zip-safe +0 -0
  92. {AccessControl-7.0 → accesscontrol-7.2}/src/AccessControl.egg-info/top_level.txt +0 -0
@@ -28,12 +28,12 @@ yum -y install libffi-devel
28
28
 
29
29
  tox_env_map() {
30
30
  case $1 in
31
- *"cp313"*) echo 'py313';;
32
31
  *"cp38"*) echo 'py38';;
33
32
  *"cp39"*) echo 'py39';;
34
33
  *"cp310"*) echo 'py310';;
35
34
  *"cp311"*) echo 'py311';;
36
35
  *"cp312"*) echo 'py312';;
36
+ *"cp313"*) echo 'py313';;
37
37
  *) echo 'py';;
38
38
  esac
39
39
  }
@@ -41,19 +41,14 @@ tox_env_map() {
41
41
  # Compile wheels
42
42
  for PYBIN in /opt/python/*/bin; do
43
43
  if \
44
- [[ "${PYBIN}" == *"cp313/"* ]] || \
45
- [[ "${PYBIN}" == *"cp311/"* ]] || \
46
- [[ "${PYBIN}" == *"cp312/"* ]] || \
47
44
  [[ "${PYBIN}" == *"cp38/"* ]] || \
48
45
  [[ "${PYBIN}" == *"cp39/"* ]] || \
49
- [[ "${PYBIN}" == *"cp310/"* ]] ; then
50
- if [[ "${PYBIN}" == *"cp313/"* ]] ; then
51
- "${PYBIN}/pip" install --pre -e /io/
52
- "${PYBIN}/pip" wheel /io/ --pre -w wheelhouse/
53
- else
54
- "${PYBIN}/pip" install -e /io/
55
- "${PYBIN}/pip" wheel /io/ -w wheelhouse/
56
- fi
46
+ [[ "${PYBIN}" == *"cp310/"* ]] || \
47
+ [[ "${PYBIN}" == *"cp311/"* ]] || \
48
+ [[ "${PYBIN}" == *"cp312/"* ]] || \
49
+ [[ "${PYBIN}" == *"cp313/"* ]] ; then
50
+ "${PYBIN}/pip" install -e /io/
51
+ "${PYBIN}/pip" wheel /io/ -w wheelhouse/
57
52
  if [ `uname -m` == 'aarch64' ]; then
58
53
  cd /io/
59
54
  ${PYBIN}/pip install tox
@@ -0,0 +1,28 @@
1
+ # Generated from:
2
+ # https://github.com/zopefoundation/meta/tree/master/config/c-code
3
+ minimum_pre_commit_version: '3.6'
4
+ repos:
5
+ - repo: https://github.com/pycqa/isort
6
+ rev: "5.13.2"
7
+ hooks:
8
+ - id: isort
9
+ - repo: https://github.com/hhatto/autopep8
10
+ rev: "v2.3.1"
11
+ hooks:
12
+ - id: autopep8
13
+ args: [--in-place, --aggressive, --aggressive]
14
+ - repo: https://github.com/asottile/pyupgrade
15
+ rev: v3.17.0
16
+ hooks:
17
+ - id: pyupgrade
18
+ args: [--py38-plus]
19
+ - repo: https://github.com/isidentical/teyit
20
+ rev: 0.4.3
21
+ hooks:
22
+ - id: teyit
23
+ - repo: https://github.com/PyCQA/flake8
24
+ rev: "7.1.1"
25
+ hooks:
26
+ - id: flake8
27
+ additional_dependencies:
28
+ - flake8-debugger == 4.1.2
@@ -3,6 +3,28 @@ Changelog
3
3
 
4
4
  For changes before version 3.0, see ``HISTORY.rst``.
5
5
 
6
+ 7.2 (2024-11-03)
7
+ ----------------
8
+
9
+ - Prevent untrusted access to ``AccessControl.userfolder.UserFolder.data``
10
+ (fixes `GHSA-g5vw-3h65-2q3v <https://github.com/zopefoundation/AccessControl/security/advisories/GHSA-g5vw-3h65-2q3v>`_).
11
+
12
+
13
+ 7.1 (2024-10-10)
14
+ ----------------
15
+
16
+ - Add final support for Python 3.13.
17
+
18
+ - Respect ``PURE_PYTHON`` environment variable set to ``0`` when running tests.
19
+
20
+ - Let the roles access in ``rolesForPermissionOn`` interpret ``AttributeError``
21
+ and ``Unauthorized`` as "no roles definition for this permission at this
22
+ object" and report any other exception (for the Python and C implementation).
23
+ We have to treat ``Unauthorized`` like ``AttributeError`` to support
24
+ ``Shared.DC.Scripts.Bindings.UnauthorizedBinding`` which raises
25
+ ``Unauthorized`` for any access.
26
+
27
+
6
28
  7.0 (2024-05-30)
7
29
  ----------------
8
30
 
@@ -5,7 +5,7 @@ include *.rst
5
5
  include *.txt
6
6
  include buildout.cfg
7
7
  include tox.ini
8
- include .coveragerc
8
+ include .pre-commit-config.yaml
9
9
 
10
10
  recursive-include src *.py
11
11
  include *.sh
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AccessControl
3
- Version: 7.0
3
+ Version: 7.2
4
4
  Summary: Security framework for Zope.
5
5
  Home-page: https://github.com/zopefoundation/AccessControl
6
6
  Author: Zope Foundation and Contributors
7
- Author-email: zope-dev@zope.org
7
+ Author-email: zope-dev@zope.dev
8
8
  License: ZPL 2.1
9
9
  Project-URL: Issue Tracker, https://github.com/zopefoundation/AccessControl/issues
10
10
  Project-URL: Sources, https://github.com/zopefoundation/AccessControl
@@ -25,6 +25,7 @@ Classifier: Programming Language :: Python :: 3.12
25
25
  Classifier: Programming Language :: Python :: 3.13
26
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
27
27
  Requires-Python: >=3.8
28
+ Description-Content-Type: text/x-rst
28
29
  License-File: LICENSE.txt
29
30
  Requires-Dist: Acquisition
30
31
  Requires-Dist: AuthEncoding
@@ -32,7 +33,7 @@ Requires-Dist: BTrees
32
33
  Requires-Dist: DateTime
33
34
  Requires-Dist: ExtensionClass>=4.2.1
34
35
  Requires-Dist: Persistence>=3.0a3
35
- Requires-Dist: RestrictedPython>=5.1
36
+ Requires-Dist: RestrictedPython>=7.4
36
37
  Requires-Dist: transaction
37
38
  Requires-Dist: zExceptions
38
39
  Requires-Dist: zope.component
@@ -43,7 +44,6 @@ Requires-Dist: zope.publisher
43
44
  Requires-Dist: zope.schema
44
45
  Requires-Dist: zope.security
45
46
  Requires-Dist: zope.testing
46
- Requires-Dist: funcsigs; python_version < "3.3"
47
47
  Provides-Extra: test
48
48
  Requires-Dist: zope.testrunner; extra == "test"
49
49
 
@@ -73,6 +73,28 @@ Changelog
73
73
 
74
74
  For changes before version 3.0, see ``HISTORY.rst``.
75
75
 
76
+ 7.2 (2024-11-03)
77
+ ----------------
78
+
79
+ - Prevent untrusted access to ``AccessControl.userfolder.UserFolder.data``
80
+ (fixes `GHSA-g5vw-3h65-2q3v <https://github.com/zopefoundation/AccessControl/security/advisories/GHSA-g5vw-3h65-2q3v>`_).
81
+
82
+
83
+ 7.1 (2024-10-10)
84
+ ----------------
85
+
86
+ - Add final support for Python 3.13.
87
+
88
+ - Respect ``PURE_PYTHON`` environment variable set to ``0`` when running tests.
89
+
90
+ - Let the roles access in ``rolesForPermissionOn`` interpret ``AttributeError``
91
+ and ``Unauthorized`` as "no roles definition for this permission at this
92
+ object" and report any other exception (for the Python and C implementation).
93
+ We have to treat ``Unauthorized`` like ``AttributeError`` to support
94
+ ``Shared.DC.Scripts.Bindings.UnauthorizedBinding`` which raises
95
+ ``Unauthorized`` for any access.
96
+
97
+
76
98
  7.0 (2024-05-30)
77
99
  ----------------
78
100
 
@@ -0,0 +1,25 @@
1
+ #
2
+ # Generated from:
3
+ # https://github.com/zopefoundation/meta/tree/master/config/c-code
4
+
5
+ [build-system]
6
+ requires = ["setuptools<74"]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [tool.coverage.run]
10
+ branch = true
11
+ source = ["AccessControl"]
12
+ relative_files = true
13
+
14
+ [tool.coverage.report]
15
+ fail_under = 80
16
+ precision = 2
17
+ ignore_errors = true
18
+ show_missing = true
19
+ exclude_lines = ["pragma: no cover", "pragma: nocover", "except ImportError:", "raise NotImplementedError", "if __name__ == '__main__':", "self.fail", "raise AssertionError", "raise unittest.Skip"]
20
+
21
+ [tool.coverage.html]
22
+ directory = "parts/htmlcov"
23
+
24
+ [tool.coverage.paths]
25
+ source = ["src/", ".tox/*/lib/python*/site-packages/", ".tox/pypy*/site-packages/"]
@@ -32,7 +32,7 @@ ext_modules = [
32
32
  join('include', 'Acquisition', 'Acquisition.h')]),
33
33
  ]
34
34
 
35
- version = '7.0'
35
+ version = '7.2'
36
36
 
37
37
 
38
38
  setup(name='AccessControl',
@@ -47,8 +47,9 @@ setup(name='AccessControl',
47
47
  description='Security framework for Zope.',
48
48
  keywords='security access authorization',
49
49
  author='Zope Foundation and Contributors',
50
- author_email='zope-dev@zope.org',
50
+ author_email='zope-dev@zope.dev',
51
51
  long_description=README + '\n\n' + CHANGES,
52
+ long_description_content_type='text/x-rst',
52
53
  packages=find_packages('src'),
53
54
  package_dir={'': 'src'},
54
55
  classifiers=[
@@ -76,7 +77,7 @@ setup(name='AccessControl',
76
77
  'DateTime', # optional dependency of RestrictedPython
77
78
  'ExtensionClass >= 4.2.1',
78
79
  'Persistence >= 3.0a3',
79
- 'RestrictedPython >= 5.1',
80
+ 'RestrictedPython >= 7.4',
80
81
  'transaction',
81
82
  'zExceptions',
82
83
  'zope.component',
@@ -87,7 +88,6 @@ setup(name='AccessControl',
87
88
  'zope.schema',
88
89
  'zope.security',
89
90
  'zope.testing',
90
- 'funcsigs;python_version<"3.3"',
91
91
  ],
92
92
  python_requires='>=3.8',
93
93
  include_package_data=True,
@@ -31,6 +31,7 @@ from Acquisition import aq_inner
31
31
  from Acquisition import aq_parent
32
32
  from ExtensionClass import Base
33
33
  from zope.interface import implementer
34
+ from zExceptions import Unauthorized as zExceptions_Unauthorized
34
35
 
35
36
  PURE_PYTHON = int(os.environ.get('PURE_PYTHON', '0'))
36
37
  if PURE_PYTHON:
@@ -71,8 +72,11 @@ def rolesForPermissionOn(perm, object, default=_default_roles, n=None):
71
72
  r = None
72
73
 
73
74
  while True:
74
- if hasattr(object, n):
75
+ try:
75
76
  roles = getattr(object, n)
77
+ except (AttributeError, zExceptions_Unauthorized):
78
+ pass
79
+ else:
76
80
  if roles is None:
77
81
  if _embed_permission_in_roles:
78
82
  return (('Anonymous',), n)
@@ -140,7 +140,7 @@ class SecurityInfo(Implicit):
140
140
  # the decorator returned is remembered in a set and will
141
141
  # remove itself upon call. self.apply will check for an empty
142
142
  # set and raise an AssertionError otherwise.
143
- key = "'{}':{}".format(permission_name, id(lambda x: x))
143
+ key = f"'{permission_name}':{id(lambda x: x)}"
144
144
 
145
145
  def decor(func):
146
146
  self.declareProtected(permission_name, func.__name__) # NOQA: D001
@@ -148,7 +148,7 @@ class SecurityInfo(Implicit):
148
148
  return func
149
149
  # make sure our key algo creates unique-enough keys
150
150
  if key in self._unused_protected_decorators:
151
- raise KeyError("Duplicate key: {}".format(key))
151
+ raise KeyError(f"Duplicate key: {key}")
152
152
  self._unused_protected_decorators.add(key)
153
153
  return decor
154
154
 
@@ -491,7 +491,7 @@ def load_module(module, mname, mnameparts, validate, globals, locals):
491
491
  if mname is None:
492
492
  mname = nextname
493
493
  else:
494
- mname = '{}.{}'.format(mname, nextname)
494
+ mname = f'{mname}.{nextname}'
495
495
  # import (if not already imported) and check for MSI
496
496
  nextmodule = secureModule(mname, globals, locals)
497
497
  if nextmodule is None: # not allowed
@@ -652,6 +652,7 @@ static PyExtensionClass imPermissionRoleType = {
652
652
  static PyObject *Containers = NULL;
653
653
  static PyObject *ContainerAssertions = NULL;
654
654
  static PyObject *Unauthorized = NULL;
655
+ static PyObject *zExceptions_Unauthorized = NULL;
655
656
  static PyObject *warn= NULL;
656
657
  static PyObject *NoSequenceFormat = NULL;
657
658
  static PyObject *_what_not_even_god_should_do = NULL;
@@ -1847,15 +1848,27 @@ c_rolesForPermissionOn(PyObject *perm, PyObject *object,
1847
1848
  Py_INCREF(r);
1848
1849
 
1849
1850
  /*
1850
- while 1:
1851
+ while True:
1851
1852
  */
1852
1853
  while (1)
1853
1854
  {
1854
1855
  /*
1855
- if hasattr(object, n):
1856
+ try:
1856
1857
  roles = getattr(object, n)
1858
+ except (AttributeError, zExceptions_Unauthorized):
1859
+ pass
1860
+ else:
1857
1861
  */
1858
1862
  PyObject *roles = PyObject_GetAttr(object, n);
1863
+ if (roles == NULL)
1864
+ {
1865
+ if (! (PyErr_ExceptionMatches(PyExc_AttributeError)
1866
+ || PyErr_ExceptionMatches(zExceptions_Unauthorized)))
1867
+ {
1868
+ /* re-raise */
1869
+ return NULL;
1870
+ }
1871
+ }
1859
1872
  if (roles != NULL)
1860
1873
  {
1861
1874
 
@@ -2313,6 +2326,7 @@ static struct PyMethodDef dtml_methods[] = {
2313
2326
  */
2314
2327
  #define IMPORT(module, name) if ((module = PyImport_ImportModule(name)) == NULL) return NULL;
2315
2328
  #define GETATTR(module, name) if ((name = PyObject_GetAttrString(module, #name)) == NULL) return NULL;
2329
+ #define GETATTR_AS(module, name, as_name) if ((as_name = PyObject_GetAttrString(module, name)) == NULL) return NULL;
2316
2330
 
2317
2331
  static struct PyModuleDef moduledef =
2318
2332
  {
@@ -2400,6 +2414,14 @@ module_init(void) {
2400
2414
  Py_DECREF(tmp);
2401
2415
  tmp = NULL;
2402
2416
 
2417
+ /*| from zExceptions import Unauthorized as zExceptions_Unauthorized
2418
+ */
2419
+
2420
+ IMPORT(tmp, "zExceptions");
2421
+ GETATTR_AS(tmp, "Unauthorized", zExceptions_Unauthorized);
2422
+ Py_DECREF(tmp);
2423
+ tmp = NULL;
2424
+
2403
2425
  /*| from AccessControl.SecurityManagement import getSecurityManager
2404
2426
  */
2405
2427
 
@@ -102,10 +102,10 @@ class ClassSecurityInfoTests(unittest.TestCase):
102
102
  self.assertEqual(object.private__roles__, ())
103
103
  imPermissionRole = [r for r in object.protected__roles__
104
104
  if not r.endswith('_Permission')]
105
- self.assertTrue(len(imPermissionRole) == 4)
105
+ self.assertEqual(len(imPermissionRole), 4)
106
106
 
107
107
  for item in ('Manager', 'Role A', 'Role B', 'Role C'):
108
- self.assertTrue(item in imPermissionRole)
108
+ self.assertIn(item, imPermissionRole)
109
109
 
110
110
  # functions exist, i.e. decorators returned them
111
111
  self.assertEqual(object.public_new.__name__, 'public_new')
@@ -117,10 +117,10 @@ class ClassSecurityInfoTests(unittest.TestCase):
117
117
  self.assertEqual(object.private_new__roles__, ())
118
118
  imPermissionRole = [r for r in object.protected_new__roles__
119
119
  if not r.endswith('_Permission')]
120
- self.assertTrue(len(imPermissionRole) == 4)
120
+ self.assertEqual(len(imPermissionRole), 4)
121
121
 
122
122
  for item in ('Manager', 'Role A', 'Role B', 'Role C'):
123
- self.assertTrue(item in imPermissionRole)
123
+ self.assertIn(item, imPermissionRole)
124
124
 
125
125
  # Make sure that a permission defined without accompanying method
126
126
  # is still reflected in __ac_permissions__
@@ -160,7 +160,7 @@ class ClassSecurityInfoTests(unittest.TestCase):
160
160
  # Do class initialization.
161
161
  exc = self.assertRaises(AssertionError,
162
162
  InitializeClass, Test)
163
- self.assertTrue('has 2 non-decorator' in str(exc))
163
+ self.assertIn('has 2 non-decorator', str(exc))
164
164
 
165
165
  def test_aq_context_in_decorators(self):
166
166
  from Acquisition import Implicit
@@ -98,12 +98,12 @@ class ModuleSecurityTests(unittest.TestCase):
98
98
  from AccessControl.SecurityInfo import _moduleSecurity as MS
99
99
  from AccessControl.ZopeGuards import guarded_import
100
100
  MSI('AccessControl.tests.nonesuch').declarePublic('pub') # NOQA: D001
101
- self.assertTrue('AccessControl.tests.nonesuch' in MS)
101
+ self.assertIn('AccessControl.tests.nonesuch', MS)
102
102
  self.assertRaises(ImportError,
103
103
  guarded_import,
104
104
  'AccessControl.tests.nonesuch',
105
105
  ())
106
- self.assertTrue('AccessControl.tests.nonesuch' in MS)
106
+ self.assertIn('AccessControl.tests.nonesuch', MS)
107
107
 
108
108
  def test_level_default(self):
109
109
  self.assertAuth('AccessControl.tests.public_module', (),
@@ -0,0 +1,170 @@
1
+ ##############################################################################
2
+ #
3
+ # Copyright (c) 2002 Zope Foundation and Contributors.
4
+ #
5
+ # This software is subject to the provisions of the Zope Public License,
6
+ # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7
+ # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
+ # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
+ # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
+ # FOR A PARTICULAR PURPOSE
11
+ #
12
+ ##############################################################################
13
+ """Tests of PermissionRole
14
+ """
15
+
16
+ import unittest
17
+
18
+ from Acquisition import Explicit
19
+ from Acquisition import Implicit
20
+ from Acquisition import aq_base
21
+
22
+ from ..Implementation import PURE_PYTHON
23
+
24
+
25
+ ViewPermission = 'View'
26
+ EditThingsPermission = 'Edit Things!'
27
+ DeletePermission = 'Delete'
28
+
29
+
30
+ class AppRoot(Explicit):
31
+ _View_Permission = None
32
+ _Edit_Things__Permission = ('Manager', 'Owner')
33
+ # No default for delete permission.
34
+
35
+
36
+ class ImplicitContainer(Implicit):
37
+ pass
38
+
39
+
40
+ class ExplicitContainer(Explicit):
41
+ pass
42
+
43
+
44
+ class RestrictiveObject(Implicit):
45
+ _View_Permission = ('Manager',)
46
+ _Delete_Permission = () # Nobody
47
+
48
+
49
+ class PermissiveObject(Explicit):
50
+ _Edit_Things__Permission = ['Anonymous']
51
+
52
+
53
+ class PermissionRoleTestBase:
54
+
55
+ def assertPRoles(self, ob, permission, expect):
56
+ """
57
+ Asserts that in the context of ob, the given permission maps to
58
+ the given roles.
59
+ """
60
+ pr = self._getTargetClass()(permission)
61
+ roles = pr.__of__(ob)
62
+ roles2 = aq_base(pr).__of__(ob)
63
+ assert roles == roles2 or tuple(roles) == tuple(roles2), (
64
+ 'Different methods of checking roles computed unequal results')
65
+ same = 0
66
+ if roles:
67
+ # When verbose security is enabled, permission names are
68
+ # embedded in the computed roles. Remove the permission
69
+ # names.
70
+ roles = [r for r in roles if not r.endswith('_Permission')]
71
+
72
+ if roles is None or expect is None:
73
+ if (roles is None or tuple(roles) == ('Anonymous', )) and \
74
+ (expect is None or tuple(expect) == ('Anonymous', )):
75
+ same = 1
76
+ else:
77
+ got = {}
78
+ for r in roles:
79
+ got[r] = 1
80
+ expected = {}
81
+ for r in expect:
82
+ expected[r] = 1
83
+ if got == expected: # Dict compare does the Right Thing.
84
+ same = 1
85
+ self.assertTrue(same, f'Expected roles: {expect!r}, got: {roles!r}')
86
+
87
+ def testRestrictive(self, explicit=0):
88
+ app = AppRoot()
89
+ if explicit:
90
+ app.c = ExplicitContainer()
91
+ else:
92
+ app.c = ImplicitContainer()
93
+ app.c.o = RestrictiveObject()
94
+ o = app.c.o
95
+ self.assertPRoles(o, ViewPermission, ('Manager', ))
96
+ self.assertPRoles(o, EditThingsPermission, ('Manager', 'Owner'))
97
+ self.assertPRoles(o, DeletePermission, ())
98
+
99
+ def testPermissive(self, explicit=0):
100
+ app = AppRoot()
101
+ if explicit:
102
+ app.c = ExplicitContainer()
103
+ else:
104
+ app.c = ImplicitContainer()
105
+ app.c.o = PermissiveObject()
106
+ o = app.c.o
107
+ self.assertPRoles(o, ViewPermission, ('Anonymous', ))
108
+ self.assertPRoles(o, EditThingsPermission, ('Anonymous',
109
+ 'Manager',
110
+ 'Owner'))
111
+ self.assertPRoles(o, DeletePermission, ('Manager', ))
112
+
113
+ def testExplicit(self):
114
+ self.testRestrictive(1)
115
+ self.testPermissive(1)
116
+
117
+ def testAppDefaults(self):
118
+ o = AppRoot()
119
+ self.assertPRoles(o, ViewPermission, ('Anonymous', ))
120
+ self.assertPRoles(o, EditThingsPermission, ('Manager', 'Owner'))
121
+ self.assertPRoles(o, DeletePermission, ('Manager', ))
122
+
123
+ def testPermissionRoleSupportsGetattr(self):
124
+ a = self._getTargetClass()('a')
125
+ self.assertEqual(getattr(a, '__roles__'), ('Manager', ))
126
+ self.assertEqual(getattr(a, '_d'), ('Manager', ))
127
+ self.assertEqual(getattr(a, '__name__'), 'a')
128
+ self.assertEqual(getattr(a, '_p'), '_a_Permission')
129
+
130
+ def testErrorsDuringGetattr(self):
131
+ pr = self._getTargetClass()('View')
132
+
133
+ class AttributeErrorObject(Implicit):
134
+ pass
135
+ self.assertEqual(
136
+ tuple(pr.__of__(AttributeErrorObject())), ('Manager', ))
137
+
138
+ # Unauthorized errors are tolerated and equivalent to no
139
+ # permission declaration
140
+ class UnauthorizedErrorObject(Implicit):
141
+ def __getattr__(self, name):
142
+ from zExceptions import Unauthorized
143
+ if name == '_View_Permission':
144
+ raise Unauthorized(name)
145
+ raise AttributeError(name)
146
+ self.assertEqual(
147
+ tuple(pr.__of__(UnauthorizedErrorObject())), ('Manager', ))
148
+
149
+ # other exceptions propagate
150
+ class ErrorObject(Implicit):
151
+ def __getattr__(self, name):
152
+ if name == '_View_Permission':
153
+ raise RuntimeError("Error raised during getattr")
154
+ raise AttributeError(name)
155
+ with self.assertRaisesRegex(
156
+ RuntimeError, "Error raised during getattr"):
157
+ tuple(pr.__of__(ErrorObject()))
158
+
159
+
160
+ class Python_PermissionRoleTests(PermissionRoleTestBase, unittest.TestCase):
161
+ def _getTargetClass(self):
162
+ from AccessControl.ImplPython import PermissionRole
163
+ return PermissionRole
164
+
165
+
166
+ @unittest.skipIf(PURE_PYTHON, reason="Test expects C impl.")
167
+ class C__PermissionRoleTests(PermissionRoleTestBase, unittest.TestCase):
168
+ def _getTargetClass(self):
169
+ from AccessControl.ImplC import PermissionRole
170
+ return PermissionRole