Merge lp:~leonardr/launchpad/distinguish-between-unauthorized-and-forbidden into lp:launchpad/db-devel

Proposed by Leonard Richardson
Status: Merged
Merge reported by: Leonard Richardson
Merged at revision: not available
Proposed branch: lp:~leonardr/launchpad/distinguish-between-unauthorized-and-forbidden
Merge into: lp:launchpad/db-devel
Diff against target: 80 lines (+34/-8)
2 files modified
lib/canonical/launchpad/browser/oauth.py (+13/-5)
lib/canonical/launchpad/pagetests/oauth/access-token.txt (+21/-3)
To merge this branch: bzr merge lp:~leonardr/launchpad/distinguish-between-unauthorized-and-forbidden
Reviewer Review Type Date Requested Status
Guilherme Salgado (community) Approve
Review via email: mp+29803@code.launchpad.net

Description of the change

This branch returns the response code 403 ("Forbidden") when the client attempts to exchange a request token for an access token, but the end-user has explicitly declined to authorize the request token. Previously the response code was 401 ("Unauthorized"), which made it impossible to distinguish between this case and the case where the end-user has not gotten around to make a decision about the request token.

I also return human-readable strings for various error conditions instead of the empty string.

To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/browser/oauth.py'
--- lib/canonical/launchpad/browser/oauth.py 2010-03-30 15:36:27 +0000
+++ lib/canonical/launchpad/browser/oauth.py 2010-07-13 15:48:51 +0000
@@ -245,15 +245,23 @@
245 token = consumer.getRequestToken(form.get('oauth_token'))245 token = consumer.getRequestToken(form.get('oauth_token'))
246 if token is None:246 if token is None:
247 self.request.unauthorized(OAUTH_CHALLENGE)247 self.request.unauthorized(OAUTH_CHALLENGE)
248 return u''248 return u'No request token specified.'
249249
250 if not check_oauth_signature(self.request, consumer, token):250 if not check_oauth_signature(self.request, consumer, token):
251 return u''251 return u'Invalid OAuth signature.'
252252
253 if (not token.is_reviewed253 if not token.is_reviewed:
254 or token.permission == OAuthPermission.UNAUTHORIZED):
255 self.request.unauthorized(OAUTH_CHALLENGE)254 self.request.unauthorized(OAUTH_CHALLENGE)
256 return u''255 return u'Request token has not yet been reviewed. Try again later.'
256
257 if token.permission == OAuthPermission.UNAUTHORIZED:
258 # The end-user explicitly refused to authorize this
259 # token. We send 403 ("Forbidden") instead of 401
260 # ("Unauthorized") to distinguish this case and to
261 # indicate that, as RFC2616 says, "authorization will not
262 # help."
263 self.request.response.setStatus(403)
264 return u'End-user refused to authorize request token.'
257265
258 access_token = token.createAccessToken()266 access_token = token.createAccessToken()
259 context_name = None267 context_name = None
260268
=== modified file 'lib/canonical/launchpad/pagetests/oauth/access-token.txt'
--- lib/canonical/launchpad/pagetests/oauth/access-token.txt 2009-10-22 13:02:12 +0000
+++ lib/canonical/launchpad/pagetests/oauth/access-token.txt 2010-07-13 15:48:51 +0000
@@ -74,9 +74,10 @@
74 ... """ % urlencode(data2))74 ... """ % urlencode(data2))
75 HTTP/1.1 401 Unauthorized75 HTTP/1.1 401 Unauthorized
76 ...76 ...
77 Request token has not yet been reviewed. Try again later.
7778
78The same will happen if the signature is wrong or if the token's79If the token is missing or the signature is wrong the response will
79permission is UNAUTHORIZED.80also be 401.
8081
81 >>> data2['oauth_signature'] = '&'.join(['foobar', token.secret])82 >>> data2['oauth_signature'] = '&'.join(['foobar', token.secret])
82 >>> print http(r"""83 >>> print http(r"""
@@ -85,6 +86,22 @@
85 ... """ % urlencode(data2))86 ... """ % urlencode(data2))
86 HTTP/1.1 401 Unauthorized87 HTTP/1.1 401 Unauthorized
87 ...88 ...
89 Invalid OAuth signature.
90
91 >>> data3 = data.copy()
92 >>> del(data3['oauth_token'])
93 >>> print http(r"""
94 ... GET /+access-token?%s HTTP/1.1
95 ... Host: launchpad.dev
96 ... """ % urlencode(data3))
97 HTTP/1.1 401 Unauthorized
98 ...
99 No request token specified.
100
101It the token's permission is set to UNAUTHORIZED, the response code is
102403 ("Forbidden"). This conveys that (to quote the HTTP RFC)
103"authorization will not help" and that the token will _never_ be
104exchanged for an access token.
88105
89 >>> token.review(salgado, OAuthPermission.UNAUTHORIZED)106 >>> token.review(salgado, OAuthPermission.UNAUTHORIZED)
90 >>> data2['oauth_signature'] = '&'.join([consumer.secret, token.secret])107 >>> data2['oauth_signature'] = '&'.join([consumer.secret, token.secret])
@@ -92,6 +109,7 @@
92 ... GET /+access-token?%s HTTP/1.1109 ... GET /+access-token?%s HTTP/1.1
93 ... Host: launchpad.dev110 ... Host: launchpad.dev
94 ... """ % urlencode(data2))111 ... """ % urlencode(data2))
95 HTTP/1.1 401 Unauthorized112 HTTP/1.1 403 Forbidden
96 ...113 ...
114 End-user refused to authorize request token.
97115

Subscribers

People subscribed via source and target branches

to status/vote changes: