Skip to content
This repository has been archived by the owner on Feb 18, 2019. It is now read-only.

Commit

Permalink
Finished implementing account recovery, with unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Willison committed May 25, 2009
1 parent 37ff232 commit 924647b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 7 deletions.
11 changes: 8 additions & 3 deletions django_openid/auth.py
Expand Up @@ -29,6 +29,7 @@ class AuthConsumer(consumer.SessionConsumer):
recovery_complete_template = 'django_openid/recovery_complete.html'

recovery_email_from = None
recovery_email_subject = 'Recover your account'

password_logins_enabled = True
account_recovery_enabled = True
Expand All @@ -43,6 +44,8 @@ class AuthConsumer(consumer.SessionConsumer):
bad_password_message = 'Incorrect username or password'
invalid_token_message = 'Invalid token'
recovery_email_sent_message = 'Check your mail for further instructions'
recovery_not_found_message = 'No matching user was found'
recovery_multiple_found_message = 'Try entering your username instead'
r_user_not_found_message = 'That user account does not exist'

account_recovery_url = None
Expand Down Expand Up @@ -303,7 +306,7 @@ def do_recover(self, request, extra_message = None):
users = self.lookup_users_by_email(submitted)
if users:
if len(users) > 1:
extra_message = 'Try entering your username instead'
extra_message = self.recovery_multiple_found_message
user = None
else:
user = users[0]
Expand All @@ -312,6 +315,8 @@ def do_recover(self, request, extra_message = None):
return self.show_message(
request, 'E-mail sent', self.recovery_email_sent_message
)
else:
extra_message = self.recovery_not_found_message
return self.render(request, self.recover_template, {
'action': request.path,
'message': extra_message,
Expand Down Expand Up @@ -371,7 +376,7 @@ def do_r(self, request, token = ''):
'associate_url': urljoin(request.path, '../../associations/'),
'user': user,
})
do_r.urlregex = '^r/([\w.]+)/$'
do_r.urlregex = '^r/([^/]+)/$'

def generate_recovery_code(self, user):
# Code is {hex-days}.{hex-userid}.{signature}
Expand All @@ -392,7 +397,7 @@ def send_recovery_email(self, request, user):
'code': code,
'user': user,
}).content
send_email(
send_mail(
subject = self.recovery_email_subject,
message = email_body,
from_email = self.recovery_email_from or \
Expand Down
2 changes: 1 addition & 1 deletion django_openid/registration.py
Expand Up @@ -235,7 +235,7 @@ def do_c(self, request, token = ''):
else:
return self.show_error(request, c_already_confirmed_message)

do_c.urlregex = '^c/([\w.]+)/$'
do_c.urlregex = '^c/([^/]+)/$'

def create_user(self, request, data, openid=None):
from django.contrib.auth.models import User
Expand Down
54 changes: 51 additions & 3 deletions django_openid/tests/auth_tests.py
Expand Up @@ -30,7 +30,10 @@ def setUp(self):
)

# Create user accounts associated with OpenIDs
self.no_openids = User.objects.create(username = 'noopenids')
self.no_openids = User.objects.create(
username = 'noopenids',
email = 'noopenids@example.com'
)
self.no_openids.set_password('password')
self.no_openids.save()
self.one_openid = User.objects.create(username = 'oneopenid')
Expand Down Expand Up @@ -99,9 +102,10 @@ def testRegisterWithPassword(self):
self.assertEqual(len(mail.outbox), 1)

# Now extract and click that link
body = mail.outbox[0].body
msg = mail.outbox[0]
self.assertEqual(msg.to, [u'test@example.com'])
link = [
l.strip() for l in body.splitlines()
l.strip() for l in msg.body.splitlines()
if l.startswith('http://testserver/')
][0]
response = self.client.get(link)
Expand All @@ -114,3 +118,47 @@ def testRegisterWithPassword(self):
self.assertEqual(
user.groups.filter(name = 'Unconfirmed users').count(), 0
)

class AccountRecoveryTest(AuthTestBase):

def testRecoverAccountBadUsername(self):
response = self.client.get('/openid/recover/')
self.assertEqual(
response.template_name, 'django_openid/recover.html'
)
response = self.client.post('/openid/recover/', {
'recover': 'does-not-exist'
})
self.assertEqual(
response.template_context['message'],
RegistrationConsumer.recovery_not_found_message
)

def testRecoverAccountByUsername(self):
self.assertEqual(len(mail.outbox), 0)
response = self.client.post('/openid/recover/', {
'recover': 'noopenids'
})
self.assertEqual(len(mail.outbox), 1)
msg = mail.outbox[0]
self.assertEqual(msg.to, [u'noopenids@example.com'])
link = [
l.strip() for l in msg.body.splitlines()
if l.startswith('http://testserver/')
][0]

# Tampering with the link should cause it to fail
bits = link.split('.')
bits[-1] = 'X' + bits[-1]
tampered = '.'.join(bits)
response = self.client.get(tampered)
self.assert_('Invalid token' in str(response))
self.assertNotEqual(
response.template_name, 'django_openid/recovery_complete.html'
)
# Following that link should log us in
response = self.client.get(link)
self.assertEqual(
response.template_name, 'django_openid/recovery_complete.html'
)

0 comments on commit 924647b

Please sign in to comment.