Merge lp:~rvb/maas/logout-fix-1.2 into lp:maas/1.2

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 1388
Proposed branch: lp:~rvb/maas/logout-fix-1.2
Merge into: lp:maas/1.2
Diff against target: 122 lines (+70/-4)
3 files modified
src/maasserver/templates/maasserver/logout_confirm.html (+21/-0)
src/maasserver/tests/test_views_account.py (+25/-2)
src/maasserver/views/account.py (+24/-2)
To merge this branch: bzr merge lp:~rvb/maas/logout-fix-1.2
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Review via email: mp+214546@code.launchpad.net

Commit message

Backport revision 2219: Add a "logout confirmation" page. Using this, the logout action is protected against CSRF attacks because it uses a POST request, in conjunction with Django's CSRF protection feature.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/maasserver/templates/maasserver/logout_confirm.html'
2--- src/maasserver/templates/maasserver/logout_confirm.html 1970-01-01 00:00:00 +0000
3+++ src/maasserver/templates/maasserver/logout_confirm.html 2014-04-14 14:00:37 +0000
4@@ -0,0 +1,21 @@
5+{% extends "maasserver/base.html" %}
6+
7+{% block nav-active-settings %}active{% endblock %}
8+{% block title %}Logout{% endblock %}
9+{% block page-title %}Logout{% endblock %}
10+
11+{% block content %}
12+ <div class="block auto-width">
13+ <h2>
14+ Are you sure you want to log out?
15+ </h2>
16+ <p>
17+ <form action="." method="post">{% csrf_token %}
18+ <input type="hidden" name="post" value="yes" />
19+ <input type="submit" value="Log out" class="right" />
20+ <a href="{% url 'index' %}">Cancel</a>
21+ </form>
22+ </p>
23+ </div>
24+{% endblock %}
25+
26
27=== modified file 'src/maasserver/tests/test_views_account.py'
28--- src/maasserver/tests/test_views_account.py 2012-06-25 13:59:23 +0000
29+++ src/maasserver/tests/test_views_account.py 2014-04-14 14:00:37 +0000
30@@ -1,4 +1,4 @@
31-# Copyright 2012 Canonical Ltd. This software is licensed under the
32+# Copyright 2012-2014 Canonical Ltd. This software is licensed under the
33 # GNU Affero General Public License version 3 (see the file LICENSE).
34
35 """Test maasserver account views."""
36@@ -13,9 +13,15 @@
37 __all__ = []
38
39 from django.conf import settings
40+from django.contrib.auth import SESSION_KEY
41+from django.core.urlresolvers import reverse
42 from lxml.html import fromstring
43+from maasserver.testing import get_content_links
44 from maasserver.testing.factory import factory
45-from maasserver.testing.testcase import TestCase
46+from maasserver.testing.testcase import (
47+ LoggedInTestCase,
48+ TestCase,
49+ )
50
51
52 class TestLogin(TestCase):
53@@ -34,3 +40,20 @@
54 response = self.client.get('/accounts/login/')
55 self.assertTrue(response.context['no_users'])
56 self.assertEqual(path, response.context['create_command'])
57+
58+
59+class TestLogout(LoggedInTestCase):
60+
61+ def test_logout_link_present_on_homepage(self):
62+ response = self.client.get(reverse('index'))
63+ logout_link = reverse('logout')
64+ self.assertIn(
65+ logout_link,
66+ get_content_links(response, element='#user-options'))
67+
68+ def test_loggout_uses_POST(self):
69+ # Using POST for logging out, along with Django's csrf_token
70+ # tag, guarantees that we're protected against CSRF attacks on
71+ # the loggout page.
72+ self.client.post(reverse('logout'))
73+ self.assertNotIn(SESSION_KEY, self.client.session.keys())
74
75=== modified file 'src/maasserver/views/account.py'
76--- src/maasserver/views/account.py 2012-06-25 13:59:23 +0000
77+++ src/maasserver/views/account.py 2014-04-14 14:00:37 +0000
78@@ -15,6 +15,7 @@
79 "logout",
80 ]
81
82+from django import forms
83 from django.conf import settings as django_settings
84 from django.contrib import messages
85 from django.contrib.auth.views import (
86@@ -22,6 +23,8 @@
87 logout as dj_logout,
88 )
89 from django.core.urlresolvers import reverse
90+from django.shortcuts import render_to_response
91+from django.template import RequestContext
92 from maasserver.models import UserProfile
93
94
95@@ -33,6 +36,25 @@
96 return dj_login(request, extra_context=extra_context)
97
98
99+class LogoutForm(forms.Form):
100+ """Log-out confirmation form.
101+
102+ There is nothing interesting in this form, but it's needed in order
103+ to get Django's CSRF protection during logout.
104+ """
105+
106+
107 def logout(request):
108- messages.info(request, "You have been logged out.")
109- return dj_logout(request, next_page=reverse('login'))
110+ if request.method == 'POST':
111+ form = LogoutForm(request.POST)
112+ if form.is_valid():
113+ messages.info(request, "You have been logged out.")
114+ return dj_logout(request, next_page=reverse('login'))
115+ else:
116+ form = LogoutForm()
117+
118+ return render_to_response(
119+ 'maasserver/logout_confirm.html',
120+ {'form': form},
121+ context_instance=RequestContext(request),
122+ )

Subscribers

People subscribed via source and target branches

to status/vote changes: