changeset 782:9133b4626a4b

Added additional security questions on the registration page.
author Brian Neal <bgneal@gmail.com>
date Tue, 13 May 2014 19:44:48 -0500
parents b6d6c9e37fae
children 004b3a90de66
files accounts/forms.py accounts/tests/test_views.py sg101/templates/accounts/register.html
diffstat 3 files changed, 199 insertions(+), 107 deletions(-) [+]
line wrap: on
line diff
--- a/accounts/forms.py	Sun May 04 15:37:56 2014 -0500
+++ b/accounts/forms.py	Tue May 13 19:44:48 2014 -0500
@@ -52,6 +52,31 @@
         widget=forms.TextInput(attrs={'class': 'text'}))
     question2 = forms.CharField(label='', required=False,
         widget=forms.TextInput(attrs={'style': 'display: none;'}))
+    question3 = forms.CharField(label="Don't put anything in this box",
+        required=False,
+        widget=forms.TextInput(attrs={'class': 'text'}))
+
+    SPAM_CHOICES = [(1, 'cat'), (2, '328'), (4, 'green'), (8, 'ocean')]
+    question4 = forms.ChoiceField(label="Pick the number", choices=SPAM_CHOICES)
+    question5 = forms.IntegerField(label="What number did you pick, above?",
+        widget=forms.TextInput(attrs={'class': 'text'}))
+    question6 = forms.ChoiceField(label="Pick the color", choices=SPAM_CHOICES)
+
+    USER_QUALITIES = [
+        (1, "I am the lowest form of life, a spammer"),
+        (2, "I'm a fan of surf music or I want to learn more"),
+        (3, "Someone is paying me to post links to this site"),
+        (4, "I am not going to spam this site"),
+        (5, "I understand my account may be removed if I post spam"),
+        (6, "I am going to spam the crap out of this site"),
+        (7, "Surf music is one of my interests, not spam"),
+    ]
+    question7 = forms.MultipleChoiceField(
+        label="Check all that apply",
+        choices=USER_QUALITIES,
+        required=False,
+        widget=forms.CheckboxSelectMultiple)
+
 
     def __init__(self, *args, **kwargs):
         self.ip = kwargs.pop('ip', '?')
@@ -119,6 +144,37 @@
             self._validation_error('Wrong answer #2', answer)
         return answer
 
+    def clean_question3(self):
+        answer = self.cleaned_data.get('question3')
+        if answer:
+            self._validation_error('Oops, that should be blank', answer)
+        return answer
+
+    def clean_question4(self):
+        answer = self.cleaned_data.get('question4')
+        if answer != u'2':
+            self._validation_error('Please pick the number', answer)
+        return answer
+
+    def clean_question5(self):
+        answer = self.cleaned_data.get('question5')
+        if answer != 328:
+            self._validation_error('Please enter the correct number', answer)
+        return answer
+
+    def clean_question6(self):
+        answer = self.cleaned_data.get('question6')
+        if answer != u'4':
+            self._validation_error('Please pick the color', answer)
+        return answer
+
+    def clean_question7(self):
+        answer = self.cleaned_data.get('question7')
+        answer.sort()
+        if answer != [u'2', u'4', u'5', u'7']:
+            self._validation_error("Sorry, try again", answer)
+        return answer
+
     def save(self):
         pending_user = PendingUser.objects.create_pending_user(self.cleaned_data['username'],
                 self.cleaned_data['email'],
--- a/accounts/tests/test_views.py	Sun May 04 15:37:56 2014 -0500
+++ b/accounts/tests/test_views.py	Tue May 13 19:44:48 2014 -0500
@@ -35,6 +35,23 @@
         IllegalUsername.objects.create(username='illegalusername')
         IllegalEmail.objects.create(email='illegal@example.com')
 
+        self.post_vals = {
+            'username': 'a_new_user',
+            'email': 'test@example.com',
+            'password1': 'my_password',
+            'password2': 'my_password',
+            'agree_age': 'on',
+            'agree_tos': 'on',
+            'agree_privacy': 'on',
+            'question1': '101',
+            'question2': '',
+            'question3': '',
+            'question4': u'2',
+            'question5': u'328',
+            'question6': u'4',
+            'question7': [u'2', u'4', u'5', u'7'],
+        }
+
     def test_get_view(self):
         """
         Test a simple get of the registration view
@@ -68,18 +85,9 @@
         Ensure we can't register with a pending username.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'pending_user',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['username'] = 'pending_user'
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'A pending user with that username already exists')
 
@@ -88,18 +96,9 @@
         Ensure we can't register with a banned username.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'illegalusername',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['username'] = 'illegalusername'
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'That username is not allowed')
 
@@ -108,18 +107,9 @@
         Ensure we can't register with a duplicate email address.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'existing_user@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['email'] = 'existing_user@example.com'
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'A user with that email address already exists')
 
@@ -128,18 +118,9 @@
         Ensure we can't register with a duplicate email address.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'pending_user@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['email'] = 'pending_user@example.com'
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'A pending user with that email address already exists')
 
@@ -148,18 +129,9 @@
         Ensure we can't register with a banned email address.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'illegal@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['email'] = 'illegal@example.com'
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'That email address is not allowed')
 
@@ -168,18 +140,9 @@
         Ensure the passwords match.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password_doesnt match',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        self.post_vals['password2'] = "doesn't match"
+        response = self.client.post(reverse('accounts-register'),
+            self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "The two password fields didn&#39;t match")
 
@@ -188,18 +151,9 @@
         Ensure our anti-spam question is answered.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password_doesnt match',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': 'huh',
-            'question2': '',
-            })
-
+        self.post_vals['question1'] = 'huh'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "Incorrect answer to our anti-spam question")
 
@@ -208,18 +162,102 @@
         Ensure our honeypot question check works.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password_doesnt match',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': 'non blank',
-            })
+        self.post_vals['question2'] = 'non blank'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
 
+    def test_question3(self):
+        """
+        Ensure our non-hidden honeypot question check works.
+
+        """
+        self.post_vals['question3'] = 'non blank'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+    def test_question4(self):
+        """
+        Ensure our security question 4 works
+
+        """
+        self.post_vals['question4'] = u'1'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question4'] = u'4'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question4'] = u'8'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+    def test_question5(self):
+        """
+        Ensure our security question 5 works
+
+        """
+        self.post_vals['question5'] = u'1'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question5'] = u'X'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question5'] = u'2983'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+    def test_question6(self):
+        """
+        Ensure our security question 6 works
+
+        """
+        self.post_vals['question6'] = u'1'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question6'] = u'2'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question6'] = u'8'
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+    def test_question7(self):
+        """Test security question 7"""
+
+        self.post_vals['question7'] = []
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question7'] = [u'1']
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question7'] = [u'6', u'2', u'4', u'5', u'7']
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
+        self.assertEqual(response.status_code, 200)
+
+        self.post_vals['question7'] = [u'4', u'3', u'7']
+        response = self.client.post(reverse('accounts-register'),
+             self.post_vals)
         self.assertEqual(response.status_code, 200)
 
     def test_success(self):
@@ -227,18 +265,8 @@
         Ensure we can successfully register.
 
         """
-        response = self.client.post(reverse('accounts-register'), {
-            'username': 'a_new_user',
-            'email': 'test@example.com',
-            'password1': 'my_password',
-            'password2': 'my_password',
-            'agree_age': 'on',
-            'agree_tos': 'on',
-            'agree_privacy': 'on',
-            'question1': '101',
-            'question2': '',
-            })
-
+        response = self.client.post(reverse('accounts-register'),
+                self.post_vals)
         self.assertEqual(response.status_code, 302)
 
         try:
--- a/sg101/templates/accounts/register.html	Sun May 04 15:37:56 2014 -0500
+++ b/sg101/templates/accounts/register.html	Tue May 13 19:44:48 2014 -0500
@@ -13,6 +13,14 @@
    <li>You must agree to our <a href="/policy/tos/" target="_blank">Terms of Service</a>.</li>
    <li>You must agree to our <a href="/policy/privacy/" target="_blank">Privacy Policy</a>.</li>
 </ul>
+<div class="notice">
+   We are really sorry about all the crazy questions in the registration form.
+   We are forced to do this to reduce the number of automated registrations by
+   spammers. Hopefully this will be a small inconvenience for you and you will
+   hold no hard feelings toward us. Hey at least we don't have one of those
+   annoying CAPTCHA things, right? Again, we are sorry for making you do
+   this...
+</div>
 <form action="." method="post">{% csrf_token %}
 <table>
    {{ form.as_table }}