Commit 8cff9ab1 authored by Michal Čihař's avatar Michal Čihař

Captcha protection for registration.

parent 4d2682c5
...@@ -23,6 +23,7 @@ from django.utils.translation import ugettext_lazy as _, get_language ...@@ -23,6 +23,7 @@ from django.utils.translation import ugettext_lazy as _, get_language
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from accounts.models import Profile, VerifiedEmail from accounts.models import Profile, VerifiedEmail
from accounts.captcha import MathCaptcha
from lang.models import Language from lang.models import Language
from trans.models import Project from trans.models import Project
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -321,6 +322,46 @@ class RegistrationForm(EmailForm): ...@@ -321,6 +322,46 @@ class RegistrationForm(EmailForm):
return '' return ''
class CaptchaRegistrationForm(RegistrationForm):
'''
Registration form with captcha protection.
'''
captcha = forms.IntegerField(required=True)
captcha_id = forms.CharField(widget=forms.HiddenInput)
def __init__(self, data=None, *args, **kwargs):
super(CaptchaRegistrationForm, self).__init__(
data,
*args,
**kwargs
)
# Load data
self.tampering = False
if data is None or 'captcha_id' not in data:
self.captcha = MathCaptcha()
else:
try:
self.captcha = MathCaptcha.from_hash(data['captcha_id'])
except ValueError:
self.captcha = MathCaptcha()
self.tampering = True
# Set correct label
self.fields['captcha'].label = _('What is %s') % self.captcha.question
self.fields['captcha_id'].initial = self.captcha.hashed
def clean_captcha(self):
'''
Validation for captcha.
'''
if (self.tampering
or not self.captcha.validate(self.cleaned_data['captcha'])):
raise forms.ValidationError(
_('Please check your math and try again.')
)
class PasswordForm(forms.Form): class PasswordForm(forms.Form):
''' '''
Form for setting password. Form for setting password.
......
...@@ -52,6 +52,8 @@ REGISTRATION_DATA = { ...@@ -52,6 +52,8 @@ REGISTRATION_DATA = {
'email': 'noreply@weblate.org', 'email': 'noreply@weblate.org',
'first_name': 'First', 'first_name': 'First',
'last_name': 'Last', 'last_name': 'Last',
'captcha_id': '00',
'captcha': '9999'
} }
...@@ -77,7 +79,20 @@ class RegistrationTest(TestCase): ...@@ -77,7 +79,20 @@ class RegistrationTest(TestCase):
reverse('password') reverse('password')
) )
def test_register_captcha(self):
response = self.client.post(
reverse('register'),
REGISTRATION_DATA
)
self.assertContains(
response,
'Please check your math and try again.'
)
def test_register(self): def test_register(self):
# Disable captcha
appsettings.REGISTRATION_CAPTCHA = False
response = self.client.post( response = self.client.post(
reverse('register'), reverse('register'),
REGISTRATION_DATA REGISTRATION_DATA
...@@ -109,6 +124,9 @@ class RegistrationTest(TestCase): ...@@ -109,6 +124,9 @@ class RegistrationTest(TestCase):
self.assertEqual(user.first_name, 'First') self.assertEqual(user.first_name, 'First')
self.assertEqual(user.last_name, 'Last') self.assertEqual(user.last_name, 'Last')
# Restore settings
appsettings.REGISTRATION_CAPTCHA = True
def test_reset(self): def test_reset(self):
''' '''
Test for password reset. Test for password reset.
......
...@@ -33,7 +33,7 @@ from urllib import urlencode ...@@ -33,7 +33,7 @@ from urllib import urlencode
from accounts.forms import ( from accounts.forms import (
RegistrationForm, PasswordForm, PasswordChangeForm, EmailForm, ResetForm, RegistrationForm, PasswordForm, PasswordChangeForm, EmailForm, ResetForm,
LoginForm, HostingForm LoginForm, HostingForm, CaptchaRegistrationForm
) )
from social.backends.utils import load_backends from social.backends.utils import load_backends
from social.apps.django_app.utils import BACKENDS from social.apps.django_app.utils import BACKENDS
...@@ -338,12 +338,17 @@ def register(request): ...@@ -338,12 +338,17 @@ def register(request):
''' '''
Registration form. Registration form.
''' '''
if appsettings.REGISTRATION_CAPTCHA:
form_class = CaptchaRegistrationForm
else:
form_class = RegistrationForm
if request.method == 'POST': if request.method == 'POST':
form = RegistrationForm(request.POST) form = form_class(request.POST)
if form.is_valid() and appsettings.REGISTRATION_OPEN: if form.is_valid() and appsettings.REGISTRATION_OPEN:
return complete(request, 'email') return complete(request, 'email')
else: else:
form = RegistrationForm() form = form_class()
return render_to_response( return render_to_response(
'accounts/register.html', 'accounts/register.html',
......
...@@ -320,6 +320,15 @@ For example you can allow script which does some cleanup: ...@@ -320,6 +320,15 @@ For example you can allow script which does some cleanup:
.. seealso:: :ref:`processing` .. seealso:: :ref:`processing`
.. setting:: REGISTRATION_CAPTCHA
REGISTRATION_CAPTCHA
--------------------
A boolean (either ``True`` or ``False``) indicating whether registration of new
accounts is protected by captcha. This setting is optional, and a default of
True will be assumed if it is not supplied.
.. setting:: REGISTRATION_OPEN .. setting:: REGISTRATION_OPEN
REGISTRATION_OPEN REGISTRATION_OPEN
......
...@@ -16,6 +16,7 @@ Released on ? 2013. ...@@ -16,6 +16,7 @@ Released on ? 2013.
* Improved source strings review. * Improved source strings review.
* Searching across all units. * Searching across all units.
* Better tracking of source strings. * Better tracking of source strings.
* Captcha protection for registration.
weblate 1.7 weblate 1.7
----------- -----------
......
...@@ -149,3 +149,6 @@ ANONYMOUS_USER_NAME = get('ANONYMOUS_USER_NAME', 'anonymous') ...@@ -149,3 +149,6 @@ ANONYMOUS_USER_NAME = get('ANONYMOUS_USER_NAME', 'anonymous')
# Enable registrations # Enable registrations
REGISTRATION_OPEN = get('REGISTRATION_OPEN', True) REGISTRATION_OPEN = get('REGISTRATION_OPEN', True)
# Captcha for registrations
REGISTRATION_CAPTCHA = get('REGISTRATION_CAPTCHA', True)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment