From d2e2d15d958d246b10f7dd3cdde5e1ffb810b8ea Mon Sep 17 00:00:00 2001
From: Tatuya Kamada <tatuya@nexedi.com>
Date: Mon, 5 Jul 2010 07:04:59 +0000
Subject: [PATCH] Fix a issue that BusinessTemplate unexpectedly removes the
 skins of another skin folder when upgrading. To fix the issue,
 (un)restrectedTraverse calling are replaced with the new method,
 (un)restrictedResolveValue.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@36841 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/BusinessTemplate.py | 77 +++++++++++++++++++++--
 1 file changed, 71 insertions(+), 6 deletions(-)

diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py
index 876b9a8faf..ab90069a71 100644
--- a/product/ERP5/Document/BusinessTemplate.py
+++ b/product/ERP5/Document/BusinessTemplate.py
@@ -32,7 +32,7 @@ from Shared.DC.ZRDB.Connection import Connection as RDBConnection
 from Products.ERP5Type.DiffUtils import DiffFile
 from Products.ERP5Type.Globals import Persistent, PersistentMapping
 from Acquisition import Implicit, aq_base, aq_inner, aq_parent
-from AccessControl import ClassSecurityInfo
+from AccessControl import ClassSecurityInfo, Unauthorized, getSecurityManager
 from Products.CMFCore.utils import getToolByName
 from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
 from Products.ERP5Type.Base import WorkflowMethod, _aq_reset
@@ -613,6 +613,71 @@ class BaseTemplateItem(Implicit, Persistent):
     """
     return self.__class__.__name__[:-12]
 
+  def restrictedResolveValue(self, context=None, relative_url=None, default=None):
+    """
+      Get the value with checking the security.
+      This method does not acquire the parent.
+    """
+    def restrictedGetOb(container, key, default):
+      validate = getSecurityManager().validate
+      obj = container._getOb(key, None)
+      if obj is not None:
+        try:
+          if not validate(container, container, key, obj):
+            raise Unauthorized('unauthorized access to element %s' % key)
+        except Unauthorized:
+          # if user can't access object try to return default passed
+          if default is not None:
+            return default
+          else:
+            raise
+      return obj
+    return self._resolveValue(context, relative_url, default, getOb=restrictedGetOb)
+
+  def unrestrictedResolveValue(self, context=None, relative_url=None, default=None):
+    """
+      Get the value without checking the security.
+      This method does not acquire the parent.
+    """
+    def unrestrictedGetOb(container, key, default):
+      return container._getOb(key, None)
+    return self._resolveValue(context, relative_url, default, getOb=unrestrictedGetOb)
+
+  def _resolveValue(self, context, relative_url, default, getOb=None):
+    """
+    Resolve the value without acquire the parent.
+    """
+    if isinstance(relative_url, basestring):
+      stack = relative_url.split('/')
+    else:
+      stack = list(relative_url)
+    stack.reverse()
+    value = None
+    if stack:
+      portal = aq_inner(self.getPortalObject())
+      # It can be passed with the context, so at first, searching from the context.
+      if context is None:
+        container = portal
+      else:
+        container = context
+      key = stack.pop()
+      value = getOb(container, key, default)
+
+      # resolve the value from top to down
+      while value is not None and stack:
+        key = stack.pop()
+        value = value._getOb(key, default)
+    else:
+      # When relative_url is empty, returns the context
+      return context
+
+    if value is None:
+      LOG('BusinessTemplate', WARNING,
+          'Could not access object %s' % relative_url)
+
+    return value
+
+
 class ObjectTemplateItem(BaseTemplateItem):
   """
     This class is used for generic objects and as a subclass.
@@ -933,7 +998,7 @@ class ObjectTemplateItem(BaseTemplateItem):
           container_path = path_list[:-1]
           object_id = path_list[-1]
           try:
-            container = portal.unrestrictedTraverse(container_path)
+            container = self.unrestrictedResolveValue(portal, container_path)
           except KeyError:
             # parent object can be set to nothing, in this case just go on
             container_url = '/'.join(container_path)
@@ -1162,7 +1227,7 @@ class ObjectTemplateItem(BaseTemplateItem):
         if recursive_path in update_dict:
           action = update_dict[recursive_path]
           if action in ('remove', 'save_and_remove'):
-            document = portal.restrictedTraverse(recursive_path, None)
+            document = self.restrictedResolveValue(portal, recursive_path, None)
             if document is None:
               # It happens if the parent of target path is removed before
               continue
@@ -1212,7 +1277,7 @@ class ObjectTemplateItem(BaseTemplateItem):
       container_path = relative_url.split('/')[0:-1]
       object_id = relative_url.split('/')[-1]
       try:
-        container = portal.unrestrictedTraverse(container_path)
+        container = self.unrestrictedResolveValue(portal, container_path)
         object = container._getOb(object_id) # We force access to the object to be sure
                                         # that appropriate exception is thrown
                                         # in case object is already backup and/or removed
@@ -1270,7 +1335,7 @@ class PathTemplateItem(ObjectTemplateItem):
         try:
           container_path = relative_url.split('/')[0:-1]
           object_id = relative_url.split('/')[-1]
-          container = portal.unrestrictedTraverse(container_path)
+          container = self.unrestrictedResolveValue(portal, container_path)
           if trash and trashbin is not None:
             self.portal_trash.backupObject(trashbin, container_path,
                                            object_id, save=1,
@@ -1757,7 +1822,7 @@ class WorkflowTemplateItem(ObjectTemplateItem):
           container_path = path.split('/')[:-1]
           object_id = path.split('/')[-1]
           try:
-            container = portal.unrestrictedTraverse(container_path)
+            container = self.unrestrictedResolveValue(portal, container_path)
           except KeyError:
             # parent object can be set to nothing, in this case just go on
             container_url = '/'.join(container_path)
-- 
2.30.9