diff --git a/seahub/auth/forms.py b/seahub/auth/forms.py index 39d4dde475..42fb58236a 100644 --- a/seahub/auth/forms.py +++ b/seahub/auth/forms.py @@ -9,6 +9,8 @@ from seahub.auth import authenticate from seahub.auth.tokens import default_token_generator from seahub.utils import IS_EMAIL_CONFIGURED +from captcha.fields import CaptchaField + class AuthenticationForm(forms.Form): """ Base class for authenticating users. Extend this to get a form that accepts @@ -26,7 +28,10 @@ class AuthenticationForm(forms.Form): """ self.request = request self.user_cache = None + use_captcha = kwargs.pop('use_captcha', False) super(AuthenticationForm, self).__init__(*args, **kwargs) + if use_captcha: + self.fields['captcha'] = CaptchaField() def clean(self): username = self.cleaned_data.get('username') diff --git a/seahub/auth/views.py b/seahub/auth/views.py index 3ebb8e6527..dcde4ac5a5 100644 --- a/seahub/auth/views.py +++ b/seahub/auth/views.py @@ -21,6 +21,9 @@ from seahub.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChange from seahub.auth.tokens import default_token_generator from seahub.base.accounts import User +from seahub.base.models import UserLoginAttempt +from settings import LOGIN_ATTEMPT_LIMIT, LOGIN_ATTEMPT_TIMEOUT +from datetime import datetime # Get an instance of a logger logger = logging.getLogger(__name__) @@ -37,9 +40,19 @@ def login(request, template_name='registration/login.html', return HttpResponseRedirect(reverse(redirect_if_logged_in)) redirect_to = request.REQUEST.get(redirect_field_name, '') + username = request.REQUEST.get('username', '') + if username: + login_attempt, created = UserLoginAttempt.objects.get_or_create(username=username) + if login_attempt.last_attempt_time + LOGIN_ATTEMPT_TIMEOUT < datetime.now(): + login_attempt.delete() + login_attempt = UserLoginAttempt.objects.create(username=username) + if login_attempt.failed_attempts >= LOGIN_ATTEMPT_LIMIT: + use_captcha = True + else: + use_captcha = False if request.method == "POST": - form = authentication_form(data=request.POST) + form = authentication_form(data=request.POST, use_captcha=use_captcha) if form.is_valid(): # Light security check -- make sure redirect_to isn't garbage. if not redirect_to or ' ' in redirect_to: @@ -58,7 +71,13 @@ def login(request, template_name='registration/login.html', if request.session.test_cookie_worked(): request.session.delete_test_cookie() + login_attempt.delete() return HttpResponseRedirect(redirect_to) + else: + login_attempt.failed_attempts += 1 + login_attempt.save() + if login_attempt.failed_attempts == LOGIN_ATTEMPT_LIMIT: + form = authentication_form(data=request.POST, use_captcha=True) else: form = authentication_form(request) diff --git a/seahub/base/models.py b/seahub/base/models.py index b4a4efe1f3..ddae04c4c7 100644 --- a/seahub/base/models.py +++ b/seahub/base/models.py @@ -322,3 +322,11 @@ class InnerPubMsgReply(models.Model): # msg_type='innerpubmsg_reply', # detail=msg_id) # n.save() + +class UserLoginAttempt(models.Model): + username = models.CharField(max_length=255) + last_attempt_time = models.DateTimeField(auto_now_add=True) + failed_attempts = models.IntegerField(default=0) + + class Meta: + ordering = ['-last_attempt_time'] diff --git a/seahub/settings.py b/seahub/settings.py index 31bcfb3bb7..390b4cb552 100644 --- a/seahub/settings.py +++ b/seahub/settings.py @@ -160,6 +160,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'registration', + 'captcha', 'seahub.api2', 'seahub.avatar', @@ -339,6 +340,11 @@ LOGGING = { } } +#Login Attempt +import datetime +LOGIN_ATTEMPT_LIMIT = 3 +LOGIN_ATTEMPT_TIMEOUT = datetime.timedelta(minutes=15) + ################# # Email sending # ################# diff --git a/seahub/templates/registration/login.html b/seahub/templates/registration/login.html index 748a13d9c2..30d0607c04 100644 --- a/seahub/templates/registration/login.html +++ b/seahub/templates/registration/login.html @@ -9,9 +9,14 @@ + {{ form.captcha }} {% if form.errors %} + {% if form.captcha.errors %} +

{% trans "Incorrect CAPTCHA" %}

+ {% else %}

{% trans "Incorrect email or password" %}

+ {% endif %} {% else %}

{% endif %} diff --git a/seahub/urls.py b/seahub/urls.py index d0394ed4cc..f8fa380b28 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -145,6 +145,7 @@ urlpatterns = patterns('', (r'^message/', include('seahub.message.urls')), (r'^profile/', include('seahub.profile.urls')), (r'^share/', include('seahub.share.urls')), + url(r'^captcha/', include('captcha.urls')), ### system admin ### url(r'^sys/seafadmin/$', sys_repo_admin, name='sys_repo_admin'),