diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml index d2b88ec63a26a1f9722aebfdae133123d8e4e9e3..433be7131e6deadcfc41791870f495a4af215b3b 100644 --- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml +++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isLoginBlocked.xml @@ -57,6 +57,7 @@ """\n from DateTime import DateTime\n \n +request = context.REQUEST\n portal = context.getPortalObject()\n portal_preferences = portal.portal_preferences\n \n @@ -86,6 +87,7 @@ if len(failures_for_period)>= max_authentication_failures:\n context.log(\'check=%s block=%s release=%s-> %s\' %(check_duration, block_duration, block_timeout, failures_for_period))\n if block_timeout > now:\n context.log(\'block %s\' %context.getReference())\n + request.set(\'is_user_account_blocked\', True)\n return 1\n \n return 0\n diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml index bb2e33ca369e302794fcee07b8f156987cbe0ac3..30d50d602eb68e50d604e0778bc119eb480c1326 100644 --- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml +++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/Person_isPasswordExpired.xml @@ -57,6 +57,7 @@ """\n from Products.ERP5Type.Cache import CachingMethod\n \n +request = context.REQUEST\n portal = context.getPortalObject()\n \n def _isPasswordExpired():\n @@ -66,15 +67,17 @@ def _isPasswordExpired():\n max_password_lifetime_duration = portal.portal_preferences.getPreferredMaxPasswordLifetimeDuration()\n last_password_modification_date = context.getLastPasswordModificationDate()\n if last_password_modification_date is not None and \\\n - (last_password_modification_date + max_password_lifetime_duration*one_hour-5) < now:\n + (last_password_modification_date + max_password_lifetime_duration*one_hour) < now:\n # password is expired\n context.log(\'expired %s\' %context.getReference())\n return 1\n return 0\n \n -_isPasswordExpired1 = CachingMethod(_isPasswordExpired,\n +_isPasswordExpired = CachingMethod(_isPasswordExpired,\n id=\'Person_isPasswordExpired\',\n cache_factory=\'erp5_content_short\')\n +is_password_expired = _isPasswordExpired()\n +request.set(\'is_user_account_password_expired\', is_password_expired)\n return _isPasswordExpired()\n diff --git a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml index 19bb2b8b8a04401489f699dd9863e1b219aca40b..171d64a72aecf7f79de1a024d4f0c520413e4504 100644 --- a/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml +++ b/bt5/erp5_authentication_policy/SkinTemplateItem/portal_skins/erp5_authentication_policy/logged_in.xml @@ -46,9 +46,32 @@ isAnon mtool/isAnonymousUser|nothing;">\n <tal:block tal:condition="isAnon">\n <tal:block tal:define="dummy python: response.expireCookie(\'__ac\', path=\'/\');\n - url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Login and/or password is incorrect.\'));\n - url python: request.get(\'came_from\') and \'%s&came_from=%s\' % (url, request[\'came_from\']) or url;\n - dummy python: response.redirect(url);" />\n + is_user_account_blocked python: request.get(\'is_user_account_blocked\', False);\n + is_user_account_password_expired python: request.get(\'is_user_account_password_expired\', False);\n + d python: context.log(\'? --%s %s\' %(is_user_account_blocked, is_user_account_password_expired));">\n +\n + <!-- Login and/or password is incorrect. -->\n + <tal:block tal:omit-tag="python: not is_user_account_blocked and not is_user_account_password_expired"\n + tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Login and/or password is incorrect.\'));\n + url python: request.get(\'came_from\') and \'%s&came_from=%s\' % (url, request[\'came_from\']) or url;\n + dummy python: response.redirect(url);">\n + </tal:block>\n +\n + <!-- Login is blocked. -->\n + <tal:block tal:condition="is_user_account_blocked">\n + <tal:block tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Account is blocked.\'));\n + url python: request.get(\'came_from\') and \'%s&came_from=%s\' % (url, request[\'came_from\']) or url;\n + dummy python: response.redirect(url);"/>\n + </tal:block>\n +\n + <!-- Password is expired. -->\n + <tal:block tal:condition="is_user_account_password_expired">\n + <tal:block tal:define="url python: \'%s/login_form?portal_status_message=%s\' % (here.absolute_url(), here.Base_translateString(\'Password is expired.\'));\n + url python: request.get(\'came_from\') and \'%s&came_from=%s\' % (url, request[\'came_from\']) or url;\n + dummy python: response.redirect(url);"/>\n + </tal:block>\n +\n + </tal:block>\n </tal:block>\n <tal:block tal:condition="not: isAnon">\n <tal:block tal:define="came_from python: request.get(\'came_from\') or here.absolute_url();\n @@ -73,7 +96,7 @@ </item> <item> <key> <string>output_encoding</string> </key> - <value> <string>utf-8</string> </value> + <value> <string>iso-8859-15</string> </value> </item> <item> <key> <string>title</string> </key> diff --git a/bt5/erp5_authentication_policy/bt/revision b/bt5/erp5_authentication_policy/bt/revision index 56a6051ca2b02b04ef92d5150c9ef600403cb1de..d8263ee9860594d2806b0dfd1bfd17528b0ba2a4 100644 --- a/bt5/erp5_authentication_policy/bt/revision +++ b/bt5/erp5_authentication_policy/bt/revision @@ -1 +1 @@ -1 \ No newline at end of file +2 \ No newline at end of file diff --git a/product/ERP5Security/ERP5UserManager.py b/product/ERP5Security/ERP5UserManager.py index 6a0e58ef8706b41822180ea22fca2535d6e8b9f1..dd6377c5d2da6f6427bd44f642663d59471dc09e 100644 --- a/product/ERP5Security/ERP5UserManager.py +++ b/product/ERP5Security/ERP5UserManager.py @@ -163,12 +163,38 @@ class ERP5UserManager(BasePlugin): id='ERP5UserManager_authenticateCredentials', cache_factory='erp5_content_short') try: - return _authenticateCredentials( - login=credentials.get('login'), - password=credentials.get('password'), - path=self.getPhysicalPath()) + authentication_result = _authenticateCredentials( + login=credentials.get('login'), + password=credentials.get('password'), + path=self.getPhysicalPath()) + except _AuthenticationFailure: + authentication_result = None + + method = getattr(self, 'ERP5Site_isAuthenticationPolicyEnabled', None) + if method is None or (method is not None and not method()): + # stop here, no authentication policy enabled + # so just return authentication check result + # XXX: move to ERP5 Site API + return authentication_result + + # authentication policy enabled, we need person object anyway + user_list = self.getUserByLogin(credentials.get('login')) + if not user_list: + # not an ERP5 Person object return None + user = user_list[0] + + if authentication_result is None: + # file a failed authentication attempt + user.notifyLoginFailure() + return None + + # check if user account is blocked and if password is expired or not + if user.isLoginBlocked() or user.isPasswordExpired(): + return None + + return authentication_result # # IUserEnumerationPlugin implementation