Mercurial > public > sg101
comparison accounts/forms.py @ 905:be233ba7ca31
Reworked registration process.
Previous one proved too challenging for some humans.
Hopefully made it simpler but still unusual to confuse bots.
Increased test coverage also.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 08 Mar 2015 11:06:07 -0500 |
parents | 79a71b9d0a2a |
children | 4aadaf3bc234 |
comparison
equal
deleted
inserted
replaced
904:d4479ebbd118 | 905:be233ba7ca31 |
---|---|
1 """forms for the accounts application""" | 1 """forms for the accounts application""" |
2 | 2 |
3 import logging | 3 import logging |
4 import random | |
4 | 5 |
5 from django import forms | 6 from django import forms |
6 from django.contrib.auth.models import User | 7 from django.contrib.auth.models import User |
7 from django.core.urlresolvers import reverse | 8 from django.core.urlresolvers import reverse |
8 from django.template.loader import render_to_string | 9 from django.template.loader import render_to_string |
14 from accounts.models import IllegalUsername | 15 from accounts.models import IllegalUsername |
15 from accounts.models import IllegalEmail | 16 from accounts.models import IllegalEmail |
16 | 17 |
17 | 18 |
18 logger = logging.getLogger('auth') | 19 logger = logging.getLogger('auth') |
20 | |
21 | |
22 CODE_WORD_CHOICES = [ | |
23 'reverb', | |
24 'sg101', | |
25 'guitar', | |
26 'showman', | |
27 'surf', | |
28 'surfer', | |
29 'tank', | |
30 'splash', | |
31 'gremmie', | |
32 'hodad', | |
33 'stomp', | |
34 'belairs', | |
35 'dickdale', | |
36 'chantays', | |
37 'astronauts', | |
38 'fender', | |
39 ] | |
40 | |
41 def generate_registration_code(): | |
42 """Generates a simple, random registration code for the user to enter.""" | |
43 return '{}-{}'.format(random.choice(CODE_WORD_CHOICES), random.randint(100, 999)) | |
19 | 44 |
20 | 45 |
21 class RegisterForm(forms.Form): | 46 class RegisterForm(forms.Form): |
22 """Form used to register with the website""" | 47 """Form used to register with the website""" |
23 username = forms.RegexField( | 48 username = forms.RegexField( |
54 widget=forms.TextInput(attrs={'style': 'display: none;'})) | 79 widget=forms.TextInput(attrs={'style': 'display: none;'})) |
55 question3 = forms.CharField(label="Don't put anything in this box", | 80 question3 = forms.CharField(label="Don't put anything in this box", |
56 required=False, | 81 required=False, |
57 widget=forms.TextInput(attrs={'class': 'text'})) | 82 widget=forms.TextInput(attrs={'class': 'text'})) |
58 | 83 |
59 SPAM_CHOICES = [(1, 'cat'), (2, '328'), (4, 'green'), (8, 'ocean')] | |
60 question4 = forms.ChoiceField(label="Pick the number", choices=SPAM_CHOICES) | |
61 question5 = forms.IntegerField(label="What number did you pick, above?", | |
62 widget=forms.TextInput(attrs={'class': 'text'})) | |
63 question6 = forms.ChoiceField(label="Pick the color", choices=SPAM_CHOICES) | |
64 | |
65 USER_QUALITIES = [ | |
66 (1, "I am the lowest form of life, a spammer"), | |
67 (2, "I'm a fan of surf music or I want to learn more"), | |
68 (3, "Someone is paying me to post links to this site"), | |
69 (4, "I am not going to spam this site"), | |
70 (5, "I understand my account may be removed if I post spam"), | |
71 (6, "I am going to spam the crap out of this site"), | |
72 (7, "Surf music is one of my interests, not spam"), | |
73 ] | |
74 question7 = forms.MultipleChoiceField( | |
75 label="Check all that apply", | |
76 choices=USER_QUALITIES, | |
77 required=False, | |
78 widget=forms.CheckboxSelectMultiple) | |
79 | |
80 | |
81 def __init__(self, *args, **kwargs): | 84 def __init__(self, *args, **kwargs): |
82 self.ip = kwargs.pop('ip', '?') | 85 self.ip = kwargs.pop('ip', '?') |
83 super(RegisterForm, self).__init__(*args, **kwargs) | 86 super(RegisterForm, self).__init__(*args, **kwargs) |
84 | 87 |
85 def clean_username(self): | 88 def clean_username(self): |
148 answer = self.cleaned_data.get('question3') | 151 answer = self.cleaned_data.get('question3') |
149 if answer: | 152 if answer: |
150 self._validation_error('Oops, that should be blank', answer) | 153 self._validation_error('Oops, that should be blank', answer) |
151 return answer | 154 return answer |
152 | 155 |
153 def clean_question4(self): | 156 def save(self, request): |
154 answer = self.cleaned_data.get('question4') | 157 request.session['reg_info'] = { |
155 if answer != u'2': | 158 'username': self.cleaned_data['username'], |
156 self._validation_error('Please pick the number', answer) | 159 'email': self.cleaned_data['email'], |
157 return answer | 160 'password': self.cleaned_data['password1'], |
158 | 161 'code': generate_registration_code(), |
159 def clean_question5(self): | 162 } |
160 answer = self.cleaned_data.get('question5') | 163 |
161 if answer != 328: | 164 def _validation_error(self, msg, param=None): |
162 self._validation_error('Please enter the correct number', answer) | 165 logger.error('Accounts/registration [%s]: %s (%s)', self.ip, msg, param) |
163 return answer | 166 raise forms.ValidationError(msg) |
164 | 167 |
165 def clean_question6(self): | 168 |
166 answer = self.cleaned_data.get('question6') | 169 class RegisterCodeForm(forms.Form): |
167 if answer != u'4': | 170 """Form for processing anti-spam code.""" |
168 self._validation_error('Please pick the color', answer) | 171 code = forms.CharField( |
169 return answer | 172 label="Registration code", |
170 | 173 widget=forms.TextInput(attrs={'class': 'text'})) |
171 def clean_question7(self): | 174 |
172 answer = self.cleaned_data.get('question7') | 175 def __init__(self, *args, **kwargs): |
173 answer.sort() | 176 self.session = kwargs.pop('session', None) |
174 if answer != [u'2', u'4', u'5', u'7']: | 177 self.ip = kwargs.pop('ip', '?') |
175 self._validation_error("Please double-check your checkboxes", answer) | 178 super(RegisterCodeForm, self).__init__(*args, **kwargs) |
176 return answer | 179 |
180 def clean_code(self): | |
181 reg_info = self.session.get('reg_info') | |
182 saved_code = reg_info.get('code') if reg_info else None | |
183 if not saved_code: | |
184 self._validation_error("Registration code error") | |
185 user_code = self.cleaned_data['code'] | |
186 if user_code: | |
187 user_code = user_code.strip() | |
188 | |
189 if user_code != saved_code: | |
190 self._validation_error("The registration code does not match") | |
177 | 191 |
178 def save(self): | 192 def save(self): |
179 pending_user = PendingUser.objects.create_pending_user(self.cleaned_data['username'], | 193 """Process successful registration.""" |
180 self.cleaned_data['email'], | 194 reg_info = self.session['reg_info'] |
181 self.cleaned_data['password1']) | 195 username = reg_info['username'] |
196 email = reg_info['email'] | |
197 password = reg_info['password'] | |
198 | |
199 pending_user = PendingUser.objects.create_pending_user( | |
200 username, | |
201 email, | |
202 password) | |
182 | 203 |
183 # Send the confirmation email | 204 # Send the confirmation email |
184 | |
185 site = Site.objects.get_current() | 205 site = Site.objects.get_current() |
186 admin_email = settings.DEFAULT_FROM_EMAIL | 206 admin_email = settings.DEFAULT_FROM_EMAIL |
187 | 207 |
188 activation_link = 'http://%s%s' % (site.domain, reverse('accounts.views.register_confirm', | 208 activation_link = 'http://%s%s' % ( |
189 kwargs = {'username' : pending_user.username, 'key' : pending_user.key})) | 209 site.domain, |
190 | 210 reverse('accounts-register_confirm', |
191 msg = render_to_string('accounts/registration_email.txt', | 211 kwargs={'username': pending_user.username, |
192 { | 212 'key': pending_user.key})) |
193 'site_name' : site.name, | 213 |
194 'site_domain' : site.domain, | 214 msg = render_to_string('accounts/registration_email.txt', { |
195 'user_email' : pending_user.email, | 215 'site_name' : site.name, |
196 'activation_link' : activation_link, | 216 'site_domain' : site.domain, |
197 'username' : pending_user.username, | 217 'user_email' : pending_user.email, |
198 'admin_email' : admin_email, | 218 'activation_link' : activation_link, |
219 'username' : pending_user.username, | |
220 'admin_email' : admin_email, | |
199 }) | 221 }) |
200 | 222 |
201 subject = 'Registration Confirmation for ' + site.name | 223 subject = 'Registration Confirmation for ' + site.name |
202 send_mail(subject, msg, admin_email, [self.cleaned_data['email']]) | 224 send_mail(subject, msg, admin_email, [email]) |
203 logger.info('Accounts/registration conf. email sent to %s for user %s; IP = %s', | 225 logger.info('Accounts/registration conf. email sent to %s for user %s; IP = %s', |
204 self.cleaned_data['email'], pending_user.username, self.ip) | 226 email, pending_user.username, self.ip) |
205 | 227 del self.session['reg_info'] |
206 return pending_user | |
207 | 228 |
208 def _validation_error(self, msg, param=None): | 229 def _validation_error(self, msg, param=None): |
209 logger.error('Accounts/registration [%s]: %s (%s)', self.ip, msg, param) | 230 logger.error('Accounts/registration [%s]: %s (%s)', self.ip, msg, param) |
210 raise forms.ValidationError(msg) | 231 raise forms.ValidationError(msg) |
211 | 232 |