X-Git-Url: https://scm.cri.ensmp.fr/git/Plinn.git/blobdiff_plain/959d888c17d1403d2eeecc19bc4b5e2c8d1debf6..ae75bab9028d6321710c42da338cef25c7afe576:/Products/Plinn/RegistrationTool.py diff --git a/Products/Plinn/RegistrationTool.py b/Products/Plinn/RegistrationTool.py index 8c2911d..738d2ee 100644 --- a/Products/Plinn/RegistrationTool.py +++ b/Products/Plinn/RegistrationTool.py @@ -35,6 +35,7 @@ from Products.CMFCore.exceptions import AccessControl_Unauthorized from Products.CMFDefault.exceptions import EmailAddressInvalid from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getUtilityByInterfaceName +from Products.CMFCore.utils import _checkPermission from Products.CMFDefault.utils import checkEmailAddress from Products.GroupUserFolder.GroupsToolPermissions import ManageGroups from Products.Plinn.utils import Message as _ @@ -44,6 +45,7 @@ from Products.Plinn.utils import encodeMailHeader from DateTime import DateTime from types import TupleType, ListType from uuid import uuid4 +import re security = ModuleSecurityInfo('Products.Plinn.RegistrationTool') MODE_ANONYMOUS = 'anonymous' @@ -70,14 +72,16 @@ class RegistrationTool(BaseRegistrationTool) : """ Create and modify users by making calls to portal_membership. """ - + meta_type = "Plinn Registration Tool" - + default_member_id_pattern = "^[A-Za-z][A-Za-z0-9_\.\-@]*$" # valid email allowed as member id + _ALLOWED_MEMBER_ID_PATTERN = re.compile(default_member_id_pattern) + manage_options = ({'label' : 'Registration mode', 'action' : 'manage_regmode'}, ) + \ BaseRegistrationTool.manage_options - + security = ClassSecurityInfo() - + security.declareProtected( ManagePortal, 'manage_regmode' ) manage_regmode = PageTemplateFile('www/configureRegistrationTool', globals(), __name__='manage_regmode') @@ -86,17 +90,17 @@ class RegistrationTool(BaseRegistrationTool) : self._mode = MODE_ANONYMOUS self._chain = '' self._passwordResetRequests = OOBTree() - + security.declareProtected(ManagePortal, 'configureTool') def configureTool(self, registration_mode, chain, REQUEST=None) : """ """ - + if registration_mode not in MODES : raise ValueError, "Unknown mode: " + registration_mode else : self._mode = registration_mode self._updatePortalRoleMappingForMode(registration_mode) - + wtool = getToolByName(self, 'portal_workflow') if registration_mode == MODE_REVIEWED : @@ -104,7 +108,7 @@ class RegistrationTool(BaseRegistrationTool) : wtool._chains_by_type = PersistentMapping() wfids = [] chain = chain.strip() - + if chain == '(Default)' : try : del wtool._chains_by_type['Member Data'] except KeyError : pass @@ -115,55 +119,55 @@ class RegistrationTool(BaseRegistrationTool) : if not wtool.getWorkflowById(wfid) : raise ValueError, '"%s" is not a workflow ID.' % wfid wfids.append(wfid) - + wtool._chains_by_type['Member Data'] = tuple(wfids) self._chain = ', '.join(wfids) else : wtool._chains_by_type['Member Data'] = tuple() - + if REQUEST : REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_regmode?manage_tabs_message=Saved changes.') def _updatePortalRoleMappingForMode(self, mode) : - + urlTool = getToolByName(self, 'portal_url') portal = urlTool.getPortalObject() - + if mode in [MODE_ANONYMOUS, MODE_PASS_ANONYMOUS, MODE_REVIEWED] : portal.manage_permission(AddPortalMember, roles = ['Anonymous', 'Manager'], acquire=1) elif mode == MODE_MANAGER : portal.manage_permission(AddPortalMember, roles = ['Manager', 'UserManager'], acquire=0) - + security.declarePublic('getMode') def getMode(self) : # """ return current mode """ return self._mode[:] - + security.declarePublic('getWfId') def getWfChain(self) : # """ return current workflow id """ return self._chain - + security.declarePublic('roleMappingMismatch') def roleMappingMismatch(self) : # """ test if the role mapping is correct for the currrent mode """ - + mode = self._mode urlTool = getToolByName(self, 'portal_url') portal = urlTool.getPortalObject() - + def rolesOfAddPortalMemberPerm() : p=Permission(AddPortalMember, [], portal) return p.getRoles() - + if mode in [MODE_ANONYMOUS, MODE_PASS_ANONYMOUS, MODE_REVIEWED] : if 'Anonymous' in rolesOfAddPortalMemberPerm() : return False - + elif mode == MODE_MANAGER : roles = rolesOfAddPortalMemberPerm() if 'Manager' in roles or 'UserManager' in roles and len(roles) == 1 and type(roles) == TupleType : return False - + return True security.declareProtected(AddPortalMember, 'addMember') @@ -175,12 +179,12 @@ class RegistrationTool(BaseRegistrationTool) : mtool = getToolByName(self, 'portal_membership') utool = getToolByName(self, 'portal_url') portal = utool.getPortalObject() - + if self.getMode() == MODE_PASS_ANONYMOUS : private_collections = portal.get('private_collections') if not private_collections : raise AccessControl_Unauthorized() - return + data = private_collections.data lines = filter(None, [l.strip() for l in data.split('\n')]) assert len(lines) % 3 == 0 @@ -191,9 +195,8 @@ class RegistrationTool(BaseRegistrationTool) : if not (collecInfos.has_key(properties.get('collection_id')) and \ collecInfos[properties.get('collection_id')]['pw'] == properties.get('collection_password')) : raise AccessControl_Unauthorized('Wrong primary credentials') - return - - + + BaseRegistrationTool.addMember(self, id, password, roles=roles, domains=domains, properties=properties) @@ -202,7 +205,7 @@ class RegistrationTool(BaseRegistrationTool) : for gid in groups: g = gtool.getGroupById(gid) - if not isGrpManager : + if not isGrpManager : if gid != DEFAULT_MEMBER_GROUP: raise AccessControl_Unauthorized, 'You are not allowed to join arbitrary group.' @@ -215,16 +218,46 @@ class RegistrationTool(BaseRegistrationTool) : BaseRegistrationTool.addMember(self, id, password, roles=roles, domains=domains, properties=properties) + security.declarePublic( 'testPasswordValidity' ) + def testPasswordValidity(self, password, confirm=None): + + """ Verify that the password satisfies the portal's requirements. + + o If the password is valid, return None. + o If not, return a string explaining why. + """ + if not password: + return _(u'You must enter a password.') + + if len(password) < 8 and not _checkPermission(ManagePortal, self): + return _(u'Your password must contain at least 8 characters.') + + if confirm is not None and confirm != password: + return _(u'Your password and confirmation did not match. ' + u'Please try again.') + + return None + + def afterAdd(self, member, id, password, properties): """ notify member creation """ member.notifyWorkflowCreated() member.indexObject() - + + security.declarePublic('generatePassword') + def generatePassword(self): + """ This password may not been entered by user. + Password generated by this method are typicaly used + on a member registration with 'validate_email' option enabled. + """ + return str(uuid4()) security.declarePublic('requestPasswordReset') - def requestPasswordReset(self, userid): - """ add uuid / (userid, expiration) pair and return uuid """ + def requestPasswordReset(self, userid, initial=False, came_from=''): + """ add uuid / (userid, expiration) pair + if ok: send an email to member. returns error message otherwise. + """ self.clearExpiredPasswordResetRequests() mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool') member = mtool.getMemberById(userid) @@ -241,7 +274,7 @@ class RegistrationTool(BaseRegistrationTool) : uuid = str(uuid4()) while self._passwordResetRequests.has_key(uuid) : uuid = str(uuid4()) - self._passwordResetRequests[uuid] = (userid, DateTime() + 1) + self._passwordResetRequests[uuid] = (userid, DateTime() + 1, came_from) utool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IURLTool') ptool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IPropertiesTool') # fuck : mailhost récupéré avec getUtilityByInterfaceName n'est pas correctement @@ -251,9 +284,15 @@ class RegistrationTool(BaseRegistrationTool) : mailhost = portal.MailHost sender = encodeQuopriEmail(ptool.getProperty('email_from_name'), ptool.getProperty('email_from_address')) to = encodeQuopriEmail(member.getMemberFullName(nameBefore=0), member.getProperty('email')) - subject = translate(_('How to reset your password on the %s website')) % ptool.getProperty('title') + if initial : + subject = translate(_('Complete your registration on the %s website')) % ptool.getProperty('title') + else : + subject = translate(_('How to reset your password on the %s website')) % ptool.getProperty('title') subject = encodeMailHeader(subject) - options = {'fullName' : member.getMemberFullName(nameBefore=0), + options = {'initial' : initial, + 'fullName' : member.getMemberFullName(nameBefore=0), + 'member_id' : member.getId(), + 'loginIsNotEmail' : member.getId() != member.getProperty('email'), 'siteName' : ptool.getProperty('title'), 'resetPasswordUrl' : '%s/password_reset_form/%s' % (utool(), uuid)} body = self.password_reset_mail(options) @@ -265,30 +304,30 @@ class RegistrationTool(BaseRegistrationTool) : body=body) mailhost.send(message) return - + return _('Unknown user name. Please retry.') - + security.declarePrivate('clearExpiredPasswordResetRequests') def clearExpiredPasswordResetRequests(self): now = DateTime() for uuid, record in self._passwordResetRequests.items() : - userid, date = record + date = record[1] if date < now : del self._passwordResetRequests[uuid] - - + + security.declarePublic('resetPassword') def resetPassword(self, uuid, password, confirm) : record = self._passwordResetRequests.get(uuid) if not record : return None, _('Invalid reset password request.') - - userid, expiration = record + + userid, expiration, came_from = record now = DateTime() if expiration < now : self.clearExpiredPasswordResetRequests() return None, _('Your reset password request has expired. You can ask a new one.') - + msg = self.testPasswordValidity(password, confirm=confirm) if not msg : # None if everything ok. Err message otherwise. mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool') @@ -296,9 +335,10 @@ class RegistrationTool(BaseRegistrationTool) : if member : member.setSecurityProfile(password=password) del self._passwordResetRequests[uuid] - return userid, _('Password successfully updated.') + return {'userid': userid, 'came_from' : came_from}, _('Password successfully updated.') else : - return None, _('"%s" username not found.') % userid - - + return None, _('"${userid}" username not found.', mapping={'userid': userid}) + else : + return None, msg + InitializeClass(RegistrationTool) \ No newline at end of file