Commit 238be699 authored by Ivan Tyagov's avatar Ivan Tyagov

Define isAuthenticationPolicyEnabled API and use it respectively.

Allow low level password validity checks (if
isAuthenticationPolicyEnabled only).
Adjust tests to handle better password history.
parent 161e5c9c
...@@ -42,6 +42,11 @@ class IEncryptedPassword(Interface): ...@@ -42,6 +42,11 @@ class IEncryptedPassword(Interface):
Check the password, usefull when changing password Check the password, usefull when changing password
""" """
def checkPasswordValueAcceptable(value):
"""
Check if the password value is acceptable - i.e. follows site rules.
"""
def setEncodedPassword(value, format='default'): def setEncodedPassword(value, format='default'):
""" """
Set an already encoded password. Set an already encoded password.
......
...@@ -60,4 +60,5 @@ class ILoginAccountProvider(Interface): ...@@ -60,4 +60,5 @@ class ILoginAccountProvider(Interface):
Return if password has already been used. Return if password has already been used.
""" """
\ No newline at end of file
...@@ -63,6 +63,19 @@ class EncryptedPasswordMixin: ...@@ -63,6 +63,19 @@ class EncryptedPasswordMixin:
return pw_validate(self.getPassword(), value) return pw_validate(self.getPassword(), value)
return False return False
def checkPasswordValueAcceptable(self, value):
"""
Check the password. This method is defined explicitly, because:
- we want to apply an authentication policy which itself may contain explicit password rules
"""
if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled():
# not a policy so basically all passwords are accceptable
return True
result = self.isPasswordValid(value)
if result <= 0:
raise ValueError, "Bad password (%s)." %result
def checkUserCanChangePassword(self): def checkUserCanChangePassword(self):
if not _checkPermission(Permissions.SetOwnPassword, self): if not _checkPermission(Permissions.SetOwnPassword, self):
raise AccessControl_Unauthorized('setPassword') raise AccessControl_Unauthorized('setPassword')
...@@ -87,6 +100,7 @@ class EncryptedPasswordMixin: ...@@ -87,6 +100,7 @@ class EncryptedPasswordMixin:
def _setPassword(self, value): def _setPassword(self, value):
self.checkUserCanChangePassword() self.checkUserCanChangePassword()
self.checkPasswordValueAcceptable(value)
self._forceSetPassword(value) self._forceSetPassword(value)
security.declarePublic('setPassword') security.declarePublic('setPassword')
......
...@@ -77,7 +77,7 @@ class LoginAccountProviderMixin: ...@@ -77,7 +77,7 @@ class LoginAccountProviderMixin:
Return if password has already been used. Return if password has already been used.
""" """
preferred_number_of_last_password_to_check = self.portal_preferences.getPreferredNumberOfLastPasswordToCheck() preferred_number_of_last_password_to_check = self.portal_preferences.getPreferredNumberOfLastPasswordToCheck()
password_list = self.getLastChangedPasswordValueList() + [self.getPassword()] password_list = self.getLastChangedPasswordValueList()
password_list.reverse() password_list.reverse()
for encoded_password in password_list[:preferred_number_of_last_password_to_check]: for encoded_password in password_list[:preferred_number_of_last_password_to_check]:
if pw_validate(encoded_password, password): if pw_validate(encoded_password, password):
......
...@@ -271,5 +271,31 @@ class PreferenceTool(BaseTool): ...@@ -271,5 +271,31 @@ class PreferenceTool(BaseTool):
finally: finally:
setSecurityManager(security_manager) setSecurityManager(security_manager)
security.declarePublic('isAuthenticationPolicyEnabled')
def isAuthenticationPolicyEnabled(self) :
"""
Return True if authentication policy is enabled.
This method exists here due to bootstrap issues.
It should work even if erp5_authentication_policy bt5 is not installed.
"""
# XXX: define an interface
def _isAuthenticationPolicyEnabled():
portal_preferences = self.getPortalObject().portal_preferences
method_id = 'isPreferredAuthenticationPolicyEnabled'
method = getattr(self, method_id, None)
if method is not None and method():
return True
return False
tv = getTransactionalVariable()
tv_key = 'PreferenceTool._isAuthenticationPolicyEnabled.%s' % getSecurityManager().getUser()
if tv.get(tv_key, None) is None:
_isAuthenticationPolicyEnabled = CachingMethod(_isAuthenticationPolicyEnabled,
id='PortalPreferences_isAuthenticationPolicyEnabled',
cache_factory='erp5_content_short')
tv[tv_key] = _isAuthenticationPolicyEnabled()
return tv[tv_key]
InitializeClass(PreferenceTool) InitializeClass(PreferenceTool)
...@@ -171,15 +171,12 @@ class ERP5UserManager(BasePlugin): ...@@ -171,15 +171,12 @@ class ERP5UserManager(BasePlugin):
except _AuthenticationFailure: except _AuthenticationFailure:
authentication_result = None authentication_result = None
method = getattr(self, 'ERP5Site_isAuthenticationPolicyEnabled', None) if not self.getPortalObject().portal_preferences.isAuthenticationPolicyEnabled():
if method is None or (method is not None and not method()):
# stop here, no authentication policy enabled # stop here, no authentication policy enabled
# so just return authentication check result # so just return authentication check result
# XXX: move to ERP5 Site API
return authentication_result return authentication_result
# authentication policy enabled, we need person object anyway # authentication policy enabled, we need person object anyway
# XXX: every request is a MySQL call
user_list = self.getUserByLogin(credentials.get('login')) user_list = self.getUserByLogin(credentials.get('login'))
if not user_list: if not user_list:
# not an ERP5 Person object # not an ERP5 Person object
......
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