[Erp5-report] r36487 mohamadou - /erp5/trunk/products/ERP5eGovSecurity/

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Jun 22 00:02:08 CEST 2010


Author: mohamadou
Date: Tue Jun 22 00:02:08 2010
New Revision: 36487

URL: http://svn.erp5.org?rev=36487&view=rev
Log:
Add EGov Group and User Manager

Added:
    erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py
    erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.pyc   (with props)
    erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py~
    erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py
    erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.pyc   (with props)
    erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py~
    erp5/trunk/products/ERP5eGovSecurity/__init__.py
    erp5/trunk/products/ERP5eGovSecurity/__init__.pyc   (with props)

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py?rev=36487&view=auto
==============================================================================
--- erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py (added)
+++ erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py [utf8] Tue Jun 22 00:02:08 2010
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
+#                 Mohamadou Mbengue <mayoro at gmail.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+""" Classes: ERP5GroupManager
+"""
+
+from Globals import InitializeClass
+from AccessControl.SecurityManagement import newSecurityManager,\
+    getSecurityManager, setSecurityManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from AccessControl import ClassSecurityInfo
+from Products.PluggableAuthService.PropertiedUser import PropertiedUser
+from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
+from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.interfaces.plugins import IGroupsPlugin
+from Products.ERP5Security.ERP5GroupManager import ERP5GroupManager
+from Products.ERP5Type.Cache import CachingMethod
+from ZODB.POSException import ConflictError
+from Products.ERP5Security.ERP5GroupManager import ConsistencyError
+
+import sys
+
+from zLOG import LOG, WARNING
+
+from Products.ERP5Security.ERP5UserManager import SUPER_USER
+
+
+NO_CACHE_MODE = 0
+manage_addEGOVGroupManagerForm = PageTemplateFile(
+    'www/ERP5Security_addERP5GroupManager', globals(),
+    __name__='manage_addERP5GroupManagerForm' )
+
+def addEGOVGroupManager( dispatcher, id, title=None, REQUEST=None ):
+  """ Add a EGOVGroupManager to a Pluggable Auth Service. """
+  egm = EGOVGroupManager(id, title)
+  dispatcher._setObject(egm.getId(), egm)
+
+  if REQUEST is not None:
+    REQUEST['RESPONSE'].redirect(
+                              '%s/manage_workspace'
+                              '?manage_tabs_message='
+                              'EGOVGroupManager+added.'
+                          % dispatcher.absolute_url())
+
+
+class EGOVGroupManager(ERP5GroupManager):
+  """ PAS plugin for dynamically adding Groups
+      this plugin permit to login with evry portal_type
+      So it's possible to login with organisation
+  """
+  meta_type = 'EGOV Group Manager'
+  security = ClassSecurityInfo()
+
+  portal_type_list = ('Person',)
+
+  _properties = BasePlugin._properties + (
+     {'label' : 'Portal Type List (Experimental)',
+       'type' : 'lines',
+       'id'   : 'portal_type_list',
+       'mode' : 'w',
+     },
+     )
+
+  def __init__(self, id, title=None):
+
+    self._id = self.id = id
+    self.title = title
+
+  #
+  #   IGroupsPlugin implementation
+  #
+  def getGroupsForPrincipal(self, principal, request=None):
+    """ See IGroupsPlugin.
+    """
+    # If this is the super user, skip the check.
+    if principal.getId() == SUPER_USER:
+      return ()
+
+    def _getGroupsForPrincipal(user_name, path):
+      security_category_dict = {} # key is the base_category_list,
+                                  # value is the list of fetched categories
+      security_group_list = []
+      security_definition_list = ()
+
+      # because we aren't logged in, we have to create our own
+      # SecurityManager to be able to access the Catalog
+      sm = getSecurityManager()
+      if sm.getUser().getId() != SUPER_USER:
+        newSecurityManager(self, self.getUser(SUPER_USER))
+      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()
+
+        # get the person from its reference - no security check needed
+        catalog_result = self.portal_catalog.unrestrictedSearchResults(
+            portal_type=self.portal_type_list, 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
+            portal = self.getPortalObject()
+
+            # this permit to get the module of the application
+            # the goal is to work with anonymous applications, even if 
+            # they are not reindexed
+
+            module_id = self.REQUEST.get('anonymous_module', None)
+            if module_id:
+              module =  getattr(portal, module_id, None)
+              if module is not None:
+                result = module._getOb(user_name, None)
+                if result is not None:
+                  person_object = result
+                else:
+                  return ()
+            else:
+              return ()
+        else:
+          person_object = catalog_result[0].getObject()
+        person_id = person_object.getId()
+
+        # Fetch category values from defined scripts
+        for (method_name, base_category_list) in security_definition_list:
+          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('EGOVGroupManager', 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)
+        if group_id_list_generator is None:
+          group_id_list_generator = getattr(self, 'ERP5Type_asSecurityGroupId')
+          generator_name = "ERP5Type_asSecurityGroupId"
+        else:
+          generator_name = 'ERP5Type_asSecurityGroupIdList'
+        for base_category_list, category_value_list in \
+            security_category_dict.items():
+          for category_dict in category_value_list:
+            try:
+              group_id_list = group_id_list_generator(
+                                        category_order=base_category_list,
+                                        **category_dict)
+              if isinstance(group_id_list, str):
+                group_id_list = [group_id_list]
+              security_group_list.extend(group_id_list)
+            except ConflictError:
+              raise
+            except:
+              LOG('EGOVGroupManager', WARNING,
+                  'could not get security groups from %s' %
+                  generator_name,
+                  error = sys.exc_info())
+      finally:
+        setSecurityManager(sm)
+      return tuple(security_group_list)
+
+    if not NO_CACHE_MODE:
+      _getGroupsForPrincipal = CachingMethod(_getGroupsForPrincipal,
+                                 id='EGOVGroupManager_getGroupsForPrincipal',
+                                 cache_factory='erp5_content_short')
+
+    return _getGroupsForPrincipal(
+                user_name=principal.getId(),
+                path=self.getPhysicalPath())
+
+
+classImplements( EGOVGroupManager
+               , IGroupsPlugin
+               )
+
+InitializeClass(EGOVGroupManager)

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.pyc
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.pyc?rev=36487&view=auto
==============================================================================
Binary file - no diff available.

Propchange: erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.pyc
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py~
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py%7E?rev=36487&view=auto
==============================================================================
--- erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py~ (added)
+++ erp5/trunk/products/ERP5eGovSecurity/EGOVGroupManager.py~ [utf8] Tue Jun 22 00:02:08 2010
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
+# Reserved.
+#
+# 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.
+#
+##############################################################################
+""" Classes: ERP5GroupManager
+"""
+
+from Globals import InitializeClass
+from AccessControl.SecurityManagement import newSecurityManager,\
+    getSecurityManager, setSecurityManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from AccessControl import ClassSecurityInfo
+from Products.PluggableAuthService.PropertiedUser import PropertiedUser
+from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
+from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.interfaces.plugins import IGroupsPlugin
+from Products.ERP5Security.ERP5GroupManager import ERP5GroupManager
+from Products.ERP5Type.Cache import CachingMethod
+from ZODB.POSException import ConflictError
+from Products.ERP5Security.ERP5GroupManager import ConsistencyError
+
+import sys
+
+from zLOG import LOG, WARNING
+
+from Products.ERP5Security.ERP5UserManager import SUPER_USER
+
+
+NO_CACHE_MODE = 0
+manage_addEGOVGroupManagerForm = PageTemplateFile(
+    'www/ERP5Security_addERP5GroupManager', globals(),
+    __name__='manage_addERP5GroupManagerForm' )
+
+def addEGOVGroupManager( dispatcher, id, title=None, REQUEST=None ):
+  """ Add a EGOVGroupManager to a Pluggable Auth Service. """
+  egm = EGOVGroupManager(id, title)
+  dispatcher._setObject(egm.getId(), egm)
+
+  if REQUEST is not None:
+    REQUEST['RESPONSE'].redirect(
+                              '%s/manage_workspace'
+                              '?manage_tabs_message='
+                              'EGOVGroupManager+added.'
+                          % dispatcher.absolute_url())
+
+
+class EGOVGroupManager(ERP5GroupManager):
+  """ PAS plugin for dynamically adding Groups
+      this plugin permit to login with evry portal_type
+      So it's possible to login with organisation
+  """
+  meta_type = 'EGOV Group Manager'
+  security = ClassSecurityInfo()
+
+  portal_type_list = ('Person',)
+
+  _properties = BasePlugin._properties + (
+     {'label' : 'Portal Type List (Experimental)',
+       'type' : 'lines',
+       'id'   : 'portal_type_list',
+       'mode' : 'w',
+     },
+     )
+
+  def __init__(self, id, title=None):
+
+    self._id = self.id = id
+    self.title = title
+
+  #
+  #   IGroupsPlugin implementation
+  #
+  def getGroupsForPrincipal(self, principal, request=None):
+    """ See IGroupsPlugin.
+    """
+    # If this is the super user, skip the check.
+    if principal.getId() == SUPER_USER:
+      return ()
+
+    def _getGroupsForPrincipal(user_name, path):
+      security_category_dict = {} # key is the base_category_list,
+                                  # value is the list of fetched categories
+      security_group_list = []
+      security_definition_list = ()
+
+      # because we aren't logged in, we have to create our own
+      # SecurityManager to be able to access the Catalog
+      sm = getSecurityManager()
+      if sm.getUser().getId() != SUPER_USER:
+        newSecurityManager(self, self.getUser(SUPER_USER))
+      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()
+
+        # get the person from its reference - no security check needed
+        catalog_result = self.portal_catalog.unrestrictedSearchResults(
+            portal_type=self.portal_type_list, 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
+            portal = self.getPortalObject()
+
+            # this permit to get the module of the application
+            # the goal is to work with anonymous applications, even if 
+            # they are not reindexed
+
+            module_id = self.REQUEST.get('anonymous_module', None)
+            if module_id:
+              module =  getattr(portal, module_id, None)
+              if module is not None:
+                result = module._getOb(user_name, None)
+                if result is not None:
+                  person_object = result
+                else:
+                  return ()
+            else:
+              return ()
+        else:
+          person_object = catalog_result[0].getObject()
+        person_id = person_object.getId()
+
+        # Fetch category values from defined scripts
+        for (method_name, base_category_list) in security_definition_list:
+          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('EGOVGroupManager', 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)
+        if group_id_list_generator is None:
+          group_id_list_generator = getattr(self, 'ERP5Type_asSecurityGroupId')
+          generator_name = "ERP5Type_asSecurityGroupId"
+        else:
+          generator_name = 'ERP5Type_asSecurityGroupIdList'
+        for base_category_list, category_value_list in \
+            security_category_dict.items():
+          for category_dict in category_value_list:
+            try:
+              group_id_list = group_id_list_generator(
+                                        category_order=base_category_list,
+                                        **category_dict)
+              if isinstance(group_id_list, str):
+                group_id_list = [group_id_list]
+              security_group_list.extend(group_id_list)
+            except ConflictError:
+              raise
+            except:
+              LOG('EGOVGroupManager', WARNING,
+                  'could not get security groups from %s' %
+                  generator_name,
+                  error = sys.exc_info())
+      finally:
+        setSecurityManager(sm)
+      return tuple(security_group_list)
+
+    if not NO_CACHE_MODE:
+      _getGroupsForPrincipal = CachingMethod(_getGroupsForPrincipal,
+                                 id='EGOVGroupManager_getGroupsForPrincipal',
+                                 cache_factory='erp5_content_short')
+
+    return _getGroupsForPrincipal(
+                user_name=principal.getId(),
+                path=self.getPhysicalPath())
+
+
+classImplements( EGOVGroupManager
+               , IGroupsPlugin
+               )
+
+InitializeClass(EGOVGroupManager)

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py?rev=36487&view=auto
==============================================================================
--- erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py (added)
+++ erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py [utf8] Tue Jun 22 00:02:08 2010
@@ -0,0 +1,311 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
+#                 Mohamadou Mbengue <mayoro at gmail.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+""" Classes: ERP5UserManager
+"""
+
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from AccessControl.SecurityManagement import getSecurityManager,\
+    setSecurityManager, newSecurityManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Products.PluggableAuthService.PluggableAuthService import \
+    _SWALLOWABLE_PLUGIN_EXCEPTIONS
+from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
+from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin
+from Products.PluggableAuthService.interfaces.plugins import IUserEnumerationPlugin
+from Products.ERP5Type.Cache import CachingMethod
+from Products.ERP5Security.ERP5UserManager import ERP5UserManager
+from ZODB.POSException import ConflictError
+import sys
+from DateTime import DateTime
+from zLOG import LOG, PROBLEM
+
+try :
+  from AccessControl.AuthEncoding import pw_validate
+except ImportError:
+  pw_validate = lambda reference, attempt: reference == attempt
+
+# This user is used to bypass all security checks.
+SUPER_USER = '__erp5security-=__'
+
+manage_addEGOVUserManagerForm = PageTemplateFile(
+    'www/ERP5Security_addERP5UserManager', globals(),
+    __name__='manage_addERP5UserManagerForm' )
+
+
+def addEGOVUserManager(dispatcher, id, title=None, REQUEST=None):
+  """ Add a EGOVUserManager to a Pluggable Auth Service. """
+  eum = EGOVUserManager(id, title)
+  dispatcher._setObject(eum.getId(), eum)
+  if REQUEST is not None:
+      REQUEST['RESPONSE'].redirect(
+                              '%s/manage_workspace'
+                              '?manage_tabs_message='
+                              'EGOVUserManager+added.'
+                          % dispatcher.absolute_url())
+
+class EGOVUserManager(ERP5UserManager):
+    """ PAS plugin for managing users in ERP5
+        this plugin permit to login with evry portal_type
+        So it's possible to login with organisation
+    """
+
+    meta_type = 'EGOV User Manager'
+
+    security = ClassSecurityInfo()
+
+    portal_type_list = ('Person',)
+
+    _properties = BasePlugin._properties + (
+       {'label' : 'Portal Type List (Experimental)',
+         'type' : 'lines',
+         'id'   : 'portal_type_list',
+         'mode' : 'w',
+       },
+       )
+
+    def __init__(self, id, title=None):
+
+        self._id = self.id = id
+        self.title = title
+
+    #
+    #   IAuthenticationPlugin implementation
+    #
+    security.declarePrivate( 'authenticateCredentials' )
+    def authenticateCredentials(self, credentials):
+        """ See IAuthenticationPlugin.
+
+        o We expect the credentials to be those returned by
+            ILoginPasswordExtractionPlugin.
+        """
+        # Forbidden the usage of the super user.
+        if credentials.get('login') == SUPER_USER:
+          return None
+
+        def _authenticateCredentials(login, password, path):
+            if not login or not password:
+                return None
+
+            user_list = self.getUserByLogin((login,))
+
+            if not user_list:
+                return None
+
+            user = user_list[0]
+            user_portal_type = user.getPortalType()
+
+            sm = getSecurityManager()
+            if sm.getUser().getId() != SUPER_USER:
+              newSecurityManager(self, self.getUser(SUPER_USER))
+
+            # search for assignment only on person entity
+            if user_portal_type == 'Person':
+              try:
+                # get assignment
+                assignment_list = [x for x in \
+                    user.contentValues(portal_type="Assignment") if \
+                    x.getValidationState() == "open"]
+                valid_assignment_list = []
+                # check dates if exist
+                login_date = DateTime()
+                for assignment in assignment_list:
+                  if assignment.getStartDate() is not None and \
+                         assignment.getStartDate() > login_date:
+                    continue
+                  if assignment.getStopDate() is not None and \
+                         assignment.getStopDate() < login_date:
+                    continue
+                  valid_assignment_list.append(assignment)
+                  
+                if pw_validate(user.getPassword(), password) and \
+                       len(valid_assignment_list): #user.getCareerRole() == 'internal':
+                  return login, login # use same for user_id and login
+              finally:
+                setSecurityManager(sm)
+            else:
+              if pw_validate(user.getPassword(), password):
+                return login, login # use same for user_id and login
+
+            return None
+
+        _authenticateCredentials = CachingMethod(_authenticateCredentials,
+                                 id='ERP5UserManager_authenticateCredentials',
+                                 cache_factory='erp5_content_short')
+        return _authenticateCredentials(
+                      login=credentials.get('login'),
+                      password=credentials.get('password'),
+                      path=self.getPhysicalPath())
+
+    #
+    #   IUserEnumerationPlugin implementation
+    #
+    security.declarePrivate( 'enumerateUsers' )
+    def enumerateUsers(self, id=None, login=None, exact_match=False,
+                       sort_by=None, max_results=None, **kw):
+        """ See IUserEnumerationPlugin.
+        """
+        def _enumerateUsers(id_tuple, exact_match, path):
+            user_info = []
+            plugin_id = self.getId()
+
+            id_list = []
+            for id in id_tuple:
+              if SUPER_USER == id:
+                info = { 'id' : SUPER_USER
+                        , 'login' : SUPER_USER
+                        , 'pluginid' : plugin_id
+                        }
+                user_info.append(info)
+              else:
+                if exact_match:
+                  id_list.append(id)
+                else:
+                  id_list.append('%%%s%%' % id)
+
+            if id_list:
+              for user in self.getUserByLogin(tuple(id_list), exact_match=exact_match):
+                  info = { 'id' : user.getReference()
+                         , 'login' : user.getReference()
+                         , 'pluginid' : plugin_id
+                         }
+
+                  user_info.append(info)
+
+            return tuple(user_info)
+
+        _enumerateUsers = CachingMethod(_enumerateUsers,
+                                        id='ERP5UserManager_enumerateUsers',
+                                        cache_factory='erp5_content_short')
+
+        if id is None:
+          id = login
+        if isinstance(id, list):
+          id = tuple(id)
+        elif not isinstance(id, tuple):
+          id = (id,)
+        return _enumerateUsers(id_tuple=id,
+                               exact_match=exact_match,
+                               path=self.getPhysicalPath())
+
+    def getUserByLogin(self, login, exact_match=True):
+        # Search the Catalog for login and return a list of person objects
+        # login can be a string or a list of strings
+        # (no docstring to prevent publishing)
+        if not login:
+          return []
+
+        portal = self.getPortalObject()
+
+        def _getUserByLogin(login, exact_match):
+          # because we aren't logged in, we have to create our own
+          # SecurityManager to be able to access the Catalog
+          sm = getSecurityManager()
+          if sm.getUser().getId() != SUPER_USER:
+            newSecurityManager(self, self.getUser(SUPER_USER))
+  
+          try:
+            try:
+              result = portal.portal_catalog.unrestrictedSearchResults(
+                            select_expression='reference',
+                            portal_type=self.portal_type_list, reference=login)
+              if len(result) != 1: # we won't proceed with groups
+                if len(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
+                  # this permit to get the module of the application
+                  # the goal is to work with anonymous applications, even if 
+                  # they are not reindexed
+                  module_id = self.REQUEST.get('anonymous_module', None)
+                  if module_id:
+                    module =  getattr(portal, module_id, None)
+                    if module is not None:
+                      result = module._getOb(login[0], None)
+                      if result is not None:
+                        return [result.getPath(),]
+                      else:
+                        return []
+                  else:
+                    return []
+            except ConflictError:
+              raise
+            except:
+              LOG('ERP5Security', PROBLEM, 'getUserByLogin failed', error=sys.exc_info())
+              # Here we must raise an exception to prevent callers from caching
+              # a result of a degraded situation.
+              # The kind of exception does not matter as long as it's catched by
+              # PAS and causes a lookup using another plugin or user folder.
+              # As PAS does not define explicitely such exception, we must use
+              # the _SWALLOWABLE_PLUGIN_EXCEPTIONS list.
+              raise _SWALLOWABLE_PLUGIN_EXCEPTIONS[0]
+          finally:
+            setSecurityManager(sm)
+          # XXX: Here, we filter catalog result list ALTHOUGH we did pass
+          # parameters to unrestrictedSearchResults to restrict result set.
+          # This is done because the following values can match person with
+          # reference "foo":
+          # "foo " because of MySQL (feature, PADSPACE collation):
+          #  mysql> SELECT reference as r FROM catalog
+          #      -> WHERE reference="foo      ";
+          #  +-----+
+          #  | r   |
+          #  +-----+
+          #  | foo |
+          #  +-----+
+          #  1 row in set (0.01 sec)
+          # " foo", "foo " and other padding variations because of
+          # ZSQLCatalog (feature ?):
+          #  (Pdb) print portal.portal_catalog.unrestrictedSearchResults(\
+          #              portal_type="Person", reference='  foo  ', src__=1)
+          #  SELECT DISTINCT
+          #     catalog.path,   catalog.uid
+          #  FROM
+          #     catalog AS catalog
+          #  WHERE
+          #    1 = 1
+          #    AND (((((catalog.portal_type = 'Person'))))) AND (((((catalog.reference = 'foo')))))
+          #  LIMIT 1000
+          # "bar OR foo" because of ZSQLCatalog tokenizing searched sgtrings
+          # by default (feature).
+          return [x.path for x in result if (not exact_match) or x['reference'] in login]
+        _getUserByLogin = CachingMethod(_getUserByLogin,
+                                        id='ERP5UserManager_getUserByLogin',
+                                        cache_factory='erp5_content_short')
+        result = _getUserByLogin(login, exact_match)
+        return [portal.unrestrictedTraverse(x) for x in result]
+
+classImplements( EGOVUserManager
+               , IAuthenticationPlugin
+               , IUserEnumerationPlugin
+               )
+
+InitializeClass(EGOVUserManager)

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.pyc
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.pyc?rev=36487&view=auto
==============================================================================
Binary file - no diff available.

Propchange: erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.pyc
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py~
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py%7E?rev=36487&view=auto
==============================================================================
--- erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py~ (added)
+++ erp5/trunk/products/ERP5eGovSecurity/EGOVUserManager.py~ [utf8] Tue Jun 22 00:02:08 2010
@@ -0,0 +1,298 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
+# Reserved.
+#
+# 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.
+#
+##############################################################################
+""" Classes: ERP5UserManager
+"""
+
+from Globals import InitializeClass
+from AccessControl import ClassSecurityInfo
+from AccessControl.SecurityManagement import getSecurityManager,\
+    setSecurityManager, newSecurityManager
+from Products.PageTemplates.PageTemplateFile import PageTemplateFile
+from Products.PluggableAuthService.PluggableAuthService import \
+    _SWALLOWABLE_PLUGIN_EXCEPTIONS
+from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
+from Products.PluggableAuthService.utils import classImplements
+from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin
+from Products.PluggableAuthService.interfaces.plugins import IUserEnumerationPlugin
+from Products.ERP5Type.Cache import CachingMethod
+from Products.ERP5Security.ERP5UserManager import ERP5UserManager
+from ZODB.POSException import ConflictError
+import sys
+from DateTime import DateTime
+from zLOG import LOG, PROBLEM
+
+try :
+  from AccessControl.AuthEncoding import pw_validate
+except ImportError:
+  pw_validate = lambda reference, attempt: reference == attempt
+
+# This user is used to bypass all security checks.
+SUPER_USER = '__erp5security-=__'
+
+manage_addEGOVUserManagerForm = PageTemplateFile(
+    'www/ERP5Security_addERP5UserManager', globals(),
+    __name__='manage_addERP5UserManagerForm' )
+
+
+def addEGOVUserManager(dispatcher, id, title=None, REQUEST=None):
+  """ Add a EGOVUserManager to a Pluggable Auth Service. """
+  eum = EGOVUserManager(id, title)
+  dispatcher._setObject(eum.getId(), eum)
+  if REQUEST is not None:
+      REQUEST['RESPONSE'].redirect(
+                              '%s/manage_workspace'
+                              '?manage_tabs_message='
+                              'EGOVUserManager+added.'
+                          % dispatcher.absolute_url())
+
+class EGOVUserManager(ERP5UserManager):
+    """ PAS plugin for managing users in ERP5
+        this plugin permit to login with evry portal_type
+        So it's possible to login with organisation
+    """
+
+    meta_type = 'EGOV User Manager'
+
+    security = ClassSecurityInfo()
+
+    portal_type_list = ('Person',)
+
+    _properties = BasePlugin._properties + (
+       {'label' : 'Portal Type List (Experimental)',
+         'type' : 'lines',
+         'id'   : 'portal_type_list',
+         'mode' : 'w',
+       },
+       )
+
+    def __init__(self, id, title=None):
+
+        self._id = self.id = id
+        self.title = title
+
+    #
+    #   IAuthenticationPlugin implementation
+    #
+    security.declarePrivate( 'authenticateCredentials' )
+    def authenticateCredentials(self, credentials):
+        """ See IAuthenticationPlugin.
+
+        o We expect the credentials to be those returned by
+            ILoginPasswordExtractionPlugin.
+        """
+        # Forbidden the usage of the super user.
+        if credentials.get('login') == SUPER_USER:
+          return None
+
+        def _authenticateCredentials(login, password, path):
+            if not login or not password:
+                return None
+
+            user_list = self.getUserByLogin((login,))
+
+            if not user_list:
+                return None
+
+            user = user_list[0]
+            user_portal_type = user.getPortalType()
+
+            sm = getSecurityManager()
+            if sm.getUser().getId() != SUPER_USER:
+              newSecurityManager(self, self.getUser(SUPER_USER))
+
+            # search for assignment only on person entity
+            if user_portal_type == 'Person':
+              try:
+                # get assignment
+                assignment_list = [x for x in \
+                    user.contentValues(portal_type="Assignment") if \
+                    x.getValidationState() == "open"]
+                valid_assignment_list = []
+                # check dates if exist
+                login_date = DateTime()
+                for assignment in assignment_list:
+                  if assignment.getStartDate() is not None and \
+                         assignment.getStartDate() > login_date:
+                    continue
+                  if assignment.getStopDate() is not None and \
+                         assignment.getStopDate() < login_date:
+                    continue
+                  valid_assignment_list.append(assignment)
+                  
+                if pw_validate(user.getPassword(), password) and \
+                       len(valid_assignment_list): #user.getCareerRole() == 'internal':
+                  return login, login # use same for user_id and login
+              finally:
+                setSecurityManager(sm)
+            else:
+              if pw_validate(user.getPassword(), password):
+                return login, login # use same for user_id and login
+
+            return None
+
+        _authenticateCredentials = CachingMethod(_authenticateCredentials,
+                                 id='ERP5UserManager_authenticateCredentials',
+                                 cache_factory='erp5_content_short')
+        return _authenticateCredentials(
+                      login=credentials.get('login'),
+                      password=credentials.get('password'),
+                      path=self.getPhysicalPath())
+
+    #
+    #   IUserEnumerationPlugin implementation
+    #
+    security.declarePrivate( 'enumerateUsers' )
+    def enumerateUsers(self, id=None, login=None, exact_match=False,
+                       sort_by=None, max_results=None, **kw):
+        """ See IUserEnumerationPlugin.
+        """
+        def _enumerateUsers(id_tuple, exact_match, path):
+            user_info = []
+            plugin_id = self.getId()
+
+            id_list = []
+            for id in id_tuple:
+              if SUPER_USER == id:
+                info = { 'id' : SUPER_USER
+                        , 'login' : SUPER_USER
+                        , 'pluginid' : plugin_id
+                        }
+                user_info.append(info)
+              else:
+                if exact_match:
+                  id_list.append(id)
+                else:
+                  id_list.append('%%%s%%' % id)
+
+            if id_list:
+              for user in self.getUserByLogin(tuple(id_list), exact_match=exact_match):
+                  info = { 'id' : user.getReference()
+                         , 'login' : user.getReference()
+                         , 'pluginid' : plugin_id
+                         }
+
+                  user_info.append(info)
+
+            return tuple(user_info)
+
+        _enumerateUsers = CachingMethod(_enumerateUsers,
+                                        id='ERP5UserManager_enumerateUsers',
+                                        cache_factory='erp5_content_short')
+
+        if id is None:
+          id = login
+        if isinstance(id, list):
+          id = tuple(id)
+        elif not isinstance(id, tuple):
+          id = (id,)
+        return _enumerateUsers(id_tuple=id,
+                               exact_match=exact_match,
+                               path=self.getPhysicalPath())
+
+    def getUserByLogin(self, login, exact_match=True):
+        # Search the Catalog for login and return a list of person objects
+        # login can be a string or a list of strings
+        # (no docstring to prevent publishing)
+        if not login:
+          return []
+
+        portal = self.getPortalObject()
+
+        def _getUserByLogin(login, exact_match):
+          # because we aren't logged in, we have to create our own
+          # SecurityManager to be able to access the Catalog
+          sm = getSecurityManager()
+          if sm.getUser().getId() != SUPER_USER:
+            newSecurityManager(self, self.getUser(SUPER_USER))
+  
+          try:
+            try:
+              result = portal.portal_catalog.unrestrictedSearchResults(
+                            select_expression='reference',
+                            portal_type=self.portal_type_list, reference=login)
+              if len(result) != 1: # we won't proceed with groups
+                if len(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
+                  # this permit to get the module of the application
+                  # the goal is to work with anonymous applications, even if 
+                  # they are not reindexed
+                  module_id = self.REQUEST.get('anonymous_module', None)
+                  if module_id:
+                    module =  getattr(portal, module_id, None)
+                    if module is not None:
+                      result = module._getOb(login[0], None)
+                      if result is not None:
+                        return [result.getPath(),]
+                      else:
+                        return []
+                  else:
+                    return []
+            except ConflictError:
+              raise
+            except:
+              LOG('ERP5Security', PROBLEM, 'getUserByLogin failed', error=sys.exc_info())
+              # Here we must raise an exception to prevent callers from caching
+              # a result of a degraded situation.
+              # The kind of exception does not matter as long as it's catched by
+              # PAS and causes a lookup using another plugin or user folder.
+              # As PAS does not define explicitely such exception, we must use
+              # the _SWALLOWABLE_PLUGIN_EXCEPTIONS list.
+              raise _SWALLOWABLE_PLUGIN_EXCEPTIONS[0]
+          finally:
+            setSecurityManager(sm)
+          # XXX: Here, we filter catalog result list ALTHOUGH we did pass
+          # parameters to unrestrictedSearchResults to restrict result set.
+          # This is done because the following values can match person with
+          # reference "foo":
+          # "foo " because of MySQL (feature, PADSPACE collation):
+          #  mysql> SELECT reference as r FROM catalog
+          #      -> WHERE reference="foo      ";
+          #  +-----+
+          #  | r   |
+          #  +-----+
+          #  | foo |
+          #  +-----+
+          #  1 row in set (0.01 sec)
+          # " foo", "foo " and other padding variations because of
+          # ZSQLCatalog (feature ?):
+          #  (Pdb) print portal.portal_catalog.unrestrictedSearchResults(\
+          #              portal_type="Person", reference='  foo  ', src__=1)
+          #  SELECT DISTINCT
+          #     catalog.path,   catalog.uid
+          #  FROM
+          #     catalog AS catalog
+          #  WHERE
+          #    1 = 1
+          #    AND (((((catalog.portal_type = 'Person'))))) AND (((((catalog.reference = 'foo')))))
+          #  LIMIT 1000
+          # "bar OR foo" because of ZSQLCatalog tokenizing searched sgtrings
+          # by default (feature).
+          return [x.path for x in result if (not exact_match) or x['reference'] in login]
+        _getUserByLogin = CachingMethod(_getUserByLogin,
+                                        id='ERP5UserManager_getUserByLogin',
+                                        cache_factory='erp5_content_short')
+        result = _getUserByLogin(login, exact_match)
+        return [portal.unrestrictedTraverse(x) for x in result]
+
+classImplements( EGOVUserManager
+               , IAuthenticationPlugin
+               , IUserEnumerationPlugin
+               )
+
+InitializeClass(EGOVUserManager)

Added: erp5/trunk/products/ERP5eGovSecurity/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/__init__.py?rev=36487&view=auto
==============================================================================
--- erp5/trunk/products/ERP5eGovSecurity/__init__.py (added)
+++ erp5/trunk/products/ERP5eGovSecurity/__init__.py [utf8] Tue Jun 22 00:02:08 2010
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2001 Zope Corporation and Contributors. All Rights
+# Reserved.
+#
+# 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.
+#
+##############################################################################
+""" ERP5Security product initialization.
+"""
+
+from copy import deepcopy
+
+from AccessControl.Permissions import manage_users as ManageUsers
+from Products.PluggableAuthService.PluggableAuthService import registerMultiPlugin
+from Products.PluggableAuthService.permissions import ManageGroups
+
+import EGOVUserManager
+import EGOVGroupManager
+from Products.ERP5Security import ERP5UserFactory
+from Products.ERP5Security import ERP5RoleManager
+
+
+def mergedLocalRoles(object):
+  """Returns a merging of object and its ancestors'
+  __ac_local_roles__."""
+  # Modified to take into account _getAcquireLocalRoles
+  merged = {}
+  object = getattr(object, 'aq_inner', object)
+  while 1:
+    if getattr(object, '__ac_local_roles__', None) is not None:
+      roles = object.__ac_local_roles__ or {}
+      if callable(roles): roles = roles()
+      for k, v in roles.iteritems():
+        merged.setdefault(k, []).extend(v)
+    # block acquisition
+    if getattr(object, '_getAcquireLocalRoles', None) is not None:
+      if not object._getAcquireLocalRoles() is not None:
+        break
+    if getattr(object, 'aq_parent', None) is not None:
+      object = object.aq_parent
+      object = getattr(object, 'aq_inner', object)
+      continue
+    if getattr(object, 'im_self', None) is not None:
+      object = object.im_self
+      object = getattr(object, 'aq_inner', object)
+      continue
+    break
+
+  return deepcopy(merged)
+
+registerMultiPlugin(EGOVUserManager.EGOVUserManager.meta_type)
+registerMultiPlugin(EGOVGroupManager.EGOVGroupManager.meta_type)
+
+def initialize(context):
+
+    context.registerClass( EGOVUserManager.EGOVUserManager
+                         , permission=ManageUsers
+                         , constructors=(
+                            EGOVUserManager.manage_addEGOVUserManagerForm,
+                            EGOVUserManager.addEGOVUserManager, )
+                         , visibility=None
+                         , icon='www/portal.gif'
+                         )
+
+    context.registerClass( EGOVGroupManager.EGOVGroupManager
+                         , permission=ManageGroups
+                         , constructors=(
+                            EGOVGroupManager.manage_addEGOVGroupManagerForm,
+                            EGOVGroupManager.addEGOVGroupManager, )
+                         , visibility=None
+                         , icon='www/portal.gif'
+                         )

Added: erp5/trunk/products/ERP5eGovSecurity/__init__.pyc
URL: http://svn.erp5.org/erp5/trunk/products/ERP5eGovSecurity/__init__.pyc?rev=36487&view=auto
==============================================================================
Binary file - no diff available.

Propchange: erp5/trunk/products/ERP5eGovSecurity/__init__.pyc
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream




More information about the Erp5-report mailing list