From 7c0bc43781d7366084827a7836e3ccf0e8303c73 Mon Sep 17 00:00:00 2001 From: Alexandre Boeglin <alex@nexedi.com> Date: Mon, 5 Sep 2005 08:00:17 +0000 Subject: [PATCH] Modified the security management : it still depends on category values, but only the part that is site specific has been taken out. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3735 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5Type/ERP5Type.py | 99 +++++++++++++++++++---- product/ERP5Type/RoleInformation.py | 60 +++++--------- product/ERP5Type/RoleProviderBase.py | 18 ++--- product/ERP5Type/dtml/editToolsRoles.dtml | 20 ++--- 4 files changed, 119 insertions(+), 78 deletions(-) diff --git a/product/ERP5Type/ERP5Type.py b/product/ERP5Type/ERP5Type.py index 1e3d353bea..e4bfc992e6 100755 --- a/product/ERP5Type/ERP5Type.py +++ b/product/ERP5Type/ERP5Type.py @@ -21,7 +21,7 @@ ############################################################################## from Globals import InitializeClass, DTMLFile -from AccessControl import ClassSecurityInfo +from AccessControl import ClassSecurityInfo, getSecurityManager from Acquisition import aq_base, aq_inner, aq_parent import Products.CMFCore.TypesTool @@ -39,7 +39,7 @@ from RoleInformation import ori from zLOG import LOG -ERP5TYPE_ROLE_INIT_SCRIPT = 'ERP5Type_initLocalRoleMapping' +ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT = 'ERP5TypeSecurity_asGroupId' class ERP5TypeInformation( FactoryTypeInformation, RoleProviderBase ): """ @@ -106,7 +106,7 @@ class ERP5TypeInformation( FactoryTypeInformation, RoleProviderBase ): hidden_content_type_list = () filter_actions = 0 allowed_action_list = [] - + # # Acquisition editing interface # @@ -119,7 +119,7 @@ class ERP5TypeInformation( FactoryTypeInformation, RoleProviderBase ): """ self.setMethodAliases({}) return 1 - + security.declarePublic('hideFromAddMenu') def hidenFromAddMenu(self): """ @@ -140,24 +140,18 @@ class ERP5TypeInformation( FactoryTypeInformation, RoleProviderBase ): """ ob = FactoryTypeInformation.constructInstance(self, container, id, *args, **kw) - # Only try to find the local role init script - # if some roles are defined + # Only try to assign roles to secutiry groups if some roles are defined # This is an optimisation to prevent defining local roles on subobjects # which acquire their security definition from their parent - # The downside of this optimisation is that it is not possible to + # The downside of this optimisation is that it is not possible to # set a local role definition if the local role list is empty if len(self._roles): - init_role_script = getattr(ob, ERP5TYPE_ROLE_INIT_SCRIPT, None) - if init_role_script is not None: - # Retrieve applicable roles - role_mapping = self.getFilteredRoleListFor(object = self) # kw provided in order to take any appropriate action - # Call the local role init script - init_role_script(role_mapping = role_mapping, **kw) + self.assignRoleToSecurityGroup(ob) if self.init_script: - # Acquire the init script in the context of this object - init_script = getattr(ob, self.init_script) - init_script(*args, **kw) + # Acquire the init script in the context of this object + init_script = getattr(ob, self.init_script) + init_script(*args, **kw) return ob @@ -193,6 +187,79 @@ class ERP5TypeInformation( FactoryTypeInformation, RoleProviderBase ): result.sort() return result + security.declareProtected(ERP5Permissions.ModifyPortalContent, 'assignRoleToSecurityGroup') + def assignRoleToSecurityGroup(self, object): + """ + Assign Local Roles to Groups on object, based on Portal Type Role Definitions + """ + user_name = getSecurityManager().getUser().getUserName() + # First of all, check that NuxUserGroups is here. Otherwise, it's not possible to give Roles to Groups + try: + import Products.NuxUserGroups + except ImportError: + raise RuntimeError, 'Product "NuxUserGroups" was not found on your setup. '\ + 'Please install it to benefit from group-based security' + + + # Retrieve applicable roles + role_mapping = self.getFilteredRoleListFor(object = self) # kw provided in order to take any appropriate action + role_category_list = {} + for role, definition_list in role_mapping.items(): + if not role_category_list.has_key(role): + role_category_list[role] = [] + # For each role definition, we look for the base_category_script + # and try to use it to retrieve the values for the base_category list + for definition in definition_list: + base_category_script = getattr(object, definition['base_category_script'], None) + if base_category_script is not None: + # call the script, which should return either a dict or a list of dicts + category_result = base_category_script(definition['base_category'], user_name, object, object.getPortalType()) + # we also need to store the user specified order of categories, as dict are not ordered + category_order_list = [] + category_order_list.extend(definition['base_category']) + for c in definition['category']: + bc = c.split('/')[0] + if bc not in category_order_list: + category_order_list.append(bc) + # add the result to role_category_list + if type(category_result) is type({}): + category_result = [category_result] + for category_dict in category_result: + category_value_dict = {'category_order':category_order_list} + category_value_dict.update(category_dict) + for c in definition['category']: + bc, value = c.split('/', 1) + category_value_dict[bc] = value + role_category_list[role].append(category_value_dict) + + # Generate security group ids from category_value_dicts + role_group_id_dict = {} + group_id_generator = getattr(object, ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT, None) + if group_id_generator is None: + raise RuntimeError, '%s script was not found' % ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT + for role, value_list in role_category_list.items(): + if not role_group_id_dict.has_key(role): + role_group_id_dict[role] = [] + role_group_dict = {} + for category_dict in value_list: + group_id = group_id_generator(**category_dict) + role_group_dict[group_id] = 1 + role_group_id_dict[role].extend(role_group_dict.keys()) + + #Switch index from role to group id + group_id_role_dict = {} + for role, group_list in role_group_id_dict.items(): + for group_id in group_list: + if not group_id_role_dict.has_key(group_id): + group_id_role_dict[group_id] = [] + group_id_role_dict[group_id].append(role) + #Clean old group roles + old_group_list = object.get_local_group_roles() + object.manage_delLocalGroupRoles([x[0] for x in old_group_list]) + #Assign new roles + for group, role_list in group_id_role_dict.items(): + object.manage_addLocalGroupRoles(group, role_list) + security.declarePublic('getFilteredRoleListFor') def getFilteredRoleListFor(self, object=None, **kw): """ diff --git a/product/ERP5Type/RoleInformation.py b/product/ERP5Type/RoleInformation.py index 78cad77b55..806dc9a3f6 100755 --- a/product/ERP5Type/RoleInformation.py +++ b/product/ERP5Type/RoleInformation.py @@ -31,7 +31,7 @@ from types import StringType class RoleInformation( SimpleItem ): """ Represent a single selectable role. - + Roles generate links to views of content, or to specific methods of the site. They can be filtered via their conditions. """ @@ -48,24 +48,21 @@ class RoleInformation( SimpleItem ): , condition='' , priority=10 , base_category=() - , user='' + , base_category_script='' ): """ Set up an instance. """ if condition and type( condition ) == type( '' ): condition = Expression( condition ) - if user and type( user ) == type( '' ): - user = Expression( user ) - self.id = id self.title = title self.description = description - self.category = category + self.category = category self.condition = condition - self.priority = priority + self.priority = priority self.base_category = base_category - self.user = user + self.base_category_script = base_category_script security.declareProtected( View, 'Title' ) def Title(self): @@ -100,36 +97,10 @@ class RoleInformation( SimpleItem ): info = {} info['id'] = self.id info['name'] = self.Title() - expr = self.getUserExpression() - __traceback_info__ = (info['id'], info['name'], expr) - if self.user: - info['user'] = self.user( ec ) or None - else: - info['user'] = getSecurityManager().getUser() # XXX The user should be a handle to the Person object info['category'] = self.getCategory() info['base_category'] = self.getBaseCategory() - return info - - security.declarePublic( 'getUserExpression' ) - def getUserExpression( self ): - - """ Return the text of the TALES expression for our URL. - """ - user = getattr(self, 'user', '') - expr = user and user.text or '' - if expr and type( expr ) is StringType: - if not expr.startswith('python:') and not expr.startswith('string:'): - expr = 'string:${object_url}/%s' % expr - self.user = Expression( expr ) - return expr - - security.declarePrivate( 'setRoleExpression' ) - def setUserExpression(self, user): - if user and type( user ) is StringType: - if not user.startswith('python:') and not user.startswith('string:'): - user = 'string:${object_url}/%s' % user - user = Expression( user ) - self.user = user + info['base_category_script'] = self.getBaseCategoryScript() + return info security.declarePublic( 'getCondition' ) def getCondition(self): @@ -141,9 +112,9 @@ class RoleInformation( SimpleItem ): security.declarePublic( 'getCategory' ) def getCategory( self ): - """ Return the category + """ Return the category as a tuple (to prevent script from modifying it) - + Strip any return or ending space """ return tuple(map(lambda x: x.strip(), filter(lambda x: x, self.category))) or () @@ -156,6 +127,13 @@ class RoleInformation( SimpleItem ): """ return tuple(getattr(self, 'base_category', ())) + security.declarePublic( 'getBaseCategoryScript' ) + def getBaseCategoryScript( self ): + + """ Return the base_category_script id + """ + return getattr(self, 'base_category_script', '') + security.declarePrivate( 'base_category' ) def clone( self ): @@ -168,7 +146,7 @@ class RoleInformation( SimpleItem ): , condition=self.getCondition() , priority =self.priority , base_category=self.base_category - , user=self.getUserExpression() + , base_category_script=self.base_category_script ) InitializeClass( RoleInformation ) @@ -182,8 +160,8 @@ class ori: def __init__( self, tool, folder, object=None ): self.portal = portal = aq_parent(aq_inner(tool)) membership = getToolByName(tool, 'portal_membership') - self.isAnonymous = membership.isAnonymousUser() - self.user_id = membership.getAuthenticatedMember().getId() + #self.isAnonymous = membership.isAnonymousUser() + #self.user_id = membership.getAuthenticatedMember().getId() self.portal_url = portal.absolute_url() if folder is not None: self.folder_url = folder.absolute_url() diff --git a/product/ERP5Type/RoleProviderBase.py b/product/ERP5Type/RoleProviderBase.py index 2327e1ea87..5f04da0107 100755 --- a/product/ERP5Type/RoleProviderBase.py +++ b/product/ERP5Type/RoleProviderBase.py @@ -41,7 +41,7 @@ class RoleProviderBase: manage_options = ( { 'label' : 'Roles' , 'action' : 'manage_editRolesForm' } - , + , ) # @@ -70,7 +70,7 @@ class RoleProviderBase: a1['name'] = a.Title() # The name of this role definition (ex. Assignor at company X) a1['category'] = a.getCategory() or [] # Category definition a1['base_category'] = a.getBaseCategory() # Base Category Definition - a1['user'] = a.getUserExpression() + a1['base_category_script'] = a.getBaseCategoryScript() # Base Category Script Id a1['condition'] = a.getCondition() roles.append(a1) @@ -85,9 +85,9 @@ class RoleProviderBase: def addRole( self , id , name - , user , condition , category + , base_category_script , base_category=() , REQUEST=None ): @@ -96,17 +96,16 @@ class RoleProviderBase: if not name: raise ValueError('A name is required.') - a_expr = user and Expression(text=str(user)) or '' c_expr = condition and Expression(text=str(condition)) or '' new_roles = self._cloneRoles() new_role = RoleInformation( id=str(id) , title=str(name) - , user=a_expr , condition=c_expr , category=category.split('\n') , base_category=base_category.split() + , base_category_script=base_category_script ) new_roles.append( new_role ) @@ -220,7 +219,7 @@ class RoleProviderBase: """ Return a list of roles, cloned from our current list. """ return map( lambda x: x.clone(), list( self._roles ) ) - + security.declarePrivate( '_extractRole' ) def _extractRole( self, properties, index ): @@ -228,26 +227,23 @@ class RoleProviderBase: """ id = str( properties.get( 'id_%d' % index, '' ) ) name = str( properties.get( 'name_%d' % index, '' ) ) - user = str( properties.get( 'user_%d' % index, '' ) ) condition = str( properties.get( 'condition_%d' % index, '' ) ) category = properties.get( 'category_%d' % index, '' ).split('\n') base_category = properties.get( 'base_category_%d' % index, '' ).split() + base_category_script = str( properties.get( 'base_category_script_%d' % index, '' ) ) if not name: raise ValueError('A name is required.') - if user is not '': - user = Expression( text=user ) - if condition is not '': condition = Expression( text=condition ) return RoleInformation( id=id , title=name - , user=user , condition=condition , category=category , base_category=base_category + , base_category_script=base_category_script ) InitializeClass(RoleProviderBase) diff --git a/product/ERP5Type/dtml/editToolsRoles.dtml b/product/ERP5Type/dtml/editToolsRoles.dtml index 83d8e687ef..103ce4d9ce 100755 --- a/product/ERP5Type/dtml/editToolsRoles.dtml +++ b/product/ERP5Type/dtml/editToolsRoles.dtml @@ -56,7 +56,7 @@ <td></td> <td> <div class="form-label"> - Condition + Condition </div> </td> <td> @@ -70,12 +70,12 @@ <td></td> <td> <div class="form-label"> - User + Base Category </div> </td> <td> <div class="form-element"> - <input type="text" name="user_&dtml-index;" value="&dtml-user;" size="80" /> + <input type="text" size="40" name="base_category_&dtml-index;" value="<dtml-var "' '.join(base_category)">" /> </div> </td> </tr> @@ -84,12 +84,12 @@ <td></td> <td> <div class="form-label"> - Base Category + Base Category Script </div> </td> <td> <div class="form-element"> - <input type="text" size="40" name="base_category_&dtml-index;" value="<dtml-var "' '.join(base_category)">" /> + <input type="text" name="base_category_script_&dtml-index;" value="&dtml-base_category_script;" size="80" /> </div> </td> </tr> @@ -169,7 +169,7 @@ Add a role <td></td> <td> <div class="form-label"> - Condition + Condition </div> </td> <td> @@ -183,12 +183,12 @@ Add a role <td></td> <td> <div class="form-label"> - User + Base Category </div> </td> <td> <div class="form-element"> - <input type="text" name="user" size="80" /> + <input type="text" size="40" name="base_category" /> </div> </td> </tr> @@ -197,12 +197,12 @@ Add a role <td></td> <td> <div class="form-label"> - Base Category + Base Category Script </div> </td> <td> <div class="form-element"> - <input type="text" size="40" name="base_category" /> + <input type="text" name="base_category_script" size="80" /> </div> </td> </tr> -- 2.30.9