Début d'implémentation du reset des mots de passe.
authorBenoît Pin <benoit.pin@gmail.com>
Sun, 23 Jun 2013 23:49:19 +0000 (01:49 +0200)
committerBenoît Pin <benoit.pin@gmail.com>
Sun, 23 Jun 2013 23:49:19 +0000 (01:49 +0200)
RegistrationTool.py
skins/custom_generic/login_form.pt
skins/generic/request_password_reset_form.pt [new file with mode: 0644]

index 77f8bdc..7d2f29b 100644 (file)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #######################################################################################
 #   Plinn - http://plinn.org                                                          #
 # -*- coding: utf-8 -*-
 #######################################################################################
 #   Plinn - http://plinn.org                                                          #
-#   Copyright (C) 2005-2007  Benoît PIN <benoit.pin@ensmp.fr>                         #
+#   © 2005-2013  Benoît PIN <pin@cri.ensmp.fr>                                        #
 #                                                                                     #
 #   This program is free software; you can redistribute it and/or                     #
 #   modify it under the terms of the GNU General Public License                       #
 #                                                                                     #
 #   This program is free software; you can redistribute it and/or                     #
 #   modify it under the terms of the GNU General Public License                       #
@@ -17,7 +17,7 @@
 #   along with this program; if not, write to the Free Software                       #
 #   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.   #
 #######################################################################################
 #   along with this program; if not, write to the Free Software                       #
 #   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.   #
 #######################################################################################
-""" Plinn registration tool: implements 3 modes to register members :
+""" Plinn registration tool: implements 3 modes to register members:
        anonymous, manager, reviewed.
 
 
        anonymous, manager, reviewed.
 
 
@@ -29,11 +29,16 @@ from Products.PageTemplates.PageTemplateFile import PageTemplateFile
 from Products.CMFDefault.RegistrationTool import RegistrationTool as BaseRegistrationTool
 from AccessControl import ClassSecurityInfo, ModuleSecurityInfo
 from AccessControl.Permission import Permission
 from Products.CMFDefault.RegistrationTool import RegistrationTool as BaseRegistrationTool
 from AccessControl import ClassSecurityInfo, ModuleSecurityInfo
 from AccessControl.Permission import Permission
+from BTrees.OOBTree import OOBTree
 from Products.CMFCore.permissions import ManagePortal, AddPortalMember
 from Products.CMFCore.exceptions import AccessControl_Unauthorized
 from Products.CMFCore.utils import getToolByName
 from Products.CMFCore.permissions import ManagePortal, AddPortalMember
 from Products.CMFCore.exceptions import AccessControl_Unauthorized
 from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.utils import getUtilityByInterfaceName
 from Products.GroupUserFolder.GroupsToolPermissions import ManageGroups
 from Products.GroupUserFolder.GroupsToolPermissions import ManageGroups
+from Products.Plinn.utils import Message as _
+from DateTime import DateTime
 from types import TupleType, ListType
 from types import TupleType, ListType
+from uuid import uuid4
 
 security = ModuleSecurityInfo('Products.Plinn.RegistrationTool')
 MODE_ANONYMOUS = 'anonymous'
 
 security = ModuleSecurityInfo('Products.Plinn.RegistrationTool')
 MODE_ANONYMOUS = 'anonymous'
@@ -72,6 +77,7 @@ class RegistrationTool(BaseRegistrationTool) :
        def __init__(self) :
                self._mode = MODE_ANONYMOUS
                self._chain = ''
        def __init__(self) :
                self._mode = MODE_ANONYMOUS
                self._chain = ''
+               self._passwordResetRequests = OOBTree()
        
        security.declareProtected(ManagePortal, 'configureTool')
        def configureTool(self, registration_mode, chain, REQUEST=None) :
        
        security.declareProtected(ManagePortal, 'configureTool')
        def configureTool(self, registration_mode, chain, REQUEST=None) :
@@ -183,5 +189,52 @@ class RegistrationTool(BaseRegistrationTool) :
                """ notify member creation """
                member.notifyWorkflowCreated()
                member.indexObject()
                """ notify member creation """
                member.notifyWorkflowCreated()
                member.indexObject()
+       
+
+       security.declarePublic('requestPasswordReset')
+       def requestPasswordReset(self, userid):
+               """ add uuid / (userid, expiration) pair and return uuid """
+               self.clearExpiredPasswordResetRequests()
+               mtool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IMembershipTool')
+               if mtool.getMemberById(userid) :
+                       uuid = str(uuid4())
+                       self._passwordResetRequests[uuid] = (userid, DateTime() + 1)
+                       return uuid
+       
+       security.declarePrivate('clearExpiredPasswordResetRequests')
+       def clearExpiredPasswordResetRequests(self):
+               now = DateTime()
+               for uuid, record in self._passwordResetRequest.items() :
+                       userid, date = record
+                       if date < now :
+                               del self._passwordResetRequests[uuid]
+       
+       
+       security.declarePublic('resetPassword')
+       def resetPassword(self, userid, uuid, password, confirm) :
+               record = self._passwordResetRequests.get(uuid)
+               if not record :
+                       return _('Invalid reset password request.')
+               
+               recUserid, expiration = record
+               
+               if recUserid != userid :
+                       return _('Invalid userid.')
+               
+               if expiration < now :
+                       self.clearExpiredPasswordResetRequests()
+                       return _('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')
+                       member = mtool.getMemberById(userid)
+                       if member :
+                               member.setSecurityProfile(password=password)
+                               del self._passwordResetRequests[uuid]
+                               return _('Password successfully resetted.')
+                       else :
+                               return _('"%s" username not found.') % userid
+                       
                
 InitializeClass(RegistrationTool)
\ No newline at end of file
                
 InitializeClass(RegistrationTool)
\ No newline at end of file
index 3d3c4bb..0ebaefc 100644 (file)
@@ -1,81 +1,66 @@
 <html xmlns:tal="http://xml.zope.org/namespaces/tal"
       xmlns:metal="http://xml.zope.org/namespaces/metal"
 <html xmlns:tal="http://xml.zope.org/namespaces/tal"
       xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
       metal:use-macro="here/main_template/macros/master">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
   </head>
       metal:use-macro="here/main_template/macros/master">
   <head>
     <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
   </head>
-<body>
-<div metal:fill-slot="main_no_tabs" i18n:domain="cmf_default">
-<div class="Desktop">
-<h1 class="Desktop" i18n:translate="">Log in</h1>
-
-<form action="" method="post" tal:attributes="action string:${here/portal_url}/logged_in">
-<input type="hidden" name="noAjax" value="1" />
-
-<!-- ****** Enable the automatic redirect ***** -->
-<span tal:condition="exists: request/came_from">
-  <input type="hidden" name="came_from" value=""
-         tal:attributes="value request/came_from" />
-</span>
-<!-- ****** Enable the automatic redirect ***** -->
-<input type="hidden" name="just_login" value="True" />
-<table class="FormLayout">
-<tr>
-  <td align="left" valign="top">
-                <strong i18n:translate="user_name">Login</strong>
+  <body>
+    <div metal:fill-slot="main_no_tabs" i18n:domain="cmf_default">
+      <div id="Desktop">
+        <h1 i18n:translate="">Log in</h1>
+        <form action="" method="post" tal:attributes="action string:${here/portal_url}/logged_in">
+          <input type="hidden" name="noAjax" value="1"/>
+          <!-- ****** Enable the automatic redirect ***** -->
+          <span tal:condition="exists: request/came_from">
+            <input type="hidden" name="came_from" value="" tal:attributes="value request/came_from"/>
+          </span>
+          <!-- ****** Enable the automatic redirect ***** -->
+          <input type="hidden" name="just_login" value="True"/>
+          <table class="TwoColumnForm">
+            <tr>
+              <th i18n:translate="user_name">Login</th>
+              <td>
+                <input type="text" name="__ac_name" size="20" value="" tal:attributes="value python: request.get('__ac_name') or ''"/>
               </td>
               </td>
-  <td align="left" valign="top">
-  <input type="text" name="__ac_name" size="20" value=""
-         tal:attributes="value python: request.get('__ac_name') or ''" />
-  </td>
-</tr>
-<tr>
-  <td align="left" valign="top">
-  <strong i18n:translate="">Password</strong>
-  </td>
-  <td align="left" valign="top">
-  <input type="password" name="__ac_password" size="20" />
-  </td>
-</tr>
-
-<tr valign="top" align="left">
-<td></td>
-<td><input type="checkbox" name="__ac_persistent" value="1" checked="checked"
-      id="cb_remember" />
-<label for="cb_remember" i18n:translate="">Remember my name.</label>
-</td></tr>
-
-<tr>
-  <td align="left" valign="top">
-  </td>
-  <td align="left" valign="top">
-  <input type="submit" name="submit" value=" Login "
-         i18n:attributes="value" />
-  </td>
-</tr>
-
-</table>
-</form>
-
-<p><a href=""
-    tal:attributes="href string:${here/portal_url}/mail_password_form"
-    i18n:translate=""
-   >Forgot your password?</a>
-</p>
-
-<p i18n:translate="">Having trouble logging in? Make sure to enable cookies in
-    your web browser.
-</p>
-<p i18n:translate="">Don't forget to logout or exit your browser when you're
-  done.
-</p>
-
-<p i18n:translate="">Setting the 'Remember my name' option will set a cookie
-  with your username, so that when you next log in, your user name will
-  already be filled in for you.
-</p>
-</div>
-</div>
-</body>
+            </tr>
+            <tr>
+              <th i18n:translate="">Password</th>
+              <td>
+                <input type="password" name="__ac_password" size="20"/>
+              </td>
+            </tr>
+            <tr>
+              <td><br/></td>
+              <td>
+                <input type="checkbox" name="__ac_persistent" value="1" checked="checked" id="cb_remember"/>
+                <label for="cb_remember" i18n:translate="">Remember my name.</label>
+              </td>
+            </tr>
+            <tr>
+              <td><br/></td>
+              <td>
+                <input type="submit" name="submit" value=" Login " i18n:attributes="value"/>
+              </td>
+            </tr>
+          </table>
+        </form>
+        <!-- <p>
+          <a href="" tal:attributes="href string:${here/portal_url}/request_password_reset_form" i18n:translate="">Forgot your password?</a>
+        </p> -->
+        <p i18n:translate="">
+          Having trouble logging in? Make sure to enable cookies in your web
+          browser.
+        </p>
+        <p i18n:translate="">
+          Don't forget to logout or exit your browser when you're done.
+        </p>
+        <p i18n:translate="">
+          Setting the 'Remember my name' option will set a cookie with your
+          username, so that when you next log in, your user name will already be
+          filled in for you.
+        </p>
+      </div>
+    </div>
+  </body>
 </html>
 </html>
-
diff --git a/skins/generic/request_password_reset_form.pt b/skins/generic/request_password_reset_form.pt
new file mode 100644 (file)
index 0000000..fe7c384
--- /dev/null
@@ -0,0 +1,30 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      metal:use-macro="here/main_template/macros/master">
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
+  </head>
+  <body>
+    <div metal:fill-slot="main_no_tabs" i18n:domain="plinn">
+      <div id="Desktop" tal:define="ptool here/portal_properties">
+        <h1 i18n:translate="">Password reset</h1>
+        <p i18n:translate="">
+          Enter your username below and click on the "Send" button. You will
+          receive an email with a link to reset your password.
+        </p>
+        <p i18n:translate="">
+          If this will not work for you (for example, if you forget your
+          username or didn't enter your email address) send email to <a
+          tal:attributes="href string:mailto:${ptool/email_from_address}"
+          tal:content="ptool/email_from_address" href="mailto:me@here.com"
+          i18n:name="admin_email">me@here.com</a>.
+        </p>
+        <form action="mail_password" tal:attributes="action string:${here/portal_url}/mail_password">
+          <input name="userid"/>
+          <input type="submit" value=" Send " i18n:attributes="value"/>
+        </form>
+      </div>
+    </div>
+  </body>
+</html>