Commit 2f5d9aaa authored by Ivan Tyagov's avatar Ivan Tyagov

Allow showing a custom message what exactly is wrong with password.

Adjust test accordingly.
parent 8e4b1a28
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string encoding="cdata"><![CDATA[
"""\n
Form validator which will check if password is valid for the user.\n
"""\n
from Products.ERP5Type.Document import newTempBase\n
from Products.Formulator.Errors import ValidationError\n
\n
portal = context.getPortalObject()\n
\n
message_dict = { 0: \'Unknown error\',\n
-1: \'Too short.\',\n
-2: \'Not complex enough.\',\n
-3: \'You have changed your password too recently.\',\n
-4: \'You have already used this password.\',\n
-5: \'You can not use any parts of your first and last name in password.\'}\n
\n
def doValidation(person, password):\n
# raise so Formulator shows proper message\n
result = person.Person_isPasswordValid(password)\n
if result<=0:\n
message = context.Base_translateString(message_dict[result])\n
raise ValidationError(\'external_validator_failed\', context, error_text=message)\n
\n
# do only for authenticated members\n
if not portal.portal_membership.isAnonymousUser():\n
# find Person object (or authenticated member) and validate it on it (password recovered for an existing account)\n
user_login = request.get(\'field_user_login\', None)\n
person = context.ERP5Site_getAuthenticatedMemberPersonValue(user_login)\n
if person is not None:\n
return doValidation(person, password)\n
\n
# use a temp object (new account created)\n
first_name = request.get(\'field_your_first_name\', None) \n
last_name = request.get(\'field_your_last_name\', None) \n
kw = {\'title\': \'%s %s\' %(first_name, last_name),\n
\'first_name\': first_name,\n
\'last_name\': last_name}\n
person = newTempBase(portal, kw[\'title\'], **kw)\n
\n
return doValidation(person, password)\n
]]></string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>password, request</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isPasswordValid</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -53,21 +53,24 @@ ...@@ -53,21 +53,24 @@
<value> <string encoding="cdata"><![CDATA[ <value> <string encoding="cdata"><![CDATA[
"""\n """\n
Returns if password is valid or not.\n Returns if password is valid or not. \n
If not valid return a negative code to indicate failure.\n
"""\n """\n
import re\n from Products.Formulator.Errors import ValidationError\n
from DateTime import DateTime\n from DateTime import DateTime\n
import re\n
\n \n
MARKER = [\'\', None]\n MARKER = [\'\', None]\n
\n \n
portal = context.getPortalObject()\n portal = context.getPortalObject()\n
request = context.REQUEST\n
is_temp_object = context.isTempObject()\n is_temp_object = context.isTempObject()\n
min_password_length = portal.portal_preferences.getPreferredMinPasswordLength()\n min_password_length = portal.portal_preferences.getPreferredMinPasswordLength()\n
\n \n
# not long enough\n # not long enough\n
if min_password_length is not None:\n if min_password_length is not None:\n
if len(password) < min_password_length:\n if len(password) < min_password_length:\n
return 0\n return -1\n
\n \n
# password contain X out of following Y regular expression groups ?\n # password contain X out of following Y regular expression groups ?\n
regular_expression_list = portal.portal_preferences.getPreferredRegularExpressionGroupList()\n regular_expression_list = portal.portal_preferences.getPreferredRegularExpressionGroupList()\n
...@@ -81,7 +84,7 @@ if regular_expression_list:\n ...@@ -81,7 +84,7 @@ if regular_expression_list:\n
#context.log(\'%s %s %s %s\' %(password, group_counter, min_regular_expression_group_number, regular_expression_list))\n #context.log(\'%s %s %s %s\' %(password, group_counter, min_regular_expression_group_number, regular_expression_list))\n
if group_counter < min_regular_expression_group_number:\n if group_counter < min_regular_expression_group_number:\n
# not enough groups match\n # not enough groups match\n
return 0\n return -2\n
\n \n
if not is_temp_object:\n if not is_temp_object:\n
# not changed in last period ?\n # not changed in last period ?\n
...@@ -93,13 +96,13 @@ if not is_temp_object:\n ...@@ -93,13 +96,13 @@ if not is_temp_object:\n
min_password_lifetime_duration is not None and \\\n min_password_lifetime_duration is not None and \\\n
(last_password_modification_date + min_password_lifetime_duration*one_hour) > now:\n (last_password_modification_date + min_password_lifetime_duration*one_hour) > now:\n
# too early to change password\n # too early to change password\n
return 0\n return -3\n
\n \n
# not already used before ?\n # not already used before ?\n
preferred_number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck()\n preferred_number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck()\n
if preferred_number_of_last_password_to_check not in [None, 0]:\n if preferred_number_of_last_password_to_check not in [None, 0]:\n
if context.isPasswordAlreadyUsed(password):\n if context.isPasswordAlreadyUsed(password):\n
return 0\n return -4\n
\n \n
# not contain the full name of the user in password or any parts of it (i.e. last and / or first name)\n # not contain the full name of the user in password or any parts of it (i.e. last and / or first name)\n
if portal.portal_preferences.isPrefferedForceUsernameCheckInPassword():\n if portal.portal_preferences.isPrefferedForceUsernameCheckInPassword():\n
...@@ -121,7 +124,7 @@ if portal.portal_preferences.isPrefferedForceUsernameCheckInPassword():\n ...@@ -121,7 +124,7 @@ if portal.portal_preferences.isPrefferedForceUsernameCheckInPassword():\n
if (first_name not in MARKER and first_name in lower_password) or \\\n if (first_name not in MARKER and first_name in lower_password) or \\\n
(last_name not in MARKER and last_name in lower_password):\n (last_name not in MARKER and last_name in lower_password):\n
# user\'s name must not be contained in password\n # user\'s name must not be contained in password\n
return 0\n return -5\n
\n \n
return 1\n return 1\n
...@@ -130,7 +133,7 @@ return 1\n ...@@ -130,7 +133,7 @@ return 1\n
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>password</string> </value> <value> <string>password, request={}</string> </value>
</item> </item>
<item> <item>
<key> <string>_proxy_roles</string> </key> <key> <string>_proxy_roles</string> </key>
......
...@@ -93,6 +93,14 @@ return session[key]\n ...@@ -93,6 +93,14 @@ return session[key]\n
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_notifyLoginFailure</string> </value> <value> <string>Person_notifyLoginFailure</string> </value>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>"""External Validator for Person_viewDetails/my_password\n
checks that password and confimation matches.\n
"""\n
# XXX: unify\n
password_confirm = request.get(\'field_your_password\',\n
request.get(\'your_password\'))\n
\n
if password_confirm == editor :\n
return 1\n
return 0\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>editor, request</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Person_validatePasswordsMatch</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
6 7
\ No newline at end of file \ No newline at end of file
...@@ -218,8 +218,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -218,8 +218,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.stepTic() self.stepTic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordValid('')) self.assertEqual(-1, person.isPasswordValid(''))
self.assertFalse(person.isPasswordValid('1234567')) self.assertEqual(-1, person.isPasswordValid('1234567'))
self.assertTrue(person.isPasswordValid('12345678')) self.assertTrue(person.isPasswordValid('12345678'))
# not changed in last x days # not changed in last x days
...@@ -232,7 +232,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -232,7 +232,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
person.setPassword('12345678') person.setPassword('12345678')
self.stepTic() self.stepTic()
self.assertFalse(person.isPasswordValid('87654321')) # if we try to change now we should fail with any password self.assertEqual(-3, person.isPasswordValid('87654321')) # if we try to change now we should fail with any password
preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction
self.stepTic() self.stepTic()
...@@ -245,7 +245,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -245,7 +245,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
person.setPassword('12345678') person.setPassword('12345678')
self.stepTic() self.stepTic()
self.assertFalse(person.isPasswordValid('12345678')) # if we try to change now we should fail with this EXACT password self.assertEqual(-4, person.isPasswordValid('12345678')) # if we try to change now we should fail with this EXACT password
self.assertTrue(person.isPasswordValid('12345678_')) # it's OK with another one not used yet self.assertTrue(person.isPasswordValid('12345678_')) # it's OK with another one not used yet
for password in ['a','b','c','d', 'e']: for password in ['a','b','c','d', 'e']:
person.setPassword(password) person.setPassword(password)
...@@ -255,9 +255,9 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -255,9 +255,9 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(person.isPasswordValid('a')) self.assertTrue(person.isPasswordValid('a'))
self.assertTrue(person.isPasswordValid('b')) self.assertTrue(person.isPasswordValid('b'))
# only last 3 (including current one are invalid) # only last 3 (including current one are invalid)
self.assertFalse(person.isPasswordValid('c')) self.assertEqual(-4, person.isPasswordValid('c'))
self.assertFalse(person.isPasswordValid('d')) self.assertEqual(-4, person.isPasswordValid('d'))
self.assertFalse(person.isPasswordValid('e')) self.assertEqual(-4, person.isPasswordValid('e'))
# if we remove restricted then all password are usable # if we remove restricted then all password are usable
preference.setPreferredNumberOfLastPasswordToCheck(None) preference.setPreferredNumberOfLastPasswordToCheck(None)
...@@ -274,7 +274,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -274,7 +274,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.stepTic() self.stepTic()
self.assertTrue(person.isPasswordValid('c')) self.assertTrue(person.isPasswordValid('c'))
self.assertTrue(person.isPasswordValid('d')) self.assertTrue(person.isPasswordValid('d'))
self.assertFalse(person.isPasswordValid('e')) self.assertEqual(-4, person.isPasswordValid('e'))
preference.setPreferredRegularExpressionGroupList(regular_expression_list) preference.setPreferredRegularExpressionGroupList(regular_expression_list)
preference.setPreferredMinPasswordLength(7) preference.setPreferredMinPasswordLength(7)
...@@ -295,7 +295,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -295,7 +295,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
for password in four_group_password_list: for password in four_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(person.isPasswordValid(password))
for password in three_group_password_list+two_group_password_list + one_group_password_list: for password in three_group_password_list+two_group_password_list + one_group_password_list:
self.assertFalse(person.isPasswordValid(password)) self.assertEqual(-2, person.isPasswordValid(password))
# min 3 out of all groups # min 3 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(3) preference.setPreferredMinRegularExpressionGroupNumber(3)
...@@ -305,7 +305,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -305,7 +305,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
for password in four_group_password_list + three_group_password_list: for password in four_group_password_list + three_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(person.isPasswordValid(password))
for password in two_group_password_list + one_group_password_list: for password in two_group_password_list + one_group_password_list:
self.assertFalse(person.isPasswordValid(password)) self.assertEqual(-2, person.isPasswordValid(password))
# min 2 out of all groups # min 2 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(2) preference.setPreferredMinRegularExpressionGroupNumber(2)
...@@ -314,7 +314,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -314,7 +314,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
for password in four_group_password_list + three_group_password_list + two_group_password_list: for password in four_group_password_list + three_group_password_list + two_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(person.isPasswordValid(password))
for password in one_group_password_list: for password in one_group_password_list:
self.assertFalse(person.isPasswordValid(password)) self.assertEqual(-2, person.isPasswordValid(password))
# min 1 out of all groups # min 1 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(1) preference.setPreferredMinRegularExpressionGroupNumber(1)
...@@ -327,8 +327,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -327,8 +327,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache() self._clearCache()
self.stepTic() self.stepTic()
self.assertFalse(person.isPasswordValid('abAB#12_%s' %person.getFirstName())) self.assertEqual(-5, person.isPasswordValid('abAB#12_%s' %person.getFirstName()))
self.assertFalse(person.isPasswordValid('abAB#12_%s' %person.getLastName())) self.assertEqual(-5, person.isPasswordValid('abAB#12_%s' %person.getLastName()))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
self.stepTic() self.stepTic()
...@@ -348,7 +348,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -348,7 +348,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.stepTic() self.stepTic()
# in this case which is basically used in new account creation only length of password matters # in this case which is basically used in new account creation only length of password matters
self.assertFalse(temp_person.Person_isPasswordValid('onlyNine1')) self.assertEqual(-1, temp_person.Person_isPasswordValid('onlyNine1'))
self.assertTrue(temp_person.Person_isPasswordValid('longEnough1')) self.assertTrue(temp_person.Person_isPasswordValid('longEnough1'))
# make sure re check works on temp as well ( i.e. min 3 out of all groups) # make sure re check works on temp as well ( i.e. min 3 out of all groups)
...@@ -360,14 +360,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -360,14 +360,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
for password in four_group_password_list + three_group_password_list: for password in four_group_password_list + three_group_password_list:
self.assertTrue(temp_person.Person_isPasswordValid(password)) self.assertTrue(temp_person.Person_isPasswordValid(password))
for password in two_group_password_list + one_group_password_list: for password in two_group_password_list + one_group_password_list:
self.assertFalse(temp_person.Person_isPasswordValid(password)) self.assertEqual(-2, temp_person.Person_isPasswordValid(password))
# make sure peron's check on username works on temp as well (i.e. not contain the full name of the user) # make sure peron's check on username works on temp as well (i.e. not contain the full name of the user)
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache() self._clearCache()
self.stepTic() self.stepTic()
self.assertFalse(temp_person.Person_isPasswordValid('abAB#12_%s' %first_name)) self.assertEqual(-5, temp_person.Person_isPasswordValid('abAB#12_%s' %first_name))
self.assertFalse(temp_person.Person_isPasswordValid('abAB#12_%s' %last_name)) self.assertEqual(-5, temp_person.Person_isPasswordValid('abAB#12_%s' %last_name))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
......
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