diff --git a/hawkpost/settings/common.py b/hawkpost/settings/common.py index f6571954..9952b84b 100644 --- a/hawkpost/settings/common.py +++ b/hawkpost/settings/common.py @@ -42,6 +42,7 @@ 'allauth.socialaccount', 'allauth.socialaccount.providers.github', 'timezone_field', + 'axes', 'humans', 'boxes', 'pages', @@ -177,6 +178,13 @@ } } +# Authentication Limits Config (AXES) +AXES_LOGIN_FAILURE_LIMIT = 5 +AXES_COOLOFF_TIME = 1 # hour +AXES_USERNAME_FORM_FIELD = 'login' +AXES_DISABLE_SUCCESS_ACCESS_LOG = True + + # GPG keyring for server-signing messages GPG_SIGN_DIR = os.environ.get("SIGN_DIR") GPG_SIGN_KEY = os.environ.get("SIGN_KEY") diff --git a/hawkpost/settings/production.py b/hawkpost/settings/production.py index 1187a6e7..051b0002 100644 --- a/hawkpost/settings/production.py +++ b/hawkpost/settings/production.py @@ -60,3 +60,7 @@ RAVEN_CONFIG = { 'dsn': os.environ.get("SENTRY_URL") } + +# Axes Behind proxy +AXES_BEHIND_REVERSE_PROXY = True +AXES_NUM_PROXIES = 1 diff --git a/hawkpost/urls.py b/hawkpost/urls.py index 1daf2876..448f7d9e 100644 --- a/hawkpost/urls.py +++ b/hawkpost/urls.py @@ -17,9 +17,13 @@ from django.conf.urls import url, include from django.contrib import admin from django.conf.urls.i18n import i18n_patterns +from axes.decorators import watch_login +from allauth.account.views import login urlpatterns = [ + url(r'^admin/login/$', watch_login(admin.site.login)), url(r'^admin/', admin.site.urls), + url(r'^users/login/$', watch_login(login)), url(r'^users/', include('allauth.urls')), url(r'^users/', include('humans.urls')), url(r'^box/', include('boxes.urls')), diff --git a/pages/static/javascripts/authform.js b/pages/static/javascripts/authform.js index a27a1ec9..bce58553 100644 --- a/pages/static/javascripts/authform.js +++ b/pages/static/javascripts/authform.js @@ -6,57 +6,56 @@ $(document).ready(function(){ method: $this.attr("method"), data: $this.serialize() }).done(function(data){ - document.location = data.location + document.location = data.location; }).fail(function(data){ var errorContainer = $("#login-form-errors-js"); errorContainer.html(""); - var errors = []; - var form_errors = data.responseJSON.form_errors; - if(form_errors.__all__){ - errors = errors.concat(form_errors.__all__); + if (data.status === 400){ + var errors = data.responseJSON.form.errors; + var form_fields = data.responseJSON.form.fields; + if(form_fields.login.errors){ + errors = errors.concat(form_fields.login.errors); + } + if(form_fields.password.errors){ + errors = errors.concat(form_fields.password.errors); + } + for(var i=0;i" + errors[i] + "

"); + } + } else if (data.status === 403){ + var msg = "Too many failed attempts. The account is locked for 1 hour. Please try again later."; + errorContainer.append("

"+ msg +"

"); } - if(form_errors.email){ - errors = errors.concat(form_errors.email); - } - if(form_errors.password){ - errors = errors.concat(form_errors.password); - } - for(var i=0;i" + errors[i] + "

"); - } - }) + }); return false; }); $("#signup-form-js").on("submit", function(){ - var $this = $(this) + var $this = $(this); $.ajax({ url: $this.attr("action"), method: $this.attr("method"), data: $this.serialize() }).done(function(data){ - document.location = data.location + document.location = data.location; }).fail(function(data){ var errorContainer = $("#signup-form-errors-js"); errorContainer.html(""); - var errors = []; - var form_errors = data.responseJSON.form_errors; - if(form_errors.__all__){ - errors = errors.concat(form_errors.__all__); - } - if(form_errors.email){ - errors = errors.concat(form_errors.email); + var errors = data.responseJSON.form.errors; + var form_fields = data.responseJSON.form.fields; + if(form_fields.email.errors){ + errors = errors.concat(form_fields.email.errors); } - if(form_errors.password1){ - errors = errors.concat(form_errors.password1); + if(form_fields.password1.errors){ + errors = errors.concat(form_fields.password1.errors); } - if(form_errors.password2){ - errors = errors.concat(form_errors.password2); + if(form_fields.password2.errors){ + errors = errors.concat(form_fields.password2.errors); } for(var i=0;i" + errors[i] + "

"); } - }) + }); return false; }); diff --git a/requirements/base.in b/requirements/base.in index 7d551ef3..05746f3f 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -10,3 +10,4 @@ gunicorn==19.7.1 raven==6.1.0 django-timezone-field==2.0 django-braces==1.11.0 +django-axes==2.3.3 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index bed13d3b..12f7b9c5 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -10,6 +10,7 @@ celery==4.0.2 contextlib2==0.5.1 # via raven defusedxml==0.4.1 # via python3-openid django-allauth==0.32.0 +django-axes==2.3.3 django-braces==1.11.0 django-dotenv==1.4.1 django-timezone-field==2.0 @@ -21,7 +22,7 @@ oauthlib==1.0.3 # via requests-oauthlib psutil==4.1.0 # via gnupg psycopg2==2.7.1 python3-openid==3.0.9 # via django-allauth -pytz==2016.3 # via celery, django, django-timezone-field +pytz==2016.3 # via celery, django, django-axes, django-timezone-field raven==6.1.0 redis==2.10.5 requests-oauthlib==0.6.1 # via django-allauth diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt index dd26867a..572f7583 100644 --- a/requirements/requirements_dev.txt +++ b/requirements/requirements_dev.txt @@ -11,6 +11,7 @@ contextlib2==0.5.1 # via raven coverage==4.2 defusedxml==0.4.1 # via python3-openid django-allauth==0.32.0 +django-axes==2.3.3 django-braces==1.11.0 django-debug-toolbar==1.5 django-dotenv==1.4.1 @@ -26,7 +27,7 @@ oauthlib==1.0.3 # via requests-oauthlib psutil==4.1.0 # via gnupg psycopg2==2.7.1 python3-openid==3.0.9 # via django-allauth -pytz==2016.3 # via celery, django, django-timezone-field, hypothesis +pytz==2016.3 # via celery, django, django-axes, django-timezone-field, hypothesis raven==6.1.0 redis==2.10.5 requests-oauthlib==0.6.1 # via django-allauth