Commit cd9feb3c authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki Committed by Vincent Pelletier

erp5_authentication_policy: migrate to ERP5 Login authentication.

parent 5ae11aeb
<workflow_chain> <workflow_chain>
<chain> <chain>
<type>Person</type> <type>ERP5 Login</type>
<workflow>password_interaction_workflow</workflow> <workflow>password_interaction_workflow</workflow>
</chain> </chain>
</workflow_chain> </workflow_chain>
\ No newline at end of file
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_analyzePassword</string> </value> <value> <string>Login_analyzePassword</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_getListboxUrl</string> </value> <value> <string>Login_getListboxUrl</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_isLoginBlocked</string> </value> <value> <string>Login_isLoginBlocked</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_isPasswordExpired</string> </value> <value> <string>Login_isPasswordExpired</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -13,27 +13,13 @@ message_dict = { 0: 'Unknown error', ...@@ -13,27 +13,13 @@ message_dict = { 0: 'Unknown error',
-4: 'You have already used this password.', -4: 'You have already used this password.',
-5: 'You can not use any parts of your first and last name in password.'} -5: 'You can not use any parts of your first and last name in password.'}
def doValidation(person, password): def doValidation(login, password):
# raise so Formulator shows proper message # raise so Formulator shows proper message
result_code_list = person.Person_analyzePassword(password) result_code_list = login.analyzePassword(password)
if result_code_list!=[]: if result_code_list!=[]:
translateString = context.Base_translateString translateString = context.Base_translateString
message = ' '.join([translateString(message_dict[x]) for x in result_code_list]) message = ' '.join([translateString(message_dict[x]) for x in result_code_list])
raise ValidationError('external_validator_failed', context, error_text=message) raise ValidationError('external_validator_failed', context, error_text=message)
return 1 return 1
user_login = request.get('field_user_login', None) return doValidation(context, password)
# find Person object (or authenticated member) and validate it on it (password recovered for an existing account)
person = context.ERP5Site_getAuthenticatedMemberPersonValue(user_login)
if person is not None:
return doValidation(person, password)
# use a temp object (new account created)
first_name = request.get('field_your_first_name', None)
last_name = request.get('field_your_last_name', None)
kw = {'title': '%s %s' %(first_name, last_name),
'first_name': first_name,
'last_name': last_name}
person = newTempBase(portal, kw['title'], **kw)
return doValidation(person, password)
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Base_isPasswordValid</string> </value> <value> <string>Login_isPasswordValid</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_notifyLoginFailure</string> </value> <value> <string>Login_notifyLoginFailure</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_notifyPasswordExpire</string> </value> <value> <string>Login_notifyPasswordExpire</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_unblockLogin</string> </value> <value> <string>Login_unblockLogin</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -174,15 +174,15 @@ ...@@ -174,15 +174,15 @@
<list> <list>
<tuple> <tuple>
<string>title</string> <string>title</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
<tuple> <tuple>
<string>reference</string> <string>reference</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
<tuple> <tuple>
<string>count</string> <string>count</string>
<string>Person_getListboxUrl</string> <string>Login_getListboxUrl</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
<key> <string>after_script_name</string> </key> <key> <string>after_script_name</string> </key>
<value> <value>
<list> <list>
<string>Person_changePassword</string> <string>afterChangePassword</string>
</list> </list>
</value> </value>
</item> </item>
...@@ -72,10 +72,16 @@ ...@@ -72,10 +72,16 @@
<key> <string>portal_type_filter</string> </key> <key> <string>portal_type_filter</string> </key>
<value> <value>
<list> <list>
<string>Person</string> <string>ERP5 Login</string>
</list> </list>
</value> </value>
</item> </item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<none/>
</value>
</item>
<item> <item>
<key> <string>script_name</string> </key> <key> <string>script_name</string> </key>
<value> <value>
......
from DateTime import DateTime login = state_change['object']
portal = context.getPortalObject() portal = login.getPortalObject()
person = state_change['object']
# check preferences and save only if set # check preferences and save only if set
number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck() number_of_last_password_to_check = portal.portal_preferences.getPreferredNumberOfLastPasswordToCheck()
if number_of_last_password_to_check is not None and number_of_last_password_to_check: if number_of_last_password_to_check is not None and number_of_last_password_to_check:
# save password and modification date # save password and modification date
current_password = person.getPassword() current_password = login.getPassword()
if current_password is not None: if current_password is not None:
password_event = portal.system_event_module.newContent(portal_type = 'Password Event', password_event = portal.system_event_module.newContent(portal_type='Password Event',
source_value = person, source_value=login,
destination_value = person, destination_value=login,
password = current_password) password=current_password)
password_event.confirm() password_event.confirm()
# Person_isPasswordExpired cache the wrong result if document is not in catalog. # Person_isPasswordExpired cache the wrong result if document is not in catalog.
# As the document is created in the same transaction, it is possible to force reindexation # As the document is created in the same transaction, it is possible to force reindexation
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Person_changePassword</string> </value> <value> <string>afterChangePassword</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
Person | password_interaction_workflow ERP5 Login | password_interaction_workflow
\ No newline at end of file \ No newline at end of file
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<value> <value>
<list> <list>
<tuple> <tuple>
<string>Person</string> <string>ERP5 Login</string>
<string>Person</string> <string>ERP5 Login</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<value> <value>
<list> <list>
<tuple> <tuple>
<string>Person</string> <string>ERP5 Login</string>
<string>Person</string> <string>ERP5 Login</string>
</tuple> </tuple>
</list> </list>
</value> </value>
......
...@@ -66,10 +66,12 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -66,10 +66,12 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
reference = 'test') reference = 'test')
if portal.portal_catalog.getResultValue(**kw) is None: if portal.portal_catalog.getResultValue(**kw) is None:
# add a loggable Person # add a loggable Person
person = portal.person_module.newContent(password = 'test', person = self.createUser(
first_name = 'First', kw['reference'],
last_name = 'Last', password='test',
**kw) person_kw={'first_name': 'First',
'last_name': 'Last'},
)
person.validate() person.validate()
assignment = person.newContent(portal_type = 'Assignment') assignment = person.newContent(portal_type = 'Assignment')
assignment.open() assignment.open()
...@@ -91,16 +93,33 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -91,16 +93,33 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
'erp5_content_short', # for authentication cache 'erp5_content_short', # for authentication cache
)) ))
def _getPasswordEventList(self, person): def _getPasswordEventList(self, login):
return [x.getObject() for x in self.portal.portal_catalog( return [x.getObject() for x in self.portal.portal_catalog(
portal_type = 'Password Event', portal_type = 'Password Event',
default_destination_uid = person.getUid(), default_destination_uid = login.getUid(),
sort_on = (('creation_date', 'DESC',),))] sort_on = (('creation_date', 'DESC',),))]
def _cleanUpPerson(self, person): def _cleanUpLogin(self, login):
self.portal.system_event_module.manage_delObjects([x.getId() for x in self._getPasswordEventList(person)]) self.portal.system_event_module.manage_delObjects([x.getId() for x in self._getPasswordEventList(login)])
def createUser(self, reference, password=None, person_kw=None):
"""
Modified version from ERP5TypeTestCase, that does set reference as
password when password is None.
"""
if person_kw is None:
person_kw = {}
person = self.portal.person_module.newContent(portal_type='Person',
reference=reference,
**person_kw)
login = person.newContent(portal_type='ERP5 Login',
reference=reference,
password=password)
login.validate()
return person
def test_01_BlockLogin(self): def test_01_BlockLogin(self):
""" """
Test that a recataloging works for Web Site documents Test that a recataloging works for Web Site documents
...@@ -110,73 +129,74 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -110,73 +129,74 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = portal.portal_catalog.getResultValue(portal_type = 'Person',
reference = 'test') reference = 'test')
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
# login should be allowed # login should be allowed
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# file some failures so we should detect and block account # file some failures so we should detect and block account
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.tic() self.tic()
# should be blocked # should be blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set check back interval to actualy disable blocking # set check back interval to actualy disable blocking
preference.setPreferredAuthenticationFailureCheckDuration(0) preference.setPreferredAuthenticationFailureCheckDuration(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
time.sleep(1) # we need to give a moment time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# .. and revert it back # .. and revert it back
preference.setPreferredAuthenticationFailureCheckDuration(600) preference.setPreferredAuthenticationFailureCheckDuration(600)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# increase failures attempts # increase failures attempts
preference.setPreferredMaxAuthenticationFailure(4) preference.setPreferredMaxAuthenticationFailure(4)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# .. and revert it back # .. and revert it back
preference.setPreferredMaxAuthenticationFailure(3) preference.setPreferredMaxAuthenticationFailure(3)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set short block interval so we can test it as well # set short block interval so we can test it as well
preference.setPreferredAuthenticationFailureBlockDuration(3) preference.setPreferredAuthenticationFailureBlockDuration(3)
self._clearCache() self._clearCache()
self.tic() self.tic()
time.sleep(4) time.sleep(4)
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# test multiple concurrent transactions without waiting for activities to be over # test multiple concurrent transactions without waiting for activities to be over
preference.setPreferredAuthenticationFailureCheckDuration(600) preference.setPreferredAuthenticationFailureCheckDuration(600)
preference.setPreferredAuthenticationFailureBlockDuration(600) preference.setPreferredAuthenticationFailureBlockDuration(600)
preference.setPreferredMaxAuthenticationFailure(3) preference.setPreferredMaxAuthenticationFailure(3)
person.Person_unblockLogin() login.Login_unblockLogin()
self._clearCache() self._clearCache()
self.tic() self.tic()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.commit() self.commit()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
self.tic() self.tic()
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# test unblock account # test unblock account
person.Person_unblockLogin() login.Login_unblockLogin()
self.tic() self.tic()
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
def test_02_PasswordHistory(self): def test_02_PasswordHistory(self):
...@@ -186,61 +206,61 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -186,61 +206,61 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
portal = self.getPortal() portal = self.getPortal()
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser('test-02')
reference = 'test-02') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
self.tic() self.tic()
# Check that last (X where X is set in preferences) passwords are saved. # Check that last (X where X is set in preferences) passwords are saved.
self.assertEqual([], self._getPasswordEventList(person)) self.assertEqual([], self._getPasswordEventList(login))
preference.setPreferredNumberOfLastPasswordToCheck(10) preference.setPreferredNumberOfLastPasswordToCheck(10)
self.tic() self.tic()
self._clearCache() self._clearCache()
person.setPassword('12345678') login.setPassword('12345678')
self.tic() self.tic()
# password change date should be saved as well hashed old password value # password change date should be saved as well hashed old password value
old_password = person.getPassword() old_password = login.getPassword()
self.assertSameSet([old_password], [x.getPassword() for x in self._getPasswordEventList(person)]) self.assertSameSet([old_password], [x.getPassword() for x in self._getPasswordEventList(login)])
# .. test one more time to check history of password is saved in a list # .. test one more time to check history of password is saved in a list
person.setPassword('123456789') login.setPassword('123456789')
self.tic() self.tic()
old_password1 = person.getPassword() old_password1 = login.getPassword()
# password change date should be saved as well hashed old password value # password change date should be saved as well hashed old password value
self.assertSameSet([old_password1, old_password], [x.getPassword() for x in self._getPasswordEventList(person)]) self.assertSameSet([old_password1, old_password], [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (_setPassword)... # other methods (_setPassword)...
person._setPassword('123456789-1') login._setPassword('123456789-1')
self.tic() self.tic()
old_password2 = person.getPassword() old_password2 = login.getPassword()
self.assertSameSet([old_password2, old_password1, old_password], \ self.assertSameSet([old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (_forceSetPassword)... # other methods (_forceSetPassword)...
person._forceSetPassword('123456789-2') login._forceSetPassword('123456789-2')
self.tic() self.tic()
old_password3 = person.getPassword() old_password3 = login.getPassword()
self.assertSameSet([old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (setEncodedPassword)... # other methods (setEncodedPassword)...
person.setEncodedPassword('123456789-3') login.setEncodedPassword('123456789-3')
self.tic() self.tic()
old_password4 = person.getPassword() old_password4 = login.getPassword()
self.assertSameSet([old_password4, old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password4, old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
# other methods (edit)... # other methods (edit)...
person.edit(password = '123456789-4') login.edit(password = '123456789-4')
self.tic() self.tic()
old_password5 = person.getPassword() old_password5 = login.getPassword()
self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \ self.assertSameSet([old_password5, old_password4, old_password3, old_password2, old_password1, old_password], \
[x.getPassword() for x in self._getPasswordEventList(person)]) [x.getPassword() for x in self._getPasswordEventList(login)])
def test_03_PasswordValidity(self): def test_03_PasswordValidity(self):
...@@ -258,105 +278,107 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -258,105 +278,107 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser(
reference = 'test-03', 'test-03',
password = 'test', password='test',
first_name = 'First', person_kw={'first_name': 'First',
last_name = 'Last') 'last_name': 'Last'},
)
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
self.tic() self.tic()
# by default an empty password if nothing set in preferences is OK # by default an empty password if nothing set in preferences is OK
self.assertTrue(person.isPasswordValid('')) self.assertTrue(login.isPasswordValid(''))
# Not long enough passwords used # Not long enough passwords used
self._cleanUpPerson(person) self._cleanUpLogin(login)
preference.setPreferredMinPasswordLength(8) preference.setPreferredMinPasswordLength(8)
preference.setPreferredNumberOfLastPasswordToCheck(0) preference.setPreferredNumberOfLastPasswordToCheck(0)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertEqual([-1], person.analyzePassword('')) self.assertEqual([-1], login.analyzePassword(''))
self.assertEqual([-1], person.analyzePassword('1234567')) self.assertEqual([-1], login.analyzePassword('1234567'))
self.assertTrue(person.isPasswordValid('12345678')) self.assertTrue(login.isPasswordValid('12345678'))
# not changed in last x days # not changed in last x days
self._cleanUpPerson(person) self._cleanUpLogin(login)
preference.setPreferredMinPasswordLifetimeDuration(24) preference.setPreferredMinPasswordLifetimeDuration(24)
preference.setPreferredNumberOfLastPasswordToCheck(3) preference.setPreferredNumberOfLastPasswordToCheck(3)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertTrue(person.isPasswordValid('12345678')) self.assertTrue(login.isPasswordValid('12345678'))
person.setPassword('12345678') login.setPassword('12345678')
self.tic() self.tic()
# if we try to change now we should fail with any password # if we try to change now we should fail with any password
self.assertSameSet([-3], person.analyzePassword('87654321')) self.assertSameSet([-3], login.analyzePassword('87654321'))
self.assertSameSet([-1, -3], person.analyzePassword('short')) # multiple failures self.assertSameSet([-1, -3], login.analyzePassword('short')) # multiple failures
self.assertFalse(person.isPasswordValid('short')) # multiple failures self.assertFalse(login.isPasswordValid('short')) # multiple failures
self.assertRaises(ValueError, person.setPassword, '87654321') self.assertRaises(ValueError, login.setPassword, '87654321')
preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction preference.setPreferredMinPasswordLifetimeDuration(0) # remove restriction
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertTrue(person.isPasswordValid('87654321')) # it's OK to change self.assertTrue(login.isPasswordValid('87654321')) # it's OK to change
# password not used in previous X passwords # password not used in previous X passwords
preference.setPreferredMinPasswordLength(None) # disable for now preference.setPreferredMinPasswordLength(None) # disable for now
self._cleanUpPerson(person) self._cleanUpLogin(login)
self._clearCache() self._clearCache()
self.tic() self.tic()
person.setPassword('12345678-new') login.setPassword('12345678-new')
self.tic() self.tic()
self.assertSameSet([-4], person.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password self.assertSameSet([-4], login.analyzePassword('12345678-new')) # if we try to change now we should fail with this EXACT password
self.assertRaises(ValueError, person.setPassword, '12345678-new') self.assertRaises(ValueError, login.setPassword, '12345678-new')
self.assertTrue(person.isPasswordValid('12345678_')) # it's OK with another one not used yet self.assertTrue(login.isPasswordValid('12345678_')) # it's OK with another one not used yet
for password in ['a','b','c','d', 'e', 'f']: for password in ['a','b','c','d', 'e', 'f']:
# this sleep is not so beautiful, but mysql datetime columns has a # this sleep is not so beautiful, but mysql datetime columns has a
# precision of one second only, and we use creation_date to order # precision of one second only, and we use creation_date to order
# "Password Event" objects. So without this sleep, the test is # "Password Event" objects. So without this sleep, the test is
# failing randomly. # failing randomly.
time.sleep(1) time.sleep(1)
person.setPassword(password) login.setPassword(password)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('12345678-new')) self.assertTrue(login.isPasswordValid('12345678-new'))
self.assertTrue(person.isPasswordValid('a')) self.assertTrue(login.isPasswordValid('a'))
self.assertTrue(person.isPasswordValid('b')) self.assertTrue(login.isPasswordValid('b'))
self.assertTrue(person.isPasswordValid('c')) self.assertTrue(login.isPasswordValid('c'))
# only last 3 (including current one are invalid) # only last 3 (including current one are invalid)
self.assertSameSet([-4], person.analyzePassword('d')) self.assertSameSet([-4], login.analyzePassword('d'))
self.assertSameSet([-4], person.analyzePassword('e')) self.assertSameSet([-4], login.analyzePassword('e'))
self.assertSameSet([-4], person.analyzePassword('f')) self.assertSameSet([-4], login.analyzePassword('f'))
# if we remove restricted then all password are usable # if we remove restricted then all password are usable
preference.setPreferredNumberOfLastPasswordToCheck(None) preference.setPreferredNumberOfLastPasswordToCheck(None)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('d')) self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e')) self.assertTrue(login.isPasswordValid('e'))
self.assertTrue(person.isPasswordValid('f')) self.assertTrue(login.isPasswordValid('f'))
# if we set only last password to check # if we set only last password to check
preference.setPreferredNumberOfLastPasswordToCheck(1) preference.setPreferredNumberOfLastPasswordToCheck(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('c')) self.assertTrue(login.isPasswordValid('c'))
self.assertTrue(person.isPasswordValid('d')) self.assertTrue(login.isPasswordValid('d'))
self.assertTrue(person.isPasswordValid('e')) self.assertTrue(login.isPasswordValid('e'))
self.assertSameSet([-4], person.analyzePassword('f')) self.assertSameSet([-4], login.analyzePassword('f'))
preference.setPreferredRegularExpressionGroupList(regular_expression_list) preference.setPreferredRegularExpressionGroupList(regular_expression_list)
preference.setPreferredMinPasswordLength(7) preference.setPreferredMinPasswordLength(7)
preference.setPreferredNumberOfLastPasswordToCheck(None) preference.setPreferredNumberOfLastPasswordToCheck(None)
self._cleanUpPerson(person) self._cleanUpLogin(login)
self._clearCache() self._clearCache()
self.tic() self.tic()
...@@ -370,47 +392,47 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -370,47 +392,47 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list: for password in four_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.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.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 3 out of all groups # min 3 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(3) preference.setPreferredMinRegularExpressionGroupNumber(3)
self._clearCache() self._clearCache()
self._cleanUpPerson(person) self._cleanUpLogin(login)
self.tic() self.tic()
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(login.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.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 2 out of all groups # min 2 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(2) preference.setPreferredMinRegularExpressionGroupNumber(2)
self._clearCache() self._clearCache()
self.tic() self.tic()
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(login.isPasswordValid(password))
for password in one_group_password_list: for password in one_group_password_list:
self.assertSameSet([-2], person.analyzePassword(password)) self.assertSameSet([-2], login.analyzePassword(password))
# min 1 out of all groups # min 1 out of all groups
preference.setPreferredMinRegularExpressionGroupNumber(1) preference.setPreferredMinRegularExpressionGroupNumber(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list + two_group_password_list+one_group_password_list: for password in four_group_password_list + three_group_password_list + two_group_password_list+one_group_password_list:
self.assertTrue(person.isPasswordValid(password)) self.assertTrue(login.isPasswordValid(password))
# not contain the full name of the user # not contain the full name of the user
preference.setPrefferedForceUsernameCheckInPassword(1) preference.setPrefferedForceUsernameCheckInPassword(1)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertSameSet([-5], person.analyzePassword('abAB#12_%s' %person.getFirstName())) self.assertSameSet([-5], login.analyzePassword('abAB#12_%s' %person.getFirstName()))
self.assertSameSet([-5], person.analyzePassword('abAB#12_%s' %person.getLastName())) self.assertSameSet([-5], login.analyzePassword('abAB#12_%s' %person.getLastName()))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getFirstName())) self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getFirstName()))
self.assertTrue(person.isPasswordValid('abAB#12_%s' %person.getLastName())) self.assertTrue(login.isPasswordValid('abAB#12_%s' %person.getLastName()))
# check on temp objects just passworrd length( i.e. simulating a new user account creation) # check on temp objects just passworrd length( i.e. simulating a new user account creation)
first_name = 'John' first_name = 'John'
...@@ -425,8 +447,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -425,8 +447,8 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
# 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.assertSameSet([-1], temp_person.Person_analyzePassword('onlyNine1')) self.assertSameSet([-1], temp_person.Login_analyzePassword('onlyNine1'))
self.assertSameSet([], temp_person.Person_analyzePassword('longEnough1')) self.assertSameSet([], temp_person.Login_analyzePassword('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)
preference.setPreferredRegularExpressionGroupList(regular_expression_list) preference.setPreferredRegularExpressionGroupList(regular_expression_list)
...@@ -435,22 +457,22 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -435,22 +457,22 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
for password in four_group_password_list + three_group_password_list: for password in four_group_password_list + three_group_password_list:
self.assertSameSet([], temp_person.Person_analyzePassword(password)) self.assertSameSet([], temp_person.Login_analyzePassword(password))
for password in two_group_password_list + one_group_password_list: for password in two_group_password_list + one_group_password_list:
self.assertSameSet([-2], temp_person.Person_analyzePassword(password)) self.assertSameSet([-2], temp_person.Login_analyzePassword(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.tic() self.tic()
self.assertSameSet([-5], temp_person.Person_analyzePassword('abAB#12_%s' %first_name)) self.assertSameSet([-5], temp_person.Login_analyzePassword('abAB#12_%s' %first_name))
self.assertSameSet([-5], temp_person.Person_analyzePassword('abAB#12_%s' %last_name)) self.assertSameSet([-5], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
preference.setPrefferedForceUsernameCheckInPassword(0) preference.setPrefferedForceUsernameCheckInPassword(0)
self._clearCache() self._clearCache()
self.tic() self.tic()
self.assertSameSet([], temp_person.Person_analyzePassword('abAB#12_%s' %first_name)) self.assertSameSet([], temp_person.Login_analyzePassword('abAB#12_%s' %first_name))
self.assertSameSet([], temp_person.Person_analyzePassword('abAB#12_%s' %last_name)) self.assertSameSet([], temp_person.Login_analyzePassword('abAB#12_%s' %last_name))
# check Base_isPasswordValid is able to work in Anonymous User fashion # check Base_isPasswordValid is able to work in Anonymous User fashion
# but with already create Person object (i.e. recover password case) # but with already create Person object (i.e. recover password case)
...@@ -461,20 +483,20 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -461,20 +483,20 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
self.tic() self.tic()
person.setPassword('used_ALREADY_1234') login.setPassword('used_ALREADY_1234')
self._clearCache() self._clearCache()
self.tic() self.tic()
# emulate Anonymous User # emulate Anonymous User
self.logout() self.logout()
request.set('field_user_login', person.getReference()) request.set('field_user_login', login.getReference())
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#12_%s' %person.getFirstName(), request) # contains name self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getFirstName(), request) # contains name
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#12_%s' %person.getLastName(), request) # contains name self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#12_%s' %person.getLastName(), request) # contains name
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abAB#1', request) # too short self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abAB#1', request) # too short
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'abABCDEFG', request) # too few groups self.assertRaises(ValidationError, login.Login_isPasswordValid, 'abABCDEFG', request) # too few groups
self.assertRaises(ValidationError, portal.Base_isPasswordValid, 'used_ALREADY_1234', request) # already used self.assertRaises(ValidationError, login.Login_isPasswordValid, 'used_ALREADY_1234', request) # already used
self.assertEqual(1, portal.Base_isPasswordValid('abAB#12_', request)) self.assertEqual(1, login.Login_isPasswordValid('abAB#12_', request))
self.assertEqual(1, portal.Base_isPasswordValid('not_used_ALREADY_1234', request)) self.assertEqual(1, login.Login_isPasswordValid('not_used_ALREADY_1234', request))
def test_04_PasswordExpire(self): def test_04_PasswordExpire(self):
""" """
...@@ -485,16 +507,16 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -485,16 +507,16 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled()) self.assertTrue(portal.portal_preferences.isAuthenticationPolicyEnabled())
person = portal.person_module.newContent(portal_type = 'Person', person = self.createUser('test-04',
reference = 'test-04', password='used_ALREADY_1234')
password = 'used_ALREADY_1234') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
preference.setPreferredMaxPasswordLifetimeDuration(24) preference.setPreferredMaxPasswordLifetimeDuration(24)
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired']) self.assertFalse(request['is_user_account_password_expired'])
...@@ -502,21 +524,21 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -502,21 +524,21 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredMaxPasswordLifetimeDuration(4*24) # password expire in 4 days preference.setPreferredMaxPasswordLifetimeDuration(4*24) # password expire in 4 days
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired']) self.assertFalse(request['is_user_account_password_expired'])
# test early warning password expire notification is detected # test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24) # password expire notification appear immediately preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24) # password expire notification appear immediately
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertTrue(request['is_user_account_password_expired_expire_date']) self.assertTrue(request['is_user_account_password_expired_expire_date'])
# test early warning password expire notification is detected # test early warning password expire notification is detected
preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24-24) # password expire notification appear 3 days befor time preference.setPreferredPasswordLifetimeExpireWarningDuration(4*24-24) # password expire notification appear 3 days befor time
self.tic() self.tic()
self._clearCache() self._clearCache()
self.assertFalse(person.isPasswordExpired()) self.assertFalse(login.isPasswordExpired())
self.assertFalse(request['is_user_account_password_expired_expire_date']) self.assertFalse(request['is_user_account_password_expired_expire_date'])
def test_05_HttpRequest(self): def test_05_HttpRequest(self):
...@@ -525,50 +547,52 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -525,50 +547,52 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
""" """
portal = self.getPortal() portal = self.getPortal()
request = self.app.REQUEST request = self.app.REQUEST
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = self.createUser('test-05')
reference = 'test') assignment = person.newContent(portal_type = 'Assignment')
assignment.open()
login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
person.setPassword('used_ALREADY_1234') login.setPassword('used_ALREADY_1234')
self.tic() self.tic()
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue('Welcome to ERP5' in response.getBody()) self.assertTrue('Welcome to ERP5' in response.getBody())
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #1 # fail request #1
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'bad_test') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'bad_test')
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #2 # fail request #2
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# fail request #3 # fail request #3
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form")) self.assertTrue(response.getHeader("Location").endswith("login_form"))
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
self.tic() self.tic()
# test message that account is blocked # test message that account is blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/logged_in?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked.")) self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked."))
# test expire password message, first unblock it # test expire password message, first unblock it
person.Person_unblockLogin() login.Login_unblockLogin()
preference.setPreferredMaxPasswordLifetimeDuration(0) preference.setPreferredMaxPasswordLifetimeDuration(0)
self.tic() self.tic()
self._clearCache() self._clearCache()
response = self.publish(path) response = self.publish(path)
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Password is expired.")) self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Password is expired."))
self.assertTrue(person.isPasswordExpired()) self.assertTrue(login.isPasswordExpired())
# test we're redirected to update password due to soon expire # test we're redirected to update password due to soon expire
preference.setPreferredMaxPasswordLifetimeDuration(24) preference.setPreferredMaxPasswordLifetimeDuration(24)
...@@ -584,7 +608,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -584,7 +608,7 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
preference.setPreferredPasswordLifetimeExpireWarningDuration(12) preference.setPreferredPasswordLifetimeExpireWarningDuration(12)
self.tic() self.tic()
self._clearCache() self._clearCache()
path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test', 'used_ALREADY_1234') path = portal.absolute_url_path() + '/view?__ac_name=%s&__ac_password=%s' %('test-05', 'used_ALREADY_1234')
response = self.publish(path) response = self.publish(path)
self.assertTrue('Welcome to ERP5' in response.getBody()) self.assertTrue('Welcome to ERP5' in response.getBody())
...@@ -593,18 +617,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -593,18 +617,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
Check that expiring old Authentication Event list works. Check that expiring old Authentication Event list works.
""" """
portal = self.getPortal() portal = self.getPortal()
person = portal.portal_catalog.getResultValue(portal_type = 'Person', person = self.createUser('test-06')
reference = 'test') login = person.objectValues(portal_type='ERP5 Login')[0]
preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference', preference = portal.portal_catalog.getResultValue(portal_type = 'System Preference',
title = 'Authentication',) title = 'Authentication',)
# file some failures so we should detect and block account # file some failures so we should detect and block account
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
person.notifyLoginFailure() login.notifyLoginFailure()
self.tic() self.tic()
# should be blocked # should be blocked
self.assertTrue(person.isLoginBlocked()) self.assertTrue(login.isLoginBlocked())
# set 0 check interval # set 0 check interval
preference.setPreferredAuthenticationFailureCheckDuration(0) preference.setPreferredAuthenticationFailureCheckDuration(0)
...@@ -612,14 +636,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase): ...@@ -612,14 +636,14 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self._clearCache() self._clearCache()
time.sleep(1) # we need to give a moment time.sleep(1) # we need to give a moment
self.assertFalse(person.isLoginBlocked()) self.assertFalse(login.isLoginBlocked())
# expire manually old # expire manually old
portal.system_event_module.SystemEventModule_expireAuthenticationEventList() portal.system_event_module.SystemEventModule_expireAuthenticationEventList()
self.tic() self.tic()
self.assertEqual(3, len(portal.portal_catalog(portal_type ="Authentication Event", self.assertEqual(3, len(portal.portal_catalog(portal_type ="Authentication Event",
default_destination_uid = person.getUid(), default_destination_uid = login.getUid(),
validation_state = "expired"))) validation_state = "expired")))
......
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