Commit dc9ffa12 authored by Jérome Perrin's avatar Jérome Perrin

Modernize Password Tool

- Use portal_status_level when redirecting
- Check that password and confirmation match
- Fix  wrong argument name `password_confirmation` sometimes used

See merge request nexedi/erp5!1792
parents 8cc780c1 05a3390b
......@@ -808,7 +808,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference = self.portal.portal_catalog.getResultValue(
portal_type='System Preference',
title='Authentication',)
# Here we activate the "password should contain usename" policy
# Here we activate the "password should contain username" policy
# as a way to check that password reset checks are done in the
# context of the login
preference.setPrefferedForceUsernameCheckInPassword(1)
......@@ -856,8 +856,11 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
# now with a password complying to the policy
ret = submit_reset_password_dialog('ok')
self.assertEqual(httplib.FOUND, ret.getStatus())
self.assertTrue(ret.getHeader('Location').endswith(
'/login_form?portal_status_message=Password+changed.'))
redirect_url = urlparse.urlparse(ret.getHeader("Location"))
self.assertEqual(redirect_url.path, '{}/login_form'.format(self.portal.absolute_url_path()))
redirect_url_params = urlparse.parse_qsl(redirect_url.query)
self.assertIn(('portal_status_message', 'Password changed.'), redirect_url_params)
self.assertIn(('portal_status_level', 'success'), redirect_url_params)
def test_PreferenceTool_changePassword_checks_policy(self):
person = self.createUser(self.id(), password='current')
......@@ -918,7 +921,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
# long enough password is accepted
ret = submit_change_password_dialog('long_enough_password')
# When password reset is succesful, user is logged out
# When password reset is successful, user is logged out
self.assertEqual(httplib.FOUND, ret.getStatus())
self.assertEqual(self.portal.portal_preferences.absolute_url(),
ret.getHeader("Location"))
......
......@@ -27,12 +27,13 @@
#
##############################################################################
import unittest
import six
from six.moves.urllib_parse import urlparse, parse_qsl
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.Sequence import SequenceList
from DateTime import DateTime
class TestPasswordTool(ERP5TypeTestCase):
"""
Test reset of password
......@@ -47,6 +48,22 @@ class TestPasswordTool(ERP5TypeTestCase):
self.portal.email_from_address = 'site@example.invalid'
self.portal.MailHost.reset()
self.portal.portal_caches.clearAllCache()
self._createUser("userA")
def _createUser(self, base_name):
person = self.portal.person_module.newContent(
portal_type="Person",
reference=base_name,
default_email_text="{base_name}@example.invalid".format(base_name=base_name))
assignment = person.newContent(portal_type='Assignment')
assignment.open()
login = person.newContent(
portal_type='ERP5 Login',
reference='{base_name}-login'.format(base_name=base_name),
password='{base_name}-password'.format(base_name=base_name),
)
login.validate()
self.tic()
def beforeTearDown(self):
self.abort()
......@@ -92,275 +109,175 @@ class TestPasswordTool(ERP5TypeTestCase):
"Plugin %s should not have authenticated '%s' with password '%s'" %
(plugin_name, login, password))
def stepAddUser(self, sequence=None, sequence_list=None, **kw):
"""
Create a user
"""
person = self.portal.person_module.newContent(portal_type="Person",
reference="userA",
default_email_text="userA@example.invalid")
assignment = person.newContent(portal_type='Assignment')
assignment.open()
login = person.newContent(
portal_type='ERP5 Login',
reference='userA-login',
password='passwordA',
)
login.validate()
def stepCheckPasswordToolExists(self, sequence=None, sequence_list=None, **kw):
"""
Check existence of password tool
"""
self.assertTrue(self.getPasswordTool() is not None)
def stepCheckUserLogin(self, sequence=None, sequence_list=None, **kw):
"""
Check existence of password tool
"""
self._assertUserExists('userA-login', 'passwordA')
def stepCheckUserLoginWithNewPassword(self, sequence=None, sequence_list=None, **kw):
"""
Check existence of password tool
"""
self._assertUserExists('userA-login', 'secret')
def stepCheckUserNotLoginWithBadPassword(self, sequence=None, sequence_list=None, **kw):
"""
Check existence of password tool
"""
self._assertUserDoesNotExists('userA', 'secret')
def stepCheckUserNotLoginWithFormerPassword(self, sequence=None, sequence_list=None, **kw):
"""
Check existence of password tool
"""
self._assertUserDoesNotExists('userA', 'passwordA')
def stepLostPassword(self, sequence=None, sequence_list=None, **kw):
"""
Required a new password
"""
self.portal.portal_password.mailPasswordResetRequest(user_login="userA-login")
def stepTryLostPasswordWithBadUser(self, sequence=None, sequence_list=None, **kw):
"""
Required a new password
"""
self.portal.portal_password.mailPasswordResetRequest(user_login="userZ-login")
def test_password_reset(self):
self._assertUserExists('userA-login', 'userA-password')
self._assertUserDoesNotExists('userA-login', 'bad')
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='userA-login', REQUEST=self.portal.REQUEST)
def stepCheckNoMailSent(self, sequence=None, sequence_list=None, **kw):
"""
Check mail has not been sent after fill in wrong the form password
"""
last_message = self.portal.MailHost._last_message
self.assertEqual((), last_message)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
self.tic()
def stepCheckMailSent(self, sequence=None, sequence_list=None, **kw):
"""
Check mail has been sent after fill in the form password
"""
last_message = self.portal.MailHost._last_message
self.assertNotEqual((), last_message)
mfrom, mto, _ = last_message
(mfrom, mto, mbody), = self.portal.MailHost.getMessageList()
self.assertEqual('Portal Administrator <site@example.invalid>', mfrom)
self.assertEqual(['userA@example.invalid'], mto)
reset_key, = list(six.iterkeys(self.portal.portal_password._password_request_dict))
self.assertIn(
('PasswordTool_viewResetPassword?reset_key=' + reset_key).encode(),
mbody)
ret = self.portal.portal_password.changeUserPassword(
user_login="userA-login",
password="new-password",
password_confirm="new-password",
password_key=reset_key)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Password changed."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
self.tic()
self._assertUserExists('userA-login', 'new-password')
self._assertUserDoesNotExists('userA-login', 'userA-password')
# key no longer work
ret = self.portal.portal_password.changeUserPassword(
user_login="userA-login",
password="new-password",
password_confirm="new-password",
password_key=reset_key)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Key not known. Please ask reset password."), query_string_param)
self.assertIn(("portal_status_level", "error"), query_string_param)
self.tic()
self._assertUserExists('userA-login', 'new-password')
self._assertUserDoesNotExists('userA-login', 'userA-password')
def stepGoToRandomAddress(self, sequence=None, sequence_list=None, **kw):
"""
Call method that change the password
We don't check use of random url in mail here as we have on request
But random is also check by changeUserPassword, so it's the same
"""
key = self.portal.portal_password._password_request_dict.keys()[0]
self.portal.portal_password.changeUserPassword(user_login="userA-login",
password="secret",
password_confirmation="secret",
password_key=key)
# reset cache
self.portal.portal_caches.clearAllCache()
def stepGoToRandomAddressWithBadUserName(self, sequence=None, sequence_list=None, **kw):
"""
Call method that change the password with a bad user name
This must not work
"""
key = self.portal.portal_password._password_request_dict.keys()[0]
sequence.edit(key=key)
self.portal.portal_password.changeUserPassword(user_login="userZ-login",
password="secret",
password_confirmation="secret",
password_key=key)
# reset cache
self.portal.portal_caches.clearAllCache()
def test_password_reset_request_for_non_existing_user(self):
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='not exist', REQUEST=self.portal.REQUEST)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
self.tic()
self.assertFalse(self.portal.MailHost.getMessageList())
def stepGoToRandomAddressTwice(self, sequence=None, sequence_list=None, **kw):
"""
As we already change password, this must npot work anylonger
"""
key = sequence.get('key')
self.portal.portal_password.changeUserPassword(user_login="userA-login",
password="passwordA",
password_confirmation="passwordA",
password_key=key)
# reset cache
self.portal.portal_caches.clearAllCache()
def test_password_reset_request_for_wildcard_username(self):
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='%', REQUEST=self.portal.REQUEST)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
self.tic()
self.assertFalse(self.portal.MailHost.getMessageList())
def test_password_reset_request_for_different_user(self):
self._createUser('userB')
self.portal.portal_password.mailPasswordResetRequest(
user_login='userA-login', REQUEST=self.portal.REQUEST)
reset_key, = list(six.iterkeys(
self.portal.portal_password._password_request_dict))
ret = self.portal.portal_password.changeUserPassword(
user_login="userB-login",
password="new-password",
password_confirm="new-password",
password_key=reset_key)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Bad login provided."), query_string_param)
self.assertIn(("portal_status_level", "error"), query_string_param)
self.tic()
self._assertUserExists('userA-login', 'userA-password')
self._assertUserExists('userB-login', 'userB-password')
self._assertUserDoesNotExists('userB-login', 'new-password')
def test_password_reset_unknown_key(self):
self.portal.portal_password.mailPasswordResetRequest(
user_login='userA-login', REQUEST=self.portal.REQUEST)
ret = self.portal.portal_password.changeUserPassword(
user_login="userA-login",
password="new-password",
password_confirm="new-password",
password_key='wrong key')
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Key not known. Please ask reset password."), query_string_param)
self.assertIn(("portal_status_level", "error"), query_string_param)
self.tic()
def stepGoToBadRandomAddress(self, sequence=None, sequence_list=None, **kw):
"""
Try to reset a password with bad random part
"""
self.portal.portal_password.changeUserPassword(user_login="userA-login",
password="secret",
password_confirmation="secret",
password_key="toto")
# reset cache
self.portal.portal_caches.clearAllCache()
def test_password_reset_date_expired(self):
self.portal.portal_password.mailPasswordResetRequest(user_login='userA-login')
(reset_key, (login, date)), = list(six.iteritems(
self.portal.portal_password._password_request_dict))
self.assertTrue(date.isFuture())
self.portal.portal_password._password_request_dict[reset_key] = (
login,
DateTime() - 1
)
ret = self.portal.portal_password.changeUserPassword(
user_login="userA-login",
password="new-password",
password_confirm="new-password",
password_key=reset_key)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Date has expired."), query_string_param)
self.assertIn(("portal_status_level", "error"), query_string_param)
self.tic()
self._assertUserExists('userA-login', 'userA-password')
self._assertUserDoesNotExists('userA-login', 'new-password')
def stepModifyExpirationDate(self, sequence=None, sequence_list=None, **kw):
"""
Change expiration date so that reset of password is not available
"""
# save key for url
key = self.portal.portal_password._password_request_dict.keys()[0]
sequence.edit(key=key)
# modify date
for k, v in self.portal.portal_password._password_request_dict.items():
login, date = v
date = DateTime() - 1
self.portal.portal_password._password_request_dict[k] = (login, date)
def stepSimulateExpirationAlarm(self, sequence=None, sequence_list=None, **kw):
"""
Simulate alarm wich remove expired request
"""
self.portal.portal_password.removeExpiredRequests()
def stepCheckNoRequestRemains(self, sequence=None, sequence_list=None, **kw):
"""
after alarm all expired request must have been removed
"""
self.assertEqual(len(self.portal.portal_password._password_request_dict), 0)
def stepLogout(self, sequence=None, sequence_list=None, **kw):
"""
Logout
"""
self.logout()
# tests
def test_01_checkPasswordTool(self):
sequence_list = SequenceList()
sequence_string = 'CheckPasswordToolExists ' \
'AddUser Tic ' \
'Logout ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
'TryLostPasswordWithBadUser Tic ' \
'CheckNoMailSent ' \
'GoToBadRandomAddress Tic ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
'LostPassword Tic ' \
'CheckMailSent GoToRandomAddress Tic ' \
'CheckUserLoginWithNewPassword ' \
'CheckUserNotLoginWithFormerPassword ' \
'GoToRandomAddressTwice Tic ' \
'CheckUserLoginWithNewPassword ' \
'CheckUserNotLoginWithFormerPassword ' \
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def test_02_checkPasswordToolDateExpired(self):
sequence_list = SequenceList()
sequence_string = 'CheckPasswordToolExists ' \
'AddUser Tic ' \
'Logout ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
'LostPassword Tic ' \
'CheckMailSent ' \
'ModifyExpirationDate ' \
'GoToRandomAddress Tic ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
def test_03_checkPasswordToolAlarm(self):
sequence_list = SequenceList()
sequence_string = 'CheckPasswordToolExists ' \
'AddUser Tic ' \
'Logout ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
'LostPassword Tic ' \
'CheckMailSent ' \
'ModifyExpirationDate ' \
'SimulateExpirationAlarm ' \
'CheckNoRequestRemains ' \
'GoToRandomAddressTwice Tic ' \
'CheckUserLogin CheckUserNotLoginWithBadPassword ' \
sequence_list.addSequenceString(sequence_string)
sequence_list.play(self)
self.assertFalse(list(six.iterkeys(
self.portal.portal_password._password_request_dict)))
def test_password_reset_password_and_confirmation_do_not_match(self):
self.portal.portal_password.mailPasswordResetRequest(
user_login='userA-login', REQUEST=self.portal.REQUEST)
reset_key, = list(six.iterkeys(
self.portal.portal_password._password_request_dict))
ret = self.portal.portal_password.changeUserPassword(
user_login="userA-login",
password="new-password",
password_confirm="wrong-password",
password_key=reset_key)
query_string_param = parse_qsl(urlparse(str(ret)).query)
self.assertIn(("portal_status_message", "Password does not match the confirm password."), query_string_param)
self.assertIn(("portal_status_level", "error"), query_string_param)
def test_two_concurrent_password_reset(self):
personA = self.portal.person_module.newContent(portal_type="Person",
reference="userA",
default_email_text="userA@example.invalid")
assignment = personA.newContent(portal_type='Assignment')
assignment.open()
login = personA.newContent(
portal_type='ERP5 Login',
reference='userA-login',
password='passwordA',
)
login.validate()
personB = self.portal.person_module.newContent(portal_type="Person",
reference="userB",
default_email_text="userB@example.invalid")
assignment = personB.newContent(portal_type='Assignment')
assignment.open()
login = personB.newContent(
portal_type='ERP5 Login',
reference='userB-login',
password='passwordB',
)
login.validate()
self.tic()
self._assertUserExists('userA-login', 'passwordA')
self._assertUserExists('userB-login', 'passwordB')
self._createUser('userB')
self._assertUserExists('userA-login', 'userA-password')
self._assertUserExists('userB-login', 'userB-password')
self.assertEqual(0, len(self.portal.portal_password._password_request_dict))
self.portal.portal_password.mailPasswordResetRequest(user_login="userA-login")
self.assertEqual(1, len(self.portal.portal_password._password_request_dict))
key_a = self.portal.portal_password._password_request_dict.keys()[0]
key_a = list(six.iterkeys(self.portal.portal_password._password_request_dict))[0]
self.tic()
self.portal.portal_password.mailPasswordResetRequest(user_login="userB-login")
possible_key_list =\
self.portal.portal_password._password_request_dict.keys()
possible_key_list = \
list(six.iterkeys(self.portal.portal_password._password_request_dict))
self.assertEqual(2, len(possible_key_list))
key_b = [k for k in possible_key_list if k != key_a][0]
self.tic()
self._assertUserExists('userA-login', 'passwordA')
self._assertUserExists('userB-login', 'passwordB')
self._assertUserExists('userA-login', 'userA-password')
self._assertUserExists('userB-login', 'userB-password')
self.portal.portal_password.changeUserPassword(user_login="userA-login",
password="newA",
password_confirmation="newA",
password_confirm="newA",
password_key=key_a)
self.tic()
self._assertUserExists('userA-login', 'newA')
self._assertUserExists('userB-login', 'passwordB')
self._assertUserExists('userB-login', 'userB-password')
self.portal.portal_password.changeUserPassword(user_login="userB-login",
password="newB",
password_confirmation="newB",
password_confirm="newB",
password_key=key_b)
self.tic()
......@@ -390,8 +307,7 @@ class TestPasswordTool(ERP5TypeTestCase):
self.assertEqual(0, len(self.portal.portal_password._password_request_dict))
self.portal.portal_password.mailPasswordResetRequest(user_login="userZ-login ")
self.assertEqual(1, len(self.portal.portal_password._password_request_dict))
key_a = self.portal.portal_password._password_request_dict.keys()[0]
key_a, = list(six.iterkeys(self.portal.portal_password._password_request_dict))
self.tic()
self._assertUserExists('userZ-login ', 'passwordZ')
......@@ -399,7 +315,7 @@ class TestPasswordTool(ERP5TypeTestCase):
# Check that password is not changed if trailing space is not entered
self.portal.portal_password.changeUserPassword(user_login="userZ-login",
password="newZ",
password_confirmation="newZ",
password_confirm="newZ",
password_key=key_a)
self.tic()
self._assertUserExists('userZ-login ', 'passwordZ')
......@@ -407,7 +323,7 @@ class TestPasswordTool(ERP5TypeTestCase):
# Check that password is changed if trailing space is entered
self.portal.portal_password.changeUserPassword(user_login="userZ-login ",
password="newZ2",
password_confirmation="newZ2",
password_confirm="newZ2",
password_key=key_a)
self.tic()
self._assertUserExists('userZ-login ', 'newZ2')
......@@ -429,10 +345,13 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST)
query_string_param = parse_qsl(urlparse(str(ret)).query)
# For security reasons, the message should always be the same
self.assertIn("portal_status_message=An+email+has+been+sent+to+you.", str(ret))
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
# But no mail has been sent
self.stepCheckNoMailSent()
self.assertFalse(self.portal.MailHost.getMessageList())
def test_unreachable_email_on_person(self):
person = self.portal.person_module.newContent(
......@@ -455,10 +374,13 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST)
query_string_param = parse_qsl(urlparse(str(ret)).query)
# For security reasons, the message should always be the same
self.assertIn("portal_status_message=An+email+has+been+sent+to+you.", str(ret))
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
# But no mail has been sent
self.stepCheckNoMailSent()
self.assertFalse(self.portal.MailHost.getMessageList())
def test_acquired_email_on_person(self):
organisation = self.portal.organisation_module.newContent(
......@@ -482,12 +404,10 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST)
query_string_param = parse_qsl(urlparse(str(ret)).query)
# For security reasons, the message should always be the same
self.assertIn("portal_status_message=An+email+has+been+sent+to+you.", str(ret))
self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.assertIn(("portal_status_level", "success"), query_string_param)
# But no mail has been sent
self.stepCheckNoMailSent()
self.assertFalse(self.portal.MailHost.getMessageList())
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestPasswordTool))
return suite
......@@ -3,7 +3,7 @@
"""
REQUEST = context.REQUEST
next_url = context.portal_password.changeUserPassword(password=REQUEST['password'],
password_confirmation=REQUEST['password_confirm'],
password_confirm=REQUEST['password_confirm'],
password_key=REQUEST['password_key'],
user_login=REQUEST.get('user_login', None),
REQUEST=REQUEST)
......
REQUEST = context.REQUEST
return context.portal_password.changeUserPassword(password=REQUEST['password'],
password_confirmation=REQUEST['password_confirm'],
password_confirm=REQUEST['password_confirm'],
password_key=REQUEST['password_key'],
user_login=REQUEST.get('user_login', None),
REQUEST=REQUEST)
......@@ -41,9 +41,9 @@ from BTrees.OOBTree import OOBTree
from six.moves.urllib.parse import urlencode
redirect_path = '/login_form'
def redirect(REQUEST, site_url, message):
def redirect(REQUEST, site_url, message, level):
if REQUEST is not None and getattr(REQUEST.RESPONSE, 'redirect', None) is not None:
parameter = urlencode({'portal_status_message': message})
parameter = urlencode({'portal_status_message': message, 'portal_status_level': level})
ret_url = '%s%s?%s' % (site_url, redirect_path, parameter)
return REQUEST.RESPONSE.redirect( ret_url )
else:
......@@ -171,10 +171,13 @@ class PasswordTool(BaseTool):
"User {user} does not have a valid email address".format(user=user_login)
)
if error_encountered:
# note that we intentionally use the same msg here regardless of whether the
# email was successfully sent or not in order not to leak information about user
# existence.
if batch:
raise RuntimeError(msg)
else:
return redirect(REQUEST, site_url, msg)
return redirect(REQUEST, site_url, msg, 'success')
key = self.getResetPasswordKey(user_login=user_login,
expiration_date=expiration_date)
......@@ -222,8 +225,7 @@ class PasswordTool(BaseTool):
message_text_format=message_text_format,
event_keyword_argument_dict=event_keyword_argument_dict)
if not batch:
return redirect(REQUEST, site_url,
translateString("An email has been sent to you."))
return redirect(REQUEST, site_url, msg, 'success')
security.declareProtected(Permissions.ModifyPortalContent, 'removeExpiredRequests')
def removeExpiredRequests(self):
......@@ -266,13 +268,12 @@ class PasswordTool(BaseTool):
"""
Reset the password for a given login
"""
# BBB: password_confirm: unused argument
def error(message):
# BBB: should "raise Redirect" instead of just returning, simplifying
# calling code and making mistakes more difficult
# BBB: should probably not translate message when REQUEST is None
message = translateString(message)
return redirect(REQUEST, site_url, message)
return redirect(REQUEST, site_url, message, 'error')
if REQUEST is None:
REQUEST = get_request()
......@@ -291,6 +292,8 @@ class PasswordTool(BaseTool):
if user_login is not None and register_user_login != user_login:
# XXX: not descriptive enough
return error("Bad login provided.")
if password_confirm is not None and password_confirm != password:
return error("Password does not match the confirm password.")
if DateTime() > expiration_date:
return error("Date has expired.")
del self._password_request_dict[password_key]
......@@ -303,6 +306,6 @@ class PasswordTool(BaseTool):
login = portal.unrestrictedTraverse(login_dict['path'])
login.setPassword(password) # this will raise if password does not match policy
return redirect(REQUEST, site_url,
translateString("Password changed."))
translateString("Password changed."), 'success')
InitializeClass(PasswordTool)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment