diff --git a/product/ERP5/Tool/PasswordTool.py b/product/ERP5/Tool/PasswordTool.py index ac0576ee7a4d189c8f6d80ed759de9c9ff387d0a..36f514aff6131623b9f98cf338507a6f9291c0a2 100644 --- a/product/ERP5/Tool/PasswordTool.py +++ b/product/ERP5/Tool/PasswordTool.py @@ -33,12 +33,12 @@ from Globals import InitializeClass, DTMLFile, get_request from Products.ERP5Type.Tool.BaseTool import BaseTool from Products.ERP5Type import Permissions from Products.ERP5 import _dtmldir -from zLOG import LOG +from zLOG import LOG, INFO import time, random, md5 from DateTime import DateTime from Products.ERP5Type.Message import translateString from Acquisition import aq_base -from BTrees.OOBTree import OOBTree +from Globals import PersistentMapping class PasswordTool(BaseTool): """ @@ -61,7 +61,7 @@ class PasswordTool(BaseTool): password_request_dict = {} def __init__(self): - self.password_request_dict = OOBTree() + self.password_request_dict = PersistentMapping() def mailPasswordResetRequest(self, user_login=None, REQUEST=None): """ @@ -88,8 +88,17 @@ class PasswordTool(BaseTool): url = "%s/portal_password/resetPassword?reset_key=%s" %(self.getPortalObject().absolute_url() , random_url) # generate expiration date expiration_date = DateTime() + self._expiration_day + + # XXX before r26093, password_request_dict was initialized by an OOBTree and + # replaced by a dict on each request, so if it's data structure is not up + # to date, we update it if needed + if not isinstance(self.password_request_dict, PersistentMapping): + LOG('ERP5.PasswordTool', INFO, 'Updating password_request_dict to' + ' PersistentMapping') + self.password_request_dict = PersistentMapping() + # register request - self.password_request_dict = {random_url : (user_login, expiration_date)} + self.password_request_dict[random_url] = (user_login, expiration_date) # send mail subject = "[%s] Reset of your password" %(self.getPortalObject().getTitle()) diff --git a/product/ERP5/tests/testPasswordTool.py b/product/ERP5/tests/testPasswordTool.py index d265dd18bc3dbd4e4a46b8e8d6da291e5cde1ad5..d73cb9c9225d9222dc6498d791e69fed4690df02 100644 --- a/product/ERP5/tests/testPasswordTool.py +++ b/product/ERP5/tests/testPasswordTool.py @@ -27,6 +27,7 @@ ############################################################################## import unittest +import transaction from Testing import ZopeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase @@ -337,6 +338,64 @@ class TestPasswordTool(ERP5TypeTestCase): sequence_list.addSequenceString(sequence_string) sequence_list.play(self, quiet=quiet) + def test_two_concurrent_password_reset(self): + personA = self.portal.person_module.newContent(portal_type="Person", + reference="userA", + password="passwordA", + default_email_text="userA@example.invalid") + assignment = personA.newContent(portal_type='Assignment') + assignment.open() + + personB = self.portal.person_module.newContent(portal_type="Person", + reference="userB", + password="passwordB", + default_email_text="userB@example.invalid") + assignment = personB.newContent(portal_type='Assignment') + assignment.open() + transaction.commit() + self.tic() + + self._assertUserExists('userA', 'passwordA') + self._assertUserExists('userB', 'passwordB') + + self.assertEquals(0, len(self.portal.portal_password.password_request_dict)) + self.portal.portal_password.mailPasswordResetRequest(user_login="userA") + self.assertEquals(1, len(self.portal.portal_password.password_request_dict)) + key_a = self.portal.portal_password.password_request_dict.keys()[0] + transaction.commit() + self.tic() + + self.portal.portal_password.mailPasswordResetRequest(user_login="userB") + possible_key_list =\ + self.portal.portal_password.password_request_dict.keys() + self.assertEquals(2, len(possible_key_list)) + key_b = [k for k in possible_key_list if k != key_a][0] + transaction.commit() + self.tic() + + self._assertUserExists('userA', 'passwordA') + self._assertUserExists('userB', 'passwordB') + + self.portal.portal_password.changeUserPassword(user_login="userA", + password="newA", + password_confirmation="newA", + password_key=key_a) + transaction.commit() + self.tic() + + self._assertUserExists('userA', 'newA') + self._assertUserExists('userB', 'passwordB') + + self.portal.portal_password.changeUserPassword(user_login="userB", + password="newB", + password_confirmation="newB", + password_key=key_b) + transaction.commit() + self.tic() + + self._assertUserExists('userA', 'newA') + self._assertUserExists('userB', 'newB') + class TestPasswordToolWithCRM(TestPasswordTool): """