Back to index

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