ERP5GroupManager.py 7.48 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
Jean-Paul Smets's avatar
Jean-Paul Smets committed
29

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

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

34 35
from ERP5UserManager import SUPER_USER

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

44 45
class ConsistencyError(Exception): pass

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

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

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

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

class ERP5GroupManager(BasePlugin):

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

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

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

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

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

Jérome Perrin's avatar
Jérome Perrin committed
186 187 188
    return _getGroupsForPrincipal(
                user_name=principal.getId(),
                path=self.getPhysicalPath())
Alexandre Boeglin's avatar
Alexandre Boeglin committed
189 190


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

InitializeClass(ERP5GroupManager)