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 !1792
parents 8cc780c1 05a3390b
...@@ -808,7 +808,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -808,7 +808,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference = self.portal.portal_catalog.getResultValue( preference = self.portal.portal_catalog.getResultValue(
portal_type='System Preference', portal_type='System Preference',
title='Authentication',) 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 # as a way to check that password reset checks are done in the
# context of the login # context of the login
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
...@@ -856,8 +856,11 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -856,8 +856,11 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
# now with a password complying to the policy # now with a password complying to the policy
ret = submit_reset_password_dialog('ok') ret = submit_reset_password_dialog('ok')
self.assertEqual(httplib.FOUND, ret.getStatus()) self.assertEqual(httplib.FOUND, ret.getStatus())
self.assertTrue(ret.getHeader('Location').endswith( redirect_url = urlparse.urlparse(ret.getHeader("Location"))
'/login_form?portal_status_message=Password+changed.')) 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): def test_PreferenceTool_changePassword_checks_policy(self):
person = self.createUser(self.id(), password='current') person = self.createUser(self.id(), password='current')
...@@ -918,7 +921,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -918,7 +921,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
# long enough password is accepted # long enough password is accepted
ret = submit_change_password_dialog('long_enough_password') 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(httplib.FOUND, ret.getStatus())
self.assertEqual(self.portal.portal_preferences.absolute_url(), self.assertEqual(self.portal.portal_preferences.absolute_url(),
ret.getHeader("Location")) ret.getHeader("Location"))
......
...@@ -27,12 +27,13 @@ ...@@ -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.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.Sequence import SequenceList
from DateTime import DateTime from DateTime import DateTime
class TestPasswordTool(ERP5TypeTestCase): class TestPasswordTool(ERP5TypeTestCase):
""" """
Test reset of password Test reset of password
...@@ -47,6 +48,22 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -47,6 +48,22 @@ class TestPasswordTool(ERP5TypeTestCase):
self.portal.email_from_address = 'site@example.invalid' self.portal.email_from_address = 'site@example.invalid'
self.portal.MailHost.reset() self.portal.MailHost.reset()
self.portal.portal_caches.clearAllCache() 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): def beforeTearDown(self):
self.abort() self.abort()
...@@ -92,275 +109,175 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -92,275 +109,175 @@ class TestPasswordTool(ERP5TypeTestCase):
"Plugin %s should not have authenticated '%s' with password '%s'" % "Plugin %s should not have authenticated '%s' with password '%s'" %
(plugin_name, login, password)) (plugin_name, login, password))
def stepAddUser(self, sequence=None, sequence_list=None, **kw): def test_password_reset(self):
""" self._assertUserExists('userA-login', 'userA-password')
Create a user self._assertUserDoesNotExists('userA-login', 'bad')
""" ret = self.portal.portal_password.mailPasswordResetRequest(
person = self.portal.person_module.newContent(portal_type="Person", user_login='userA-login', REQUEST=self.portal.REQUEST)
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 stepCheckNoMailSent(self, sequence=None, sequence_list=None, **kw): query_string_param = parse_qsl(urlparse(str(ret)).query)
""" self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
Check mail has not been sent after fill in wrong the form password self.assertIn(("portal_status_level", "success"), query_string_param)
""" self.tic()
last_message = self.portal.MailHost._last_message
self.assertEqual((), last_message)
def stepCheckMailSent(self, sequence=None, sequence_list=None, **kw): (mfrom, mto, mbody), = self.portal.MailHost.getMessageList()
"""
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
self.assertEqual('Portal Administrator <site@example.invalid>', mfrom) self.assertEqual('Portal Administrator <site@example.invalid>', mfrom)
self.assertEqual(['userA@example.invalid'], mto) 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): def test_password_reset_request_for_non_existing_user(self):
""" ret = self.portal.portal_password.mailPasswordResetRequest(
Call method that change the password user_login='not exist', REQUEST=self.portal.REQUEST)
We don't check use of random url in mail here as we have on request query_string_param = parse_qsl(urlparse(str(ret)).query)
But random is also check by changeUserPassword, so it's the same self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
""" self.assertIn(("portal_status_level", "success"), query_string_param)
key = self.portal.portal_password._password_request_dict.keys()[0] self.tic()
self.portal.portal_password.changeUserPassword(user_login="userA-login", self.assertFalse(self.portal.MailHost.getMessageList())
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 stepGoToRandomAddressTwice(self, sequence=None, sequence_list=None, **kw): def test_password_reset_request_for_wildcard_username(self):
""" ret = self.portal.portal_password.mailPasswordResetRequest(
As we already change password, this must npot work anylonger user_login='%', REQUEST=self.portal.REQUEST)
""" query_string_param = parse_qsl(urlparse(str(ret)).query)
key = sequence.get('key') self.assertIn(("portal_status_message", "An email has been sent to you."), query_string_param)
self.portal.portal_password.changeUserPassword(user_login="userA-login", self.assertIn(("portal_status_level", "success"), query_string_param)
password="passwordA", self.tic()
password_confirmation="passwordA", self.assertFalse(self.portal.MailHost.getMessageList())
password_key=key)
# reset cache def test_password_reset_request_for_different_user(self):
self.portal.portal_caches.clearAllCache() 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): def test_password_reset_date_expired(self):
""" self.portal.portal_password.mailPasswordResetRequest(user_login='userA-login')
Try to reset a password with bad random part (reset_key, (login, date)), = list(six.iteritems(
""" self.portal.portal_password._password_request_dict))
self.portal.portal_password.changeUserPassword(user_login="userA-login", self.assertTrue(date.isFuture())
password="secret", self.portal.portal_password._password_request_dict[reset_key] = (
password_confirmation="secret", login,
password_key="toto") DateTime() - 1
# reset cache )
self.portal.portal_caches.clearAllCache() 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() self.portal.portal_password.removeExpiredRequests()
self.assertFalse(list(six.iterkeys(
def stepCheckNoRequestRemains(self, sequence=None, sequence_list=None, **kw): self.portal.portal_password._password_request_dict)))
"""
after alarm all expired request must have been removed def test_password_reset_password_and_confirmation_do_not_match(self):
""" self.portal.portal_password.mailPasswordResetRequest(
self.assertEqual(len(self.portal.portal_password._password_request_dict), 0) user_login='userA-login', REQUEST=self.portal.REQUEST)
reset_key, = list(six.iterkeys(
def stepLogout(self, sequence=None, sequence_list=None, **kw): self.portal.portal_password._password_request_dict))
"""
Logout ret = self.portal.portal_password.changeUserPassword(
""" user_login="userA-login",
self.logout() password="new-password",
password_confirm="wrong-password",
# tests password_key=reset_key)
def test_01_checkPasswordTool(self): query_string_param = parse_qsl(urlparse(str(ret)).query)
sequence_list = SequenceList() self.assertIn(("portal_status_message", "Password does not match the confirm password."), query_string_param)
sequence_string = 'CheckPasswordToolExists ' \ self.assertIn(("portal_status_level", "error"), query_string_param)
'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)
def test_two_concurrent_password_reset(self): def test_two_concurrent_password_reset(self):
personA = self.portal.person_module.newContent(portal_type="Person", self._createUser('userB')
reference="userA", self._assertUserExists('userA-login', 'userA-password')
default_email_text="userA@example.invalid") self._assertUserExists('userB-login', 'userB-password')
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.assertEqual(0, len(self.portal.portal_password._password_request_dict)) self.assertEqual(0, len(self.portal.portal_password._password_request_dict))
self.portal.portal_password.mailPasswordResetRequest(user_login="userA-login") self.portal.portal_password.mailPasswordResetRequest(user_login="userA-login")
self.assertEqual(1, len(self.portal.portal_password._password_request_dict)) 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.tic()
self.portal.portal_password.mailPasswordResetRequest(user_login="userB-login") self.portal.portal_password.mailPasswordResetRequest(user_login="userB-login")
possible_key_list =\ possible_key_list = \
self.portal.portal_password._password_request_dict.keys() list(six.iterkeys(self.portal.portal_password._password_request_dict))
self.assertEqual(2, len(possible_key_list)) self.assertEqual(2, len(possible_key_list))
key_b = [k for k in possible_key_list if k != key_a][0] key_b = [k for k in possible_key_list if k != key_a][0]
self.tic() self.tic()
self._assertUserExists('userA-login', 'passwordA') self._assertUserExists('userA-login', 'userA-password')
self._assertUserExists('userB-login', 'passwordB') self._assertUserExists('userB-login', 'userB-password')
self.portal.portal_password.changeUserPassword(user_login="userA-login", self.portal.portal_password.changeUserPassword(user_login="userA-login",
password="newA", password="newA",
password_confirmation="newA", password_confirm="newA",
password_key=key_a) password_key=key_a)
self.tic() self.tic()
self._assertUserExists('userA-login', 'newA') 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", self.portal.portal_password.changeUserPassword(user_login="userB-login",
password="newB", password="newB",
password_confirmation="newB", password_confirm="newB",
password_key=key_b) password_key=key_b)
self.tic() self.tic()
...@@ -390,8 +307,7 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -390,8 +307,7 @@ class TestPasswordTool(ERP5TypeTestCase):
self.assertEqual(0, len(self.portal.portal_password._password_request_dict)) self.assertEqual(0, len(self.portal.portal_password._password_request_dict))
self.portal.portal_password.mailPasswordResetRequest(user_login="userZ-login ") self.portal.portal_password.mailPasswordResetRequest(user_login="userZ-login ")
self.assertEqual(1, len(self.portal.portal_password._password_request_dict)) self.assertEqual(1, len(self.portal.portal_password._password_request_dict))
key_a, = list(six.iterkeys(self.portal.portal_password._password_request_dict))
key_a = self.portal.portal_password._password_request_dict.keys()[0]
self.tic() self.tic()
self._assertUserExists('userZ-login ', 'passwordZ') self._assertUserExists('userZ-login ', 'passwordZ')
...@@ -399,7 +315,7 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -399,7 +315,7 @@ class TestPasswordTool(ERP5TypeTestCase):
# Check that password is not changed if trailing space is not entered # Check that password is not changed if trailing space is not entered
self.portal.portal_password.changeUserPassword(user_login="userZ-login", self.portal.portal_password.changeUserPassword(user_login="userZ-login",
password="newZ", password="newZ",
password_confirmation="newZ", password_confirm="newZ",
password_key=key_a) password_key=key_a)
self.tic() self.tic()
self._assertUserExists('userZ-login ', 'passwordZ') self._assertUserExists('userZ-login ', 'passwordZ')
...@@ -407,7 +323,7 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -407,7 +323,7 @@ class TestPasswordTool(ERP5TypeTestCase):
# Check that password is changed if trailing space is entered # Check that password is changed if trailing space is entered
self.portal.portal_password.changeUserPassword(user_login="userZ-login ", self.portal.portal_password.changeUserPassword(user_login="userZ-login ",
password="newZ2", password="newZ2",
password_confirmation="newZ2", password_confirm="newZ2",
password_key=key_a) password_key=key_a)
self.tic() self.tic()
self._assertUserExists('userZ-login ', 'newZ2') self._assertUserExists('userZ-login ', 'newZ2')
...@@ -429,10 +345,13 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -429,10 +345,13 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest( ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST) 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 # 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 # But no mail has been sent
self.stepCheckNoMailSent() self.assertFalse(self.portal.MailHost.getMessageList())
def test_unreachable_email_on_person(self): def test_unreachable_email_on_person(self):
person = self.portal.person_module.newContent( person = self.portal.person_module.newContent(
...@@ -455,10 +374,13 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -455,10 +374,13 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest( ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST) 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 # 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 # But no mail has been sent
self.stepCheckNoMailSent() self.assertFalse(self.portal.MailHost.getMessageList())
def test_acquired_email_on_person(self): def test_acquired_email_on_person(self):
organisation = self.portal.organisation_module.newContent( organisation = self.portal.organisation_module.newContent(
...@@ -482,12 +404,10 @@ class TestPasswordTool(ERP5TypeTestCase): ...@@ -482,12 +404,10 @@ class TestPasswordTool(ERP5TypeTestCase):
ret = self.portal.portal_password.mailPasswordResetRequest( ret = self.portal.portal_password.mailPasswordResetRequest(
user_login='user-login', REQUEST=self.portal.REQUEST) 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 # 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 # 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 @@ ...@@ -3,7 +3,7 @@
""" """
REQUEST = context.REQUEST REQUEST = context.REQUEST
next_url = context.portal_password.changeUserPassword(password=REQUEST['password'], next_url = context.portal_password.changeUserPassword(password=REQUEST['password'],
password_confirmation=REQUEST['password_confirm'], password_confirm=REQUEST['password_confirm'],
password_key=REQUEST['password_key'], password_key=REQUEST['password_key'],
user_login=REQUEST.get('user_login', None), user_login=REQUEST.get('user_login', None),
REQUEST=REQUEST) REQUEST=REQUEST)
......
REQUEST = context.REQUEST REQUEST = context.REQUEST
return context.portal_password.changeUserPassword(password=REQUEST['password'], return context.portal_password.changeUserPassword(password=REQUEST['password'],
password_confirmation=REQUEST['password_confirm'], password_confirm=REQUEST['password_confirm'],
password_key=REQUEST['password_key'], password_key=REQUEST['password_key'],
user_login=REQUEST.get('user_login', None), user_login=REQUEST.get('user_login', None),
REQUEST=REQUEST) REQUEST=REQUEST)
...@@ -41,9 +41,9 @@ from BTrees.OOBTree import OOBTree ...@@ -41,9 +41,9 @@ from BTrees.OOBTree import OOBTree
from six.moves.urllib.parse import urlencode from six.moves.urllib.parse import urlencode
redirect_path = '/login_form' 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: 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) ret_url = '%s%s?%s' % (site_url, redirect_path, parameter)
return REQUEST.RESPONSE.redirect( ret_url ) return REQUEST.RESPONSE.redirect( ret_url )
else: else:
...@@ -171,10 +171,13 @@ class PasswordTool(BaseTool): ...@@ -171,10 +171,13 @@ class PasswordTool(BaseTool):
"User {user} does not have a valid email address".format(user=user_login) "User {user} does not have a valid email address".format(user=user_login)
) )
if error_encountered: 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: if batch:
raise RuntimeError(msg) raise RuntimeError(msg)
else: else:
return redirect(REQUEST, site_url, msg) return redirect(REQUEST, site_url, msg, 'success')
key = self.getResetPasswordKey(user_login=user_login, key = self.getResetPasswordKey(user_login=user_login,
expiration_date=expiration_date) expiration_date=expiration_date)
...@@ -222,8 +225,7 @@ class PasswordTool(BaseTool): ...@@ -222,8 +225,7 @@ class PasswordTool(BaseTool):
message_text_format=message_text_format, message_text_format=message_text_format,
event_keyword_argument_dict=event_keyword_argument_dict) event_keyword_argument_dict=event_keyword_argument_dict)
if not batch: if not batch:
return redirect(REQUEST, site_url, return redirect(REQUEST, site_url, msg, 'success')
translateString("An email has been sent to you."))
security.declareProtected(Permissions.ModifyPortalContent, 'removeExpiredRequests') security.declareProtected(Permissions.ModifyPortalContent, 'removeExpiredRequests')
def removeExpiredRequests(self): def removeExpiredRequests(self):
...@@ -266,13 +268,12 @@ class PasswordTool(BaseTool): ...@@ -266,13 +268,12 @@ class PasswordTool(BaseTool):
""" """
Reset the password for a given login Reset the password for a given login
""" """
# BBB: password_confirm: unused argument
def error(message): def error(message):
# BBB: should "raise Redirect" instead of just returning, simplifying # BBB: should "raise Redirect" instead of just returning, simplifying
# calling code and making mistakes more difficult # calling code and making mistakes more difficult
# BBB: should probably not translate message when REQUEST is None # BBB: should probably not translate message when REQUEST is None
message = translateString(message) message = translateString(message)
return redirect(REQUEST, site_url, message) return redirect(REQUEST, site_url, message, 'error')
if REQUEST is None: if REQUEST is None:
REQUEST = get_request() REQUEST = get_request()
...@@ -291,6 +292,8 @@ class PasswordTool(BaseTool): ...@@ -291,6 +292,8 @@ class PasswordTool(BaseTool):
if user_login is not None and register_user_login != user_login: if user_login is not None and register_user_login != user_login:
# XXX: not descriptive enough # XXX: not descriptive enough
return error("Bad login provided.") 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: if DateTime() > expiration_date:
return error("Date has expired.") return error("Date has expired.")
del self._password_request_dict[password_key] del self._password_request_dict[password_key]
...@@ -303,6 +306,6 @@ class PasswordTool(BaseTool): ...@@ -303,6 +306,6 @@ class PasswordTool(BaseTool):
login = portal.unrestrictedTraverse(login_dict['path']) login = portal.unrestrictedTraverse(login_dict['path'])
login.setPassword(password) # this will raise if password does not match policy login.setPassword(password) # this will raise if password does not match policy
return redirect(REQUEST, site_url, return redirect(REQUEST, site_url,
translateString("Password changed.")) translateString("Password changed."), 'success')
InitializeClass(PasswordTool) 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