Back to index

plone3  3.1.7
groupdata.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 """
00017 from Globals import InitializeClass
00018 from Acquisition import aq_base
00019 from AccessControl import ClassSecurityInfo
00020 from AccessControl import Unauthorized
00021 
00022 from zope.interface import implements
00023 from zope.interface import implementedBy
00024 
00025 from Products.CMFCore.utils import getToolByName
00026 from Products.CMFCore.utils import registerToolInterface
00027 from Products.CMFPlone.GroupDataTool import GroupDataTool as BaseGroupDataTool
00028 from Products.GroupUserFolder.GroupDataTool import GroupData as BaseGroupData
00029 from Products.GroupUserFolder.GroupDataTool import _marker
00030 
00031 from Products.PluggableAuthService.utils import classImplements
00032 from Products.PluggableAuthService.interfaces.authservice \
00033         import IPluggableAuthService
00034 from Products.PluggableAuthService.PluggableAuthService \
00035         import _SWALLOWABLE_PLUGIN_EXCEPTIONS
00036 
00037 from Products.PlonePAS.interfaces.group import IGroupManagement
00038 from Products.PlonePAS.interfaces.group import IGroupDataTool
00039 from Products.PlonePAS.interfaces.capabilities import IManageCapabilities
00040 from Products.PlonePAS.interfaces.capabilities import IDeleteCapability
00041 from Products.PlonePAS.interfaces.propertysheets import IMutablePropertySheet
00042 from Products.PlonePAS.tools.memberdata import MemberData
00043 from AccessControl.requestmethod import postonly
00044 
00045 import logging
00046 
00047 logger = logging.getLogger('Plone')
00048 
00049 try:
00050     from Products.CMFCore.MemberDataTool import CleanupTemp
00051     _have_cleanup_temp = 1
00052 except:
00053     _have_cleanup_temp = None
00054 
00055 
00056 class GroupDataTool(BaseGroupDataTool):
00057     """PAS-specific implementation of groupdata tool. Uses Plone
00058     GroupDataTool as a base.
00059     """
00060 
00061     meta_type = "PlonePAS GroupData Tool"
00062     toolicon = 'tool.gif'
00063     implements(IGroupDataTool)
00064 
00065     #### an exact copy from the base, so that we pick up the new GroupData.
00066     #### wrapGroup should have a GroupData factory method to over-ride (or even
00067     #### set at run-time!) so that we don't have to do this.
00068     def wrapGroup(self, g):
00069         """Returns an object implementing the GroupData interface."""
00070 
00071         id = g.getId()
00072         members = self._members
00073         if not members.has_key(id):
00074             # Get a temporary member that might be
00075             # registered later via registerMemberData().
00076             temps = self._v_temps
00077             if temps is not None and temps.has_key(id):
00078                 portal_group = temps[id]
00079             else:
00080                 base = aq_base(self)
00081                 portal_group = GroupData(base, id)
00082                 if temps is None:
00083                     self._v_temps = {id:portal_group}
00084                     if hasattr(self, 'REQUEST'):
00085                         # No REQUEST during tests.
00086                         # XXX jcc => CleanupTemp doesn't seem to work
00087                         # on Plone 1.0.3.  Have to find a way to pass
00088                         # around...
00089                         if _have_cleanup_temp:
00090                             self.REQUEST._hold(CleanupTemp(self))
00091                 else:
00092                     temps[id] = portal_group
00093         else:
00094             portal_group = members[id]
00095         # Return a wrapper with self as containment and
00096         # the user as context.
00097         return portal_group.__of__(self).__of__(g)
00098 
00099 InitializeClass(GroupDataTool)
00100 registerToolInterface('portal_groupdata', IGroupDataTool)
00101 
00102 
00103 class GroupData(BaseGroupData):
00104 
00105     security = ClassSecurityInfo()
00106 
00107     def _getGroup(self):
00108         """Get the underlying group object in a PAS-acceptable way.
00109         (I don't even know why there's the two different ways for GRUF. Speed?)
00110         """
00111         return self.getGroup()
00112 
00113     ## setProperties uses setGroupProperties. no need to override.
00114     def setGroupProperties(self, mapping):
00115         """PAS-specific method to set the properties of a group.
00116         """
00117         sheets = None
00118 
00119         if not IPluggableAuthService.providedBy(self.acl_users):
00120             # Defer to base impl in absence of PAS, a PAS group, or
00121             # property sheets
00122             return BaseGroupData.setGroupProperties(self, mapping)
00123         else:
00124             # It's a PAS! Whee!
00125             group = self.getGroup()
00126             sheets = getattr(group, 'getOrderedPropertySheets', lambda: None)()
00127 
00128             # We won't always have PlonePAS groups, due to acquisition,
00129             # nor are guaranteed property sheets
00130             if not sheets:
00131                 # Defer to base impl if we have a PAS but no property
00132                 # sheets.
00133                 return BaseGroupData.setGroupProperties(self, mapping)
00134 
00135         # If we got this far, we have a PAS and some property sheets.
00136         # XXX track values set to defer to default impl
00137         # property routing?
00138         modified = False
00139         for k, v in mapping.items():
00140             for sheet in sheets:
00141                 if not sheet.hasProperty(k):
00142                     continue
00143                 if IMutablePropertySheet.providedBy(sheet):
00144                     sheet.setProperty(group, k, v)
00145                     modified = True
00146                 else:
00147                     raise RuntimeError, ("Mutable property provider "
00148                                          "shadowed by read only provider")
00149         if modified:
00150             self.notifyModified()
00151 
00152     def getProperty(self, id, default=_marker):
00153         """PAS-specific method to fetch a group's properties. Looks
00154         through the ordered property sheets.
00155         """
00156         sheets = None
00157         if not IPluggableAuthService.providedBy(self.acl_users):
00158             return BaseGroupData.getProperty(self, id)
00159         else:
00160             # It's a PAS! Whee!
00161             group = self.getGroup()
00162             sheets = getattr(group, 'getOrderedPropertySheets', lambda: None)()
00163             # we won't always have PlonePAS groups, due to acquisition,
00164             # nor are guaranteed property sheets
00165             if not sheets:
00166                 return BaseGroupData.getProperty(self, id)
00167 
00168         # If we made this far, we found a PAS and some property sheets.
00169         for sheet in sheets:
00170             if sheet.hasProperty(id):
00171                 # Return the first one that has the property.
00172                 return sheet.getProperty(id)
00173         # Couldn't find the property in the property sheets. Try to
00174         # delegate back to the base implementation.
00175         return BaseGroupData.getProperty(self, id, default)
00176 
00177     def getUserName(self):
00178         return self.getName()
00179     getUserNameWithoutGroupPrefix = getUserName
00180 
00181     def getGroupName(self):
00182         return self.getName()
00183 
00184     ## GRUF 3.2 methods...
00185 
00186     def _getGRUF(self,):
00187         return self.acl_users
00188 
00189     @postonly
00190     def addMember(self, id, REQUEST=None):
00191         """ Add the existing member with the given id to the group"""
00192         if not self.canAdministrateGroup():
00193             raise Unauthorized, "You cannot add a member to the group."
00194         
00195         plugins = self._getPlugins()
00196         managers = plugins.listPlugins(IGroupManagement)
00197         for mid, manager in managers:
00198             try:
00199                 if manager.addPrincipalToGroup(id, self.getId()):
00200                     break
00201             except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00202                 pass
00203 
00204     @postonly
00205     def removeMember(self, id, REQUEST=None):
00206         """Remove the member with the provided id from the group.
00207         """
00208         if not self.canAdministrateGroup():
00209             raise Unauthorized, "You cannot remove a member from the group."
00210 
00211         plugins = self._getPlugins()
00212         managers = plugins.listPlugins(IGroupManagement)
00213         for mid, manager in managers:
00214             try:
00215                 if manager.removePrincipalFromGroup(id, self.getId()):
00216                     break
00217             except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
00218                 pass
00219 
00220 
00221     def getAllGroupMembers(self, ):
00222         """
00223         Returns a list of the portal_memberdata-ish members of the group.
00224         This will include transitive groups / users
00225         """
00226         md = self.portal_memberdata
00227         gd = self.portal_groupdata
00228         ret = []
00229         for u_name in self.getGroup().getMemberIds():
00230             usr = self._getGRUF().getUserById(u_name)
00231             if not usr:
00232                 logger.debug("Group has a non-existing user %s" % u_name)
00233                 continue
00234             if usr.isGroup():
00235                 ret.append(gd.wrapGroup(usr))
00236             else:
00237                 ret.append(md.wrapUser(usr))
00238         return ret
00239 
00240     def getGroupMembers(self):
00241         """
00242         Returns a list of the portal_memberdata-ish members of the group.
00243         This doesn't include TRANSITIVE groups/users.
00244         """
00245         md = self.portal_memberdata
00246         gd = self.portal_groupdata
00247         gtool = self.portal_groups
00248         ret = []
00249         for u_name in gtool.getGroupMembers(self.getId()):
00250             usr = self._getGRUF().getUserById(u_name)
00251             if not usr:
00252                 logger.debug("Group has a non-existing user %s" % u_name)
00253                 continue
00254             elif usr.isGroup():
00255                 ret.append(gd.wrapGroup(usr))
00256             else:
00257                 ret.append(md.wrapUser(usr))
00258         return ret
00259 
00260     def setProperties(self, properties=None, **kw):
00261         """Allows the manager group to set his/her own properties.
00262         Accepts either keyword arguments or a mapping for the "properties"
00263         argument.
00264         """
00265         if properties is None:
00266             properties = kw
00267         return self.setGroupProperties(properties)
00268 
00269     def getProperties(self):
00270         """ Return the properties of this group. Properties are as usual in Zope.
00271         """
00272         tool = self.getTool()
00273         ret = {}
00274         for pty in tool.propertyIds():
00275             try:
00276                 ret[pty] = self.getProperty(pty)
00277             except ValueError:
00278                 # We ignore missing ptys
00279                 continue
00280         return ret
00281 
00282     ## IManageCapabilities methods
00283     def canDelete(self):
00284         """True iff user can be removed from the Plone UI.
00285         """
00286         # IGroupManagement provides removeGroup
00287         plugins = self._getPlugins()
00288         managers = plugins.listPlugins(IGroupManagement)
00289         if managers:
00290             for mid, manager in managers:
00291                 if (IDeleteCapability.providedBy(manager) and
00292                         manager.allowDeletePrincipal(self.getId())):
00293                     return True
00294         return False
00295 
00296     def canPasswordSet(self):
00297         """Always false for groups, which have no password.
00298         """
00299         return False
00300 
00301     def passwordInClear(self):
00302         """True iff password can be retrieved in the clear (not hashed.)
00303 
00304         False for PAS. It provides no API for getting passwords,
00305         though it would be possible to add one in the future.
00306         """
00307         return Flase
00308 
00309     def _groupdataHasProperty(self, prop_name):
00310         gdata = getToolByName(self, 'portal_groupdata', None)
00311         if gdata:
00312             return gdata.hasProperty(prop_name)
00313         return 0
00314 
00315     def canWriteProperty(self, prop_name):
00316         """True iff the group property named in 'prop_name'
00317         can be changed.
00318         """
00319         # this looks almost exactly like in memberdata. refactor?
00320         if not IPluggableAuthService.providedBy(self.acl_users):
00321             # not PAS; Groupdata is writable
00322             return self._groupdataHasProperty(prop_name)
00323         else:
00324             # it's PAS
00325             group = self.getGroup()
00326             sheets = getattr(group, 'getOrderedPropertySheets', lambda: None)()
00327             if not sheets:
00328                 return self._groupdataHasProperty(prop_name)
00329 
00330             for sheet in sheets:
00331                 if not sheet.hasProperty(prop_name):
00332                     continue
00333                 if IMutablePropertySheet.providedBy(sheet):
00334                     return 1
00335                 else:
00336                     break  # shadowed by read-only
00337         return 0
00338 
00339     canAddToGroup = MemberData.canAddToGroup.im_func
00340     canRemoveFromGroup = MemberData.canRemoveFromGroup.im_func
00341     canAssignRole = MemberData.canAssignRole.im_func
00342 
00343     ## plugin getters
00344 
00345     security.declarePrivate('_getPlugins')
00346     def _getPlugins(self):
00347         return self.acl_users.plugins
00348 
00349 classImplements(GroupData,
00350                 implementedBy(BaseGroupData),
00351                 IManageCapabilities)
00352 
00353 InitializeClass(GroupData)