Skip to content
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.

Commit

Permalink
fixes bug 921672 - Upgrade to django-browserid 0.9
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbe committed Oct 2, 2013
1 parent 5172f48 commit 04481ea
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 119 deletions.
123 changes: 111 additions & 12 deletions airmozilla/auth/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

from django.conf import settings
from django.test import TestCase
from django.utils.importlib import import_module

from funfactory.urlresolvers import reverse

import mock
from nose.tools import ok_
from nose.tools import ok_, eq_

from airmozilla.auth.browserid_mock import mock_browserid
from airmozilla.auth import mozillians
from airmozilla.main.models import UserProfile


VOUCHED_FOR = """
Expand Down Expand Up @@ -125,23 +127,49 @@ def mocked_get(url, **options):

class TestViews(TestCase):

def _login_attempt(self, email, assertion='fakeassertion123'):
def setUp(self):
super(TestViews, self).setUp()

engine = import_module(settings.SESSION_ENGINE)
store = engine.SessionStore()
store.save() # we need to make load() work, or the cookie is worthless
self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key

def shortDescription(self):
# Stop nose using the test docstring and instead the test method name.
pass

def get_messages(self):
return self.client.session['_messages']

def _login_attempt(self, email, assertion='fakeassertion123', next=None):
if not next:
next = '/'
with mock_browserid(email):
r = self.client.post(reverse('auth:mozilla_browserid_verify'),
{'assertion': assertion})
return r
post_data = {
'assertion': assertion,
'next': next
}
return self.client.post(
'/browserid/login/',
post_data
)

def test_invalid(self):
"""Bad BrowserID form (i.e. no assertion) -> failure."""
response = self._login_attempt(None, None)
self.assertRedirects(response,
reverse(settings.LOGIN_REDIRECT_URL_FAILURE))
self.assertRedirects(
response,
settings.LOGIN_REDIRECT_URL_FAILURE + '?bid_login_failed=1'
)

def test_bad_verification(self):
"""Bad verification -> failure."""
response = self._login_attempt(None)
self.assertRedirects(response,
reverse(settings.LOGIN_REDIRECT_URL_FAILURE))
self.assertRedirects(
response,
settings.LOGIN_REDIRECT_URL_FAILURE + '?bid_login_failed=1'
)

@mock.patch('requests.get')
def test_nonmozilla(self, rget):
Expand All @@ -155,17 +183,88 @@ def mocked_get(url, **options):
rget.side_effect = mocked_get

response = self._login_attempt('tmickel@mit.edu')
self.assertRedirects(
response,
settings.LOGIN_REDIRECT_URL_FAILURE + '?bid_login_failed=1'
)

# now with a non-mozillian that is vouched for
response = self._login_attempt('peterbe@gmail.com')
self.assertRedirects(response,
reverse(settings.LOGIN_REDIRECT_URL_FAILURE))
settings.LOGIN_REDIRECT_URL)

@mock.patch('requests.get')
def test_nonmozilla_vouched_for_second_time(self, rget):
assert not UserProfile.objects.all()

def mocked_get(url, **options):
return Response(VOUCHED_FOR)

rget.side_effect = mocked_get

# now with a non-mozillian that is vouched for
response = self._login_attempt('peterbe@gmail.com')
self.assertRedirects(response,
reverse(settings.LOGIN_REDIRECT_URL))
settings.LOGIN_REDIRECT_URL)

# should be logged in
response = self.client.get('/')
eq_(response.status_code, 200)
ok_('Sign in' not in response.content)
ok_('Sign out' in response.content)

profile, = UserProfile.objects.all()
ok_(profile.contributor)

# sign out
response = self.client.get(reverse('browserid_logout'))
eq_(response.status_code, 302)
# should be logged out
response = self.client.get('/')
eq_(response.status_code, 200)
ok_('Sign in' in response.content)
ok_('Sign out' not in response.content)

# sign in again
response = self._login_attempt('peterbe@gmail.com')
self.assertRedirects(response,
settings.LOGIN_REDIRECT_URL)
# should not have created another one
eq_(UserProfile.objects.all().count(), 1)

# sign out again
response = self.client.get(reverse('browserid_logout'))
eq_(response.status_code, 302)
# pretend this is lost
profile.contributor = False
profile.save()
response = self._login_attempt('peterbe@gmail.com')
self.assertRedirects(response,
settings.LOGIN_REDIRECT_URL)
# should not have created another one
eq_(UserProfile.objects.filter(contributor=True).count(), 1)

def test_mozilla(self):
"""Mozilla email -> success."""
# Try the first allowed domain
response = self._login_attempt('tmickel@' + settings.ALLOWED_BID[0])
self.assertRedirects(response,
reverse(settings.LOGIN_REDIRECT_URL))
settings.LOGIN_REDIRECT_URL)

@mock.patch('airmozilla.auth.views.logger')
@mock.patch('requests.get')
def test_nonmozilla_mozillians_unhappy(self, rget, rlogger):
assert not UserProfile.objects.all()

def mocked_get(url, **options):
raise mozillians.BadStatusCodeError('crap!')

rget.side_effect = mocked_get

# now with a non-mozillian that is vouched for
response = self._login_attempt('peterbe@gmail.com')
self.assertRedirects(
response,
settings.LOGIN_REDIRECT_URL_FAILURE + '?bid_login_failed=1'
)
eq_(rlogger.error.call_count, 1)
11 changes: 0 additions & 11 deletions airmozilla/auth/urls.py

This file was deleted.

93 changes: 36 additions & 57 deletions airmozilla/auth/views.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,51 @@
import logging
from django.conf import settings
from django.contrib import auth
from django.shortcuts import redirect, render
from django.views.decorators.http import require_POST
from django.contrib import messages

from django_browserid.base import get_audience
from django_browserid.auth import verify
from django_browserid.forms import BrowserIDForm
from .mozillians import is_vouched, BadStatusCodeError
from airmozilla.main.models import UserProfile

from django_browserid.views import Verify

@require_POST
def mozilla_browserid_verify(request):
"""Custom BrowserID verifier for mozilla addresses."""
form = BrowserIDForm(request.POST)
if form.is_valid():
assertion = form.cleaned_data['assertion']
audience = get_audience(request)
result = verify(assertion, audience)
try:
_ok_assertion = False
_is_contributor = False
if result:
_domain = result['email'].split('@')[-1]
if _domain in settings.ALLOWED_BID:
_ok_assertion = True
elif is_vouched(result['email']):
_ok_assertion = True
_is_contributor = True
logger = logging.getLogger('auth')

if _ok_assertion:
user = auth.authenticate(
assertion=assertion,
audience=audience
)
auth.login(request, user)
if _is_contributor:
try:
profile = user.get_profile()
if not profile.contributor:
profile.contributor = True
profile.save()
except UserProfile.DoesNotExist:
profile = UserProfile.objects.create(
user=user,
contributor=True
)
return redirect(settings.LOGIN_REDIRECT_URL)
elif result:

class CustomBrowserIDVerify(Verify):

def login_success(self):
"""the user passed the BrowserID hurdle, but do they have a valid
email address or vouched for in Mozillians"""
domain = self.user.email.split('@')[-1]
try:
if domain in settings.ALLOWED_BID:
# awesome!
pass
elif is_vouched(self.user.email):
try:
profile = self.user.get_profile()
if not profile.contributor:
profile.contributor = True
profile.save()
except UserProfile.DoesNotExist:
profile = UserProfile.objects.create(
user=self.user,
contributor=True
)
else:
messages.error(
request,
'Email (%s) authenticated but not vouched for' %
result['email']
self.request,
'Email {0} authenticated but not vouched for'
.format(self.user.email)
)
return super(CustomBrowserIDVerify, self).login_failure()
except BadStatusCodeError:
logging.error('Unable to call out to mozillians',
exc_info=True)
logger.error('Unable to call out to mozillians', exc_info=True)
messages.error(
request,
'Email (%s) authenticated but unable to connect to '
'Mozillians to see if are vouched. ' %
result['email']
self.request,
'Email {0} authenticated but unable to connect to '
'Mozillians to see if are vouched. '
.format(self.user.email)
)
return super(CustomBrowserIDVerify, self).login_failure()

return redirect(settings.LOGIN_REDIRECT_URL_FAILURE)


def login_failure(request):
return render(request, 'auth/login_failure.html')
return super(CustomBrowserIDVerify, self).login_success()
5 changes: 5 additions & 0 deletions airmozilla/main/static/main/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ body #page { background-color: #20272a; background-image: url("/static/main/img/
}
}

/* Persona ***************/
a.persona-button span {
color: #fff;
}

/* @Main feature *********/
.main-feature { background: #eee; color: #506168; padding: 60px 48px 48px 232px; margin: 0 -48px 48px; position: relative; min-height: 160px; }
.main-feature .feature-type { font-size: 16px; text-transform: uppercase; position: absolute; left: 48px; top: 24px; }
Expand Down
2 changes: 1 addition & 1 deletion airmozilla/main/templates/main/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h2 class="section-title">{{ _('Sign In') }}</h2>
<article>
{% if request.user.is_active %}
<p>{{ _('You are signed in as {email}')|f(email=request.user.email) }}.
<a href="{{ url('auth:logout') }}">{{ _('Sign out') }}</a>
{{ browserid_logout(text='Sign out') }}
</p>
{% else %}
<p>{{ _('This page requires that you are signed-in.
Expand Down
34 changes: 13 additions & 21 deletions airmozilla/main/templates/main/main_base.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{% extends "base.html" %}
{% set page = page|default('home') -%}
{% set nav_main = [
('Home', 'main:home', 'home', True),
('About', '/about/', 'about', True),
('Channels', 'main:channels', 'channels', True),
('Tag Cloud', 'main:tag_cloud', 'tag_cloud', True),
('Requests', 'suggest:start', 'suggest', request.user.is_active),
('Management', 'manage:home', '', request.user.is_staff),
('Sign out', 'auth:logout', '', request.user.is_active),
('Home', 'main:home', 'home', True, ''),
('About', '/about/', 'about', True, ''),
('Channels', 'main:channels', 'channels', True, ''),
('Tag Cloud', 'main:tag_cloud', 'tag_cloud', True, ''),
('Requests', 'suggest:start', 'suggest', request.user.is_active, ''),
('Management', 'manage:home', '', request.user.is_staff, ''),
('Sign out', '/browserid/logout/', '', request.user.is_active, 'browserid-logout'),
] -%}

{% block page_title %}
Expand All @@ -22,22 +22,23 @@
{% compress css %}
<link href="{{ static('main/css/onemozilla.css') }}" rel="stylesheet" type="text/css">
<link href="{{ static('main/css/main.css') }}" rel="stylesheet" type="text/css">
<link href="{{ static('main/css/persona-button.css') }}" rel="stylesheet" type="text/css">
{% endcompress %}
{{ browserid_css() }}
<link href="//mozorg.cdn.mozilla.net/media/css/tabzilla-min.css" rel="stylesheet">
{% endblock %}

{% block content %}
{{ browserid_info() }}
<div id="page">
<div class="wrap">
<header id="masthead">
<nav id="nav-main" role="navigation">
<ul>
{% for title, view, id, show in nav_main %}
{% for title, view, id, show, class in nav_main %}
{% if show %}
<li>
<a href="{% if '/' in view %}{{ view }}{% else %}{{ url(view) }}{% endif %}"
{% if id == page %} class="current"{% endif %}>
class="{{ class }}{% if id == page %} current"{% endif %}">
{{ title }}
</a>
</li>
Expand Down Expand Up @@ -77,14 +78,7 @@ <h3 class="widget-title">{{ _('Your Local Time') }}</h3>
</p>
</aside>
{% if not request.user.is_active %}
<a href="#" class="persona-button dark" id="browserid"
title="Sign in with Persona">
<span>Sign In</span>
</a>
<form method="post" action="{{ url('auth:mozilla_browserid_verify') }}">
{{ csrf() }}
{{ browserid_form.as_p() }}
</form>
{{ browserid_login(text='Sign in', color='dark', next=request.build_absolute_uri()) }}
{% endif %}

{% if sidebar_top %}
Expand Down Expand Up @@ -142,7 +136,5 @@ <h3 class="widget-title">{{ _('Your Local Time') }}</h3>
{{ super() }}
{# the day Air Mozilla uses L10n we can remove this /en-US/ here. For not it reduces redirects. #}
<script src="//mozorg.cdn.mozilla.net/en-US/tabzilla/tabzilla.js"></script>
{% if not request.user.is_active %}
{{ browserid_form.media }}
{% endif %}
{{ browserid_js() }}
{% endblock %}
4 changes: 1 addition & 3 deletions airmozilla/manage/templates/manage/manage_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
{% set navigation_bar = [
('manage:home', 'Dashboard', 'home', 'icon-home', True, ''),
('main:home', 'Exit Management', '', 'icon-backward', True, ''),
('auth:logout', 'Sign out', '', 'icon-off', True, ''),

('', 'Event Tools', '', '', perms.main.add_event, ''),
('', 'Event Tools', '', '', perms.main.add_event, ''),
('manage:event_request', 'Initiate event', 'erequest', 'icon-calendar',
perms.main.add_event, ''),
('manage:participants', 'Participants', 'part_edit', 'icon-picture',
Expand Down
Loading

0 comments on commit 04481ea

Please sign in to comment.