ERP5UserManager.py 7.16 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
Jean-Paul Smets's avatar
Jean-Paul Smets committed
32 33 34

from zLOG import LOG

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 99 100 101 102 103 104 105 106
            sm = getSecurityManager()
            if sm.getUser() != SUPER_USER:
              newSecurityManager(self, self.getUser(SUPER_USER))
            try:
              if pw_validate(user.getPassword(), password) and\
                  user.getCareerRole() == 'internal':
                return login, login # use same for user_id and login
            finally:
              setSecurityManager(sm)
107

Jean-Paul Smets's avatar
Jean-Paul Smets committed
108
            return None
109

Jérome Perrin's avatar
Jérome Perrin committed
110 111 112 113 114 115
        _authenticateCredentials = CachingMethod(_authenticateCredentials,
                                    id='ERP5UserManager_authenticateCredentials')
        return _authenticateCredentials(
                      login=credentials.get('login'),
                      password=credentials.get('password'),
                      path=self.getPhysicalPath())
116

Jean-Paul Smets's avatar
Jean-Paul Smets committed
117 118 119 120
    #
    #   IUserEnumerationPlugin implementation
    #
    security.declarePrivate( 'enumerateUsers' )
Jérome Perrin's avatar
Jérome Perrin committed
121 122
    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
123
        """ See IUserEnumerationPlugin.
124 125
        """
        def _enumerateUsers(id_tuple, exact_match, path):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
126 127
            user_info = []
            plugin_id = self.getId()
128

129 130 131 132 133 134 135 136 137 138 139 140 141
            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)
142

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

150
                  user_info.append(info)
151

Jean-Paul Smets's avatar
Jean-Paul Smets committed
152
            return tuple(user_info)
153

Jérome Perrin's avatar
Jérome Perrin committed
154 155
        _enumerateUsers = CachingMethod(_enumerateUsers,
                                        id='ERP5UserManager_enumerateUsers')
156

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

    def getUserByLogin(self, login):
168
        """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
169
        Search the Catalog for login and return a list of person objects
Vincent Pelletier's avatar
Vincent Pelletier committed
170
        login can be a string or a list of strings
171
        """
172 173
        if not login:
          return []
Jean-Paul Smets's avatar
Jean-Paul Smets committed
174 175
        # because we aren't logged in, we have to create our own
        # SecurityManager to be able to access the Catalog
176
        sm = getSecurityManager()
177 178
        if sm.getUser() != SUPER_USER:
          newSecurityManager(self, self.getUser(SUPER_USER))
179

180
        try:
181 182 183 184 185 186 187
          try:
            result = self.getPortalObject().portal_catalog(
                                    portal_type="Person", reference=login)
          except ConflictError:
            raise
          except:
            LOG('ERP5Security', 0, 'getUserByLogin failed', error=sys.exc_info())
Vincent Pelletier's avatar
Vincent Pelletier committed
188 189 190 191 192 193 194
            # Here we must raise an exception to prevent calers 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]
195 196
        finally:
          setSecurityManager(sm)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
197
        return [item.getObject() for item in result]
198

Jean-Paul Smets's avatar
Jean-Paul Smets committed
199 200 201 202 203 204
classImplements( ERP5UserManager
               , IAuthenticationPlugin
               , IUserEnumerationPlugin
               )

InitializeClass(ERP5UserManager)