Back to index

plone3  3.1.7
pas.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # PlonePAS - Adapt PluggableAuthService for use in Plone
00004 # Copyright (C) 2005 Enfold Systems, Kapil Thangavelu, et al
00005 #
00006 # This software is subject to the provisions of the Zope Public License,
00007 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this
00008 # distribution.
00009 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00010 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00011 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00012 # FOR A PARTICULAR PURPOSE.
00013 #
00014 ##############################################################################
00015 """
00016 pas alterations and monkies
00017 """
00018 from sets import Set
00019 from Products.CMFCore.utils import getToolByName
00020 
00021 from AccessControl import Unauthorized, getSecurityManager
00022 from AccessControl.Permissions import manage_users as ManageUsers
00023 from AccessControl.Permissions import manage_properties, change_permissions
00024 
00025 from Products.PluggableAuthService.PluggableAuthService import \
00026      PluggableAuthService, _SWALLOWABLE_PLUGIN_EXCEPTIONS
00027 from Products.PluggableAuthService.PluggableAuthService import security
00028 from Products.PluggableAuthService.interfaces.plugins import IRoleAssignerPlugin
00029 from Products.PluggableAuthService.interfaces.plugins import IUserEnumerationPlugin
00030 from Products.PluggableAuthService.interfaces.plugins import IGroupEnumerationPlugin
00031 
00032 from Products.PlonePAS.interfaces.plugins import IUserManagement, ILocalRolesPlugin
00033 from Products.PlonePAS.interfaces.group import IGroupIntrospection
00034 from Products.PlonePAS.interfaces.plugins import IUserIntrospection
00035 from AccessControl.requestmethod import postonly
00036 
00037 # Register the PAS acl_users as a utility
00038 from Products.CMFCore.utils import registerToolInterface
00039 from Products.PluggableAuthService.interfaces.authservice import IPluggableAuthService
00040 registerToolInterface('acl_users', IPluggableAuthService)
00041 
00042 
00043 #################################
00044 # pas folder monkies - standard zope user folder api
00045 
00046 _old_doAddUser = PluggableAuthService._doAddUser
00047 def _doAddUser(self, login, password, roles, domains, groups=None, **kw ):
00048     """Masking of PAS._doAddUser to add groups param."""
00049     retval = _old_doAddUser(self, login, password, roles, domains)
00050     if groups is not None:
00051         self.userSetGroups(login, groups)
00052     return retval
00053 
00054 PluggableAuthService._doAddUser = _doAddUser
00055 
00056 def _doDelUsers(self, names, REQUEST=None):
00057     """
00058     Delete users given by a list of user ids.
00059     Has no return value, like the original.
00060     """
00061     for name in names:
00062         self._doDelUser(name)
00063 
00064 PluggableAuthService._doDelUsers = _doDelUsers
00065 
00066  
00067 def _doDelUser(self, id):
00068     """
00069     Given a user id, hand off to a deleter plugin if available.
00070     """
00071     plugins = self._getOb('plugins')
00072     userdeleters = plugins.listPlugins(IUserManagement)
00073 
00074     if not userdeleters:
00075         raise NotImplementedError("There is no plugin that can "
00076                                    " delete users.")
00077 
00078     for userdeleter_id, userdeleter in userdeleters:
00079         try:
00080             userdeleter.doDeleteUser(id)
00081         except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00082             pass
00083 PluggableAuthService._doDelUser = _doDelUser
00084 
00085 security.declareProtected(ManageUsers, 'userFolderDelUsers')
00086 PluggableAuthService.userFolderDelUsers = postonly(PluggableAuthService._doDelUsers)
00087 
00088 
00089 def _doChangeUser(self, principal_id, password, roles, domains=(), groups=None,
00090                   REQUEST=None, **kw):
00091     """
00092     Given a principal id, change its password, roles, domains, iff
00093     respective plugins for such exist.
00094 
00095     XXX domains are currently ignored.
00096     """
00097     # Might be called with 'None' as password from the Plone UI, in
00098     # prefs_users_overview when resetPassword is not set.
00099     if password is not None:
00100         self.userSetPassword(principal_id, password)
00101 
00102     plugins = self._getOb('plugins')
00103     rmanagers = plugins.listPlugins(IRoleAssignerPlugin)
00104 
00105     if not (rmanagers):
00106         raise NotImplementedError("There is no plugin that can modify roles")
00107 
00108     for rid, rmanager in rmanagers:
00109         rmanager.assignRolesToPrincipal(roles, principal_id)
00110 
00111     if groups is not None:
00112         self.userSetGroups(principal_id, groups)
00113 
00114     return True
00115 
00116 PluggableAuthService._doChangeUser = _doChangeUser
00117 
00118 security.declareProtected(ManageUsers, 'userFolderEditUser')
00119 PluggableAuthService.userFolderEditUser = postonly(PluggableAuthService._doChangeUser)
00120 
00121 
00122 # ttw alias
00123 # XXX need to security restrict these methods, no base class sec decl
00124 #PluggableAuthService.userFolderAddUser__roles__ = ()
00125 def userFolderAddUser(self, login, password, roles, domains, groups=None, REQUEST=None, **kw ):
00126     self._doAddUser(login, password, roles, domains, **kw)
00127     if groups is not None:
00128         self.userSetGroups(login, groups)
00129 
00130 PluggableAuthService.userFolderAddUser = postonly(userFolderAddUser)
00131 
00132 
00133 def _doAddGroup(self, id, roles, groups=None, **kw):
00134     gtool = getToolByName(self, 'portal_groups')
00135     return gtool.addGroup(id, roles, groups, **kw)
00136 
00137 PluggableAuthService._doAddGroup = _doAddGroup
00138 
00139 # for prefs_group_manage compatibility. really should be using tool.
00140 def _doDelGroups(self, names, REQUEST=None):
00141     gtool = getToolByName(self, 'portal_groups')
00142     for group_id in names:
00143         gtool.removeGroup(group_id)
00144 
00145 PluggableAuthService._doDelGroups = _doDelGroups
00146 
00147 security.declareProtected(ManageUsers, 'userFolderDelGroups')
00148 PluggableAuthService.userFolderDelGroups = postonly(PluggableAuthService._doDelGroups)
00149 
00150 
00151 def _doChangeGroup(self, principal_id, roles, groups=None, REQUEST=None, **kw):
00152     """
00153     Given a group's id, change its roles, domains, iff respective
00154     plugins for such exist.
00155 
00156     XXX domains are currently ignored.
00157 
00158     See also _doChangeUser
00159     """
00160     gtool = getToolByName(self, 'portal_groups')
00161     gtool.editGroup(principal_id, roles, groups, **kw)
00162     return True
00163 PluggableAuthService._doChangeGroup = _doChangeGroup
00164 
00165 def _updateGroup(self, principal_id, roles=None, groups=None, **kw):
00166     """
00167     Given a group's id, change its roles, groups, iff respective
00168     plugins for such exist.
00169 
00170     XXX domains are currently ignored.
00171 
00172     This is not an alias to _doChangeGroup because its params are different (slightly).
00173     """
00174     return self._doChangeGroup(principal_id, roles, groups, **kw)
00175 PluggableAuthService._updateGroup = _updateGroup
00176 
00177 security.declareProtected(ManageUsers, 'userFolderEditGroup')
00178 PluggableAuthService.userFolderEditGroup = postonly(PluggableAuthService._doChangeGroup)
00179 
00180 
00181 security.declareProtected(ManageUsers, 'getGroups')
00182 def getGroups(self):
00183     gtool = getToolByName(self, 'portal_groups')
00184     return gtool.listGroups()
00185 PluggableAuthService.getGroups = getGroups
00186 
00187 security.declareProtected(ManageUsers, 'getGroupNames')
00188 def getGroupNames(self):
00189     gtool = getToolByName(self, 'portal_groups')
00190     return gtool.getGroupIds()
00191 PluggableAuthService.getGroupNames = getGroupNames
00192 
00193 security.declareProtected(ManageUsers, 'getGroupIds')
00194 def getGroupIds(self):
00195     gtool = getToolByName(self, 'portal_groups')
00196     return gtool.getGroupIds()
00197 PluggableAuthService.getGroupIds = getGroupIds
00198 
00199 security.declareProtected(ManageUsers, 'getGroup')
00200 def getGroup(self, group_id):
00201     """Like getGroupById in groups tool, but doesn't wrap.
00202     """
00203     group = None
00204     introspectors = self.plugins.listPlugins(IGroupIntrospection)
00205 
00206     if not introspectors:
00207         raise ValueError, 'No plugins allow for group management'
00208     for iid, introspector in introspectors:
00209         group = introspector.getGroupById(group_id)
00210         if group is not None:
00211             break
00212     return group
00213 PluggableAuthService.getGroup = getGroup
00214 
00215 
00216 security.declareProtected(ManageUsers, 'getGroupByName')
00217 def getGroupByName(self, name, default = None):
00218     ret = self.getGroup(name)
00219     if ret is None:
00220         return default
00221     return ret
00222 PluggableAuthService.getGroupByName = getGroupByName
00223 
00224 
00225 security.declareProtected(ManageUsers, 'getGroupById')
00226 def getGroupById(self, id, default = None):
00227     gtool = getToolByName(self, "portal_groups")
00228     ret = gtool.getGroupById(id)
00229     if ret is None:
00230         return default
00231     else:
00232         return ret
00233            
00234 PluggableAuthService.getGroupById = getGroupById
00235 
00236 
00237 security.declarePublic("getLocalRolesForDisplay")
00238 def getLocalRolesForDisplay(self, object):
00239     """This is used for plone's local roles display
00240 
00241     This method returns a tuple (massagedUsername, roles, userType,
00242     actualUserName).  This method is protected by the 'access content
00243     information' permission. We may change that if it's too
00244     permissive...
00245 
00246     A GRUF method originally.
00247     """
00248     # Perform security check on destination object
00249     if not getSecurityManager().checkPermission(manage_properties, object):
00250         raise Unauthorized(name = "getLocalRolesForDisplay")
00251     
00252     return self._getLocalRolesForDisplay(object)
00253 PluggableAuthService.getLocalRolesForDisplay = getLocalRolesForDisplay
00254     
00255 def _getLocalRolesForDisplay(self, object):
00256     result = []
00257     # we don't have a PAS-side way to get this
00258     local_roles = object.get_local_roles()
00259     for one_user in local_roles:
00260         username = userid = one_user[0]
00261         roles = one_user[1]
00262         userType = 'user'
00263         if self.getGroup(userid):
00264             userType = 'group'
00265         else:
00266             user = self.getUserById(userid) or self.getUser(username)
00267             if user:
00268                 username = user.getUserName()
00269                 userid = user.getId()
00270         result.append((username, roles, userType, userid))
00271     return tuple(result)
00272 PluggableAuthService._getLocalRolesForDisplay = _getLocalRolesForDisplay
00273 
00274 
00275 def getUsers(self):
00276     """
00277     Return a list of all users from plugins that implement the user
00278     introspection interface.
00279 
00280     Could potentially be very long.
00281     """
00282     # We should have a method that's cheap about returning number of users.
00283     retval = []
00284     plugins = self._getOb('plugins')
00285     try:
00286         introspectors = self.plugins.listPlugins(IUserIntrospection)
00287     except KeyError:
00288         return retval
00289 
00290     for iid, introspector in introspectors:
00291         retval += introspector.getUsers()
00292 
00293     return retval
00294 
00295 PluggableAuthService.getUsers = getUsers
00296 PluggableAuthService.getPureUsers = getUsers   # this'll make listMembers work
00297 
00298 
00299 def canListAllUsers(self):
00300     plugins = self._getOb('plugins')
00301 
00302     # Do we have multiple user plugins?
00303     if len(plugins.listPlugins(IUserEnumerationPlugin)) != len(plugins.listPlugins(IUserIntrospection)):
00304         return False
00305 
00306     # Does our single user enumerator support the needed API?
00307     #for method in [#'countAllUsers',
00308     #               'getUsers',
00309     #               'getUserNames']:
00310     #    if not hasattr(pas, method):
00311     #        return False
00312 
00313     return True
00314 PluggableAuthService.canListAllUsers = canListAllUsers
00315 
00316 
00317 def canListAllGroups(self):
00318     plugins = self._getOb('plugins')
00319 
00320     # Do we have multiple user plugins?
00321     if len(plugins.listPlugins(IGroupEnumerationPlugin)) != len(plugins.listPlugins(IGroupIntrospection)):
00322         return False
00323     return True
00324 PluggableAuthService.canListAllGroups = canListAllGroups
00325 
00326 
00327 def userSetPassword(self, userid, password):
00328     """Emulate GRUF 3 call for password set, for use with PwRT."""
00329     # used by _doChangeUser
00330     plugins = self._getOb('plugins')
00331     managers = plugins.listPlugins(IUserManagement)
00332 
00333     if not (managers):
00334         raise NotImplementedError("There is no plugin that can modify users")
00335 
00336     modified = False
00337     for mid, manager in managers:
00338         try:
00339             manager.doChangeUser(userid, password)            
00340         except RuntimeError:
00341             # XXX: why silent ignore this Error?
00342             pass
00343         else:
00344             modified = True
00345 
00346     if not modified:
00347         raise RuntimeError ("No user management plugins were able "
00348                             "to successfully modify the user")
00349 PluggableAuthService.userSetPassword = userSetPassword
00350 
00351 
00352 def credentialsChanged(self, user, name, new_password):
00353     """Notifies the authentication mechanism that this user has changed
00354     passwords.  This can be used to update the authentication cookie.
00355     Note that this call should *not* cause any change at all to user
00356     databases.
00357 
00358     For use by CMFCore.MembershipTool.credentialsChanged
00359     """
00360     request = self.REQUEST
00361     response = request.RESPONSE
00362     login = name
00363 
00364     self.updateCredentials(request, response, login, new_password)
00365 PluggableAuthService.credentialsChanged = credentialsChanged
00366 
00367 
00368 # for ZopeVersionControl, we need to check 'plugins' for more than
00369 # existence, since it replaces objects (like 'plugins') with SimpleItems
00370 # and calls _delOb, which tries to use special methods of 'plugins'
00371 from OFS.Folder import Folder
00372 def _delOb( self, id ):
00373     #
00374     #   Override ObjectManager's version to clean up any plugin
00375     #   registrations for the deleted object
00376     #
00377     # XXX imo this is a evil one
00378     #
00379     plugins = self._getOb( 'plugins', None )
00380 
00381     if getattr(plugins, 'removePluginById', None) is not None:
00382         plugins.removePluginById( id )
00383 
00384     Folder._delOb( self, id )
00385 PluggableAuthService._delOb = _delOb
00386 
00387 def addRole( self, role ):
00388     plugins = self._getOb('plugins')
00389     roles = plugins.listPlugins(IRoleAssignerPlugin)
00390 
00391     for plugin_id, plugin in roles:
00392         try:
00393             plugin.addRole( role )
00394             return
00395         except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00396             pass
00397 PluggableAuthService.addRole = addRole
00398 
00399 def getAllLocalRoles( self, context ):
00400     # Perform security check on destination object
00401     if not getSecurityManager().checkPermission(change_permissions, context):
00402         raise Unauthorized(name = "getAllLocalRoles")
00403     return self._getAllLocalRoles(context)
00404 PluggableAuthService.getAllLocalRoles = getAllLocalRoles
00405     
00406 def _getAllLocalRoles(self, context):
00407     plugins = self._getOb('plugins')
00408     lrmanagers = plugins.listPlugins(ILocalRolesPlugin)
00409 
00410     roles={}
00411     for lrid, lrmanager in lrmanagers:
00412         newroles=lrmanager.getAllLocalRolesInContext(context)
00413         for k,v in newroles.items():
00414             if k not in roles:
00415                 roles[k]=Set()
00416             roles[k].update(v)
00417 
00418     return roles
00419 PluggableAuthService._getAllLocalRoles = _getAllLocalRoles
00420 
00421 from Products.PluggableAuthService.plugins.ZODBUserManager import ZODBUserManager
00422 def noKeywordEnumerateusers(self, id=None, login=None, exact_match=False,
00423         sort_by=None, max_results=None, **kw):
00424     if kw:
00425         return ()
00426     return self._oldEnumerateUsers(id, login, exact_match, sort_by,
00427             max_results, **kw)
00428 
00429 ZODBUserManager._oldEnumerateUsers = ZODBUserManager.enumerateUsers
00430 ZODBUserManager.enumerateUsers = noKeywordEnumerateusers