Commit 3a6e9d3b authored by iv's avatar iv

ERP5Workflow: use patched version of updateRoleMappingsFor

and improve its performance by avoiding multiple calls to ac_inherited_permissions
by calling multiple times modifyRolesForPermission, by passing a dict as parameter
parent 700675ee
......@@ -31,6 +31,7 @@ import sys
from AccessControl import ClassSecurityInfo
from AccessControl.unauthorized import Unauthorized
from AccessControl.Permission import Permission
from AccessControl.SecurityManagement import getSecurityManager
from Acquisition import aq_base, aq_inner, aq_parent
from copy import deepcopy
......@@ -44,8 +45,7 @@ from Products.CMFCore.WorkflowCore import WorkflowException, ObjectDeleted,\
ObjectMoved
from Products.DCWorkflow.DCWorkflow import ValidationFailed
from Products.DCWorkflow.Expression import StateChangeInfo
from Products.DCWorkflow.utils import Message as _
from Products.DCWorkflow.utils import modifyRolesForPermission
from Products.DCWorkflow.utils import ac_inherited_permissions, Message as _
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.Globals import PersistentMapping
......@@ -65,6 +65,38 @@ from zLOG import LOG, INFO, WARNING
ACTIVITY_GROUPING_COUNT = 100
def modifyRolesForPermissionDict(ob, new_permission_roles_dict):
# copied and modified version of modifyRolesForPermission
# in Products.DCWorkflow.utils
# this has been refactored to pass a dict as parameter
# and avoid multiple expensive calls to ac_inherited_permissions
'''
Modifies multiple role to permission mappings. roles is a list to
acquire, a tuple to not acquire.
'''
# This mimics what AccessControl/Role.py does.
new_permission_roles_dict_length = len(new_permission_roles_dict)
modified_dict = {}
for permission in ac_inherited_permissions(ob, 1):
name, value = permission[:2]
if name in new_permission_roles_dict:
p = Permission(name, value, ob)
new_roles = new_permission_roles_dict[name]
if p.getRoles() != new_roles:
p.setRoles(new_roles)
modified_dict[name] = True
else:
modified_dict[name] = False
# once modified_dict is fulled with according permissions, there is no
# need to continue to loop over all possible permissions
if len(modified_dict) == new_permission_roles_dict_length:
break
return modified_dict
class Workflow(IdAsReferenceMixin("", "prefix"), XMLObject):
"""
A ERP5 Workflow.
......@@ -214,25 +246,65 @@ class Workflow(IdAsReferenceMixin("", "prefix"), XMLObject):
return transition
security.declarePrivate('updateRoleMappingsFor')
def updateRoleMappingsFor(self, document):
"""
Changes the object permissions according to the current state.
"""
def updateRoleMappingsFor(self, ob):
# Patch updateRoleMappingsFor so that if 2 workflows define security, then
# we should do an AND operation between each permission
'''
Changes the object permissions according to the current
state.
'''
changed = False
state = self._getWorkflowStateOf(document, id_only=False)
if state is not None:
state_permission_list = state.getAcquirePermissionList()
for permission, role_list in state.getStatePermissionRolesDict().items():
# tuple means "don't acquire" in zope internal security and list
# is used when acquisition should be done
if permission in state_permission_list:
role_list = list(role_list)
else:
role_list = tuple(role_list)
if modifyRolesForPermission(document, permission, role_list):
state = self._getWorkflowStateOf(ob)
tool = aq_parent(aq_inner(self))
other_workflow_list = [x for x in tool.getWorkflowsFor(ob)
if x.id != self.id and x.getPortalType() in
('DCWorkflowDefinition', 'Workflow')]
other_data_list = []
new_permission_roles_dict = {}
for other_workflow in other_workflow_list:
other_state = other_workflow._getWorkflowStateOf(ob)
if other_state is not None:
other_state_permission_roles_dict = other_state.getStatePermissionRolesDict()
if other_state_permission_roles_dict is not None:
other_data_list.append((other_workflow,other_state))
# Be carefull, permissions_roles should not change
# from list to tuple or vice-versa. (in modifyRolesForPermission,
# list means acquire roles, tuple means do not acquire)
if state is not None and self.workflow_managed_permission:
for p in self.workflow_managed_permission:
roles = []
refused_roles = []
other_role_type_list = []
state_permission_roles_dict = state.getStatePermissionRolesDict()
if state_permission_roles_dict is not None:
roles = state_permission_roles_dict.get(p, roles)
role_type = type(roles)
roles = list(roles)
# We will check that each role is activated
# in each DCWorkflow
for other_workflow, other_state in other_data_list:
if p in other_workflow.workflow_managed_permission:
other_roles = other_state.getStatePermissionRolesDict().get(p, [])
other_role_type_list.append(type(other_roles))
for role in roles:
if role not in other_roles:
refused_roles.append(role)
for role in refused_roles:
if role in roles:
roles.remove(role)
if role_type is tuple and ((not other_role_type_list) or
(list not in other_role_type_list)):
# If at least, one of other workflows manage security and for all
# are role_type are tuple
roles = tuple(roles)
new_permission_roles_dict[p] = roles
if True in modifyRolesForPermissionDict(ob, new_permission_roles_dict).values():
changed = True
return changed
# This method allows to update all objects using one workflow, for example
# after the permissions per state for this workflow were modified
def updateRoleMappings(self, REQUEST=None):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment