[gen] Implemented a mechanism 'forgot password?'.
This commit is contained in:
parent
8a5ca81746
commit
ad14c1258c
|
@ -487,6 +487,14 @@ class ZopeGenerator(Generator):
|
||||||
msg('enable_cookies', '', msg.ENABLE_COOKIES),
|
msg('enable_cookies', '', msg.ENABLE_COOKIES),
|
||||||
msg('page_previous', '', msg.PAGE_PREVIOUS),
|
msg('page_previous', '', msg.PAGE_PREVIOUS),
|
||||||
msg('page_next', '', msg.PAGE_NEXT),
|
msg('page_next', '', msg.PAGE_NEXT),
|
||||||
|
msg('forgot_password', '', msg.FORGOT_PASSWORD),
|
||||||
|
msg('ask_password_reinit', '', msg.ASK_PASSWORD_REINIT),
|
||||||
|
msg('reinit_mail_sent', '', msg.REINIT_MAIL_SENT),
|
||||||
|
msg('reinit_password', '', msg.REINIT_PASSWORD),
|
||||||
|
msg('reinit_password_body', '', msg.REINIT_PASSWORD_BODY),
|
||||||
|
msg('new_password', '', msg.NEW_PASSWORD),
|
||||||
|
msg('new_password_body', '', msg.NEW_PASSWORD_BODY),
|
||||||
|
msg('new_password_sent', '', msg.NEW_PASSWORD_SENT),
|
||||||
]
|
]
|
||||||
# Create a label for every role added by this application
|
# Create a label for every role added by this application
|
||||||
for role in self.getAllUsedRoles():
|
for role in self.getAllUsedRoles():
|
||||||
|
|
|
@ -4,11 +4,12 @@ from appy.shared import mimeTypes
|
||||||
from appy.shared.utils import getOsTempFolder, sequenceTypes
|
from appy.shared.utils import getOsTempFolder, sequenceTypes
|
||||||
from appy.shared.data import languages
|
from appy.shared.data import languages
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Type, Search, Selection
|
from appy.gen import Type, Search, Selection, String
|
||||||
from appy.gen.utils import SomeObjects, getClassName
|
from appy.gen.utils import SomeObjects, getClassName
|
||||||
from appy.gen.mixins import BaseMixin
|
from appy.gen.mixins import BaseMixin
|
||||||
from appy.gen.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen.descriptors import ClassDescriptor
|
from appy.gen.descriptors import ClassDescriptor
|
||||||
|
from appy.gen.mail import sendMail
|
||||||
try:
|
try:
|
||||||
from AccessControl.ZopeSecurityPolicy import _noroles
|
from AccessControl.ZopeSecurityPolicy import _noroles
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -1042,4 +1043,68 @@ class ToolMixin(BaseMixin):
|
||||||
if hasattr(self.o.aq_base, 'pages') and self.o.pages:
|
if hasattr(self.o.aq_base, 'pages') and self.o.pages:
|
||||||
return [self.getObject(uid) for uid in self.o.pages ]
|
return [self.getObject(uid) for uid in self.o.pages ]
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
|
def askPasswordReinit(self):
|
||||||
|
'''A user (anonymmous) does not remember its password. Here we will
|
||||||
|
send him a mail containing a link that will trigger password
|
||||||
|
re-initialisation.'''
|
||||||
|
login = self.REQUEST.get('login').strip()
|
||||||
|
appyTool = self.appy()
|
||||||
|
user = appyTool.search1('User', login=login, noSecurity=True)
|
||||||
|
msg = self.translate('reinit_mail_sent')
|
||||||
|
backUrl = self.REQUEST['HTTP_REFERER']
|
||||||
|
if not user:
|
||||||
|
# Return the message nevertheless. This way, malicious users can't
|
||||||
|
# deduce information about existing users.
|
||||||
|
return self.goto(backUrl, msg)
|
||||||
|
# If login is an email, use it. Else, use user.email instead.
|
||||||
|
email = user.login
|
||||||
|
if not String.EMAIL.match(email):
|
||||||
|
email = user.email
|
||||||
|
if not email:
|
||||||
|
# Impossible to re-initialise the password.
|
||||||
|
return self.goto(backUrl, msg)
|
||||||
|
# Create a temporary file whose name is the user login and whose
|
||||||
|
# content is a generated token.
|
||||||
|
f = file(os.path.join(getOsTempFolder(), login), 'w')
|
||||||
|
token = String().generatePassword()
|
||||||
|
f.write(token)
|
||||||
|
f.close()
|
||||||
|
# Send an email
|
||||||
|
initUrl = '%s/doPasswordReinit?login=%s&token=%s' % \
|
||||||
|
(self.absolute_url(), login, token)
|
||||||
|
subject = self.translate('reinit_password')
|
||||||
|
map = {'url':initUrl, 'siteUrl':self.getSiteUrl()}
|
||||||
|
body= self.translate('reinit_password_body', mapping=map, format='text')
|
||||||
|
sendMail(appyTool, email, subject, body)
|
||||||
|
return self.goto(backUrl, msg)
|
||||||
|
|
||||||
|
def doPasswordReinit(self):
|
||||||
|
'''Performs the password re-initialisation.'''
|
||||||
|
rq = self.REQUEST
|
||||||
|
login = rq['login']
|
||||||
|
token = rq['token']
|
||||||
|
# Check if such token exists in temp folder
|
||||||
|
tokenFile = os.path.join(getOsTempFolder(), login)
|
||||||
|
if os.path.exists(tokenFile):
|
||||||
|
f = file(tokenFile)
|
||||||
|
storedToken = f.read()
|
||||||
|
f.close()
|
||||||
|
if storedToken == token:
|
||||||
|
# Generate a new password for this user
|
||||||
|
appyTool = self.appy()
|
||||||
|
user = appyTool.search1('User', login=login, noSecurity=True)
|
||||||
|
newPassword = user.setPassword()
|
||||||
|
# Send the new password by email
|
||||||
|
email = login
|
||||||
|
if not String.EMAIL.match(email):
|
||||||
|
email = user.email
|
||||||
|
subject = self.translate('new_password')
|
||||||
|
siteUrl = self.getSiteUrl()
|
||||||
|
map = {'password': newPassword, 'siteUrl': siteUrl}
|
||||||
|
body = self.translate('new_password_body', mapping=map,
|
||||||
|
format='text')
|
||||||
|
sendMail(appyTool, email, subject, body)
|
||||||
|
os.remove(tokenFile)
|
||||||
|
return self.goto(siteUrl, self.translate('new_password_sent'))
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
15
gen/po.py
15
gen/po.py
|
@ -136,6 +136,21 @@ class PoMessage:
|
||||||
ENABLE_COOKIES = 'You must enable cookies before you can log in.'
|
ENABLE_COOKIES = 'You must enable cookies before you can log in.'
|
||||||
PAGE_PREVIOUS = 'Previous page'
|
PAGE_PREVIOUS = 'Previous page'
|
||||||
PAGE_NEXT = 'Next page'
|
PAGE_NEXT = 'Next page'
|
||||||
|
FORGOT_PASSWORD = 'Forgot password?'
|
||||||
|
ASK_PASSWORD_REINIT = 'Ask new password'
|
||||||
|
REINIT_MAIL_SENT = 'A mail has been sent to you. Please follow the ' \
|
||||||
|
'instructions from this email.'
|
||||||
|
REINIT_PASSWORD = 'Password re-initialisation'
|
||||||
|
REINIT_PASSWORD_BODY = 'Hello,<br/><br/>A password re-initialisation ' \
|
||||||
|
'has been requested, tied to this email address, for the website ' \
|
||||||
|
'${siteUrl}. If you are not at the origin of this request, please ' \
|
||||||
|
'ignore this email. Else, click on the link below to receive a new ' \
|
||||||
|
'password.<br/><br/>${url}'
|
||||||
|
NEW_PASSWORD = 'Your new password'
|
||||||
|
NEW_PASSWORD_BODY = 'Hello,<br/><br/>The new password you have ' \
|
||||||
|
'requested for website ${siteUrl} is ${password}<br/>' \
|
||||||
|
'<br/>Best regards.'
|
||||||
|
NEW_PASSWORD_SENT = 'Your new password has been sent to you by email.'
|
||||||
|
|
||||||
def __init__(self, id, msg, default, fuzzy=False, comments=[],
|
def __init__(self, id, msg, default, fuzzy=False, comments=[],
|
||||||
niceDefault=False):
|
niceDefault=False):
|
||||||
|
|
|
@ -67,6 +67,7 @@ img { border: 0; vertical-align: middle}
|
||||||
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9;}
|
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9;}
|
||||||
.focus td { padding: 4px 0px 4px 4px }
|
.focus td { padding: 4px 0px 4px 4px }
|
||||||
.discreet { font-size: 90%; }
|
.discreet { font-size: 90%; }
|
||||||
|
.lostPassword a { font-size: 90%; color: white; padding-left: 1em;}
|
||||||
.portlet { width: 150px; border-right: 1px solid #5F7983;}
|
.portlet { width: 150px; border-right: 1px solid #5F7983;}
|
||||||
.portletContent { margin: 9px; }
|
.portletContent { margin: 9px; }
|
||||||
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
|
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
|
||||||
|
|
|
@ -451,6 +451,19 @@ function doConfirm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wrongTextInput = '#ff934a none';
|
||||||
|
// Function triggered when the user ask password reinitialisation
|
||||||
|
function doAskPasswordReinit() {
|
||||||
|
// Check that the user has typed a login
|
||||||
|
var theForm = document.getElementById('askPasswordReinitForm');
|
||||||
|
var login = theForm.login.value.replace(' ', '');
|
||||||
|
if (!login) { theForm.login.style.background = wrongTextInput; }
|
||||||
|
else {
|
||||||
|
closePopup('askPasswordReinitPopup');
|
||||||
|
theForm.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Function that finally posts the edit form after the user has confirmed that
|
// Function that finally posts the edit form after the user has confirmed that
|
||||||
// she really wants to post it.
|
// she really wants to post it.
|
||||||
function postConfirmedEditForm() {
|
function postConfirmedEditForm() {
|
||||||
|
|
|
@ -97,6 +97,21 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<tal:comment replace="nothing">Popup for reinitialing the password</tal:comment>
|
||||||
|
<div id="askPasswordReinitPopup" class="popup" tal:condition="isAnon">
|
||||||
|
<form id="askPasswordReinitForm" method="post"
|
||||||
|
tal:attributes="action python: tool.absolute_url() + '/askPasswordReinit'">
|
||||||
|
<div align="center">
|
||||||
|
<p tal:content="python: _('app_login')"></p>
|
||||||
|
<input type="text" size="35" name="login" id="login" value=""/>
|
||||||
|
<br/><br/>
|
||||||
|
<input type="button" onClick="doAskPasswordReinit()"
|
||||||
|
tal:attributes="value python:_('ask_password_reinit')"/>
|
||||||
|
<input type="button" value="No" onClick="closePopup('askPasswordReinitPopup')"
|
||||||
|
tal:attributes="value python:_('object_cancel')"/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tal:comment replace="nothing">The user strip</tal:comment>
|
<tal:comment replace="nothing">The user strip</tal:comment>
|
||||||
|
@ -108,7 +123,8 @@
|
||||||
<tal:comment replace="nothing">The user login form for anonymous users</tal:comment>
|
<tal:comment replace="nothing">The user login form for anonymous users</tal:comment>
|
||||||
<table align="center" tal:condition="python: isAnon and ('/temp_folder/' not in req['ACTUAL_URL'])"
|
<table align="center" tal:condition="python: isAnon and ('/temp_folder/' not in req['ACTUAL_URL'])"
|
||||||
class="login">
|
class="login">
|
||||||
<tr><td class="userStripText">
|
<tr>
|
||||||
|
<td class="userStripText">
|
||||||
<form name="loginform" method="post"
|
<form name="loginform" method="post"
|
||||||
tal:attributes="action python: tool.absolute_url() + '/performLogin'">
|
tal:attributes="action python: tool.absolute_url() + '/performLogin'">
|
||||||
|
|
||||||
|
@ -124,7 +140,11 @@
|
||||||
<input type="submit" name="submit" onclick="setLoginVars()"
|
<input type="submit" name="submit" onclick="setLoginVars()"
|
||||||
tal:define="label python: _('app_connect')" tal:attributes="value label; alt label;"/>
|
tal:define="label python: _('app_connect')" tal:attributes="value label; alt label;"/>
|
||||||
</form>
|
</form>
|
||||||
</td></tr>
|
</td>
|
||||||
|
<td class="lostPassword">
|
||||||
|
<a href="javascript: openPopup('askPasswordReinitPopup')"
|
||||||
|
tal:content="python: _('forgot_password')"></a></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<tal:comment replace="nothing">User info and controls for authenticated users</tal:comment>
|
<tal:comment replace="nothing">User info and controls for authenticated users</tal:comment>
|
||||||
<table tal:condition="not: isAnon" class="buttons" width="99%">
|
<table tal:condition="not: isAnon" class="buttons" width="99%">
|
||||||
|
|
Loading…
Reference in a new issue