Merge lp:~xnox/launchpad/devel into lp:launchpad

Proposed by Dimitri John Ledkov
Status: Work in progress
Proposed branch: lp:~xnox/launchpad/devel
Merge into: lp:launchpad
Diff against target: 96 lines (+68/-2)
3 files modified
lib/lp/services/oauth/browser/__init__.py (+1/-1)
lib/lp/services/oauth/doc/oauth.txt (+1/-1)
lib/lp/services/oauth/stories/access-token-header.txt (+66/-0)
To merge this branch: bzr merge lp:~xnox/launchpad/devel
Reviewer Review Type Date Requested Status
William Grant code Needs Fixing
Barry Warsaw Pending
Review via email: mp+217079@code.launchpad.net
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

This seems to remove the test that verifies the behaviour that every existing Launchpad API client relies on. Why do you want to change the behaviour in the first place?

review: Needs Fixing (code)
Revision history for this message
Dimitri John Ledkov (xnox) wrote :

> This seems to remove the test that verifies the behaviour that every existing
> Launchpad API client relies on. Why do you want to change the behaviour in the
> first place?

Right, i'll readd it back again. At the moment it exposes a bug though, since header-signature type doesn't appear to invalidate request token, thus one can exchange for access token unlimited amount of times. Only body-signature type seems to correctly raise 401 upon subsequent requests.

So at the moment the test change exposes buggy behaviour with header-signature. I'll make sure to re-introduce body-signature tests and test header-signatures separately.

----------------------------------------------------------------------
File "lib/lp/services/oauth/stories/access-token.txt", line 42, in access-token.txt
Failed example:
    auth_browser.open(
        'http://launchpad.dev/+access-token')
Differences (ndiff with -expected +actual):
    - Traceback (most recent call last):
    - ...
    - HTTPError: HTTP Error 401: Unauthorized

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

Settings status to "work in progress" appropriately.

lp:~xnox/launchpad/devel updated
16989. By Dimitri John Ledkov

Support oauth_callback from header authentication

16990. By Dimitri John Ledkov

OAuth Revert access-token.txt to current, add header-signature story.

16991. By Dimitri John Ledkov

Revert callback change for now

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

Barry, help! =) what's going on wrong here? See the added story, run with:

  ./bin/test -vvct access-token-header

It should fail at second token exchange, but it doesn't fail and instead allows exchanging request token over and over again.

Unmerged revisions

16991. By Dimitri John Ledkov

Revert callback change for now

16990. By Dimitri John Ledkov

OAuth Revert access-token.txt to current, add header-signature story.

16989. By Dimitri John Ledkov

Support oauth_callback from header authentication

16988. By Dimitri John Ledkov

Change access-token story to use header authentication.

16987. By Dimitri John Ledkov

Fix tests file references in the doctests comment.

16986. By Dimitri John Ledkov

Accept oauth signature from either headers or body in +access-token requests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/oauth/browser/__init__.py'
2--- lib/lp/services/oauth/browser/__init__.py 2013-04-11 02:12:09 +0000
3+++ lib/lp/services/oauth/browser/__init__.py 2014-04-25 11:04:15 +0000
4@@ -416,7 +416,7 @@
5 (or is not associated with the consumer), the signature does not match
6 or no permission has been granted by the user, respond with a 401.
7 """
8- form = self.request.form
9+ form = get_oauth_authorization(self.request)
10 consumer = getUtility(IOAuthConsumerSet).getByKey(
11 form.get('oauth_consumer_key'))
12
13
14=== modified file 'lib/lp/services/oauth/doc/oauth.txt'
15--- lib/lp/services/oauth/doc/oauth.txt 2014-01-30 15:04:06 +0000
16+++ lib/lp/services/oauth/doc/oauth.txt 2014-04-25 11:04:15 +0000
17@@ -1,7 +1,7 @@
18 = OAuth =
19
20 Most of the OAuth doctests have been converted into unit tests and
21-moved to test_oauth_tokens.py
22+moved to test_tokens.py
23
24 == Nonces and timestamps ==
25
26
27=== added file 'lib/lp/services/oauth/stories/access-token-header.txt'
28--- lib/lp/services/oauth/stories/access-token-header.txt 1970-01-01 00:00:00 +0000
29+++ lib/lp/services/oauth/stories/access-token-header.txt 2014-04-25 11:04:15 +0000
30@@ -0,0 +1,66 @@
31+= Exchanging a request token for an access token, using headers =
32+
33+Once the user has logged into Launchpad and granted access to the
34+consumer, the request token is eligible for being exchanged for an
35+access token.
36+
37+This is boiler-plate from access-token.txt to begin with
38+
39+ # First we create a new request token and review it.
40+ >>> from zope.component import getUtility
41+ >>> from lp.services.oauth.interfaces import IOAuthConsumerSet
42+ >>> from lp.services.webapp.interfaces import OAuthPermission
43+ >>> from lp.registry.interfaces.person import IPersonSet
44+ >>> from lp.registry.interfaces.product import IProductSet
45+ >>> from lp.testing import login, logout
46+ >>> login('salgado@ubuntu.com')
47+ >>> consumer = getUtility(IOAuthConsumerSet).getByKey('foobar123451432')
48+ >>> token = consumer.newRequestToken()
49+ >>> salgado = getUtility(IPersonSet).getByName('salgado')
50+ >>> token.review(salgado, OAuthPermission.WRITE_PUBLIC)
51+ >>> logout()
52+
53+ >>> import time
54+ >>> from urllib import urlencode
55+ >>> data = dict(
56+ ... oauth_consumer_key='foobar123451432',
57+ ... oauth_version='1.0',
58+ ... oauth_token=token.key,
59+ ... oauth_signature_method='PLAINTEXT',
60+ ... oauth_signature='&'.join([consumer.secret, token.secret]),
61+ ... oauth_timestamp=time.time(),
62+ ... oauth_nonce='4572616e48616d6d65724c61686176')
63+
64+Now, let's do the exchange using the OAuth 1.0 preferred way, by
65+setting Authentication Header, instead of urlencoding oauth_
66+parameters in the body of the request.
67+
68+ >>> auth = 'OAuth '+', '.join(['%s="%s"' % item for item in data.items()])
69+ >>> auth_browser = setupBrowser(auth=auth)
70+ >>> auth_browser.open(
71+ ... 'http://launchpad.dev/+access-token')
72+
73+ >>> print auth_browser.contents
74+ oauth_token=...&oauth_token_secret=...
75+
76+Yeah, token exchange was successful! Let's try again!
77+
78+ >>> auth_browser.open(
79+ ... 'http://launchpad.dev/+access-token')
80+
81+ >>> print auth_browser.contents
82+ oauth_token=...&oauth_token_secret=...
83+
84+Wait a minute, this sounds like a broken record ( or a PCD song "wait
85+a minute"). I expected a 401 here! Let's assert that:
86+
87+ >>> auth_browser.open(
88+ ... 'http://launchpad.dev/+access-token')
89+ Traceback (most recent call last):
90+ ...
91+ HTTPError: HTTP Error 401: Unauthorized
92+
93+Bugger! +access-token essentially calls 'access_token =
94+token.createAccessToken()' which does call 'self.destroySelf()' which
95+totally works with body OAuth signature type in access-token.txt
96+story, but not with my one-liner patch to enable header signatures.