ERP5GroupManager.py 8.03 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 18 19
#
##############################################################################
""" Classes: ERP5GroupManager
"""

from Globals import InitializeClass
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
from Products.PluggableAuthService.PropertiedUser import PropertiedUser
28
from ZODB.POSException import ConflictError
29
from Shared.DC.ZRDB.DA import DatabaseError
Jean-Paul Smets's avatar
Jean-Paul Smets committed
30

31
import sys
Alexandre Boeglin's avatar
Alexandre Boeglin committed
32

33
from zLOG import LOG, WARNING
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34

35 36
from ERP5UserManager import SUPER_USER

37 38 39 40 41 42 43 44
# 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

45 46
class ConsistencyError(Exception): pass

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

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

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

Alexandre Boeglin's avatar
Alexandre Boeglin committed
57 58 59 60 61 62
  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
63 64 65

class ERP5GroupManager(BasePlugin):

Alexandre Boeglin's avatar
Alexandre Boeglin committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
  """ 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
83
    """
84 85 86 87
    # If this is the super user, skip the check.
    if principal.getId() == SUPER_USER:
      return ()

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

      # because we aren't logged in, we have to create our own
      # SecurityManager to be able to access the Catalog
96
      sm = getSecurityManager()
97
      if sm.getUser().getId() != SUPER_USER:
98
        newSecurityManager(self, self.getUser(SUPER_USER))
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
      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()

123 124
        # get the person from its reference - no security check needed
        catalog_result = self.portal_catalog.unrestrictedSearchResults(
125 126 127 128 129 130 131 132 133 134 135 136
            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
137
        for (method_name, base_category_list) in security_definition_list:
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
          base_category_list = tuple(base_category_list)
          method = getattr(self, method_name)
          security_category_list = security_category_dict.setdefault(
                                            base_category_list, [])
          try:
            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
        group_id_list_generator = getattr(self,
                                      'ERP5Type_asSecurityGroupIdList', None)
156 157 158
        if group_id_list_generator is None:
          group_id_list_generator = getattr(self, 'ERP5Type_asSecurityGroupId')
          generator_name = "ERP5Type_asSecurityGroupId"
159
        else:
160 161 162 163 164
          generator_name = 'ERP5Type_asSecurityGroupIdList'
        for base_category_list, category_value_list in \
            security_category_dict.items():
          for category_dict in category_value_list:
            try:
165 166
              group_id_list = group_id_list_generator(
                                        category_order=base_category_list,
167
                                        **category_dict)
168 169
              if isinstance(group_id_list, str):
                group_id_list = [group_id_list]
170 171 172 173 174 175 176 177
              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())
178 179
      finally:
        setSecurityManager(sm)
Alexandre Boeglin's avatar
Alexandre Boeglin committed
180 181
      return tuple(security_group_list)

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

187 188
    try:
      return _getGroupsForPrincipal(
Jérome Perrin's avatar
Jérome Perrin committed
189 190
                user_name=principal.getId(),
                path=self.getPhysicalPath())
191 192 193 194 195 196 197 198 199 200 201 202 203
    except ConflictError:
      raise
    except:
      # Hiding this exception is necessary in order
      # to be able to configure database connection
      # on a existing site.
      # Improved version may use ProgrammingError
      # and DatabaseError exceptions explicitely
      LOG('ERP5GroupManager', WARNING,
          'could not call _getGroupsForPrincipal probably because of '
          'misconfigured database connection',
                  error = sys.exc_info())
      return ()
Alexandre Boeglin's avatar
Alexandre Boeglin committed
204

Jean-Paul Smets's avatar
Jean-Paul Smets committed
205 206 207 208 209
classImplements( ERP5GroupManager
               , IGroupsPlugin
               )

InitializeClass(ERP5GroupManager)