Back to index

plone3  3.1.7
memberdata.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 
00021 from zope.interface import implementedBy
00022 
00023 from Products.CMFPlone.MemberDataTool import MemberDataTool as BaseMemberDataTool
00024 
00025 from Products.CMFCore.MemberDataTool import MemberData as BaseMemberData
00026 
00027 
00028 from Products.CMFCore.utils import getToolByName
00029 from Products.CMFCore.MemberDataTool import CleanupTemp
00030 from Products.CMFPlone.MemberDataTool import _marker
00031 
00032 from Products.PluggableAuthService.utils import classImplements
00033 from Products.PluggableAuthService.interfaces.authservice import IPluggableAuthService
00034 from Products.PluggableAuthService.interfaces.plugins import IPropertiesPlugin, IRoleAssignerPlugin
00035 
00036 from Products.PlonePAS.interfaces.plugins import IUserManagement
00037 from Products.PlonePAS.interfaces.group import IGroupManagement
00038 from Products.PlonePAS.interfaces.propertysheets import IMutablePropertySheet
00039 from Products.PlonePAS.interfaces.capabilities import IDeleteCapability, IPasswordSetCapability
00040 from Products.PlonePAS.interfaces.capabilities import IGroupCapability, IAssignRoleCapability
00041 from Products.PlonePAS.interfaces.capabilities import IManageCapabilities
00042 from Products.PlonePAS.utils import getCharset
00043 from AccessControl.requestmethod import postonly
00044 
00045 
00046 class MemberDataTool(BaseMemberDataTool):
00047     """PAS-specific implementation of memberdata tool. Uses Plone
00048     MemberDataTool as a base.
00049     """
00050 
00051     meta_type = "PlonePAS MemberData Tool"
00052     toolicon = 'tool.gif'
00053 
00054     #### an exact copy from the base, so that we pick up the new MemberData.
00055     #### wrapUser should have a MemberData factory method to over-ride (or even
00056     #### set at run-time!) so that we don't have to do this.
00057     def wrapUser(self, u):
00058         '''
00059         If possible, returns the Member object that corresponds
00060         to the given User object.
00061         We override this to ensure OUR MemberData class is used
00062         '''
00063         id = u.getId()
00064         members = self._members
00065         if not members.has_key(id):
00066             base = aq_base(self)
00067             members[id] = MemberData(base, id)
00068         # Return a wrapper with self as containment and
00069         # the user as context.
00070         return members[id].__of__(self).__of__(u)
00071 
00072     @postonly
00073     def deleteMemberData(self, member_id, REQUEST=None):
00074         """ Delete member data of specified member.
00075         """
00076         if IPluggableAuthService.providedBy(self.acl_users):
00077             # It's a PAS! Whee!
00078             # XXX: can we safely assume that user name == member_id
00079             plugins = self._getPlugins()
00080             prop_managers = plugins.listPlugins(IPropertiesPlugin)
00081             for mid, prop_manager in prop_managers:
00082                 # Not all PropertiesPlugins support user deletion
00083                 try:
00084                     prop_manager.deleteUser(member_id)
00085                 except AttributeError:
00086                     pass
00087 
00088         # we won't always have PlonePAS users, due to acquisition,
00089         # nor are guaranteed property sheets
00090         members = self._members
00091         if members.has_key(member_id):
00092             del members[member_id]
00093             return 1
00094         else:
00095             return 0
00096 
00097     ## plugin getter
00098     def _getPlugins(self):
00099         return self.acl_users.plugins
00100 
00101 InitializeClass(MemberDataTool)
00102 
00103 
00104 class MemberData(BaseMemberData):
00105 
00106     security = ClassSecurityInfo()
00107 
00108     ## setProperties uses setMemberProperties. no need to override.
00109 
00110     def setMemberProperties(self, mapping, force_local = 0):
00111         """PAS-specific method to set the properties of a
00112         member. Ignores 'force_local', which is not reliably present.
00113         """
00114         sheets = None
00115 
00116         # We could pay attention to force_local here...
00117         if not IPluggableAuthService.providedBy(self.acl_users):
00118             # Defer to base impl in absence of PAS, a PAS user, or
00119             # property sheets
00120             return BaseMemberData.setMemberProperties(self, mapping)
00121         else:
00122             # It's a PAS! Whee!
00123             user = self.getUser()
00124             sheets = getattr(user, 'getOrderedPropertySheets', lambda: None)()
00125 
00126             # We won't always have PlonePAS users, due to acquisition,
00127             # nor are guaranteed property sheets
00128             if not sheets:
00129                 # Defer to base impl if we have a PAS but no property
00130                 # sheets.
00131                 return BaseMemberData.setMemberProperties(self, mapping)
00132 
00133         # If we got this far, we have a PAS and some property sheets.
00134         # XXX track values set to defer to default impl
00135         # property routing?
00136         modified = False
00137         for k, v in mapping.items():
00138             for sheet in sheets:
00139                 if not sheet.hasProperty(k):
00140                     continue
00141                 if IMutablePropertySheet.providedBy(sheet):
00142                     sheet.setProperty(user, k, v)
00143                     modified = True
00144                 else:
00145                     break
00146                     #raise RuntimeError, ("Mutable property provider "
00147                     #                     "shadowed by read only provider")
00148         if modified:
00149             self.notifyModified()
00150 
00151     def getProperty(self, id, default=_marker):
00152         """PAS-specific method to fetch a user's properties. Looks
00153         through the ordered property sheets.
00154         """
00155         sheets = None
00156         if not IPluggableAuthService.providedBy(self.acl_users):
00157             return BaseMemberData.getProperty(self, id)
00158         else:
00159             # It's a PAS! Whee!
00160             user = self.getUser()
00161             sheets = getattr(user, 'getOrderedPropertySheets', lambda: None)()
00162 
00163             # we won't always have PlonePAS users, due to acquisition,
00164             # nor are guaranteed property sheets
00165             if not sheets:
00166                 return BaseMemberData.getProperty(self, id, default)
00167 
00168         charset = getCharset(self)
00169 
00170         # If we made this far, we found a PAS and some property sheets.
00171         for sheet in sheets:
00172             if sheet.hasProperty(id):
00173                 # Return the first one that has the property.
00174                 value = sheet.getProperty(id)
00175                 if isinstance(value, unicode):
00176                     # XXX Temporarily work around the fact that
00177                     # property sheets blindly store and return
00178                     # unicode. This is sub-optimal and should be
00179                     # dealed with at the property sheets level by
00180                     # using Zope's converters.
00181                     return value.encode(charset)
00182                 return value
00183 
00184         # Couldn't find the property in the property sheets. Try to
00185         # delegate back to the base implementation.
00186         return BaseMemberData.getProperty(self, id, default)
00187 
00188     def getPassword(self):
00189         """Returns None. Present to avoid NotImplementedError."""
00190         return None
00191 
00192     ## IManageCapabilities methods
00193 
00194     def canDelete(self):
00195         """True iff user can be removed from the Plone UI."""
00196         # IUserManagement provides doDeleteUser
00197         plugins = self._getPlugins()
00198         managers = plugins.listPlugins(IUserManagement)
00199         for mid, manager in managers:
00200             if (IDeleteCapability.providedBy(manager) and
00201                     manager.allowDeletePrincipal(self.getId())):
00202                 return True
00203         return False
00204 
00205     def canPasswordSet(self):
00206         """True iff user can change password."""
00207         # IUserManagement provides doChangeUser
00208         plugins = self._getPlugins()
00209         managers = plugins.listPlugins(IUserManagement)
00210         for mid, manager in managers:
00211             if (IPasswordSetCapability.providedBy(manager) and
00212                     manager.allowPasswordSet(self.getId())):
00213                 return True
00214         return False
00215 
00216     def passwordInClear(self):
00217         """True iff password can be retrieved in the clear (not hashed.)
00218 
00219         False for PAS. It provides no API for getting passwords,
00220         though it would be possible to add one in the future.
00221         """
00222         return 0
00223 
00224     def _memberdataHasProperty(self, prop_name):
00225         mdata = getToolByName(self, 'portal_memberdata', None)
00226         if mdata:
00227             return mdata.hasProperty(prop_name)
00228         return 0
00229 
00230     def canWriteProperty(self, prop_name):
00231         """True iff the member/group property named in 'prop_name'
00232         can be changed.
00233         """
00234         if not IPluggableAuthService.providedBy(self.acl_users):
00235             # not PAS; Memberdata is writable
00236             return self._memberdataHasProperty(prop_name)
00237         else:
00238             # it's PAS
00239             user = self.getUser()
00240             sheets = getattr(user, 'getOrderedPropertySheets', lambda: None)()
00241             if not sheets:
00242                 return self._memberdataHasProperty(prop_name)
00243 
00244             for sheet in sheets:
00245                 if not sheet.hasProperty(prop_name):
00246                     continue
00247                 if IMutablePropertySheet.providedBy(sheet):
00248                     # BBB for plugins implementing an older version of IMutablePropertySheet
00249                     if hasattr(sheet, 'canWriteProperty'):
00250                         return sheet.canWriteProperty(user, prop_name)
00251                     return True
00252                 else:
00253                     break  # shadowed by read-only
00254         return False
00255 
00256     def canAddToGroup(self, group_id):
00257         """True iff member can be added to group."""
00258         # IGroupManagement provides IGroupCapability
00259         plugins = self._getPlugins()
00260         managers = plugins.listPlugins(IGroupManagement)
00261         for mid, manager in managers:
00262             if (IGroupCapability.providedBy(manager) and
00263                     manager.allowGroupAdd(self.getId(), group_id)):
00264                 return True
00265         return False
00266 
00267     def canRemoveFromGroup(self, group_id):
00268         """True iff member can be removed from group."""
00269         # IGroupManagement provides IGroupCapability
00270         plugins = self._getPlugins()
00271         managers = plugins.listPlugins(IGroupManagement)
00272         for mid, manager in managers:
00273             if (IGroupCapability.providedBy(manager) and
00274                     manager.allowGroupRemove(self.getId(), group_id)):
00275                 return True
00276         return False
00277 
00278 
00279     def canAssignRole(self, role_id):
00280         """True iff member can be assigned role. Role id is string."""
00281         # IRoleAssignerPlugin provides IAssignRoleCapability
00282         plugins = self._getPlugins()
00283         managers = plugins.listPlugins(IRoleAssignerPlugin)
00284         for mid, manager in managers:
00285             if (IAssignRoleCapability.providedBy(manager) and
00286                     manager.allowRoleAssign(self.getId(), role_id)):
00287                 return True
00288         return False
00289 
00290     ## plugin getters
00291 
00292     security.declarePrivate('_getPlugins')
00293     def _getPlugins(self):
00294         return self.acl_users.plugins
00295 
00296 classImplements(MemberData,
00297                 implementedBy(BaseMemberData),
00298                 IManageCapabilities)
00299 
00300 InitializeClass(MemberData)