ERP5UserManager.py 8.02 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: ERP5UserManager
"""

from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
20 21
from AccessControl.SecurityManagement import getSecurityManager,\
    setSecurityManager, newSecurityManager
Jean-Paul Smets's avatar
Jean-Paul Smets committed
22
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
Jérome Perrin's avatar
Jérome Perrin committed
23 24
from Products.PluggableAuthService.PluggableAuthService import \
    _SWALLOWABLE_PLUGIN_EXCEPTIONS
Jean-Paul Smets's avatar
Jean-Paul Smets committed
25 26 27 28 29
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
30
from ZODB.POSException import ConflictError
Vincent Pelletier's avatar
Vincent Pelletier committed
31
import sys
32
from DateTime import DateTime
33
from zLOG import LOG, PROBLEM
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34

Jérome Perrin's avatar
Jérome Perrin committed
35 36 37 38 39
try :
  from AccessControl.AuthEncoding import pw_validate
except ImportError:
  pw_validate = lambda reference, attempt: reference == attempt

40 41 42
# This user is used to bypass all security checks.
SUPER_USER = '__erp5security-=__'

Jean-Paul Smets's avatar
Jean-Paul Smets committed
43
manage_addERP5UserManagerForm = PageTemplateFile(
Jérome Perrin's avatar
Jérome Perrin committed
44 45
    'www/ERP5Security_addERP5UserManager', globals(),
    __name__='manage_addERP5UserManagerForm' )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
46 47

def addERP5UserManager(dispatcher, id, title=None, REQUEST=None):
Jérome Perrin's avatar
Jérome Perrin committed
48
    """ Add a ERP5UserManager to a Pluggable Auth Service. """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

    eum = ERP5UserManager(id, title)
    dispatcher._setObject(eum.getId(), eum)

    if REQUEST is not None:
        REQUEST['RESPONSE'].redirect(
                                '%s/manage_workspace'
                                '?manage_tabs_message='
                                'ERP5UserManager+added.'
                            % dispatcher.absolute_url())

class ERP5UserManager(BasePlugin):
    """ PAS plugin for managing users in ERP5
    """

    meta_type = 'ERP5 User Manager'

    security = ClassSecurityInfo()

    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.
79

Jean-Paul Smets's avatar
Jean-Paul Smets committed
80 81 82
        o We expect the credentials to be those returned by
            ILoginPasswordExtractionPlugin.
        """
83 84 85 86
        # Forbidden the usage of the super user.
        if credentials.get('login') == SUPER_USER:
          return None

Jean-Paul Smets's avatar
Jean-Paul Smets committed
87
        def _authenticateCredentials(login, password, path):
88
            if not login or not password:
Jean-Paul Smets's avatar
Jean-Paul Smets committed
89
                return None
90

Jean-Paul Smets's avatar
Jean-Paul Smets committed
91
            user_list = self.getUserByLogin(login)
92

Jean-Paul Smets's avatar
Jean-Paul Smets committed
93 94
            if not user_list:
                return None
95

Jean-Paul Smets's avatar
Jean-Paul Smets committed
96
            user = user_list[0]
97

98
            sm = getSecurityManager()
99
            if sm.getUser().getId() != SUPER_USER:
100 101
              newSecurityManager(self, self.getUser(SUPER_USER))
            try:
102 103 104 105
              # get assignment
              assignment_list = [x for x in user.contentValues(portal_type="Assignment") if x.getValidationState() == "open"]
              if pw_validate(user.getPassword(), password) and \
                     len(assignment_list): #user.getCareerRole() == 'internal':
106 107 108
                return login, login # use same for user_id and login
            finally:
              setSecurityManager(sm)
109

Jean-Paul Smets's avatar
Jean-Paul Smets committed
110
            return None
111

Jérome Perrin's avatar
Jérome Perrin committed
112
        _authenticateCredentials = CachingMethod(_authenticateCredentials,
Aurel's avatar
Aurel committed
113
                                                 id='ERP5UserManager_authenticateCredentials',
Aurel's avatar
Aurel committed
114
                                                 cache_factory='erp5_content_short')
Jérome Perrin's avatar
Jérome Perrin committed
115 116 117 118
        return _authenticateCredentials(
                      login=credentials.get('login'),
                      password=credentials.get('password'),
                      path=self.getPhysicalPath())
119

Jean-Paul Smets's avatar
Jean-Paul Smets committed
120 121 122 123
    #
    #   IUserEnumerationPlugin implementation
    #
    security.declarePrivate( 'enumerateUsers' )
Jérome Perrin's avatar
Jérome Perrin committed
124 125
    def enumerateUsers(self, id=None, login=None, exact_match=False,
                       sort_by=None, max_results=None, **kw):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
126
        """ See IUserEnumerationPlugin.
127 128
        """
        def _enumerateUsers(id_tuple, exact_match, path):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
129 130
            user_info = []
            plugin_id = self.getId()
131

132 133 134 135 136 137 138 139 140 141 142 143 144
            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)
145

146
            if id_list:
147
              for user in self.getUserByLogin(tuple(id_list)):
148
                  info = { 'id' : user.getReference()
149 150 151
                         , 'login' : user.getReference()
                         , 'pluginid' : plugin_id
                         }
152

153
                  user_info.append(info)
154

Jean-Paul Smets's avatar
Jean-Paul Smets committed
155
            return tuple(user_info)
156

Jérome Perrin's avatar
Jérome Perrin committed
157
        _enumerateUsers = CachingMethod(_enumerateUsers,
Aurel's avatar
Aurel committed
158
                                        id='ERP5UserManager_enumerateUsers',
Aurel's avatar
Aurel committed
159
                                        cache_factory='erp5_content_short')
160

161 162
        if id is None:
          id = login
163
        if isinstance(id, str):
164
          id = (id,)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
165
        if isinstance(id, list):
166
          id = tuple(id)
Jérome Perrin's avatar
Jérome Perrin committed
167 168 169
        return _enumerateUsers(id_tuple=id,
                               exact_match=exact_match,
                               path=self.getPhysicalPath())
Jean-Paul Smets's avatar
Jean-Paul Smets committed
170 171

    def getUserByLogin(self, login):
172 173 174
        # 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)
175 176
        if not login:
          return []
177

178 179
        portal = self.getPortalObject()

180 181 182 183
        def _getUserByLogin(login):
          # because we aren't logged in, we have to create our own
          # SecurityManager to be able to access the Catalog
          sm = getSecurityManager()
184
          if sm.getUser().getId() != SUPER_USER:
185 186
            newSecurityManager(self, self.getUser(SUPER_USER))
  
187
          try:
188
            try:
189
              result = portal.portal_catalog.unrestrictedSearchResults(
190 191 192 193 194
                                      portal_type="Person", reference=login)
            except ConflictError:
              raise
            except:
              LOG('ERP5Security', PROBLEM, 'getUserByLogin failed', error=sys.exc_info())
195
              # Here we must raise an exception to prevent callers from caching
196 197 198 199 200 201 202 203
              # 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)
204
          return [x.path for x in result]
205 206 207 208
        _getUserByLogin = CachingMethod(_getUserByLogin,
                                        id='ERP5UserManager_getUserByLogin',
                                        cache_factory='erp5_content_short')
        result = _getUserByLogin(login)
209
        return [portal.unrestrictedTraverse(x) for x in result]
210

Jean-Paul Smets's avatar
Jean-Paul Smets committed
211 212 213 214 215 216
classImplements( ERP5UserManager
               , IAuthenticationPlugin
               , IUserEnumerationPlugin
               )

InitializeClass(ERP5UserManager)