Back to index

plone3  3.1.7
group.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 ZODB Group Implementation with basic introspection and
00017 management (ie. rw) capabilities.
00018 
00019 """
00020 
00021 import logging
00022 from Acquisition import aq_base
00023 from BTrees.OOBTree import OOBTree, OOSet
00024 from Globals import DTMLFile
00025 from Globals import InitializeClass
00026 from AccessControl import ClassSecurityInfo
00027 
00028 from zope.interface import implementedBy
00029 
00030 from Products.PluggableAuthService.PluggableAuthService import _SWALLOWABLE_PLUGIN_EXCEPTIONS
00031 from Products.PluggableAuthService.plugins.ZODBGroupManager import ZODBGroupManager
00032 from Products.PluggableAuthService.interfaces.plugins import IGroupEnumerationPlugin
00033 from Products.PluggableAuthService.interfaces.plugins import IPropertiesPlugin
00034 from Products.PluggableAuthService.interfaces.plugins import IRolesPlugin
00035 from Products.PluggableAuthService.utils import createViewName
00036 from Products.PluggableAuthService.utils import classImplements
00037 
00038 from Products.PlonePAS.interfaces.group import IGroupManagement, IGroupIntrospection
00039 from Products.PlonePAS.interfaces.capabilities import IGroupCapability
00040 from Products.PlonePAS.interfaces.capabilities import IDeleteCapability
00041 from ufactory import PloneUser
00042 
00043 manage_addGroupManagerForm = DTMLFile("../zmi/GroupManagerForm", globals())
00044 logger = logging.getLogger('Plone')
00045 
00046 def manage_addGroupManager(self, id, title='', RESPONSE=None):
00047     """
00048     Add a zodb group manager with management and introspection
00049     capabilities to pas.
00050     """
00051     grum = GroupManager(id, title)
00052 
00053     self._setObject(grum.getId(), grum)
00054 
00055     if RESPONSE is not None:
00056         return RESPONSE.redirect('manage_workspace')
00057 
00058 
00059 class GroupManager(ZODBGroupManager):
00060 
00061     meta_type = "Group Manager"
00062     security = ClassSecurityInfo()
00063 
00064     def __init__(self, *args, **kw):
00065         ZODBGroupManager.__init__(self, *args, **kw)
00066         self._group_principal_map = OOBTree() # reverse index of groups->principal
00067 
00068     #################################
00069     # overrides to ease group principal lookups for introspection api
00070 
00071     def addGroup(self, group_id, *args, **kw):
00072         ZODBGroupManager.addGroup(self, group_id, *args, **kw)
00073         self._group_principal_map[ group_id ] = OOSet()
00074         return True
00075 
00076     def removeGroup(self, group_id):
00077         ZODBGroupManager.removeGroup(self, group_id)
00078         del self._group_principal_map[ group_id ]
00079         return True
00080 
00081     def addPrincipalToGroup(self, principal_id, group_id):
00082         ZODBGroupManager.addPrincipalToGroup(self, principal_id, group_id)
00083         self._group_principal_map[ group_id ].insert(principal_id)
00084         return True
00085 
00086     def removePrincipalFromGroup(self, principal_id, group_id):
00087         already = ZODBGroupManager.removePrincipalFromGroup(self, principal_id, group_id)
00088         if already: self._group_principal_map[ group_id ].remove(principal_id)
00089         return True
00090 
00091     #################################
00092     # overrides for api matching/massage
00093 
00094     def updateGroup(self, group_id, **kw):
00095         kw['title'].setdefault('')
00096         kw['description'].setdefault('')
00097         ZODBGroupManager.updateGroup(self, group_id, **kw)
00098         return True
00099 
00100     #################################
00101     # introspection interface
00102 
00103     def getGroupById(self, group_id, default=None):
00104         plugins = self._getPAS()._getOb('plugins')
00105 
00106         group_id = self._verifyGroup(plugins, group_id=group_id)
00107         title = None
00108 
00109         if not group_id:
00110             return default
00111 
00112         return self._findGroup(plugins, group_id, title)
00113 
00114     def getGroups(self):
00115         return map(self.getGroupById, self.getGroupIds())
00116 
00117     def getGroupIds(self):
00118         return self.listGroupIds()
00119 
00120     def getGroupMembers(self, group_id):
00121         return tuple(self._group_principal_map.get(group_id,()))
00122 
00123     #################################
00124     # capabilties interface impls.
00125 
00126     security.declarePublic('allowDeletePrincipal')
00127     def allowDeletePrincipal(self, principal_id):
00128         """True iff this plugin can delete a certain group.
00129         This is true if this plugin manages the group.
00130         """
00131         if self._groups.get(principal_id) is not None:
00132             return 1
00133         return 0
00134 
00135     def getGroupInfo( self, group_id ):
00136         """Over-ride parent to not explode when getting group info dict by group id."""
00137         return self._groups.get(group_id,None)
00138 
00139     def allowGroupAdd(self, user_id, group_id):
00140         """True iff this plugin will allow adding a certain user to a certain group."""
00141         present = self.getGroupInfo(group_id)
00142         if present: return 1   # if we have a group, we can add users to it
00143                                 # slightly naive, but should be okay.
00144         return 0
00145 
00146     def allowGroupRemove(self, user_id, group_id):
00147         """True iff this plugin will allow removing a certain user from a certain group."""
00148         present = self.getGroupInfo(group_id)
00149         if not present: return 0   # if we don't have a group, we can't do anything
00150 
00151         group_members = self.getGroupMembers(group_id)
00152         if user_id in group_members: return 1
00153         return 0
00154 
00155     #################################
00156     # group wrapping mechanics
00157 
00158     security.declarePrivate('_createGroup')
00159     def _createGroup(self, plugins, group_id, name):
00160         """ Create group object. For users, this can be done with a
00161         plugin, but I don't care to define one for that now. Just uses
00162         PloneGroup.  But, the code's still here, just commented out.
00163         This method based on PluggableAuthervice._createUser
00164         """
00165 
00166         #factories = plugins.listPlugins(IUserFactoryPlugin)
00167 
00168         #for factory_id, factory in factories:
00169 
00170         #    user = factory.createUser(user_id, name)
00171 
00172         #    if user is not None:
00173         #        return user.__of__(self)
00174 
00175         return PloneGroup(group_id, name).__of__(self)
00176 
00177     security.declarePrivate('_findGroup')
00178     def _findGroup(self, plugins, group_id, title=None, request=None):
00179         """ group_id -> decorated_group
00180         This method based on PluggableAuthService._findGroup
00181         """
00182 
00183         # See if the group can be retrieved from the cache
00184         view_name = '_findGroup-%s' % group_id
00185         keywords = { 'group_id' : group_id
00186                    , 'title' : title
00187                    }
00188         group = self.ZCacheable_get(view_name=view_name
00189                                   , keywords=keywords
00190                                   , default=None
00191                                  )
00192 
00193         if group is None:
00194 
00195             group = self._createGroup(plugins, group_id, title)
00196 
00197             propfinders = plugins.listPlugins(IPropertiesPlugin)
00198             for propfinder_id, propfinder in propfinders:
00199 
00200                 data = propfinder.getPropertiesForUser(group, request)
00201                 if data:
00202                     group.addPropertysheet(propfinder_id, data)
00203 
00204             groups = self._getPAS()._getGroupsForPrincipal(group, request
00205                                                 , plugins=plugins)
00206             group._addGroups(groups)
00207 
00208             rolemakers = plugins.listPlugins(IRolesPlugin)
00209 
00210             for rolemaker_id, rolemaker in rolemakers:
00211                 roles = rolemaker.getRolesForPrincipal(group, request)
00212                 if roles:
00213                     group._addRoles(roles)
00214 
00215             group._addRoles(['Authenticated'])
00216 
00217             # Cache the group if caching is enabled
00218             base_group = aq_base(group)
00219             if getattr(base_group, '_p_jar', None) is None:
00220                 self.ZCacheable_set(base_group
00221                                    , view_name=view_name
00222                                    , keywords=keywords)
00223 
00224         return group.__of__(self)
00225 
00226     security.declarePrivate('_verifyGroup')
00227     def _verifyGroup(self, plugins, group_id=None, title=None):
00228 
00229         """ group_id -> boolean
00230         This method based on PluggableAuthService._verifyUser
00231         """
00232         criteria = {}
00233 
00234         if group_id is not None:
00235             criteria[ 'id' ] = group_id
00236             criteria[ 'exact_match' ] = True
00237 
00238         if title is not None:
00239             criteria[ 'title' ] = title
00240 
00241         if criteria:
00242             view_name = createViewName('_verifyGroup', group_id)
00243             cached_info = self.ZCacheable_get(view_name=view_name
00244                                              , keywords=criteria
00245                                              , default=None)
00246 
00247             if cached_info is not None:
00248                 return cached_info
00249 
00250             enumerators = plugins.listPlugins(IGroupEnumerationPlugin)
00251 
00252             for enumerator_id, enumerator in enumerators:
00253                 try:
00254                     info = enumerator.enumerateGroups(**criteria)
00255 
00256                     if info:
00257                         id = info[0]['id']
00258                         # Put the computed value into the cache
00259                         self.ZCacheable_set(id
00260                                            , view_name=view_name
00261                                            , keywords=criteria
00262                                            )
00263                         return id
00264 
00265                 except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00266                     logger.info(
00267                         'PluggableAuthService: GroupEnumerationPlugin %s error',
00268                         enumerator_id, exc_info=1)
00269 
00270         return 0
00271 
00272 classImplements(GroupManager,
00273                 IGroupManagement, IGroupIntrospection, IGroupCapability,
00274                 IDeleteCapability, *implementedBy(ZODBGroupManager))
00275 
00276 InitializeClass(GroupManager)
00277 
00278 
00279 class PloneGroup(PloneUser):
00280     """Plone expects a user to come, with approximately the same
00281     behavior as a user.
00282     """
00283 
00284     security = ClassSecurityInfo()
00285     _isGroup = True
00286 
00287     def getId(self, unprefixed=None):
00288         """ -> user ID
00289         Modified to accept silly GRUF param.
00290         """
00291         return self._id
00292 
00293     security.declarePrivate("getMemberIds")
00294     def getMemberIds(self, transitive = 1):
00295         """Return member ids of this group, including or not
00296         transitive groups.
00297         """
00298         # acquired from the groups_source
00299         plugins = self._getPAS().plugins
00300         introspectors = plugins.listPlugins(IGroupIntrospection)
00301         members=[]
00302         for iid, introspector in introspectors:
00303             try:
00304                 members.extend(list(introspector.getGroupMembers(self.getId())))
00305             except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00306                 logger.info(
00307                     'PluggableAuthService: getGroupMembers %s error',
00308                     iid, exc_info=1)
00309 
00310         return members
00311 
00312 
00313     security.declarePublic('addMember')
00314     def addMember(self, id):
00315         """Add the existing member with the given id to the group
00316         """
00317         self.addPrincipalToGroup(id, self.getId())
00318 
00319     security.declarePublic('removeMember')
00320     def removeMember(self, id):
00321         """Remove the member with the provided id from the group.
00322         """
00323         self.removePrincipalFromGroup(id, self.getId())
00324 
00325     security.declarePublic('getRolesInContext')
00326     def getRolesInContext(self, object):
00327         """Since groups can't actually log in, do nothing.
00328         """
00329         return []
00330 
00331     security.declarePublic('allowed')
00332     def allowed(self, object, object_roles=None):
00333         """Since groups can't actually log in, do nothing.
00334         """
00335         return 0
00336 
00337 InitializeClass(PloneGroup)