Merge lp:~gary/launchpad/bug538236 into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Deryck Hodge
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~gary/launchpad/bug538236
Merge into: lp:launchpad
Diff against target: 194 lines (+79/-12)
6 files modified
lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt (+32/-8)
lib/canonical/launchpad/templates/launchpad-noreferrer.pt (+26/-0)
lib/canonical/launchpad/webapp/error.py (+5/-0)
lib/canonical/launchpad/webapp/interfaces.py (+4/-0)
lib/canonical/launchpad/webapp/publication.py (+3/-4)
lib/canonical/launchpad/zcml/launchpad.zcml (+9/-0)
To merge this branch: bzr merge lp:~gary/launchpad/bug538236
Reviewer Review Type Date Requested Status
Gary Poster (community) Abstain
Deryck Hodge (community) release-critical Approve
Michael Nelson (community) ui Approve
Guilherme Salgado (community) Approve
Review via email: mp+22276@code.launchpad.net

Commit message

add an exception and an exception view for the case of a browser not including a REFERER header.

Description of the change

This fixes bug 538236 by adding an exception and an exception view for the case of a browser not including a REFERER header.

Tests passed on EC2.

This needs a code and a ui review, and is also a release-critical candidate.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt
  lib/canonical/launchpad/templates/launchpad-noreferrer.pt
  lib/canonical/launchpad/webapp/error.py
  lib/canonical/launchpad/webapp/interfaces.py
  lib/canonical/launchpad/webapp/publication.py
  lib/canonical/launchpad/zcml/launchpad.zcml

== Pylint notices ==

lib/canonical/launchpad/webapp/interfaces.py
    16: [F0401] Unable to import 'lazr.batchnavigator.interfaces' (No module named batchnavigator)
    17: [F0401] Unable to import 'lazr.enum' (No module named enum)

lib/canonical/launchpad/webapp/publication.py
    63: [F0401] Unable to import 'lazr.uri' (No module named uri)

To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :
Revision history for this message
Guilherme Salgado (salgado) wrote :

Hi Gary,

This looks good. I have just one suggestion below.

 review approve

On Sat, 2010-03-27 at 03:39 +0000, Gary Poster wrote:
> === modified file 'lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt'
> --- lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt 2010-03-19 20:02:25 +0000
> +++ lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt 2010-03-27 03:39:20 +0000
> @@ -66,10 +66,43 @@
> >>> browser.getControl('Create').click()
> Traceback (most recent call last):
> ...
> - OffsiteFormPostError: No value for REFERER header
> -
> -Though, to support apport, we let POST requests without a REFERER header
> -go through to +storeblob.
> + NoReferrerError: No value for REFERER header
> +
> +When a POST request is rejected because the REFERER header is missing, it
> +may be because the user is trying to enforce anonymity. Therefore, we
> +present a hopefully helpful error message.
> +
> + >>> browser.handleErrors = True
> + >>> browser.open('http://launchpad.dev/people/+newteam')
> + MONKEYPATCH ACTIVATED
> + >>> browser.getControl('Name', index=0).value = 'team3'
> + >>> browser.getControl('Display Name').value = 'Team 3'
> + >>> browser.getControl('Create').click()
> + Traceback (most recent call last):
> + ...
> + HTTPError...
> + >>> print browser.headers['status']
> + 403 Forbidden
> + >>> print extract_text(find_main_content(browser.contents))
> + No REFERER Header
> + Launchpad requires a REFERER header to perform
> + this action. There is no REFERER header present.
> + Perhaps you have blocked REFERER headers or perhaps
> + you navigated to this URL in an unexpected way.
> + Unblock REFERER headers for launchpad.net and try
> + again, or see this
> + FAQ for more information.
> + You can also join the
> + #launchpad IRC support channel on irc.freenode.net for further
> + assistance.

I'd elide most of the text above, to make the test less likely to
(unnecessarily) fail when somebody changes the page's content.

> + >>> browser.getLink('this FAQ').url
> + 'https://answers.edge.launchpad.net/launchpad/+faq/1024'
> + >>> browser.handleErrors = False
> +
> +We have a few exceptional cases in which we allow POST requests without a
> +REFERER header.
> +
> +To support apport, we allow it for +storeblob.
>
> >>> browser.post('http://launchpad.dev/+storeblob', 'x=1')
> MONKEYPATCH ACTIVATED
> @@ -95,7 +128,7 @@
> We also let the request go through if the referrer is from a site
> managed by launchpad:
>
> - # Go behing the curtains and change the hostname of one of our sites so that
> + # Go behind the curtains and change the hostname of one of our sites so that
> # we can test this.
> >>> from canonical.launchpad.webapp.vhosts import allvhosts
> >>> allvhosts._hostnames.add('bzr.dev')

--
Guilherme Salgado <email address hidden>

review: Approve
Revision history for this message
Michael Nelson (michael.nelson) wrote :

> This is a screenshot of the new error page, for UI review.
>
> http://launchpadlibrarian.net/42379021/Screen%20shot%202010-03-29%20at%209.26.
> 18%20AM.png

Hi Gary,

The language is nice and clear. I have two suggestions:

1. I think it would be better to use a descriptive link text instead of "this FAQ", perhaps "You can find out more information in the <link>FAQ: the relevant faq title</link>."

2. Instead of "Perhaps you have....", I think this should be "This can be caused by..." (but mrevell is the wordsmith around here if you want a second opinion :)). See what you think.

Thanks!

review: Approve (ui)
Revision history for this message
Deryck Hodge (deryck) :
review: Approve (release-critical)
Revision history for this message
Gary Poster (gary) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt'
2--- lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt 2010-03-19 20:02:25 +0000
3+++ lib/canonical/launchpad/pagetests/standalone/xx-offsite-form-post.txt 2010-03-29 16:14:26 +0000
4@@ -66,10 +66,34 @@
5 >>> browser.getControl('Create').click()
6 Traceback (most recent call last):
7 ...
8- OffsiteFormPostError: No value for REFERER header
9-
10-Though, to support apport, we let POST requests without a REFERER header
11-go through to +storeblob.
12+ NoReferrerError: No value for REFERER header
13+
14+When a POST request is rejected because the REFERER header is missing, it
15+may be because the user is trying to enforce anonymity. Therefore, we
16+present a hopefully helpful error message.
17+
18+ >>> browser.handleErrors = True
19+ >>> browser.open('http://launchpad.dev/people/+newteam')
20+ MONKEYPATCH ACTIVATED
21+ >>> browser.getControl('Name', index=0).value = 'team3'
22+ >>> browser.getControl('Display Name').value = 'Team 3'
23+ >>> browser.getControl('Create').click()
24+ Traceback (most recent call last):
25+ ...
26+ HTTPError...
27+ >>> print browser.headers['status']
28+ 403 Forbidden
29+ >>> print extract_text(find_main_content(browser.contents))
30+ No REFERER Header
31+ ...
32+ >>> browser.getLink('the FAQ').url
33+ 'https://answers.edge.launchpad.net/launchpad/+faq/1024'
34+ >>> browser.handleErrors = False
35+
36+We have a few exceptional cases in which we allow POST requests without a
37+REFERER header.
38+
39+To support apport, we allow it for +storeblob.
40
41 >>> browser.post('http://launchpad.dev/+storeblob', 'x=1')
42 MONKEYPATCH ACTIVATED
43@@ -95,7 +119,7 @@
44 We also let the request go through if the referrer is from a site
45 managed by launchpad:
46
47- # Go behing the curtains and change the hostname of one of our sites so that
48+ # Go behind the curtains and change the hostname of one of our sites so that
49 # we can test this.
50 >>> from canonical.launchpad.webapp.vhosts import allvhosts
51 >>> allvhosts._hostnames.add('bzr.dev')
52@@ -108,7 +132,7 @@
53 >>> print browser.url
54 http://launchpad.dev/~team4
55
56- # Now retore our site's hostname.
57+ # Now restore our site's hostname.
58 >>> allvhosts._hostnames.remove('bzr.dev')
59
60 Cheaters never prosper
61@@ -131,7 +155,7 @@
62 ... 'http://launchpad.dev/api/devel/people', 'ws.op=foo&x=1')
63 Traceback (most recent call last):
64 ...
65- OffsiteFormPostError: No value for REFERER header
66+ NoReferrerError: No value for REFERER header
67
68 You can't cheat by making your referrerless POST request seem as
69 though it were signed with OAuth.
70@@ -146,7 +170,7 @@
71 ... 'http://launchpad.dev/', 'oauth_consumer_key=foo&oauth_token=bar')
72 Traceback (most recent call last):
73 ...
74- OffsiteFormPostError: No value for REFERER header
75+ NoReferrerError: No value for REFERER header
76
77 You might think you can actually sign a request with an anonymous
78 OAuth credential. You don't need any knowledge of the user account to
79
80=== added file 'lib/canonical/launchpad/templates/launchpad-noreferrer.pt'
81--- lib/canonical/launchpad/templates/launchpad-noreferrer.pt 1970-01-01 00:00:00 +0000
82+++ lib/canonical/launchpad/templates/launchpad-noreferrer.pt 2010-03-29 16:14:26 +0000
83@@ -0,0 +1,26 @@
84+<html
85+ xmlns="http://www.w3.org/1999/xhtml"
86+ xmlns:tal="http://xml.zope.org/namespaces/tal"
87+ xmlns:metal="http://xml.zope.org/namespaces/metal"
88+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
89+ metal:use-macro="view/macro:page/locationless"
90+ i18n:domain="launchpad"
91+>
92+ <body>
93+ <div class="top-portlet" metal:fill-slot="main">
94+ <h1 class="exception">No <code>REFERER</code> Header</h1>
95+ <p>Launchpad requires a <code>REFERER</code> header to perform
96+ this action. There is no <code>REFERER</code> header present.
97+ This can be caused by configuring your browser to block
98+ <code>REFERER</code> headers.</p>
99+ <p>Unblock <code>REFERER</code> headers for launchpad.net and try
100+ again, or see <a
101+ href="https://answers.edge.launchpad.net/launchpad/+faq/1024">the
102+ FAQ <em>Why does Launchpad require a REFERER header?</em></a> for
103+ more information.</p>
104+ <p>You can also join <a href="irc://irc.freenode.net/launchpad">the
105+ #launchpad IRC support channel on irc.freenode.net</a> for further
106+ assistance.</p>
107+ </div>
108+ </body>
109+</html>
110
111=== modified file 'lib/canonical/launchpad/webapp/error.py'
112--- lib/canonical/launchpad/webapp/error.py 2010-03-18 18:55:02 +0000
113+++ lib/canonical/launchpad/webapp/error.py 2010-03-29 16:14:26 +0000
114@@ -283,3 +283,8 @@
115 def __call__(self):
116 return self.index()
117
118+
119+class NoReferrerErrorView(SystemErrorView):
120+ """View rendered when a POST request does not include a REFERER header."""
121+
122+ response_code = 403 # Forbidden.
123
124=== modified file 'lib/canonical/launchpad/webapp/interfaces.py'
125--- lib/canonical/launchpad/webapp/interfaces.py 2010-02-22 11:09:22 +0000
126+++ lib/canonical/launchpad/webapp/interfaces.py 2010-03-29 16:14:26 +0000
127@@ -109,6 +109,10 @@
128 """An attempt was made to post a form from a remote site."""
129
130
131+class NoReferrerError(Exception):
132+ """At attempt was made to post a form without a REFERER header."""
133+
134+
135 class UnsafeFormGetSubmissionError(Exception):
136 """An attempt was made to submit an unsafe form action with GET."""
137
138
139=== modified file 'lib/canonical/launchpad/webapp/publication.py'
140--- lib/canonical/launchpad/webapp/publication.py 2010-03-23 19:07:44 +0000
141+++ lib/canonical/launchpad/webapp/publication.py 2010-03-29 16:14:26 +0000
142@@ -37,7 +37,7 @@
143 from zope.interface import implements, providedBy
144 from zope.publisher.interfaces import IPublishTraverse, Retry
145 from zope.publisher.interfaces.browser import (
146- IDefaultSkin, IBrowserRequest, IBrowserApplicationRequest)
147+ IDefaultSkin, IBrowserRequest)
148 from zope.publisher.publish import mapply
149 from zope.security.proxy import removeSecurityProxy
150 from zope.security.management import newInteraction
151@@ -55,13 +55,12 @@
152 from canonical.launchpad.webapp.interfaces import (
153 IDatabasePolicy, ILaunchpadRoot, INotificationResponse, IOpenLaunchBag,
154 IPlacelessAuthUtility, IPrimaryContext, IStoreSelector, MAIN_STORE,
155- MASTER_FLAVOR, OffsiteFormPostError, SLAVE_FLAVOR)
156+ MASTER_FLAVOR, OffsiteFormPostError, NoReferrerError, SLAVE_FLAVOR)
157 from canonical.launchpad.webapp.dbpolicy import (
158 DatabaseBlockedPolicy, LaunchpadDatabasePolicy)
159 from canonical.launchpad.webapp.menu import structured
160 from canonical.launchpad.webapp.opstats import OpStats
161 from lazr.uri import URI, InvalidURIError
162-from lazr.restful.interfaces import IWebServiceClientRequest
163 from canonical.launchpad.webapp.vhosts import allvhosts
164
165
166@@ -353,7 +352,7 @@
167 return
168 referrer = request.getHeader('referer') # match HTTP spec misspelling
169 if not referrer:
170- raise OffsiteFormPostError('No value for REFERER header')
171+ raise NoReferrerError('No value for REFERER header')
172 # XXX: jamesh 2007-04-26 bug=98437:
173 # The Zope testing infrastructure sets a default (incorrect)
174 # referrer value of "localhost" or "localhost:9000" if no
175
176=== modified file 'lib/canonical/launchpad/zcml/launchpad.zcml'
177--- lib/canonical/launchpad/zcml/launchpad.zcml 2010-02-18 10:52:51 +0000
178+++ lib/canonical/launchpad/zcml/launchpad.zcml 2010-03-29 16:14:26 +0000
179@@ -82,6 +82,15 @@
180 class="canonical.launchpad.webapp.error.SystemErrorView"
181 />
182
183+ <!-- NoReferrerError -->
184+ <browser:page
185+ for="canonical.launchpad.webapp.interfaces.NoReferrerError"
186+ name="index.html"
187+ permission="zope.Public"
188+ template="../templates/launchpad-noreferrer.pt"
189+ class="canonical.launchpad.webapp.error.NoReferrerErrorView"
190+ />
191+
192 <!-- UnsafeFormGetSubmissionError -->
193 <browser:page
194 for="canonical.launchpad.webapp.interfaces.UnsafeFormGetSubmissionError"