diff --git a/README.md b/README.md index 3162599..5ff39d2 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.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 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/ diff --git a/ckanext/security/authenticator.py b/ckanext/security/authenticator.py index 84137d7..7921d32 100644 --- a/ckanext/security/authenticator.py +++ b/ckanext/security/authenticator.py @@ -6,12 +6,14 @@ import pylons from ckan.lib.cli import MockTranslator +from ckan import model 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 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__) @@ -73,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 @@ -105,6 +111,13 @@ def authenticate(self, environ, identity): # Increment the throttle counter if the login failed. throttle.increment() + # 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 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 diff --git a/ckanext/security/helpers.py b/ckanext/security/helpers.py new file mode 100644 index 0000000..af54a54 --- /dev/null +++ b/ckanext/security/helpers.py @@ -0,0 +1,5 @@ +from ckan.plugins.toolkit import asbool, config + + +def security_enable_totp(): + return asbool(config.get('ckanext.security.enable_totp', True)) diff --git a/ckanext/security/plugin/__init__.py b/ckanext/security/plugin/__init__.py index fc0f311..e52f186 100644 --- a/ckanext/security/plugin/__init__.py +++ b/ckanext/security/plugin/__init__.py @@ -9,6 +9,8 @@ validate_upload_type, validate_upload_presence ) 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") @@ -26,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 @@ -50,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): @@ -105,4 +116,5 @@ def get_auth_functions(self): def get_helpers(self): return { 'check_ckan_version': tk.check_ckan_version, + 'security_enable_totp': security_enable_totp, } diff --git a/ckanext/security/templates/user/edit_user_form.html b/ckanext/security/templates/user/edit_user_form.html index 1bd5954..5674618 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'}) }} -
+ {% if h.security_enable_totp() %} + + {% endif %}