AccessControl 6.3__tar.gz → 7.1__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 (93) hide show
  1. {AccessControl-6.3 → accesscontrol-7.1}/.manylinux-install.sh +7 -7
  2. accesscontrol-7.1/.pre-commit-config.yaml +28 -0
  3. {AccessControl-6.3 → accesscontrol-7.1}/CHANGES.rst +34 -0
  4. {AccessControl-6.3 → accesscontrol-7.1}/MANIFEST.in +1 -2
  5. {AccessControl-6.3 → accesscontrol-7.1}/PKG-INFO +40 -6
  6. accesscontrol-7.1/pyproject.toml +25 -0
  7. {AccessControl-6.3 → accesscontrol-7.1}/setup.cfg +0 -3
  8. {AccessControl-6.3 → accesscontrol-7.1}/setup.py +6 -6
  9. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/ImplPython.py +5 -1
  10. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/SecurityInfo.py +10 -9
  11. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/ZopeGuards.py +49 -12
  12. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/cAccessControl.c +29 -7
  13. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/logger_wrapper.py +1 -1
  14. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/actual_python.py +33 -0
  15. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testClassSecurityInfo.py +5 -5
  16. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testModuleSecurity.py +2 -2
  17. accesscontrol-7.1/src/AccessControl/tests/testPermissionRole.py +170 -0
  18. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testSecurityManager.py +28 -27
  19. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testZopeGuards.py +26 -10
  20. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testZopeSecurityPolicy.py +18 -2
  21. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_tainted.py +75 -57
  22. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_userfolder.py +1 -1
  23. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/users.py +1 -1
  24. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/PKG-INFO +40 -6
  25. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/SOURCES.txt +2 -2
  26. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/requires.txt +1 -4
  27. {AccessControl-6.3 → accesscontrol-7.1}/tox.ini +17 -20
  28. AccessControl-6.3/.coveragerc +0 -29
  29. AccessControl-6.3/appveyor.yml +0 -50
  30. AccessControl-6.3/src/AccessControl/tests/testPermissionRole.py +0 -129
  31. {AccessControl-6.3 → accesscontrol-7.1}/.manylinux.sh +0 -0
  32. {AccessControl-6.3 → accesscontrol-7.1}/CONTRIBUTING.md +0 -0
  33. {AccessControl-6.3 → accesscontrol-7.1}/COPYRIGHT.txt +0 -0
  34. {AccessControl-6.3 → accesscontrol-7.1}/HISTORY.rst +0 -0
  35. {AccessControl-6.3 → accesscontrol-7.1}/LICENSE.txt +0 -0
  36. {AccessControl-6.3 → accesscontrol-7.1}/README.rst +0 -0
  37. {AccessControl-6.3 → accesscontrol-7.1}/buildout.cfg +0 -0
  38. {AccessControl-6.3 → accesscontrol-7.1}/docs/AccessControl.rst +0 -0
  39. {AccessControl-6.3 → accesscontrol-7.1}/docs/requestmethod.rst +0 -0
  40. {AccessControl-6.3 → accesscontrol-7.1}/include/Acquisition/Acquisition.h +0 -0
  41. {AccessControl-6.3 → accesscontrol-7.1}/include/ExtensionClass/ExtensionClass.h +0 -0
  42. {AccessControl-6.3 → accesscontrol-7.1}/include/ExtensionClass/_compat.h +0 -0
  43. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/AuthEncoding.py +0 -0
  44. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/ImplC.py +0 -0
  45. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/Implementation.py +0 -0
  46. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/Owned.py +0 -0
  47. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/Permission.py +0 -0
  48. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/PermissionMapping.py +0 -0
  49. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/PermissionRole.py +0 -0
  50. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/Permissions.py +0 -0
  51. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/Role.py +0 -0
  52. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/SecurityManagement.py +0 -0
  53. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/SecurityManager.py +0 -0
  54. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/SimpleObjectPolicies.py +0 -0
  55. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/SpecialUsers.py +0 -0
  56. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/User.py +0 -0
  57. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/ZopeSecurityPolicy.py +0 -0
  58. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/__init__.py +0 -0
  59. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/_compat.h +0 -0
  60. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/class_init.py +0 -0
  61. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/configure.zcml +0 -0
  62. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/interfaces.py +0 -0
  63. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/meta.zcml +0 -0
  64. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/metaconfigure.py +0 -0
  65. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/owner.py +0 -0
  66. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/permissions.zcml +0 -0
  67. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/requestmethod.py +0 -0
  68. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/rolemanager.py +0 -0
  69. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/safe_formatter.py +0 -0
  70. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/security.py +0 -0
  71. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tainted.py +0 -0
  72. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/__init__.py +0 -0
  73. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/mixed_module/__init__.py +0 -0
  74. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/mixed_module/submodule/__init__.py +0 -0
  75. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/private_module/__init__.py +0 -0
  76. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/private_module/submodule/__init__.py +0 -0
  77. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/public_module/__init__.py +0 -0
  78. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/public_module/submodule/__init__.py +0 -0
  79. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testImplementation.py +0 -0
  80. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testPermissionMapping.py +0 -0
  81. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/testZCML.py +0 -0
  82. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_owner.py +0 -0
  83. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_requestmethod.py +0 -0
  84. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_rolemanager.py +0 -0
  85. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_safe_formatter.py +0 -0
  86. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_safeiter.py +0 -0
  87. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/tests/test_users.py +0 -0
  88. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/unauthorized.py +0 -0
  89. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl/userfolder.py +0 -0
  90. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/dependency_links.txt +0 -0
  91. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/entry_points.txt +0 -0
  92. {AccessControl-6.3 → accesscontrol-7.1}/src/AccessControl.egg-info/not-zip-safe +0 -0
  93. {AccessControl-6.3 → accesscontrol-7.1}/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
- *"cp37"*) echo 'py37';;
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,12 +41,12 @@ tox_env_map() {
41
41
  # Compile wheels
42
42
  for PYBIN in /opt/python/*/bin; do
43
43
  if \
44
- [[ "${PYBIN}" == *"cp311"* ]] || \
45
- [[ "${PYBIN}" == *"cp312"* ]] || \
46
- [[ "${PYBIN}" == *"cp37"* ]] || \
47
- [[ "${PYBIN}" == *"cp38"* ]] || \
48
- [[ "${PYBIN}" == *"cp39"* ]] || \
49
- [[ "${PYBIN}" == *"cp310"* ]] ; then
44
+ [[ "${PYBIN}" == *"cp38/"* ]] || \
45
+ [[ "${PYBIN}" == *"cp39/"* ]] || \
46
+ [[ "${PYBIN}" == *"cp310/"* ]] || \
47
+ [[ "${PYBIN}" == *"cp311/"* ]] || \
48
+ [[ "${PYBIN}" == *"cp312/"* ]] || \
49
+ [[ "${PYBIN}" == *"cp313/"* ]] ; then
50
50
  "${PYBIN}/pip" install -e /io/
51
51
  "${PYBIN}/pip" wheel /io/ -w wheelhouse/
52
52
  if [ `uname -m` == 'aarch64' ]; then
@@ -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,40 @@ Changelog
3
3
 
4
4
  For changes before version 3.0, see ``HISTORY.rst``.
5
5
 
6
+ 7.1 (2024-10-10)
7
+ ----------------
8
+
9
+ - Add final support for Python 3.13.
10
+
11
+ - Respect ``PURE_PYTHON`` environment variable set to ``0`` when running tests.
12
+
13
+ - Let the roles access in ``rolesForPermissionOn`` interpret ``AttributeError``
14
+ and ``Unauthorized`` as "no roles definition for this permission at this
15
+ object" and report any other exception (for the Python and C implementation).
16
+ We have to treat ``Unauthorized`` like ``AttributeError`` to support
17
+ ``Shared.DC.Scripts.Bindings.UnauthorizedBinding`` which raises
18
+ ``Unauthorized`` for any access.
19
+
20
+
21
+ 7.0 (2024-05-30)
22
+ ----------------
23
+
24
+ - Add preliminary support for Python 3.13 as of 3.13b1.
25
+
26
+ - Remove support for Python 3.7.
27
+
28
+ - Build Windows wheels on GHA.
29
+
30
+ - Make dict views (`.keys()`, `.items()` and `.values()`) behave like their
31
+ unrestricted versions.
32
+ (`#147 <https://github.com/zopefoundation/AccessControl/pull/147>`_)
33
+
34
+ - Make `.items()` validate each keys and values, like `.keys()` and
35
+ `.values()` do.
36
+
37
+ - Fix build errors on recent macOS versions.
38
+
39
+
6
40
  6.3 (2023-11-20)
7
41
  ----------------
8
42
 
@@ -5,8 +5,7 @@ include *.rst
5
5
  include *.txt
6
6
  include buildout.cfg
7
7
  include tox.ini
8
- include appveyor.yml
9
- include .coveragerc
8
+ include .pre-commit-config.yaml
10
9
 
11
10
  recursive-include src *.py
12
11
  include *.sh
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AccessControl
3
- Version: 6.3
3
+ Version: 7.1
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
@@ -17,14 +17,15 @@ Classifier: License :: OSI Approved :: Zope Public License
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python
19
19
  Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.7
21
20
  Classifier: Programming Language :: Python :: 3.8
22
21
  Classifier: Programming Language :: Python :: 3.9
23
22
  Classifier: Programming Language :: Python :: 3.10
24
23
  Classifier: Programming Language :: Python :: 3.11
25
24
  Classifier: Programming Language :: Python :: 3.12
25
+ Classifier: Programming Language :: Python :: 3.13
26
26
  Classifier: Programming Language :: Python :: Implementation :: CPython
27
- Requires-Python: >=3.7
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,40 @@ Changelog
73
73
 
74
74
  For changes before version 3.0, see ``HISTORY.rst``.
75
75
 
76
+ 7.1 (2024-10-10)
77
+ ----------------
78
+
79
+ - Add final support for Python 3.13.
80
+
81
+ - Respect ``PURE_PYTHON`` environment variable set to ``0`` when running tests.
82
+
83
+ - Let the roles access in ``rolesForPermissionOn`` interpret ``AttributeError``
84
+ and ``Unauthorized`` as "no roles definition for this permission at this
85
+ object" and report any other exception (for the Python and C implementation).
86
+ We have to treat ``Unauthorized`` like ``AttributeError`` to support
87
+ ``Shared.DC.Scripts.Bindings.UnauthorizedBinding`` which raises
88
+ ``Unauthorized`` for any access.
89
+
90
+
91
+ 7.0 (2024-05-30)
92
+ ----------------
93
+
94
+ - Add preliminary support for Python 3.13 as of 3.13b1.
95
+
96
+ - Remove support for Python 3.7.
97
+
98
+ - Build Windows wheels on GHA.
99
+
100
+ - Make dict views (`.keys()`, `.items()` and `.values()`) behave like their
101
+ unrestricted versions.
102
+ (`#147 <https://github.com/zopefoundation/AccessControl/pull/147>`_)
103
+
104
+ - Make `.items()` validate each keys and values, like `.keys()` and
105
+ `.values()` do.
106
+
107
+ - Fix build errors on recent macOS versions.
108
+
109
+
76
110
  6.3 (2023-11-20)
77
111
  ----------------
78
112
 
@@ -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/"]
@@ -1,6 +1,3 @@
1
- [bdist_wheel]
2
- universal = 0
3
-
4
1
  [zest.releaser]
5
2
  create-wheel = no
6
3
 
@@ -32,7 +32,7 @@ ext_modules = [
32
32
  join('include', 'Acquisition', 'Acquisition.h')]),
33
33
  ]
34
34
 
35
- version = '6.3'
35
+ version = '7.1'
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=[
@@ -60,12 +61,12 @@ setup(name='AccessControl',
60
61
  'Operating System :: OS Independent',
61
62
  'Programming Language :: Python',
62
63
  'Programming Language :: Python :: 3',
63
- 'Programming Language :: Python :: 3.7',
64
64
  'Programming Language :: Python :: 3.8',
65
65
  'Programming Language :: Python :: 3.9',
66
66
  'Programming Language :: Python :: 3.10',
67
67
  'Programming Language :: Python :: 3.11',
68
68
  'Programming Language :: Python :: 3.12',
69
+ 'Programming Language :: Python :: 3.13',
69
70
  'Programming Language :: Python :: Implementation :: CPython',
70
71
  ],
71
72
  ext_modules=ext_modules,
@@ -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,9 +88,8 @@ setup(name='AccessControl',
87
88
  'zope.schema',
88
89
  'zope.security',
89
90
  'zope.testing',
90
- 'funcsigs;python_version<"3.3"',
91
91
  ],
92
- python_requires='>=3.7',
92
+ python_requires='>=3.8',
93
93
  include_package_data=True,
94
94
  zip_safe=False,
95
95
  extras_require={
@@ -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)
@@ -78,7 +78,8 @@ class SecurityInfo(Implicit):
78
78
  def _setaccess(self, names, access):
79
79
  for name in names:
80
80
  if self.names.get(name, access) != access:
81
- LOG.warn('Conflicting security declarations for "%s"' % name)
81
+ LOG.warning(
82
+ f'Conflicting security declarations for {name!r}')
82
83
  self._warnings = 1
83
84
  self.names[name] = access
84
85
 
@@ -139,7 +140,7 @@ class SecurityInfo(Implicit):
139
140
  # the decorator returned is remembered in a set and will
140
141
  # remove itself upon call. self.apply will check for an empty
141
142
  # set and raise an AssertionError otherwise.
142
- key = "'{}':{}".format(permission_name, id(lambda x: x))
143
+ key = f"'{permission_name}':{id(lambda x: x)}"
143
144
 
144
145
  def decor(func):
145
146
  self.declareProtected(permission_name, func.__name__) # NOQA: D001
@@ -147,7 +148,7 @@ class SecurityInfo(Implicit):
147
148
  return func
148
149
  # make sure our key algo creates unique-enough keys
149
150
  if key in self._unused_protected_decorators:
150
- raise KeyError("Duplicate key: {}".format(key))
151
+ raise KeyError(f"Duplicate key: {key}")
151
152
  self._unused_protected_decorators.add(key)
152
153
  return decor
153
154
 
@@ -159,8 +160,8 @@ class SecurityInfo(Implicit):
159
160
  for role in roles:
160
161
  rdict[role] = 1
161
162
  if self.roles.get(permission_name, rdict) != rdict:
162
- LOG.warn('Conflicting default role'
163
- 'declarations for permission "%s"' % permission_name)
163
+ LOG.warning('Conflicting default role'
164
+ ' declarations for permission "%s"' % permission_name)
164
165
  self._warnings = 1
165
166
  self.roles[permission_name] = rdict
166
167
 
@@ -248,8 +249,8 @@ class ClassSecurityInfo(SecurityInfo):
248
249
  access)
249
250
 
250
251
  if getattr(self, '_warnings', None):
251
- LOG.warn('Class "%s" had conflicting '
252
- 'security declarations' % classobj.__name__)
252
+ LOG.warning('Class "%s" had conflicting'
253
+ ' security declarations' % classobj.__name__)
253
254
 
254
255
 
255
256
  class ClassSecurityInformation(ClassSecurityInfo):
@@ -343,8 +344,8 @@ class _ModuleSecurityInfo(SecurityInfo):
343
344
  dict['__allow_access_to_unprotected_subobjects__'] = self
344
345
 
345
346
  if getattr(self, '_warnings', None):
346
- LOG.warn('Module "%s" had conflicting '
347
- 'security declarations' % dict['__name__'])
347
+ LOG.warning('Module "%s" had conflicting'
348
+ ' security declarations' % dict['__name__'])
348
349
 
349
350
  declareProtected__roles__ = ACCESS_PRIVATE
350
351
 
@@ -12,6 +12,7 @@
12
12
  ##############################################################################
13
13
 
14
14
 
15
+ import collections.abc
15
16
  import math
16
17
  import random
17
18
  import string
@@ -127,13 +128,18 @@ def get_dict_pop(d, name):
127
128
  return guarded_pop
128
129
 
129
130
 
130
- def get_iter(c, name):
131
- iter = getattr(c, name)
131
+ def get_mapping_view(c, name):
132
132
 
133
- def guarded_iter():
134
- return SafeIter(iter(), c)
133
+ view_class = {
134
+ 'keys': SafeKeysView,
135
+ 'items': SafeItemsView,
136
+ 'values': SafeValuesView,
137
+ }
135
138
 
136
- return guarded_iter
139
+ def guarded_mapping_view():
140
+ return view_class[name](c)
141
+
142
+ return guarded_mapping_view
137
143
 
138
144
 
139
145
  def get_list_pop(lst, name):
@@ -153,18 +159,15 @@ _dict_white_list = {
153
159
  'copy': 1,
154
160
  'fromkeys': 1,
155
161
  'get': get_dict_get,
156
- 'items': 1,
162
+ 'items': get_mapping_view,
163
+ 'keys': get_mapping_view,
157
164
  'pop': get_dict_pop,
158
165
  'popitem': 1,
159
166
  'setdefault': 1,
160
167
  'update': 1,
168
+ 'values': get_mapping_view,
161
169
  }
162
170
 
163
- _dict_white_list.update({
164
- 'keys': get_iter,
165
- 'values': get_iter,
166
- })
167
-
168
171
 
169
172
  def _check_dict_access(name, value):
170
173
  # Check whether value is a dict method
@@ -272,6 +275,40 @@ class NullIter(SafeIter):
272
275
  next = __next__
273
276
 
274
277
 
278
+ # The following three view classes are used only for mappings of type `dict`
279
+ # (not subclasses). Therefore, the mapping does not have security assertions
280
+ # and cannot acquire ones. As a consequence, the `guard` calls used in their
281
+ # methods verify that the checked key or value is accessible based solely on
282
+ # its own virtues, i.e. either because it is public or has its own security
283
+ # assertions allowing access.
284
+ class _SafeMappingView:
285
+ __allow_access_to_unprotected_subobjects__ = 1
286
+
287
+ def __iter__(self):
288
+ for e in super().__iter__():
289
+ guard(self._mapping, e)
290
+ yield e
291
+
292
+
293
+ class SafeKeysView(_SafeMappingView, collections.abc.KeysView):
294
+ pass
295
+
296
+
297
+ class SafeValuesView(_SafeMappingView, collections.abc.ValuesView):
298
+ pass
299
+
300
+
301
+ class SafeItemsView(_SafeMappingView, collections.abc.ItemsView):
302
+ def __iter__(self):
303
+ for k, v in super().__iter__():
304
+ guard(self._mapping, k)
305
+ # When checking the value, we check the guard with index=None,
306
+ # not with index=k, the key name does not matter. guard just
307
+ # needs to verify that the value itself can be accessed.
308
+ guard(self._mapping, v)
309
+ yield k, v
310
+
311
+
275
312
  def _error(index):
276
313
  raise Unauthorized('unauthorized access to element')
277
314
 
@@ -454,7 +491,7 @@ def load_module(module, mname, mnameparts, validate, globals, locals):
454
491
  if mname is None:
455
492
  mname = nextname
456
493
  else:
457
- mname = '{}.{}'.format(mname, nextname)
494
+ mname = f'{mname}.{nextname}'
458
495
  # import (if not already imported) and check for MSI
459
496
  nextmodule = secureModule(mname, globals, locals)
460
497
  if nextmodule is None: # not allowed
@@ -425,7 +425,7 @@ static PyExtensionClass ZopeSecurityPolicyType = {
425
425
  0, /* tp_itemsize */
426
426
  /* Standard methods */
427
427
  (destructor) ZopeSecurityPolicy_dealloc,/* tp_dealloc */
428
- NULL, /* tp_print */
428
+ 0, /* tp_vectorcall_offset */
429
429
  NULL, /* tp_getattr */
430
430
  NULL, /* tp_setattr */
431
431
  NULL, /* tp_compare */
@@ -484,7 +484,7 @@ static PyExtensionClass SecurityManagerType = {
484
484
  0, /* tp_itemsize */
485
485
  /* Standard methods */
486
486
  (destructor) SecurityManager_dealloc,/* tp_dealloc */
487
- NULL, /* tp_print */
487
+ 0, /* tp_vectorcall_offset*/
488
488
  NULL, /* tp_getattr */
489
489
  NULL, /* tp_setattr */
490
490
  NULL, /* tp_compare */
@@ -543,7 +543,7 @@ static PyExtensionClass PermissionRoleType = {
543
543
  0, /* tp_itemsize */
544
544
  /* Standard methods */
545
545
  (destructor) PermissionRole_dealloc, /* tp_dealloc */
546
- NULL, /* tp_print */
546
+ 0, /* tp_vectorcall_offset */
547
547
  NULL, /* tp_getattr */
548
548
  NULL, /* tp_setattr */
549
549
  NULL, /* tp_compare */
@@ -613,7 +613,7 @@ static PyExtensionClass imPermissionRoleType = {
613
613
  0, /* tp_itemsize */
614
614
  /* Standard methods */
615
615
  (destructor) imPermissionRole_dealloc, /* tp_dealloc */
616
- NULL, /* tp_print */
616
+ 0, /* tp_vectorcall_offset */
617
617
  NULL, /* tp_getattr */
618
618
  NULL, /* tp_setattr */
619
619
  NULL, /* tp_compare */
@@ -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;
@@ -1071,7 +1072,7 @@ static PyObject *ZopeSecurityPolicy_validate(PyObject *self, PyObject *args) {
1071
1072
  **| try:
1072
1073
  **| if roles is None or 'Anonymous' in roles: return 1
1073
1074
  **| except TypeError:
1074
- **| LOG.warn('"%s' passed as roles"
1075
+ **| LOG.warning('"%s' passed as roles"
1075
1076
  **| " during validation of '%s' is not a sequence." %
1076
1077
  **| ('roles', name))
1077
1078
  **| raise
@@ -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
 
@@ -5,4 +5,4 @@ from logging import getLogger
5
5
 
6
6
 
7
7
  LOG = getLogger('AccessControl')
8
- warn = LOG.warn
8
+ warn = LOG.warning
@@ -123,6 +123,39 @@ def f7():
123
123
  access = getattr(d, meth)
124
124
  result = sorted(access())
125
125
  assert result == expected[kind], (meth, kind, result, expected[kind])
126
+ assert len(access()) == len(expected[kind]), (meth, kind, "len")
127
+ iter_ = access() # iterate twice on the same view
128
+ assert list(iter_) == list(iter_)
129
+
130
+ assert sorted([k for k in getattr(d, meth)()]) == expected[kind]
131
+ assert sorted(k for k in getattr(d, meth)()) == expected[kind]
132
+ assert {k: v for k, v in d.items()} == d
133
+
134
+ assert 1 in d
135
+ assert 1 in d.keys()
136
+ assert 2 in d.values()
137
+ assert (1, 2) in d.items()
138
+
139
+ assert d
140
+ assert d.keys()
141
+ assert d.values()
142
+ assert d.items()
143
+
144
+ empty_d = {}
145
+ assert not empty_d
146
+ assert not empty_d.keys()
147
+ assert not empty_d.values()
148
+ assert not empty_d.items()
149
+
150
+ smaller_d = {1: 2}
151
+ for m, _ in methods:
152
+ assert getattr(d, m)() != getattr(smaller_d, m)()
153
+ assert not getattr(d, m)() == getattr(smaller_d, m)()
154
+ if m != 'values':
155
+ assert getattr(d, m)() > getattr(smaller_d, m)()
156
+ assert getattr(d, m)() >= getattr(smaller_d, m)()
157
+ assert getattr(smaller_d, m)() < getattr(d, m)()
158
+ assert getattr(smaller_d, m)() <= getattr(d, m)()
126
159
 
127
160
 
128
161
  f7()
@@ -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', (),