From 2e8a7993c018ac09ce90397a6c8f2e9f60d9fd8c Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:12:20 -0400 Subject: [PATCH 01/25] check disable_totp config before totp authentication --- ckanext/security/authenticator.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 84137d7..e7247bd 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -13,6 +13,7 @@ import ckan.plugins as p from ckanext.security.cache.login import LoginThrottle from ckanext.security.model import SecurityTOTP, ReplayAttackException +from paste.deploy.converters import asbool log = logging.getLogger(__name__) @@ -105,15 +106,21 @@ def authenticate(self, environ, identity): # Increment the throttle counter if the login failed. throttle.increment() - # if the CKAN authenticator has successfully authenticated - # the request and the user wasn't locked out above, - # then check the TOTP parameter to see if it is valid - if auth_user_name is not None: - totp_success = self.authenticate_totp(environ, auth_user_name) - # if TOTP was successful -- reset the log in throttle - if totp_success: - throttle.reset() - return totp_success + # totp authentication is enabled by default for all users + # totp can be disabled, if needed, by setting + # ckanext.security.disable_totp to True in configurations + if asbool(config.get('ckanext.security.disable_totp', False)): + return auth_user_name + else: + # if the CKAN authenticator has successfully authenticated + # the request and the user wasn't locked out above, + # then check the TOTP parameter to see if it is valid + if auth_user_name is not None: + totp_success = self.authenticate_totp(environ, auth_user_name) + # if TOTP was successful -- reset the log in throttle + if totp_success: + throttle.reset() + return totp_success def authenticate_totp(self, environ, auth_user): totp_challenger = SecurityTOTP.get_for_user(auth_user) From 8b618558994e80fb00c663670c19eca6caa34e91 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:16:57 -0400 Subject: [PATCH 02/25] add helper for checking disable_totp configuration --- ckanext/security/helpers.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ckanext/security/helpers.py diff --git a/ckanext/security/helpers.py b/ckanext/security/helpers.py new file mode 100644 index 0000000..d23d61a --- /dev/null +++ b/ckanext/security/helpers.py @@ -0,0 +1,6 @@ +from ckan.common import config +from paste.deploy.converters import asbool + + +def security_disable_totp(): + return asbool(config.get('ckanext.security.disable_totp')) From 3bf339f982c13f685d2200e2054910da85ca6aea Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:22:34 -0400 Subject: [PATCH 03/25] add security_disable_totp helper to plugin --- ckanext/security/plugin/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ckanext/security/plugin/__init__.py b/ckanext/security/plugin/__init__.py index fc0f311..030032b 100644 --- a/ckanext/security/plugin/__init__.py +++ b/ckanext/security/plugin/__init__.py @@ -9,6 +9,7 @@ validate_upload_type, validate_upload_presence ) from ckanext.security.logic import auth, action +from ckanext.security.helpers import security_disable_totp try: tk.requires_ckan_version("2.9") @@ -105,4 +106,5 @@ def get_auth_functions(self): def get_helpers(self): return { 'check_ckan_version': tk.check_ckan_version, + 'security_disable_totp': security_disable_totp, } From 67497582238201ed8f622afce117b2d02dcfd5ae Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:35:20 -0400 Subject: [PATCH 04/25] hide display of 2fa fieldset if disable_totp is set --- .../templates/user/edit_user_form.html | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ckanext/security/templates/user/edit_user_form.html b/ckanext/security/templates/user/edit_user_form.html index 1bd5954..d6b42c3 100644 --- a/ckanext/security/templates/user/edit_user_form.html +++ b/ckanext/security/templates/user/edit_user_form.html @@ -47,14 +47,16 @@ {{ form.input('password2', type='password', label=_('Confirm Password'), id='field-password-confirm', value=data.password2, error=errors.password2, classes=['control-medium'], attrs={'autocomplete': 'off'}) }} -
- {{_('Two factor authentication')}} - {% if h.check_ckan_version('2.9') %} - {% link_for _('Manage two factor authentication'), controller='mfa_user', action='configure_mfa', id=data.id, class_='btn btn-default pull-left', icon='cog' %} - {% else %} - {% link_for _('Manage two factor authentication'), controller='ckanext.security.controllers:MFAUserController', action='configure_mfa', id=data.id, class_='btn btn-default pull-left', icon='cog' %} - {% endif %} -
+ {% if h.security_disable_totp() == false %} +
+ {{_('Two factor authentication')}} + {% if h.check_ckan_version('2.9') %} + {% link_for _('Manage two factor authentication'), controller='mfa_user', action='configure_mfa', id=data.id, class_='btn btn-default pull-left', icon='cog' %} + {% else %} + {% link_for _('Manage two factor authentication'), controller='ckanext.security.controllers:MFAUserController', action='configure_mfa', id=data.id, class_='btn btn-default pull-left', icon='cog' %} + {% endif %} +
+ {% endif %}
{% block delete_button %} From 20429509452f7f1135371d244604a4697909de4f Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:44:59 -0400 Subject: [PATCH 05/25] disable_totp is False by default --- ckanext/security/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/security/helpers.py b/ckanext/security/helpers.py index d23d61a..a9cce32 100644 --- a/ckanext/security/helpers.py +++ b/ckanext/security/helpers.py @@ -3,4 +3,4 @@ def security_disable_totp(): - return asbool(config.get('ckanext.security.disable_totp')) + return asbool(config.get('ckanext.security.disable_totp', False)) From 984b1dd7aa7a02873ed560750a6f912a8fcfdf69 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 10:57:52 -0400 Subject: [PATCH 06/25] add ckanext.security.disable_totp to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3162599..23f51ac 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,10 @@ ckanext.security.brute_force_key = user_name # Detect brute force attempts by u # You can disable the fix in this plugin by: ckanext.security.disable_password_reset_override = true +# Two factor authentication is enabled for all users by default +# optional configuration to disable 2fa +ckanext.security.disable_totp = false # set to true to disable 2fa + # Provide a help page to allow 2fa users to contact support or get more information # Shows up as 'Need help?' on the 2fa entry form beside the submit button. Does not display a link if none provided ckanext.security.mfa_help_link = https://data.govt.nz/catalogue-guide/releasing-data-on-data-govt-nz/how-do-i-set-up-two-factor-authentication/ From f9b6d3dfa953a8ad8d6a2538b998c950c097e3f3 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:35:32 -0400 Subject: [PATCH 07/25] Update edit_user_form.html --- ckanext/security/templates/user/edit_user_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/security/templates/user/edit_user_form.html b/ckanext/security/templates/user/edit_user_form.html index d6b42c3..904c521 100644 --- a/ckanext/security/templates/user/edit_user_form.html +++ b/ckanext/security/templates/user/edit_user_form.html @@ -47,7 +47,7 @@ {{ form.input('password2', type='password', label=_('Confirm Password'), id='field-password-confirm', value=data.password2, error=errors.password2, classes=['control-medium'], attrs={'autocomplete': 'off'}) }} - {% if h.security_disable_totp() == false %} + {% if not h.security_disable_totp() %}
{{_('Two factor authentication')}} {% if h.check_ckan_version('2.9') %} From 6a2371dd21f2cad8a88f31a62121f87b0aee2a5a Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:48:39 -0400 Subject: [PATCH 08/25] replace disable_totp with enable_totp --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23f51ac..5ff39d2 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ ckanext.security.disable_password_reset_override = true # Two factor authentication is enabled for all users by default # optional configuration to disable 2fa -ckanext.security.disable_totp = false # set to true to disable 2fa +ckanext.security.enable_totp = true # set to false to disable 2fa # Provide a help page to allow 2fa users to contact support or get more information # Shows up as 'Need help?' on the 2fa entry form beside the submit button. Does not display a link if none provided From 8b6de1f359b34df1b2ca571889141d6509b35766 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:54:27 -0400 Subject: [PATCH 09/25] replace disable_totp with enable_totp --- ckanext/security/authenticator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index e7247bd..1243154 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -108,8 +108,8 @@ def authenticate(self, environ, identity): # totp authentication is enabled by default for all users # totp can be disabled, if needed, by setting - # ckanext.security.disable_totp to True in configurations - if asbool(config.get('ckanext.security.disable_totp', False)): + # ckanext.security.enable_totp to false in configurations + if not asbool(config.get('ckanext.security.enable_totp', True)): return auth_user_name else: # if the CKAN authenticator has successfully authenticated From 80d6be824e84364d3616f7538e4cb2965929583e Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:56:45 -0400 Subject: [PATCH 10/25] replace disable_totp with enable_totp --- ckanext/security/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/security/helpers.py b/ckanext/security/helpers.py index a9cce32..4a713ea 100644 --- a/ckanext/security/helpers.py +++ b/ckanext/security/helpers.py @@ -2,5 +2,5 @@ from paste.deploy.converters import asbool -def security_disable_totp(): - return asbool(config.get('ckanext.security.disable_totp', False)) +def security_enable_totp(): + return asbool(config.get('ckanext.security.enable_totp', True)) From ef25c37e24f483bd750abd860782a4d87f25a2cf Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:57:46 -0400 Subject: [PATCH 11/25] replace disable_totp with enable_totp --- ckanext/security/plugin/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/security/plugin/__init__.py b/ckanext/security/plugin/__init__.py index 030032b..4850eaa 100644 --- a/ckanext/security/plugin/__init__.py +++ b/ckanext/security/plugin/__init__.py @@ -9,7 +9,7 @@ validate_upload_type, validate_upload_presence ) from ckanext.security.logic import auth, action -from ckanext.security.helpers import security_disable_totp +from ckanext.security.helpers import security_enable_totp try: tk.requires_ckan_version("2.9") @@ -106,5 +106,5 @@ def get_auth_functions(self): def get_helpers(self): return { 'check_ckan_version': tk.check_ckan_version, - 'security_disable_totp': security_disable_totp, + 'security_disable_totp': security_enable_totp, } From 09a867dad8c4665b0cb9a6967df159bf7978d818 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 11:58:41 -0400 Subject: [PATCH 12/25] replace disable_totp with enable_totp --- ckanext/security/templates/user/edit_user_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/security/templates/user/edit_user_form.html b/ckanext/security/templates/user/edit_user_form.html index 904c521..5674618 100644 --- a/ckanext/security/templates/user/edit_user_form.html +++ b/ckanext/security/templates/user/edit_user_form.html @@ -47,7 +47,7 @@ {{ form.input('password2', type='password', label=_('Confirm Password'), id='field-password-confirm', value=data.password2, error=errors.password2, classes=['control-medium'], attrs={'autocomplete': 'off'}) }}
- {% if not h.security_disable_totp() %} + {% if h.security_enable_totp() %}
{{_('Two factor authentication')}} {% if h.check_ckan_version('2.9') %} From 0b9c7debde1fb7a06f15ed5c192ae0f022ff7111 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 1 Sep 2022 12:02:55 -0400 Subject: [PATCH 13/25] replace disable_totp with enable_totp --- ckanext/security/plugin/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckanext/security/plugin/__init__.py b/ckanext/security/plugin/__init__.py index 4850eaa..5ad28b2 100644 --- a/ckanext/security/plugin/__init__.py +++ b/ckanext/security/plugin/__init__.py @@ -106,5 +106,5 @@ def get_auth_functions(self): def get_helpers(self): return { 'check_ckan_version': tk.check_ckan_version, - 'security_disable_totp': security_enable_totp, + 'security_enable_totp': security_enable_totp, } From 992c55db1ee0586d665fa633cc33e5f12d8574ed Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Fri, 2 Sep 2022 08:58:55 -0400 Subject: [PATCH 14/25] replace ckan.common with ckan.plugins.toolkit --- ckanext/security/helpers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ckanext/security/helpers.py b/ckanext/security/helpers.py index 4a713ea..af54a54 100644 --- a/ckanext/security/helpers.py +++ b/ckanext/security/helpers.py @@ -1,5 +1,4 @@ -from ckan.common import config -from paste.deploy.converters import asbool +from ckan.plugins.toolkit import asbool, config def security_enable_totp(): From 0e059d45fb0f1919bf787309aa01f3529c2ecf39 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Fri, 2 Sep 2022 09:11:48 -0400 Subject: [PATCH 15/25] improve code readability by removing double negative if not...else condition --- ckanext/security/authenticator.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 1243154..991f464 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -8,12 +8,11 @@ from ckan.lib.authenticator import UsernamePasswordAuthenticator from ckan.model import User -from ckan.common import config from webob.request import Request import ckan.plugins as p +from ckan.plugins.toolkit import asbool, config from ckanext.security.cache.login import LoginThrottle from ckanext.security.model import SecurityTOTP, ReplayAttackException -from paste.deploy.converters import asbool log = logging.getLogger(__name__) @@ -109,9 +108,7 @@ def authenticate(self, environ, identity): # totp authentication is enabled by default for all users # totp can be disabled, if needed, by setting # ckanext.security.enable_totp to false in configurations - if not asbool(config.get('ckanext.security.enable_totp', True)): - return auth_user_name - else: + if asbool(config.get('ckanext.security.enable_totp', True)): # if the CKAN authenticator has successfully authenticated # the request and the user wasn't locked out above, # then check the TOTP parameter to see if it is valid @@ -121,6 +118,8 @@ def authenticate(self, environ, identity): if totp_success: throttle.reset() return totp_success + else: + return auth_user_name def authenticate_totp(self, environ, auth_user): totp_challenger = SecurityTOTP.get_for_user(auth_user) From 791e4e4b5788cffc94a4a6dd7e7d48e1202a64b2 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Thu, 29 Dec 2022 10:25:59 -0500 Subject: [PATCH 16/25] use security_enable_totp helper in authenticator --- ckanext/security/authenticator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 991f464..7a7e9a7 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -10,8 +10,8 @@ from ckan.model import User from webob.request import Request import ckan.plugins as p -from ckan.plugins.toolkit import asbool, config from ckanext.security.cache.login import LoginThrottle +from ckanext.security.helpers import security_enable_totp from ckanext.security.model import SecurityTOTP, ReplayAttackException log = logging.getLogger(__name__) @@ -108,7 +108,7 @@ def authenticate(self, environ, identity): # totp authentication is enabled by default for all users # totp can be disabled, if needed, by setting # ckanext.security.enable_totp to false in configurations - if asbool(config.get('ckanext.security.enable_totp', True)): + if security_enable_totp(): # if the CKAN authenticator has successfully authenticated # the request and the user wasn't locked out above, # then check the TOTP parameter to see if it is valid From 67a3c418987a23d3b354dfde2b7e2d45f18d8285 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Tue, 3 Jan 2023 21:22:23 -0500 Subject: [PATCH 17/25] reset throttle if security_enable_totp is false --- ckanext/security/authenticator.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 7a7e9a7..18dbd54 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -108,18 +108,19 @@ def authenticate(self, environ, identity): # totp authentication is enabled by default for all users # totp can be disabled, if needed, by setting # ckanext.security.enable_totp to false in configurations - if security_enable_totp(): - # if the CKAN authenticator has successfully authenticated - # the request and the user wasn't locked out above, - # then check the TOTP parameter to see if it is valid - if auth_user_name is not None: - totp_success = self.authenticate_totp(environ, auth_user_name) - # if TOTP was successful -- reset the log in throttle - if totp_success: - throttle.reset() - return totp_success - else: + if not security_enable_totp(): + throttle.reset() return auth_user_name + + # if the CKAN authenticator has successfully authenticated + # the request and the user wasn't locked out above, + # then check the TOTP parameter to see if it is valid + if auth_user_name is not None: + totp_success = self.authenticate_totp(environ, auth_user_name) + # if TOTP was successful -- reset the log in throttle + if totp_success: + throttle.reset() + return totp_success def authenticate_totp(self, environ, auth_user): totp_challenger = SecurityTOTP.get_for_user(auth_user) From c80f37847a24fde6f1d6955c78835e1af1345e32 Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Fri, 20 Jan 2023 14:32:14 -0500 Subject: [PATCH 18/25] import ckan config in authenticator --- ckanext/security/authenticator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 18dbd54..04dd4e8 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -10,6 +10,7 @@ from ckan.model import User from webob.request import Request import ckan.plugins as p +from ckan.plugins.toolkit import config from ckanext.security.cache.login import LoginThrottle from ckanext.security.helpers import security_enable_totp from ckanext.security.model import SecurityTOTP, ReplayAttackException From dbff92659b4521ac06c64058a7484fcd8f5693ff Mon Sep 17 00:00:00 2001 From: Rabia Sajjad Date: Tue, 24 Jan 2023 16:15:51 -0500 Subject: [PATCH 19/25] check security_enable_totp config in login form --- .../templates/user/snippets/login_form.html | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/ckanext/security/templates/user/snippets/login_form.html b/ckanext/security/templates/user/snippets/login_form.html index 6268c72..3b7a5c3 100644 --- a/ckanext/security/templates/user/snippets/login_form.html +++ b/ckanext/security/templates/user/snippets/login_form.html @@ -8,11 +8,13 @@ {% set username_error = true if error_summary %} {% set password_error = true if error_summary %} -{% if h.check_ckan_version('2.9') %} - {% asset 'security/mfa_login' %} -{% else %} - {% resource 'security/login_ajax.js' %} - {% resource 'security/qrious.js' %} +{% if h.security_enable_totp() %} + {% if h.check_ckan_version('2.9') %} + {% asset 'security/mfa_login' %} + {% else %} + {% resource 'security/login_ajax.js' %} + {% resource 'security/qrious.js' %} + {% endif %} {% endif %} @@ -43,27 +45,29 @@
- + {% endif %} From 668783d3e3b78d0f0131bdccd8cd6a6b903b4e61 Mon Sep 17 00:00:00 2001 From: saj-derilinx Date: Fri, 17 Nov 2023 12:21:11 +0000 Subject: [PATCH 20/25] Enable email login --- ckanext/security/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ckanext/security/utils.py b/ckanext/security/utils.py index bd19991..3da7385 100644 --- a/ckanext/security/utils.py +++ b/ckanext/security/utils.py @@ -123,6 +123,8 @@ def login(): user_name = identity['login'] user = model.User.by_name(user_name) + if not user: + user = model.User.by_email(user_name) login_throttle_key = get_login_throttle_key(request, user_name) if login_throttle_key is None: @@ -174,7 +176,6 @@ def login(): log.info('User %s supplied invalid 2fa code', user_name) response_status = 403 throttle.increment() - return (response_status, json.dumps(res)) except Exception as err: From f04118c0dd0a986b956f506ce3b0daac90932f50 Mon Sep 17 00:00:00 2001 From: saj-derilinx Date: Fri, 17 Nov 2023 12:40:16 +0000 Subject: [PATCH 21/25] Get user name from user model object --- ckanext/security/authenticator.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 04dd4e8..7921d32 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -6,6 +6,7 @@ import pylons from ckan.lib.cli import MockTranslator +from ckan import model from ckan.lib.authenticator import UsernamePasswordAuthenticator from ckan.model import User from webob.request import Request @@ -74,6 +75,10 @@ def authenticate(self, environ, identity): to log into a specific account within a period of time.""" try: user_name = identity['login'] + user = model.User.by_name(user_name) + if not user: + user = model.User.by_email(user_name) + user_name = user.name except KeyError: return None From e8f0fc64e8ce143bbb04f9e8bdcdeec90f5ee072 Mon Sep 17 00:00:00 2001 From: saj-derilinx Date: Fri, 17 Nov 2023 12:44:27 +0000 Subject: [PATCH 22/25] Fix form label and disable help link --- ckanext/security/templates/user/snippets/login_form.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckanext/security/templates/user/snippets/login_form.html b/ckanext/security/templates/user/snippets/login_form.html index 3b7a5c3..24e88e8 100644 --- a/ckanext/security/templates/user/snippets/login_form.html +++ b/ckanext/security/templates/user/snippets/login_form.html @@ -31,7 +31,7 @@
- {{ form.input('login', label=_("Username"), id='field-login', value="", error=username_error, classes=["control-medium"]) }} + {{ form.input('login', label=_("Username or Email"), id='field-login', value="", error=username_error, classes=["control-medium"]) }} {{ form.input('password', label=_("Password"), id='field-password', type="password", value="", error=password_error, classes=["control-medium"]) }}
@@ -64,7 +64,7 @@
- + {# #}
From 20b2c501e6134cae6785672277b479c2fb1a8fea Mon Sep 17 00:00:00 2001 From: saj-derilinx Date: Mon, 4 Dec 2023 13:32:00 +0000 Subject: [PATCH 23/25] Change text From 913e14bc3ffb962b29ff978db0efbb0285e44b25 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 11 Dec 2023 11:44:09 +0000 Subject: [PATCH 24/25] Add the validations as get_validators instead of monkey patching --- ckanext/security/plugin/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ckanext/security/plugin/__init__.py b/ckanext/security/plugin/__init__.py index 5ad28b2..e52f186 100644 --- a/ckanext/security/plugin/__init__.py +++ b/ckanext/security/plugin/__init__.py @@ -10,6 +10,7 @@ ) from ckanext.security.logic import auth, action from ckanext.security.helpers import security_enable_totp +from ckanext.security import validators try: tk.requires_ckan_version("2.9") @@ -27,6 +28,7 @@ class CkanSecurityPlugin(MixinPlugin, p.SingletonPlugin): p.implements(p.IActions) p.implements(p.IAuthFunctions) p.implements(p.ITemplateHelpers) + p.implements(p.IValidators) # BEGIN Hooks for IConfigurer @@ -51,6 +53,14 @@ def update_config(self, config): # END Hooks for IConfigurer + # BEGIN hooks for IValidators + def get_validators(self): + return { + 'user_password_validator': validators.user_password_validator, + 'old_username_validator': validators.old_username_validator, + } + # END hooks for IValidators + # BEGIN Hooks for IResourceController def before_create(self, context, resource): From f561c23d80295b9621c004e5927045e3e474e294 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Wed, 20 Dec 2023 17:47:35 +0000 Subject: [PATCH 25/25] add validation for too many repeated characters --- ckanext/security/validators.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ckanext/security/validators.py b/ckanext/security/validators.py index 147f056..0a49ade 100644 --- a/ckanext/security/validators.py +++ b/ckanext/security/validators.py @@ -1,6 +1,7 @@ # encoding: utf-8 import six import string +import collections from ckan import authz from ckan.common import _ @@ -10,9 +11,24 @@ MIN_LEN_ERROR = ( 'Your password must be {} characters or longer, and consist of at least ' 'three of the following character sets: uppercase characters, lowercase ' - 'characters, digits, punctuation & special characters.' + 'characters, digits, punctuation & special characters, and not contain ' + 'too many repeated characters.' ) +def _too_many_repeated_characters(value): + """ does the password contain too many repeated characters + + Returns True if the most frequent character is >= 1/3 of the characters. + e.g. "password" is false: ct(s)==2 < 8/3 + "aaaaword" is true: ct(a)==4 > 8/3 + + :param s: proposed password + :returns: boolean, True if password is ok by this criteria + """ + char_counts = collections.Counter(value) + # note, will fail on empty password, but caller checks MIN_PASSWORD_LENGTH + return (char_counts.most_common(1)[0][1] >= (len(value)/3)) + def user_password_validator(key, data, errors, context): value = data[key] @@ -31,7 +47,8 @@ def user_password_validator(key, data, errors, context): any(x.isdigit() for x in value), any(x in string.punctuation for x in value) ] - if len(value) < MIN_PASSWORD_LENGTH or sum(rules) < 3: + if len(value) < MIN_PASSWORD_LENGTH or sum(rules) < 3 \ + or _too_many_repeated_characters(value): raise Invalid(_(MIN_LEN_ERROR.format(MIN_PASSWORD_LENGTH)))