From 9cfe4e1c0d0a7219a4f9c32eb2a2ea7588e6278f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com> Date: Wed, 1 Apr 2020 09:35:23 +0200 Subject: [PATCH] patches/Restricted: Fix the way we allow classes make our type checker falseish, so that ZopeGuard security checks don't short circuit this. --- product/ERP5Type/patches/Restricted.py | 60 +++++++++++++++----------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/product/ERP5Type/patches/Restricted.py b/product/ERP5Type/patches/Restricted.py index 7074b64c6f..44b808f81d 100644 --- a/product/ERP5Type/patches/Restricted.py +++ b/product/ERP5Type/patches/Restricted.py @@ -81,36 +81,48 @@ def allow_class_attribute(klass, access=1): assert(inspect.isclass(klass)) _safe_class_attribute_dict[klass] = access -def _check_type_access(name, v): + +class TypeAccessChecker: + """Check Access for class instances (whose type() is `type`). """ - Create a method which checks the access if the context type is <type 'type'>s. + def __call__(self, name, v): + """ + Create a callable which checks the access if the context type is <type 'type'>s. Since the 'type' can be any types of classes, we support the three ways defined in AccessControl/SimpleObjectPolicies. We implement this as "a method which returing a method" because we can not know what is the type until it is actually called. So the three ways are simulated the - returning method inide this method. - """ - def factory(inst, name): + function returned by this method. """ - Check function used with ContainerAssetions checked by cAccessControl. - """ - access = _safe_class_attribute_dict.get(inst, 0) - # The next 'dict' only checks the access configuration type - if access == 1 or (isinstance(access, dict) and access.get(name, 0) == 1): - pass - elif isinstance(access, dict) and callable(access.get(name, 0)): - guarded_method = access.get(name) - return guarded_method(inst, name) - elif callable(access): - # Only check whether the access configuration raise error or not - access(inst, name) - else: - # fallback to default security - aq_acquire(inst, name, aq_validate, getSecurityManager().validate) - return v - return factory - -ContainerAssertions[type] = _check_type_access + def factory(inst, name): + """ + Check function used with ContainerAssertions checked by cAccessControl. + """ + access = _safe_class_attribute_dict.get(inst, 0) + # The next 'dict' only checks the access configuration type + if access == 1 or (isinstance(access, dict) and access.get(name, 0) == 1): + pass + elif isinstance(access, dict) and callable(access.get(name, 0)): + guarded_method = access.get(name) + return guarded_method(inst, name) + elif callable(access): + # Only check whether the access configuration raise error or not + access(inst, name) + else: + # fallback to default security + aq_acquire(inst, name, aq_validate, getSecurityManager().validate) + return v + return factory + + def __nonzero__(self): + # If Containers(type(x)) is true, ZopeGuard checks will short circuit, + # thinking it's a simple type, but we don't want this for type, because + # type(x) is type for classes, being trueish would skip security check on + # classes. + return False + +ContainerAssertions[type] = TypeAccessChecker() + class SafeIterItems(SafeIter): -- 2.30.9