ERP5GroupManager.py 7.68 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2
##############################################################################
#
3 4
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
# Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
5
#
6 7 8 9 10 11 12
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this
# distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
13 14 15 16 17
#
##############################################################################
""" Classes: ERP5GroupManager
"""

18
from Products.ERP5Type.Globals import InitializeClass
Jean-Paul Smets's avatar
Jean-Paul Smets committed
19
from AccessControl import ClassSecurityInfo
20 21
from AccessControl.SecurityManagement import newSecurityManager,\
    getSecurityManager, setSecurityManager
Jean-Paul Smets's avatar
Jean-Paul Smets committed
22 23 24 25 26
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
from Products.PluggableAuthService.utils import classImplements
from Products.PluggableAuthService.interfaces.plugins import IGroupsPlugin
from Products.ERP5Type.Cache import CachingMethod
27 28
from Products.ERP5Type.ERP5Type \
  import ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
29
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
30
from Products.PluggableAuthService.PropertiedUser import PropertiedUser
31
from ZODB.POSException import ConflictError
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32

33
import sys
Alexandre Boeglin's avatar
Alexandre Boeglin committed
34

35
from zLOG import LOG, WARNING
Jean-Paul Smets's avatar
Jean-Paul Smets committed
36

37 38
from ERP5UserManager import SUPER_USER

39 40 41 42 43 44 45 46
# It can be useful to set NO_CACHE_MODE to 1 in order to debug
# complex security issues related to caching groups. For example,
# the use of scripts instead of external methods for
# assignment category lookup may make the system unstable and
# hard to debug. Setting NO_CACHE_MODE allows to debug such
# issues.
NO_CACHE_MODE = 0

47 48
class ConsistencyError(Exception): pass

Jean-Paul Smets's avatar
Jean-Paul Smets committed
49
manage_addERP5GroupManagerForm = PageTemplateFile(
Jérome Perrin's avatar
Jérome Perrin committed
50 51
    'www/ERP5Security_addERP5GroupManager', globals(),
    __name__='manage_addERP5GroupManagerForm' )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
52 53

def addERP5GroupManager( dispatcher, id, title=None, REQUEST=None ):
Alexandre Boeglin's avatar
Alexandre Boeglin committed
54
  """ Add a ERP5GroupManager to a Pluggable Auth Service. """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
55

Alexandre Boeglin's avatar
Alexandre Boeglin committed
56 57
  egm = ERP5GroupManager(id, title)
  dispatcher._setObject(egm.getId(), egm)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
58

Alexandre Boeglin's avatar
Alexandre Boeglin committed
59 60 61 62 63 64
  if REQUEST is not None:
    REQUEST['RESPONSE'].redirect(
                              '%s/manage_workspace'
                              '?manage_tabs_message='
                              'ERP5GroupManager+added.'
                          % dispatcher.absolute_url())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
65 66 67

class ERP5GroupManager(BasePlugin):

Alexandre Boeglin's avatar
Alexandre Boeglin committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
  """ PAS plugin for dynamically adding Groups
  based on Assignments in ERP5
  """
  meta_type = 'ERP5 Group Manager'

  security = ClassSecurityInfo()

  def __init__(self, id, title=None):

    self._id = self.id = id
    self.title = title

  #
  #   IGroupsPlugin implementation
  #
  def getGroupsForPrincipal(self, principal, request=None):
    """ See IGroupsPlugin.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
85
    """
86 87 88 89
    # If this is the super user, skip the check.
    if principal.getId() == SUPER_USER:
      return ()

90
    @UnrestrictedMethod
Alexandre Boeglin's avatar
Alexandre Boeglin committed
91 92 93 94
    def _getGroupsForPrincipal(user_name, path):
      security_category_dict = {} # key is the base_category_list,
                                  # value is the list of fetched categories
      security_group_list = []
95
      security_definition_list = ()
Alexandre Boeglin's avatar
Alexandre Boeglin committed
96

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
      try:
        # To get the complete list of groups, we try to call the
        # ERP5Type_getSecurityCategoryMapping which should return a list
        # of lists of two elements (script, base_category_list) like :
        # (
        #   ('script_1', ['base_category_1', 'base_category_2', ...]),
        #   ('script_2', ['base_category_1', 'base_category_3', ...])
        # )
        #
        # else, if the script does not exist, falls back to a list containng
        # only one list :
        # (('ERP5Type_getSecurityCategoryFromAssignment',
        #   self.getPortalAssignmentBaseCategoryList() ),)

        mapping_method = getattr(self,
            'ERP5Type_getSecurityCategoryMapping', None)
        if mapping_method is None:
          security_definition_list = ((
              'ERP5Type_getSecurityCategoryFromAssignment',
              self.getPortalAssignmentBaseCategoryList()
          ),)
        else:
          security_definition_list = mapping_method()

121 122
        # get the person from its reference - no security check needed
        catalog_result = self.portal_catalog.unrestrictedSearchResults(
123 124 125 126 127 128 129 130 131 132 133 134
            portal_type="Person", reference=user_name)
        if len(catalog_result) != 1: # we won't proceed with groups
          if len(catalog_result) > 1: # configuration is screwed
            raise ConsistencyError, 'There is more than one Person whose \
                login is %s : %s' % (user_name,
                repr([r.getObject() for r in catalog_result]))
          else: # no person is linked to this user login
            return ()
        person_object = catalog_result[0].getObject()
        person_id = person_object.getId()

        # Fetch category values from defined scripts
135
        for (method_name, base_category_list) in security_definition_list:
136 137 138 139 140
          base_category_list = tuple(base_category_list)
          method = getattr(self, method_name)
          security_category_list = security_category_dict.setdefault(
                                            base_category_list, [])
          try:
141 142 143 144
            # The called script may want to distinguish if it is called
            # from here or from _updateLocalRolesOnSecurityGroups.
            # Currently, passing portal_type='' (instead of 'Person')
            # is the only way to make the difference.
145 146 147 148 149 150 151 152 153 154 155
            security_category_list.extend(
              method(base_category_list, user_name, person_object, '')
            )
          except ConflictError:
            raise
          except:
            LOG('ERP5GroupManager', WARNING,
                'could not get security categories from %s' % (method_name,),
                error = sys.exc_info())

        # Get group names from category values
156 157 158
        # XXX try ERP5Type_asSecurityGroupIdList first for compatibility
        generator_name = 'ERP5Type_asSecurityGroupIdList'
        group_id_list_generator = getattr(self, generator_name, None)
159
        if group_id_list_generator is None:
160 161
          generator_name = ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
          group_id_list_generator = getattr(self, generator_name)
162
        for base_category_list, category_value_list in \
163
            security_category_dict.iteritems():
164 165
          for category_dict in category_value_list:
            try:
166 167
              group_id_list = group_id_list_generator(
                                        category_order=base_category_list,
168
                                        **category_dict)
169 170
              if isinstance(group_id_list, str):
                group_id_list = [group_id_list]
171 172 173 174 175 176 177 178
              security_group_list.extend(group_id_list)
            except ConflictError:
              raise
            except:
              LOG('ERP5GroupManager', WARNING,
                  'could not get security groups from %s' %
                  generator_name,
                  error = sys.exc_info())
179
      finally:
180
        pass
Alexandre Boeglin's avatar
Alexandre Boeglin committed
181 182
      return tuple(security_group_list)

183 184
    if not NO_CACHE_MODE:
      _getGroupsForPrincipal = CachingMethod(_getGroupsForPrincipal,
Aurel's avatar
Aurel committed
185
                                             id='ERP5GroupManager_getGroupsForPrincipal',
Aurel's avatar
Aurel committed
186
                                             cache_factory='erp5_content_short')
187

Romain Courteaud's avatar
Romain Courteaud committed
188
    return _getGroupsForPrincipal(
Jérome Perrin's avatar
Jérome Perrin committed
189 190
                user_name=principal.getId(),
                path=self.getPhysicalPath())
Romain Courteaud's avatar
Romain Courteaud committed
191

Alexandre Boeglin's avatar
Alexandre Boeglin committed
192

Jean-Paul Smets's avatar
Jean-Paul Smets committed
193 194 195 196 197
classImplements( ERP5GroupManager
               , IGroupsPlugin
               )

InitializeClass(ERP5GroupManager)