Skip to content

Commit 3616eb1

Browse files
author
Adam Fast
committed
Added detection to Discover Failures on OpenID (triggered by incorrect hostnames, sites that aren't OpenID providers, etc.) and now handling them gracefully instead of with a 500. Also added open_id_errors templatetag to retrieve those errors for use in an error page (we're using it on the login page) and a test for that templatetag.
1 parent 17f862e commit 3616eb1

File tree

5 files changed

+89
-3
lines changed

5 files changed

+89
-3
lines changed

socialregistration/templatetags/socialregistration_tags.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import re
12
from django import template
3+
from django.template import resolve_variable, Variable
24

35
register = template.Library()
46

@@ -16,4 +18,39 @@ def render(self, context):
1618
from django.template.defaulttags import CsrfTokenNode
1719
return CsrfTokenNode().render(context)
1820
except ImportError:
19-
return u''
21+
return u''
22+
23+
24+
@register.tag
25+
def open_id_errors(parser, token):
26+
"""
27+
Retrieve OpenID errors and the provider that caused them from session for display to the user.
28+
"""
29+
30+
# This version uses a regular expression to parse tag contents.
31+
try:
32+
# Splitting by None == splitting by spaces.
33+
tag_name, arg = token.contents.split(None, 1)
34+
except ValueError:
35+
raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
36+
37+
m = re.search(r'(\w+)', arg)
38+
if not m:
39+
raise template.TemplateSyntaxError, "%r tag had invalid arguments" % tag_name
40+
request = m.groups()[0]
41+
return OpenIDErrorsNode(request)
42+
43+
class OpenIDErrorsNode(template.Node):
44+
def __init__(self, request):
45+
self.request = Variable(request)
46+
47+
def render(self, context):
48+
request = self.request.resolve(context)
49+
context['openid_error'] = request.session.get('openid_error', False)
50+
context['openid_provider'] = request.session.get('openid_provider', '')
51+
52+
# clear the error once it's been displayed once
53+
request.session['openid_error'] = ''
54+
request.session['openid_provider'] = ''
55+
56+
return u''

socialregistration/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from socialregistration.tests.templatetags import *
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from django import template
2+
from django.conf import settings
3+
from django.contrib.sites.models import Site
4+
from django.http import HttpRequest
5+
from django.test import TestCase
6+
7+
class MockHttpRequest(HttpRequest):
8+
9+
def __init__(self, *args, **kwargs):
10+
super(MockHttpRequest, self).__init__(*args, **kwargs)
11+
12+
self.session = {}
13+
14+
class SocialRegistrationTemplateTagTests(TestCase):
15+
16+
def setUp(self):
17+
# set up a site object in case the current site ID doesn't exist
18+
site = Site.objects.get_or_create(pk=settings.SITE_ID)
19+
20+
def render(self, template_string, context={}):
21+
"""Return the rendered string or the exception raised while rendering."""
22+
try:
23+
t = template.Template(template_string)
24+
c = template.Context(context)
25+
return t.render(c)
26+
except Exception, e:
27+
return e
28+
29+
def test_open_id_error(self):
30+
request = MockHttpRequest()
31+
32+
request.session['openid_error'] = True
33+
request.session['openid_provider'] = 'whizzle'
34+
35+
template = """{% load socialregistration_tags %}{% open_id_errors request %}{{ openid_error }}|{{ openid_provider }}"""
36+
result = self.render(template, {'request': request,})
37+
self.assertEqual(result, u'True|whizzle')
38+
39+
# but accessing it a second time, the error should have cleared.
40+
template = """{% load socialregistration_tags %}{% open_id_errors request %}{{ openid_error }}|{{ openid_provider }}"""
41+
result = self.render(template, {'request': request,})
42+
self.assertEqual(result, u'|')
43+

socialregistration/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import oauth2 as oauth
2525
from openid.consumer import consumer as openid
26+
from openid.consumer.discover import DiscoveryFailure
2627
from openid.store.interface import OpenIDStore as OIDStore
2728
from openid.association import Association as OIDAssociation
2829

socialregistration/views.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from socialregistration.forms import UserForm, ClaimForm, ExistingUser
2121
from socialregistration.utils import (OAuthClient, OAuthTwitter,
22-
OpenID, _https)
22+
OpenID, _https, DiscoveryFailure)
2323
from socialregistration.models import FacebookProfile, TwitterProfile, OpenIDProfile
2424

2525

@@ -273,7 +273,11 @@ def openid_redirect(request):
273273
),
274274
request.GET.get('openid_provider')
275275
)
276-
return client.get_redirect()
276+
try:
277+
return client.get_redirect()
278+
except DiscoveryFailure:
279+
request.session['openid_error'] = True
280+
return HttpResponseRedirect(reverse('django.contrib.auth.views.login'))
277281

278282
def openid_callback(request, template='socialregistration/openid.html',
279283
extra_context=dict(), account_inactive_template='socialregistration/account_inactive.html'):

0 commit comments

Comments
 (0)