Strip pour éviter que le firstChild soit un nœud text.
[Plinn.git] / RegistrationTool.py
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # © 2005-2013 Benoît PIN <pin@cri.ensmp.fr> #
5 # #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ Plinn registration tool: implements 3 modes to register members:
21 anonymous, manager, reviewed.
22
23
24
25 """
26
27 from Globals import InitializeClass, PersistentMapping
28 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
29 from Products.CMFDefault.RegistrationTool import RegistrationTool as BaseRegistrationTool
30 from AccessControl import ClassSecurityInfo, ModuleSecurityInfo
31 from AccessControl.Permission import Permission
32 from BTrees.OOBTree import OOBTree
33 from Products.CMFCore.permissions import ManagePortal, AddPortalMember
34 from Products.CMFCore.exceptions import AccessControl_Unauthorized
35 from Products.CMFCore.utils import getToolByName
36 from Products.CMFCore.utils import getUtilityByInterfaceName
37 from Products.GroupUserFolder.GroupsToolPermissions import ManageGroups
38 from Products.Plinn.utils import Message as _
39 from DateTime import DateTime
40 from types import TupleType, ListType
41 from uuid import uuid4
42
43 security = ModuleSecurityInfo('Products.Plinn.RegistrationTool')
44 MODE_ANONYMOUS = 'anonymous'
45 security.declarePublic('MODE_ANONYMOUS')
46
47 MODE_MANAGER = 'manager'
48 security.declarePublic('MODE_MANAGER')
49
50 MODE_REVIEWED = 'reviewed'
51 security.declarePublic('MODE_REVIEWED')
52
53 MODES = [MODE_ANONYMOUS, MODE_MANAGER, MODE_REVIEWED]
54 security.declarePublic('MODES')
55
56 DEFAULT_MEMBER_GROUP = 'members'
57 security.declarePublic('DEFAULT_MEMBER_GROUP')
58
59
60
61 class RegistrationTool(BaseRegistrationTool) :
62
63 """ Create and modify users by making calls to portal_membership.
64 """
65
66 meta_type = "Plinn Registration Tool"
67
68 manage_options = ({'label' : 'Registration mode', 'action' : 'manage_regmode'}, ) + \
69 BaseRegistrationTool.manage_options
70
71 security = ClassSecurityInfo()
72
73 security.declareProtected( ManagePortal, 'manage_regmode' )
74 manage_regmode = PageTemplateFile('www/configureRegistrationTool', globals(),
75 __name__='manage_regmode')
76
77 def __init__(self) :
78 self._mode = MODE_ANONYMOUS
79 self._chain = ''
80 self._passwordResetRequests = OOBTree()
81
82 security.declareProtected(ManagePortal, 'configureTool')
83 def configureTool(self, registration_mode, chain, REQUEST=None) :
84 """ """
85
86 if registration_mode not in MODES :
87 raise ValueError, "Unknown mode: " + registration_mode
88 else :
89 self._mode = registration_mode
90 self._updatePortalRoleMappingForMode(registration_mode)
91
92 wtool = getToolByName(self, 'portal_workflow')
93
94 if registration_mode == MODE_REVIEWED :
95 if not hasattr(wtool, '_chains_by_type') :
96 wtool._chains_by_type = PersistentMapping()
97 wfids = []
98 chain = chain.strip()
99
100 if chain == '(Default)' :
101 try : del wtool._chains_by_type['Member Data']
102 except KeyError : pass
103 self._chain = chain
104 else :
105 for wfid in chain.replace(',', ' ').split(' ') :
106 if wfid :
107 if not wtool.getWorkflowById(wfid) :
108 raise ValueError, '"%s" is not a workflow ID.' % wfid
109 wfids.append(wfid)
110
111 wtool._chains_by_type['Member Data'] = tuple(wfids)
112 self._chain = ', '.join(wfids)
113 else :
114 wtool._chains_by_type['Member Data'] = tuple()
115
116 if REQUEST :
117 REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_regmode?manage_tabs_message=Saved changes.')
118
119 def _updatePortalRoleMappingForMode(self, mode) :
120
121 urlTool = getToolByName(self, 'portal_url')
122 portal = urlTool.getPortalObject()
123
124 if mode in [MODE_ANONYMOUS, MODE_REVIEWED] :
125 portal.manage_permission(AddPortalMember, roles = ['Anonymous', 'Manager'], acquire=1)
126 elif mode == MODE_MANAGER :
127 portal.manage_permission(AddPortalMember, roles = ['Manager', 'UserManager'], acquire=0)
128
129 security.declarePublic('getMode')
130 def getMode(self) :
131 # """ return current mode """
132 return self._mode[:]
133
134 security.declarePublic('getWfId')
135 def getWfChain(self) :
136 # """ return current workflow id """
137 return self._chain
138
139 security.declarePublic('roleMappingMismatch')
140 def roleMappingMismatch(self) :
141 # """ test if the role mapping is correct for the currrent mode """
142
143 mode = self._mode
144 urlTool = getToolByName(self, 'portal_url')
145 portal = urlTool.getPortalObject()
146
147 def rolesOfAddPortalMemberPerm() :
148 p=Permission(AddPortalMember, [], portal)
149 return p.getRoles()
150
151 if mode in [MODE_ANONYMOUS, MODE_REVIEWED] :
152 if 'Anonymous' in rolesOfAddPortalMemberPerm() : return False
153
154 elif mode == MODE_MANAGER :
155 roles = rolesOfAddPortalMemberPerm()
156 if 'Manager' in roles or 'UserManager' in roles and len(roles) == 1 and type(roles) == TupleType :
157 return False
158
159 return True
160
161 security.declareProtected(AddPortalMember, 'addMember')
162 def addMember(self, id, password, roles=(), groups=(DEFAULT_MEMBER_GROUP,), domains='', properties=None) :
163 """ Idem CMFCore but without default role """
164 BaseRegistrationTool.addMember(self, id, password, roles=roles,
165 domains=domains, properties=properties)
166
167 if self.getMode() in [MODE_ANONYMOUS, MODE_MANAGER] :
168 gtool = getToolByName(self, 'portal_groups')
169 mtool = getToolByName(self, 'portal_membership')
170 utool = getToolByName(self, 'portal_url')
171 portal = utool.getPortalObject()
172 isGrpManager = mtool.checkPermission(ManageGroups, portal) ## TODO : CMF2.1 compat
173 aclu = self.aq_inner.acl_users
174
175 for gid in groups:
176 g = gtool.getGroupById(gid)
177 if not isGrpManager :
178 if gid != DEFAULT_MEMBER_GROUP:
179 raise AccessControl_Unauthorized, 'You are not allowed to join arbitrary group.'
180
181 if g is None :
182 gtool.addGroup(gid)
183 aclu.changeUser(aclu.getGroupPrefix() +gid, roles=['Member', ])
184 g = gtool.getGroupById(gid)
185 g.addMember(id)
186
187
188 def afterAdd(self, member, id, password, properties):
189 """ notify member creation """
190 member.notifyWorkflowCreated()
191 member.indexObject()
192
193
194 security.declarePublic('requestPasswordReset')
195 def requestPasswordReset(self, userid):
196 """ add uuid / (userid, expiration) pair and return uuid """
197 self.clearExpiredPasswordResetRequests()
198 mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool')
199 if mtool.getMemberById(userid) :
200 uuid = str(uuid4())
201 self._passwordResetRequests[uuid] = (userid, DateTime() + 1)
202 return uuid
203
204 security.declarePrivate('clearExpiredPasswordResetRequests')
205 def clearExpiredPasswordResetRequests(self):
206 now = DateTime()
207 for uuid, record in self._passwordResetRequest.items() :
208 userid, date = record
209 if date < now :
210 del self._passwordResetRequests[uuid]
211
212
213 security.declarePublic('resetPassword')
214 def resetPassword(self, userid, uuid, password, confirm) :
215 record = self._passwordResetRequests.get(uuid)
216 if not record :
217 return _('Invalid reset password request.')
218
219 recUserid, expiration = record
220
221 if recUserid != userid :
222 return _('Invalid userid.')
223
224 if expiration < now :
225 self.clearExpiredPasswordResetRequests()
226 return _('Your reset password request has expired. You can ask a new one.')
227
228 msg = self.testPasswordValidity(password, confirm=confirm)
229 if not msg : # None if everything ok. Err message otherwise.
230 mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool')
231 member = mtool.getMemberById(userid)
232 if member :
233 member.setSecurityProfile(password=password)
234 del self._passwordResetRequests[uuid]
235 return _('Password successfully resetted.')
236 else :
237 return _('"%s" username not found.') % userid
238
239
240 InitializeClass(RegistrationTool)