Back to index

plone3  3.1.7
ZODBRoleManager.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights
00004 # Reserved.
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 """ Classes: ZODBRoleManager
00016 
00017 $Id: ZODBRoleManager.py 76618 2007-06-11 19:09:58Z jens $
00018 """
00019 from Acquisition import aq_parent, aq_inner
00020 from AccessControl import ClassSecurityInfo
00021 from BTrees.OOBTree import OOBTree
00022 from Globals import InitializeClass
00023 
00024 from zope.interface import Interface
00025 
00026 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00027 
00028 from Products.PluggableAuthService.interfaces.plugins \
00029     import IRolesPlugin
00030 from Products.PluggableAuthService.interfaces.plugins \
00031     import IRoleEnumerationPlugin
00032 from Products.PluggableAuthService.interfaces.plugins \
00033     import IRoleAssignerPlugin
00034 
00035 from Products.PluggableAuthService.permissions import ManageUsers
00036 from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin
00037 from Products.PluggableAuthService.utils import classImplements
00038 from Products.PluggableAuthService.utils import postonly
00039 
00040 class IZODBRoleManager(Interface):
00041     """ Marker interface.
00042     """
00043 
00044 manage_addZODBRoleManagerForm = PageTemplateFile(
00045     'www/zrAdd', globals(), __name__='manage_addZODBRoleManagerForm' )
00046 
00047 def addZODBRoleManager( dispatcher, id, title=None, REQUEST=None ):
00048     """ Add a ZODBRoleManager to a Pluggable Auth Service. """
00049 
00050     zum = ZODBRoleManager(id, title)
00051     dispatcher._setObject(zum.getId(), zum)
00052 
00053     if REQUEST is not None:
00054         REQUEST['RESPONSE'].redirect(
00055                                 '%s/manage_workspace'
00056                                 '?manage_tabs_message='
00057                                 'ZODBRoleManager+added.'
00058                             % dispatcher.absolute_url())
00059 
00060 class ZODBRoleManager( BasePlugin ):
00061 
00062     """ PAS plugin for managing roles in the ZODB.
00063     """
00064     meta_type = 'ZODB Role Manager'
00065 
00066     security = ClassSecurityInfo()
00067 
00068     def __init__(self, id, title=None):
00069 
00070         self._id = self.id = id
00071         self.title = title
00072 
00073         self._roles = OOBTree()
00074         self._principal_roles = OOBTree()
00075 
00076     def manage_afterAdd( self, item, container ):
00077 
00078         if item is self:
00079             role_holder = aq_parent( aq_inner( container ) )
00080             for role in getattr( role_holder, '__ac_roles__', () ):
00081                 try:
00082                     if role not in ('Anonymous', 'Authenticated'):
00083                         self.addRole( role )
00084                 except KeyError:
00085                     pass
00086 
00087         if 'Manager' not in self._roles:
00088             self.addRole( 'Manager' )
00089 
00090     #
00091     #   IRolesPlugin implementation
00092     #
00093     security.declarePrivate( 'getRolesForPrincipal' )
00094     def getRolesForPrincipal( self, principal, request=None ):
00095 
00096         """ See IRolesPlugin.
00097         """
00098         result = list( self._principal_roles.get( principal.getId(), () ) )
00099 
00100         getGroups = getattr( principal, 'getGroups', lambda x: () )
00101         for group_id in getGroups():
00102             result.extend( self._principal_roles.get( group_id, () ) )
00103 
00104         return tuple( result )
00105 
00106     #
00107     #   IRoleEnumerationPlugin implementation
00108     #
00109     def enumerateRoles( self
00110                       , id=None
00111                       , exact_match=False
00112                       , sort_by=None
00113                       , max_results=None
00114                       , **kw
00115                       ):
00116 
00117         """ See IRoleEnumerationPlugin.
00118         """
00119         role_info = []
00120         role_ids = []
00121         plugin_id = self.getId()
00122 
00123         if isinstance( id, str ):
00124             id = [ id ]
00125 
00126         if exact_match and ( id ):
00127             role_ids.extend( id )
00128 
00129         if role_ids:
00130             role_filter = None
00131 
00132         else:   # Searching
00133             role_ids = self.listRoleIds()
00134             role_filter = _ZODBRoleFilter( id, **kw )
00135 
00136         for role_id in role_ids:
00137 
00138             if self._roles.get( role_id ):
00139                 e_url = '%s/manage_roles' % self.getId()
00140                 p_qs = 'role_id=%s' % role_id
00141                 m_qs = 'role_id=%s&assign=1' % role_id
00142 
00143                 info = {}
00144                 info.update( self._roles[ role_id ] )
00145 
00146                 info[ 'pluginid' ] = plugin_id
00147                 info[ 'properties_url'  ] = '%s?%s' % (e_url, p_qs)
00148                 info[ 'members_url'  ] = '%s?%s' % (e_url, m_qs)
00149 
00150                 if not role_filter or role_filter( info ):
00151                     role_info.append( info )
00152 
00153         return tuple( role_info )
00154 
00155     #
00156     #   IRoleAssignerPlugin implementation
00157     #
00158     security.declarePrivate( 'doAssignRoleToPrincipal' )
00159     def doAssignRoleToPrincipal( self, principal_id, role ):
00160         return self.assignRoleToPrincipal( role, principal_id )
00161 
00162     security.declarePrivate( 'doRemoveRoleFromPrincipal' )
00163     def doRemoveRoleFromPrincipal( self, principal_id, role ):
00164         return self.removeRoleFromPrincipal( role, principal_id )
00165 
00166     #
00167     #   Role management API
00168     #
00169     security.declareProtected( ManageUsers, 'listRoleIds' )
00170     def listRoleIds( self ):
00171 
00172         """ Return a list of the role IDs managed by this object.
00173         """
00174         return self._roles.keys()
00175 
00176     security.declareProtected( ManageUsers, 'listRoleInfo' )
00177     def listRoleInfo( self ):
00178 
00179         """ Return a list of the role mappings.
00180         """
00181         return self._roles.values()
00182 
00183     security.declareProtected( ManageUsers, 'getRoleInfo' )
00184     def getRoleInfo( self, role_id ):
00185 
00186         """ Return a role mapping.
00187         """
00188         return self._roles[ role_id ]
00189 
00190     security.declareProtected( ManageUsers, 'addRole' )
00191     def addRole( self, role_id, title='', description='' ):
00192 
00193         """ Add 'role_id' to the list of roles managed by this object.
00194 
00195         o Raise KeyError on duplicate.
00196         """
00197         if self._roles.get( role_id ) is not None:
00198             raise KeyError, 'Duplicate role: %s' % role_id
00199 
00200         self._roles[ role_id ] = { 'id' : role_id
00201                                  , 'title' : title
00202                                  , 'description' : description
00203                                  }
00204 
00205     security.declareProtected( ManageUsers, 'updateRole' )
00206     def updateRole( self, role_id, title, description ):
00207 
00208         """ Update title and description for the role.
00209 
00210         o Raise KeyError if not found.
00211         """
00212         self._roles[ role_id ].update( { 'title' : title
00213                                        , 'description' : description
00214                                        } )
00215 
00216     security.declareProtected( ManageUsers, 'removeRole' )
00217     def removeRole( self, role_id, REQUEST=None ):
00218 
00219         """ Remove 'role_id' from the list of roles managed by this object.
00220 
00221         o Raise KeyError if not found.
00222         """
00223         for principal_id in self._principal_roles.keys():
00224             self.removeRoleFromPrincipal( role_id, principal_id )
00225 
00226         del self._roles[ role_id ]
00227     removeRole = postonly(removeRole)
00228 
00229     #
00230     #   Role assignment API
00231     #
00232     security.declareProtected( ManageUsers, 'listAvailablePrincipals' )
00233     def listAvailablePrincipals( self, role_id, search_id ):
00234 
00235         """ Return a list of principal IDs to whom a role can be assigned.
00236 
00237         o If supplied, 'search_id' constrains the principal IDs;  if not,
00238           return empty list.
00239 
00240         o Omit principals with existing assignments.
00241         """
00242         result = []
00243 
00244         if search_id:  # don't bother searching if no criteria
00245 
00246             parent = aq_parent( self )
00247 
00248             for info in parent.searchPrincipals( max_results=20
00249                                                , sort_by='id'
00250                                                , id=search_id
00251                                                , exact_match=False
00252                                                ):
00253                 id = info[ 'id' ]
00254                 title = info.get( 'title', id )
00255                 if ( role_id not in self._principal_roles.get( id, () )
00256                  and role_id != id ):
00257                     result.append( ( id, title ) )
00258 
00259         return result
00260 
00261     security.declareProtected( ManageUsers, 'listAssignedPrincipals' )
00262     def listAssignedPrincipals( self, role_id ):
00263 
00264         """ Return a list of principal IDs to whom a role is assigned.
00265         """
00266         result = []
00267 
00268         for k, v in self._principal_roles.items():
00269             if role_id in v:
00270                 # should be at most one and only one mapping to 'k'
00271 
00272                 parent = aq_parent( self )
00273                 info = parent.searchPrincipals( id=k, exact_match=True )
00274                 assert( len( info ) in ( 0, 1 ) )
00275                 if len( info ) == 0:
00276                     title = '<%s: not found>' % k
00277                 else:
00278                     title = info[0].get( 'title', k )
00279                 result.append( ( k, title ) )
00280 
00281         return result
00282 
00283     security.declareProtected( ManageUsers, 'assignRoleToPrincipal' )
00284     def assignRoleToPrincipal( self, role_id, principal_id, REQUEST=None ):
00285 
00286         """ Assign a role to a principal (user or group).
00287 
00288         o Return a boolean indicating whether a new assignment was created.
00289 
00290         o Raise KeyError if 'role_id' is unknown.
00291         """
00292         role_info = self._roles[ role_id ] # raise KeyError if unknown!
00293 
00294         current = self._principal_roles.get( principal_id, () )
00295         already = role_id in current
00296 
00297         if not already:
00298             new = current + ( role_id, )
00299             self._principal_roles[ principal_id ] = new
00300 
00301         return not already
00302     assignRoleToPrincipal = postonly(assignRoleToPrincipal)
00303 
00304     security.declareProtected( ManageUsers, 'removeRoleFromPrincipal' )
00305     def removeRoleFromPrincipal( self, role_id, principal_id, REQUEST=None ):
00306 
00307         """ Remove a role from a principal (user or group).
00308 
00309         o Return a boolean indicating whether the role was already present.
00310 
00311         o Raise KeyError if 'role_id' is unknown.
00312 
00313         o Ignore requests to remove a role not already assigned to the
00314           principal.
00315         """
00316         role_info = self._roles[ role_id ] # raise KeyError if unknown!
00317 
00318         current = self._principal_roles.get( principal_id, () )
00319         new = tuple( [ x for x in current if x != role_id ] )
00320         already = current != new
00321 
00322         if already:
00323             self._principal_roles[ principal_id ] = new
00324 
00325         return already
00326     removeRoleFromPrincipal = postonly(removeRoleFromPrincipal)
00327 
00328     #
00329     #   ZMI
00330     #
00331     manage_options = ( ( { 'label': 'Roles', 
00332                            'action': 'manage_roles', }
00333                          ,
00334                        )
00335                      + BasePlugin.manage_options
00336                      )
00337 
00338     security.declareProtected( ManageUsers, 'manage_roles' )
00339     manage_roles = PageTemplateFile( 'www/zrRoles'
00340                                    , globals()
00341                                    , __name__='manage_roles'
00342                                    )
00343 
00344     security.declareProtected( ManageUsers, 'manage_twoLists' )
00345     manage_twoLists = PageTemplateFile( '../www/two_lists'
00346                                       , globals()
00347                                       , __name__='manage_twoLists'
00348                                       )
00349 
00350     security.declareProtected( ManageUsers, 'manage_addRole' )
00351     def manage_addRole( self
00352                       , role_id
00353                       , title
00354                       , description
00355                       , RESPONSE
00356                       ):
00357         """ Add a role via the ZMI.
00358         """
00359         self.addRole( role_id, title, description )
00360 
00361         message = 'Role+added'
00362 
00363         RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s'
00364                          % ( self.absolute_url(), message )
00365                          )
00366 
00367     security.declareProtected( ManageUsers, 'manage_updateRole' )
00368     def manage_updateRole( self
00369                          , role_id
00370                          , title
00371                          , description
00372                          , RESPONSE
00373                          ):
00374         """ Update a role via the ZMI.
00375         """
00376         self.updateRole( role_id, title, description )
00377 
00378         message = 'Role+updated'
00379 
00380         RESPONSE.redirect( '%s/manage_roles?role_id=%s&manage_tabs_message=%s'
00381                          % ( self.absolute_url(), role_id, message )
00382                          )
00383 
00384     security.declareProtected( ManageUsers, 'manage_removeRoles' )
00385     def manage_removeRoles( self
00386                           , role_ids
00387                           , RESPONSE
00388                           , REQUEST=None
00389                           ):
00390         """ Remove one or more roles via the ZMI.
00391         """
00392         role_ids = filter( None, role_ids )
00393 
00394         if not role_ids:
00395             message = 'no+roles+selected'
00396 
00397         else:
00398 
00399             for role_id in role_ids:
00400                 self.removeRole( role_id )
00401 
00402             message = 'Roles+removed'
00403 
00404         RESPONSE.redirect( '%s/manage_roles?manage_tabs_message=%s'
00405                          % ( self.absolute_url(), message )
00406                          )
00407     manage_removeRoles = postonly(manage_removeRoles)
00408 
00409     security.declareProtected( ManageUsers, 'manage_assignRoleToPrincipals' )
00410     def manage_assignRoleToPrincipals( self
00411                                      , role_id
00412                                      , principal_ids
00413                                      , RESPONSE
00414                                      , REQUEST=None
00415                                      ):
00416         """ Assign a role to one or more principals via the ZMI.
00417         """
00418         assigned = []
00419 
00420         for principal_id in principal_ids:
00421             if self.assignRoleToPrincipal( role_id, principal_id ):
00422                 assigned.append( principal_id )
00423 
00424         if not assigned:
00425             message = 'Role+%s+already+assigned+to+all+principals' % role_id
00426         else:
00427             message = 'Role+%s+assigned+to+%s' % ( role_id
00428                                                  , '+'.join( assigned )
00429                                                  )
00430 
00431         RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1'
00432                            + '&manage_tabs_message=%s'
00433                            ) % ( self.absolute_url(), role_id, message )
00434                          )
00435     manage_assignRoleToPrincipals = postonly(manage_assignRoleToPrincipals)
00436 
00437     security.declareProtected( ManageUsers, 'manage_removeRoleFromPrincipals' )
00438     def manage_removeRoleFromPrincipals( self
00439                                        , role_id
00440                                        , principal_ids
00441                                        , RESPONSE
00442                                        , REQUEST=None
00443                                        ):
00444         """ Remove a role from one or more principals via the ZMI.
00445         """
00446         removed = []
00447 
00448         for principal_id in principal_ids:
00449             if self.removeRoleFromPrincipal( role_id, principal_id ):
00450                 removed.append( principal_id )
00451 
00452         if not removed:
00453             message = 'Role+%s+alread+removed+from+all+principals' % role_id
00454         else:
00455             message = 'Role+%s+removed+from+%s' % ( role_id
00456                                                   , '+'.join( removed )
00457                                                   )
00458 
00459         RESPONSE.redirect( ( '%s/manage_roles?role_id=%s&assign=1'
00460                            + '&manage_tabs_message=%s'
00461                            ) % ( self.absolute_url(), role_id, message )
00462                          )
00463     manage_removeRoleFromPrincipals = postonly(manage_removeRoleFromPrincipals)
00464 
00465 classImplements( ZODBRoleManager
00466                , IZODBRoleManager
00467                , IRolesPlugin
00468                , IRoleEnumerationPlugin
00469                , IRoleAssignerPlugin
00470                )
00471 
00472 
00473 InitializeClass( ZODBRoleManager )
00474 
00475 class _ZODBRoleFilter:
00476 
00477     def __init__( self, id=None, **kw ):
00478 
00479         self._filter_ids = id
00480 
00481     def __call__( self, role_info ):
00482 
00483         if self._filter_ids:
00484 
00485             key = 'id'
00486 
00487         else:
00488             return 1 # TODO:  try using 'kw'
00489 
00490         value = role_info.get( key )
00491 
00492         if not value:
00493             return False
00494 
00495         for id in self._filter_ids:
00496             if value.find( id ) >= 0:
00497                 return 1
00498 
00499         return False