Commit fddb39e7 authored by Julien Muchembled's avatar Julien Muchembled

Interfaces for roles

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/portal_types@29254 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 86f086de
......@@ -33,6 +33,10 @@ import unittest
# this list can be generated automatically using introspection or can be set
# manually and treated as reference to what implements what
implements_tuple_list = [
## ERP5Type
('ERP5Type.ERP5Type.ERP5TypeInformation', 'ILocalRoleAssignor'),
('RoleInformation', 'ILocalRoleGenerator'),
## ERP5
('BusinessPath', 'IArrowBase'),
('BusinessPath', 'IBusinessPath'),
('BusinessPath', 'ICategoryAccessProvider'),
......@@ -67,13 +71,14 @@ class TestERP5Interfaces(ERP5TypeTestCase):
from Products.ERP5.interfaces.transformation import IAggregatedAmountList
from Products.ERP5.AggregatedAmountList import AggregatedAmountList
verifyClass(IAggregatedAmountList, AggregatedAmountList)
def makeTestMethod(document, interface):
"""Common method which checks if documents implements interface"""
def testMethod(self):
_temp = __import__('Products.ERP5Type.Document.%s' % document, globals(),
locals(), ['%s' % document])
Document = getattr(_temp, document)
parts = document.rsplit('.', 1)
module = len(parts) > 1 and parts[0] or 'ERP5Type.Document.' + parts[-1]
_temp = __import__('Products.' + module, globals(), locals(), parts[-1:])
Document = getattr(_temp, parts[-1])
_temp = __import__('Products.ERP5Type.interfaces', globals(), locals(),
['%s' % interface])
Interface = getattr(_temp, interface)
......
......@@ -13,6 +13,7 @@
""" Information about customizable roles.
"""
import zope.interface
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Globals import InitializeClass
......@@ -21,7 +22,7 @@ from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.Expression import Expression
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type import interfaces, Permissions, PropertySheet
from Products.ERP5Type.ERP5Type \
import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
from Products.ERP5Type.Permissions import AccessContentsInformation
......@@ -34,8 +35,6 @@ class RoleInformation(XMLObject):
Roles definitions defines local roles on ERP5Type documents. They are
applied by the updateLocalRolesOnDocument method.
"""
# ILocalRoleGenerator
meta_type = 'ERP5 Role Information'
portal_type = 'Role Information'
add_permission = Permissions.AddPortalContent
......@@ -45,6 +44,8 @@ class RoleInformation(XMLObject):
security = ClassSecurityInfo()
security.declareObjectProtected(AccessContentsInformation)
zope.interface.implements(interfaces.ILocalRoleGenerator)
# Declarative properties
property_sheets = ( PropertySheet.CategoryCore
, PropertySheet.DublinCore
......
......@@ -20,6 +20,7 @@
#
##############################################################################
import zope.interface
from Globals import InitializeClass, DTMLFile
from AccessControl import ClassSecurityInfo, getSecurityManager
from Acquisition import aq_base, aq_inner, aq_parent
......@@ -36,12 +37,12 @@ from Products.CMFCore.utils import SimpleItemWithProperties
from Products.CMFCore.Expression import createExprContext, Expression
from Products.CMFCore.exceptions import AccessControl_Unauthorized
from Products.CMFCore.utils import _checkPermission
from Products.ERP5Type import PropertySheet
from Products.ERP5Type import _dtmldir
from Products.ERP5Type import Permissions
from Products.ERP5Type import _dtmldir, interfaces, Permissions, PropertySheet
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Type.XMLObject import XMLObject
ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT = 'ERP5Type_asSecurityGroupId'
# Security uses ERP5Security by default
try:
from Products.ERP5Security import ERP5UserManager
......@@ -61,10 +62,158 @@ from sys import exc_info
from zLOG import LOG, ERROR
from Products.CMFCore.exceptions import zExceptions_Unauthorized
ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT = 'ERP5Type_asSecurityGroupId'
class LocalRoleAssignorMixIn(object):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
zope.interface.implements(interfaces.ILocalRoleAssignor)
security.declarePrivate('updateLocalRolesOnObject')
def updateLocalRolesOnDocument(self, *args, **kw):
return UnrestrictedMethod(self._updateLocalRolesOnDocument)(*args, **kw)
def _updateLocalRolesOnDocument(self, ob, user_name=None, reindex=True):
"""
Assign Local Roles to Groups on object 'ob', based on Portal Type Role
Definitions and "ERP5 Role Definition" objects contained inside 'ob'.
"""
#FIXME We should check the type of the acl_users folder instead of
# checking which product is installed.
if user_name is None:
# First try to guess from the owner
try:
user_name = ob.getOwnerInfo()['id']
except (AttributeError, TypeError):
pass
if user_name is None:
if ERP5UserManager is not None:
# We use id for roles in ERP5Security
user_name = getSecurityManager().getUser().getId()
elif NuxUserGroups is not None:
user_name = getSecurityManager().getUser().getUserName()
else:
raise RuntimeError, 'Product "ERP5Security" was not found on'\
'your setup. '\
'Please install it to benefit from group-based security'
group_id_role_dict = self.getLocalRolesFor(ob, user_name)
# Update role assignments to groups
if ERP5UserManager is not None: # Default implementation
# Clean old group roles
old_group_list = ob.get_local_roles()
ob.manage_delLocalRoles([x[0] for x in old_group_list])
# Save the owner
for group, role_list in old_group_list:
if 'Owner' in role_list:
group_id_role_dict.setdefault(group, set()).add('Owner')
# Assign new roles
for group, role_list in group_id_role_dict.iteritems():
if role_list:
ob.manage_addLocalRoles(group, role_list)
else: # NuxUserGroups implementation
# Clean old group roles
old_group_list = ob.get_local_group_roles()
# We duplicate role settings to mimic PAS
ob.manage_delLocalGroupRoles([x[0] for x in old_group_list])
ob.manage_delLocalRoles([x[0] for x in old_group_list])
# Save the owner
for group, role_list in old_group_list:
if 'Owner' in role_list:
group_id_role_dict.setdefault(group, set()).add('Owner')
# Assign new roles
for group, role_list in group_id_role_dict.iteritems():
# We duplicate role settings to mimic PAS
ob.manage_addLocalGroupRoles(group, role_list)
ob.manage_addLocalRoles(group, role_list)
# Make sure that the object is reindexed
if reindex:
ob.reindexObjectSecurity()
security.declarePrivate("getLocalRolesFor")
def getLocalRolesFor(self, ob, user_name=None):
"""Compute the security that should be applied on an object
Returned value is a dict: {groud_id: role_name_set, ...}
"""
group_id_role_dict = {}
# Merge results from applicable roles
for role in self.getFilteredRoleListFor(ob):
for group_id, role_list \
in role.getLocalRolesFor(ob, user_name).iteritems():
group_id_role_dict.setdefault(group_id, set()).update(role_list)
return group_id_role_dict
security.declarePrivate('getFilteredRoleListFor')
def getFilteredRoleListFor(self, ob=None):
"""Return all role generators applicable to the object."""
portal = self.getPortalObject()
if ob is None:
folder = portal
else:
folder = aq_parent(ob)
# Search up the containment hierarchy until we find an
# object that claims it's a folder.
while folder is not None:
if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
break # found it.
else:
folder = aq_parent(folder)
ec = createExprContext(folder, portal, ob)
for role in self.getRoleInformationList():
if role.testCondition(ec):
yield role
# Return also explicit local roles defined as subobjects of the document
if getattr(aq_base(ob), 'isPrincipiaFolderish', 0) and \
self.allowType('Role Definition'):
for role in ob.objectValues(portal_type='Role Definition'):
if role.getRoleName():
yield role
security.declareProtected(Permissions.AccessContentsInformation,
'getRoleInformationList')
def getRoleInformationList(self):
"""Return all Role Information objects stored on this portal type"""
return self.objectValues(portal_type='Role Information')
security.declareProtected(Permissions.ModifyPortalContent,
'updateRoleMapping')
def updateRoleMapping(self, REQUEST=None, form_id=''):
"""Update the local roles in existing objects.
XXX This should be implemented the same way as
ERP5Site_checkCatalogTable (cf erp5_administration).
"""
portal = self.getPortalObject()
update_role_tag = self.__class__.__name__ + ".updateRoleMapping"
object_list = [x.path for x in
portal.portal_catalog(portal_type=self.id, limit=None)]
object_list_len = len(object_list)
# We need to use activities in order to make sure it will
# work for an important number of objects
activate = portal.portal_activities.activate
for i in xrange(0, object_list_len, 100):
current_path_list = object_list[i:i+100]
activate(activity='SQLQueue', priority=3, tag=update_role_tag) \
.callMethodOnObjectList(current_path_list,
'updateLocalRolesOnSecurityGroups',
reindex=False)
activate(activity='SQLQueue', priority=3, after_tag=update_role_tag) \
.callMethodOnObjectList(current_path_list,
'reindexObjectSecurity')
if REQUEST is not None:
message = '%d objects updated' % object_list_len
return REQUEST.RESPONSE.redirect('%s/%s?portal_status_message=%s'
% (self.absolute_url_path(), form_id, message))
class ERP5TypeInformation(XMLObject,
FactoryTypeInformation,
LocalRoleAssignorMixIn,
TranslationProviderBase):
"""
ERP5 Types are based on FactoryTypeInformation
......@@ -89,12 +238,7 @@ class ERP5TypeInformation(XMLObject,
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.SimpleItem
, PropertySheet.Folder
, PropertySheet.BaseType
)
property_sheets = ( PropertySheet.BaseType, )
acquire_local_roles = False
property_sheet_list = ()
......@@ -317,140 +461,6 @@ class ERP5TypeInformation(XMLObject,
id = id + "d"
return factory_method(portal, id).propertyMap()
security.declarePrivate('updateLocalRolesOnObject')
def updateLocalRolesOnDocument(self, *args, **kw):
return UnrestrictedMethod(self._updateLocalRolesOnDocument)(*args, **kw)
def _updateLocalRolesOnDocument(self, ob, user_name=None, reindex=True):
"""
Assign Local Roles to Groups on object 'ob', based on Portal Type Role
Definitions and "ERP5 Role Definition" objects contained inside 'ob'.
"""
#FIXME We should check the type of the acl_users folder instead of
# checking which product is installed.
if user_name is None:
# First try to guess from the owner
try:
user_name = ob.getOwnerInfo()['id']
except (AttributeError, TypeError):
pass
if user_name is None:
if ERP5UserManager is not None:
# We use id for roles in ERP5Security
user_name = getSecurityManager().getUser().getId()
elif NuxUserGroups is not None:
user_name = getSecurityManager().getUser().getUserName()
else:
raise RuntimeError, 'Product "ERP5Security" was not found on'\
'your setup. '\
'Please install it to benefit from group-based security'
group_id_role_dict = self.getLocalRolesFor(ob, user_name)
# Update role assignments to groups
if ERP5UserManager is not None: # Default implementation
# Clean old group roles
old_group_list = ob.get_local_roles()
ob.manage_delLocalRoles([x[0] for x in old_group_list])
# Save the owner
for group, role_list in old_group_list:
if 'Owner' in role_list:
group_id_role_dict.setdefault(group, set()).add('Owner')
# Assign new roles
for group, role_list in group_id_role_dict.iteritems():
if role_list:
ob.manage_addLocalRoles(group, role_list)
else: # NuxUserGroups implementation
# Clean old group roles
old_group_list = ob.get_local_group_roles()
# We duplicate role settings to mimic PAS
ob.manage_delLocalGroupRoles([x[0] for x in old_group_list])
ob.manage_delLocalRoles([x[0] for x in old_group_list])
# Save the owner
for group, role_list in old_group_list:
if 'Owner' in role_list:
group_id_role_dict.setdefault(group, set()).add('Owner')
# Assign new roles
for group, role_list in group_id_role_dict.iteritems():
# We duplicate role settings to mimic PAS
ob.manage_addLocalGroupRoles(group, role_list)
ob.manage_addLocalRoles(group, role_list)
# Make sure that the object is reindexed
if reindex:
ob.reindexObjectSecurity()
security.declarePrivate("getLocalRolesFor")
def getLocalRolesFor(self, ob, user_name=None):
"""Compute the security that should be applied on an object
Returned value is a dict: {groud_id: role_name_set, ...}
"""
group_id_role_dict = {}
# Merge results from applicable roles
for role in self.getFilteredRoleListFor(ob):
for group_id, role_list \
in role.getLocalRolesFor(ob, user_name).iteritems():
group_id_role_dict.setdefault(group_id, set()).update(role_list)
return group_id_role_dict
security.declarePrivate('getFilteredRoleListFor')
def getFilteredRoleListFor(self, ob=None):
"""Return all roles applicable to the object."""
portal = self.getPortalObject()
if ob is None:
folder = portal
else:
folder = aq_parent(ob)
# Search up the containment hierarchy until we find an
# object that claims it's a folder.
while folder is not None:
if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
break # found it.
else:
folder = aq_parent(folder)
ec = createExprContext(folder, portal, ob)
for role in self.getRoleInformationList():
if role.testCondition(ec):
yield role
# Return also explicit local roles defined as subobjects of the document
if getattr(aq_base(ob), 'isPrincipiaFolderish', 0) and \
self.allowType('Role Definition'):
for role in ob.objectValues(portal_type='Role Definition'):
if role.getRoleName():
yield role
security.declareProtected(Permissions.ManagePortal, 'updateRoleMapping')
def updateRoleMapping(self, REQUEST=None, form_id=''):
"""Update the local roles in existing objects.
XXX This should be implemented the same way as
ERP5Site_checkCatalogTable (cf erp5_administration).
"""
portal = self.getPortalObject()
update_role_tag = self.__class__.__name__ + ".updateRoleMapping"
object_list = [x.path for x in
portal.portal_catalog(portal_type=self.id, limit=None)]
object_list_len = len(object_list)
# We need to use activities in order to make sure it will
# work for an important number of objects
activate = portal.portal_activities.activate
for i in xrange(0, object_list_len, 100):
current_path_list = object_list[i:i+100]
activate(activity='SQLQueue', priority=3, tag=update_role_tag) \
.callMethodOnObjectList(current_path_list,
'updateLocalRolesOnSecurityGroups',
reindex=False)
activate(activity='SQLQueue', priority=3, after_tag=update_role_tag) \
.callMethodOnObjectList(current_path_list,
'reindexObjectSecurity')
if REQUEST is not None:
message = '%d objects updated' % object_list_len
return REQUEST.RESPONSE.redirect('%s/%s?portal_status_message=%s'
% (self.absolute_url_path(), form_id, message))
#
# Helper methods
#
......@@ -477,7 +487,7 @@ class ERP5TypeInformation(XMLObject,
security.declareProtected(Permissions.AccessContentsInformation,
'PrincipiaSearchSource')
def PrincipiaSearchSource(self):
"""Return keywords for "Find" tab in ZMI"""
"""Return keywords for "Find" tab in ZMI"""
search_source_list = [self.getId(),
self.getTypeFactoryMethodId(),
self.getTypeAddPermission(),
......@@ -486,13 +496,6 @@ class ERP5TypeInformation(XMLObject,
search_source_list += self.getTypeBaseCategoryList(())
return ' '.join(filter(None, search_source_list))
security.declareProtected(Permissions.AccessContentsInformation,
'getRoleInformationList')
def getRoleInformationList(self):
"""Return all Role Information objects stored on this portal type"""
return self.objectValues(portal_type='Role Information')
security.declareProtected(Permissions.AccessContentsInformation,
'getActionInformationList')
def getActionInformationList(self):
......
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