From 211bf8a89fa774cb3b7758fbfc40bde3396545a3 Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Mon, 1 Jul 2013 16:02:10 +0900
Subject: [PATCH] ZODB Components: Ensure that Permissions on Developer can be
 updated later (through Filesystem).

In aed4f30, Permissions were set in ComponentTool __init__ and it could not be
modified later on because it is set directly in the ZODB. Moreover, Permissions
could be modified after execution by modifying attributes.
---
 product/ERP5Type/Tool/ComponentTool.py | 50 +++++++++++++-------------
 product/ERP5Type/dynamic/lazy_class.py |  8 +++++
 2 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/product/ERP5Type/Tool/ComponentTool.py b/product/ERP5Type/Tool/ComponentTool.py
index 1bc701cf3a..e9ee4e027a 100644
--- a/product/ERP5Type/Tool/ComponentTool.py
+++ b/product/ERP5Type/Tool/ComponentTool.py
@@ -62,32 +62,34 @@ class ComponentTool(BaseTool):
   security = ClassSecurityInfo()
   security.declareObjectProtected(Permissions.AccessContentsInformation)
 
-  def __init__(self, *args, **kwargs):
+  @classmethod
+  def _applyAllStaticSecurity(cls):
     """
-    Except 'Access *', 'View*' and 'WebDAV' permissions (Acquired) and 'Reset
-    dynamic classes' (Manager, required to reset Components), everything
-    requires Developer Role.
-
-    Another solution would be to load it from XML as it was previously done,
-    but from a security point of view, it's better to forbid everything and
-    allows only some.
-
-    XXX-arnau: Really all 'Access *' and 'View*'? nothing else?
+    Apply static security on portal_components to ensure that nobody can
+    change Permissions, only 'ghost' Developer Role has Permissions to
+    add/modify/delete Components. Also, make these permissions read-only
+    thanks to 'property'.
+
+    cls is erp5.portal_type.Component Tool and not this class as this function
+    is called on Portal Type class when loading Componet Tool Portal Type
+    class
     """
-    obj = BaseTool.__init__(self, *args, **kwargs)
-    for permission_tuple in self.ac_inherited_permissions(1):
-      name = permission_tuple[0]
-      value = permission_tuple[1]
-      if name == 'Reset dynamic classes':
-        p = Permission(name, value, self)
-        p.setRoles(('Manager',))
-      elif not (name.startswith('Access ') or
-                name.startswith('View') or
-                name.startswith('WebDAV')):
-        p = Permission(name, value, self)
-        p.setRoles(('Developer',))
-
-    return obj
+    # XXX-Cosmetic: From Zope >= 2.13, getPermissions() can be used instead of
+    # protected _registeredPermissions module attribute
+    from AccessControl.Permission import _registeredPermissions, pname
+    for permission_name in _registeredPermissions:
+      if permission_name == 'Reset dynamic classes':
+        permission_function = lambda self: ('Manager',)
+      elif permission_name in ('Change permissions', 'Define permissions'):
+        permission_function = lambda self: ()
+      elif not (permission_name.startswith('Access ') or
+                permission_name.startswith('View') or
+                permission_name.startswith('WebDAV')):
+        permission_function = lambda self: ('Developer',)
+      else:
+        continue
+
+      setattr(cls, pname(permission_name), property(permission_function))
 
   def _isBootstrapRequired(self):
     """
diff --git a/product/ERP5Type/dynamic/lazy_class.py b/product/ERP5Type/dynamic/lazy_class.py
index 0613552844..b7c3a268d6 100644
--- a/product/ERP5Type/dynamic/lazy_class.py
+++ b/product/ERP5Type/dynamic/lazy_class.py
@@ -193,9 +193,17 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
       pmc_init_of(subclass)
 
   def setupSecurity(cls):
+    apply_security_function = getattr(cls, '_applyAllStaticSecurity', None)
+    if apply_security_function:
+      apply_security_function()
+
     # note that after this call the 'security' attribute will be gone.
     InitializeClass(cls)
     for subclass in PortalTypeMetaClass.getSubclassList(cls):
+      apply_security_function = getattr(cls, '_applyAllStaticSecurity', None)
+      if apply_security_function:
+        apply_security_function()
+
       InitializeClass(subclass)
 
   def restoreGhostState(cls):
-- 
2.30.9