Merge lp:~twom/canonical-identity-provider/2fa-qrcode-fix into lp:canonical-identity-provider/release

Proposed by Tom Wardill
Status: Merged
Approved by: Tom Wardill
Approved revision: no longer in the source branch.
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: lp:~twom/canonical-identity-provider/2fa-qrcode-fix
Merge into: lp:canonical-identity-provider/release
Diff against target: 91 lines (+37/-5)
3 files modified
src/identityprovider/templatetags/qrcode.py (+6/-3)
src/identityprovider/tests/test_templatetags.py (+30/-1)
src/webui/tests/test_views_devices.py (+1/-1)
To merge this branch: bzr merge lp:~twom/canonical-identity-provider/2fa-qrcode-fix
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Ricardo Kirkner (community) Approve
Review via email: mp+341510@code.launchpad.net

Commit message

Mark the output of the qrcode template tag as safe.

Description of the change

Mark the output of the qrcode template tag as safe.

Avoids encoding the google url, but the data is quoted explicitly, so should be safe.

Change notice for django:
https://docs.djangoproject.com/en/dev/releases/1.9/#simple-tag-now-wraps-tag-output-in-conditional-escape

Final output of string:
https://chart.googleapis.com/chart?chs=250x250&chld=L|0&cht=qr&chl=otpauth%3A//totp/UbuntuSSO/zl%40beyfvift.org%3Fsecret%3DABJYTWOCRT5P7QK4NEKTAYN2FWU5PHKI%26period%3D30

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve
Revision history for this message
Colin Watson (cjwatson) :
review: Approve
Revision history for this message
Otto Co-Pilot (otto-copilot) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/identityprovider/templatetags/qrcode.py'
2--- src/identityprovider/templatetags/qrcode.py 2016-12-14 10:53:13 +0000
3+++ src/identityprovider/templatetags/qrcode.py 2018-03-16 13:31:48 +0000
4@@ -2,12 +2,14 @@
5 # GNU Affero General Public License version 3 (see the file LICENSE).
6
7 from base64 import b32encode
8-from urllib import quote
9+from urllib import quote_plus
10 from django import template
11 from django.conf import settings
12+from django.utils.safestring import mark_safe
13
14 register = template.Library()
15-URL = 'https://chart.googleapis.com/chart?chs=250x250&chld=L|0&cht=qr&chl=%s'
16+URL = ('https://chart.googleapis.com/chart'
17+ '?chs=250x250&chld=L%%7C0&cht=qr&chl=%s')
18
19
20 def _encode(s):
21@@ -30,4 +32,5 @@
22 otp_url += '&period={}'.format(settings.TOTP_PERIOD)
23
24 # note: for https, you *must* use this domain
25- return URL % quote(otp_url)
26+ # mark this as safe as otherwise django will urlquote our data for us
27+ return mark_safe(URL % quote_plus(otp_url))
28
29=== modified file 'src/identityprovider/tests/test_templatetags.py'
30--- src/identityprovider/tests/test_templatetags.py 2014-11-28 19:08:01 +0000
31+++ src/identityprovider/tests/test_templatetags.py 2018-03-16 13:31:48 +0000
32@@ -1,12 +1,12 @@
33 # Copyright 2010 Canonical Ltd. This software is licensed under the
34 # GNU Affero General Public License version 3 (see the file LICENSE).
35
36+from django.template import Context, Template
37 from django.test import TestCase
38
39 from identityprovider.templatetags.google_analytics import expand_ga_commands
40 from identityprovider.templatetags.static_url import static_url
41
42-
43 SNIPPETS = [
44 None,
45 '',
46@@ -56,3 +56,32 @@
47 url = '/+relative'
48 with self.settings(RELATIVE_URL=url):
49 self.assertEqual(static_url('relative'), url)
50+
51+
52+class QRCodeURLTestCase(TestCase):
53+
54+ def test_not_encoded(self):
55+ """The url string should be marked as safe as we can't escape the &"""
56+ qr_data = {
57+ 'oath_mode': 'totp',
58+ 'ident': u'UbuntuSSO/zl@beyfvift.org',
59+ 'hex_key': 'A10EE5139362F3150BFABB7DDBEC5EE6FA2CA989',
60+ }
61+
62+ # Render the template tag so we see the final result
63+ # as calling the method won't result in the string being encoded
64+ template = Template(
65+ '{% load qrcode %}{% qrcode_url mode ident hex_key %}'
66+ )
67+ context = Context(qr_data)
68+ result = template.render(context)
69+
70+ # Ideally, we should just decode the string here and check if it
71+ # changes but the email address is correctly encoded,
72+ # so that changes if so.
73+ # instead, check the whole string we have generated
74+ expected_result = (
75+ u'https://chart.googleapis.com/chart?chs=250x250&'
76+ u'chld=L%7C0&cht=qr&chl=otpauth%3A%2F%2F%2FUbuntuSSO%2Fzl%40'
77+ u'beyfvift.org%3Fsecret%3DUEHOKE4TMLZRKC72XN65X3C6435CZKMJ')
78+ self.assertEqual(result, expected_result)
79
80=== modified file 'src/webui/tests/test_views_devices.py'
81--- src/webui/tests/test_views_devices.py 2018-02-09 20:56:16 +0000
82+++ src/webui/tests/test_views_devices.py 2018-03-16 13:31:48 +0000
83@@ -5,7 +5,7 @@
84
85 import re
86 from base64 import b16encode, b32encode
87-from urllib import quote as urlquote
88+from urllib import quote_plus as urlquote
89
90 import mock
91 from django.conf import settings