Merge lp:~barry/ubuntu-sso-client/lp1077087 into lp:ubuntu-sso-client
- lp1077087
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | dobey | ||||
Approved revision: | 1020 | ||||
Merged at revision: | 1020 | ||||
Proposed branch: | lp:~barry/ubuntu-sso-client/lp1077087 | ||||
Merge into: | lp:ubuntu-sso-client | ||||
Diff against target: |
246 lines (+66/-53) 4 files modified
run-tests (+4/-1) ubuntu_sso/utils/webclient/common.py (+28/-29) ubuntu_sso/utils/webclient/tests/test_webclient.py (+23/-23) ubuntu_sso/utils/webclient/txweb.py (+11/-0) |
||||
To merge this branch: | bzr merge lp:~barry/ubuntu-sso-client/lp1077087 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
dobey (community) | Approve | ||
Roberto Alsina (community) | Approve | ||
Michał Karnicki (community) | Approve | ||
Review via email: mp+141983@code.launchpad.net |
Commit message
Switch from python-oauth to python-oauthlib for using OAuth.
Description of the change
mp based on as-yet-unreleased oauthlib patch for issue #86.
Barry Warsaw (barry) wrote : | # |
Barry Warsaw (barry) wrote : | # |
We should be getting a new upstream oauthlib release this weekend. As soon as that's ready, I'll prep it for raring, and then I'll update this branch as ready for review.
Barry Warsaw (barry) wrote : | # |
python-oauthlib 0.3.5 is now in raring, and I've updated this branch against trunk. It should be ready for review and merging now.
Note that I get 5 test errors, but I get these in the unaltered trunk branch too, so my changes should not be the cause.
ubuntu_
ubuntu_
ubuntu_
ubuntu_
ubuntu_
Michał Karnicki (karni) wrote : | # |
Does oauth_client.sign really take uri first, then method? Funny.
I can't expand changes from rev 1016, 'fixes'. Empty commit?
The as_query is the only bit slightly vague to me.
Barry Warsaw (barry) wrote : | # |
On Feb 05, 2013, at 07:33 PM, Michał Karnicki wrote:
>Does oauth_client.sign really take uri first, then method? Funny.
Yep, probably because GET is the most common method, and the argument defaults
to 'GET'. There's no reasonable default for uri, and you can't have default
arguments following non-default arguments.
>I can't expand changes from rev 1016, 'fixes'. Empty commit?
Ha! Yes, it looks like it. Not sure what I was thinking there. ;)
>The as_query is the only bit slightly vague to me.
The issue is that the OAuth signature can either live in the Authorization
header, e.g. for POSTs, or as a query parameter for GETs. oauthlib supports
both signing regimes, and afaict ubuntu-sso-client uses both in different
situations. The only solution I could come up with was to pass a flag
indicating which of the two types of signatures clients of the
build_oauth_
With as_query=True, the method selects oauthlib's signature_type of
SIGNATURE_
as_query=False, you get the Authorization header.
I suppose another way might have been to look at the method, and decipher
as_query from that. E.g. if method='GET', use the equivalent of as_query=True
and if method='POST', use as_query=False.
I didn't do it that way because if you look at build_request_
ubuntu_
'GET' but the API clearly still wants an Authorization header, so I thought it
would be better to be explicit.
Suggestions welcome, and thanks for the review!
Michał Karnicki (karni) wrote : | # |
Ah, it all makes sense to me now. Thanks for thorough explanations, Barry :)
Roberto Alsina (ralsina) : | # |
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~barry/ubuntu-sso-client/lp1077087 into lp:ubuntu-sso-client failed. Below is the output from the failed tests.
*** Running QT test suite for ubuntu_sso ***
running build
Compiled data/qt/
Compiled data/qt/
compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
running build_py
creating build
creating build/lib.
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~barry/ubuntu-sso-client/lp1077087 into lp:ubuntu-sso-client failed. Below is the output from the failed tests.
*** Running QT test suite for ubuntu_sso ***
running build
Compiled data/qt/
Compiled data/qt/
compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
running build_py
creating build
creating build/lib.
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
Barry Warsaw (barry) wrote : | # |
Please note that I get the exact same test failures on trunk (including the failure in kill_squid) so these are not related to my change afaict.
dobey (dobey) wrote : | # |
On Wed, 2013-02-06 at 17:57 +0000, Barry Warsaw wrote:
> Please note that I get the exact same test failures on trunk
> (including the failure in kill_squid) so these are not related to my
> change afaict.
Yes, this is due to a crash occurring in squid3, which we require to
test the proxy support. We are also seeing a similar crash, and test
failures, when running the tests for ubuntuone-client on Raring. I've
filed a bug about it, and there is a new squid3 build failing due to a
new attribute on the setuid() call in glibc 2.17, which may be related
to the cause of the crash in squid3 after the glibc update. Hopefully it
will be fixed soon, and we can land branches again. :)
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~barry/ubuntu-sso-client/lp1077087 into lp:ubuntu-sso-client failed. Below is the output from the failed tests.
*** Running QT test suite for ubuntu_sso ***
running build
Compiled data/qt/
Compiled data/qt/
compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
Compiled data/qt/
running build_py
creating build
creating build/lib.
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
creating build/lib.
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
copying ubuntu_
dobey (dobey) wrote : | # |
From pep8:
./ubuntu_
dobey (dobey) wrote : | # |
129 -)
130 + )
This change needs to be reverted.
- 1020. By Barry Warsaw
-
Revert change in whitespace to appease pep8 complaint.
Barry Warsaw (barry) wrote : | # |
r1020 reverts that.
dobey (dobey) : | # |
Preview Diff
1 | === modified file 'run-tests' |
2 | --- run-tests 2012-12-04 19:28:44 +0000 |
3 | +++ run-tests 2013-02-11 21:11:20 +0000 |
4 | @@ -33,6 +33,9 @@ |
5 | QT_TEST_FILES="test_qt.py,test_qtwisted.py" |
6 | |
7 | LINUX_IGNORE_FILES="test_darwin.py,test_windows.py,test_pykeyring.py" |
8 | +# Allow alternative python executable via environment variable. This is |
9 | +# useful for virtualenv testing. |
10 | +PYTHON=${PYTHON:-''} |
11 | |
12 | set -e |
13 | |
14 | @@ -61,7 +64,7 @@ |
15 | |
16 | echo "*** Running QT test suite for ""$MODULE"" ***" |
17 | ./setup.py build |
18 | -$XVFB_CMDLINE u1trial --reactor=qt4 --gui -i $LINUX_IGNORE_FILES "$MODULE" |
19 | +$XVFB_CMDLINE $PYTHON /usr/bin/u1trial --reactor=qt4 --gui -i $LINUX_IGNORE_FILES "$MODULE" |
20 | ./setup.py clean |
21 | rm -rf _trial_temp |
22 | rm -rf build |
23 | |
24 | === modified file 'ubuntu_sso/utils/webclient/common.py' |
25 | --- ubuntu_sso/utils/webclient/common.py 2012-12-04 19:28:44 +0000 |
26 | +++ ubuntu_sso/utils/webclient/common.py 2013-02-11 21:11:20 +0000 |
27 | @@ -32,7 +32,9 @@ |
28 | import collections |
29 | |
30 | from httplib2 import iri2uri |
31 | -from oauth import oauth |
32 | +from oauthlib.oauth1 import ( |
33 | + Client, SIGNATURE_HMAC, SIGNATURE_PLAINTEXT, SIGNATURE_TYPE_AUTH_HEADER, |
34 | + SIGNATURE_TYPE_QUERY) |
35 | from twisted.internet import defer |
36 | |
37 | try: |
38 | @@ -146,31 +148,29 @@ |
39 | return bytes(iri2uri(iri)) |
40 | |
41 | def build_oauth_request(self, method, uri, credentials, timestamp, |
42 | - parameters=None): |
43 | + parameters=None, as_query=True): |
44 | """Build an oauth request given some credentials.""" |
45 | - consumer = oauth.OAuthConsumer(credentials["consumer_key"], |
46 | - credentials["consumer_secret"]) |
47 | - token = oauth.OAuthToken(credentials["token"], |
48 | - credentials["token_secret"]) |
49 | + |
50 | + oauth_client = Client(credentials['consumer_key'], |
51 | + credentials['consumer_secret'], |
52 | + credentials['token'], |
53 | + credentials['token_secret'], |
54 | + signature_method=(SIGNATURE_PLAINTEXT |
55 | + if self.oauth_sign_plain |
56 | + else SIGNATURE_HMAC), |
57 | + signature_type=(SIGNATURE_TYPE_QUERY |
58 | + if as_query |
59 | + else SIGNATURE_TYPE_AUTH_HEADER), |
60 | + timestamp=timestamp) |
61 | if not parameters: |
62 | _, _, _, _, query, _ = urlparse(uri) |
63 | parameters = dict(cgi.parse_qsl(query)) |
64 | |
65 | - if timestamp: |
66 | - parameters["oauth_timestamp"] = timestamp |
67 | + url, signed_headers, body = oauth_client.sign( |
68 | + uri, method, parameters, |
69 | + {'Content-Type': 'application/x-www-form-urlencoded'}) |
70 | |
71 | - request = oauth.OAuthRequest.from_consumer_and_token( |
72 | - http_url=uri, |
73 | - http_method=method, |
74 | - parameters=parameters, |
75 | - oauth_consumer=consumer, |
76 | - token=token) |
77 | - if self.oauth_sign_plain: |
78 | - sig_method = oauth.OAuthSignatureMethod_PLAINTEXT() |
79 | - else: |
80 | - sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1() |
81 | - request.sign_request(sig_method, consumer, token) |
82 | - return request |
83 | + return url, signed_headers, body |
84 | |
85 | @defer.inlineCallbacks |
86 | def build_request_headers(self, uri, method="GET", extra_headers=None, |
87 | @@ -183,10 +183,10 @@ |
88 | |
89 | if oauth_credentials: |
90 | timestamp = yield self.get_timestamp() |
91 | - oauth_request = self.build_oauth_request(method, uri, |
92 | - oauth_credentials, timestamp) |
93 | - oauth_headers = oauth_request.to_header() |
94 | - headers.update(oauth_headers) |
95 | + url, signed_headers, body = self.build_oauth_request( |
96 | + method, uri, oauth_credentials, timestamp, |
97 | + as_query=False) |
98 | + headers.update(signed_headers) |
99 | |
100 | defer.returnValue(headers) |
101 | |
102 | @@ -195,11 +195,10 @@ |
103 | """Build a new iri signing 'iri' with 'credentials'.""" |
104 | uri = self.iri_to_uri(iri) |
105 | timestamp = yield self.get_timestamp() |
106 | - request = self.build_oauth_request(method='GET', uri=uri, |
107 | - credentials=credentials, |
108 | - timestamp=timestamp, |
109 | - parameters=parameters) |
110 | - defer.returnValue(request.to_url()) |
111 | + url, signed_headers, body = self.build_oauth_request( |
112 | + method='GET', uri=uri, credentials=credentials, |
113 | + timestamp=timestamp, parameters=parameters) |
114 | + defer.returnValue(url) |
115 | |
116 | def shutdown(self): |
117 | """Shut down all pending requests (if possible).""" |
118 | |
119 | === modified file 'ubuntu_sso/utils/webclient/tests/test_webclient.py' |
120 | --- ubuntu_sso/utils/webclient/tests/test_webclient.py 2013-02-08 19:36:21 +0000 |
121 | +++ ubuntu_sso/utils/webclient/tests/test_webclient.py 2013-02-11 21:11:20 +0000 |
122 | @@ -69,7 +69,6 @@ |
123 | from ubuntu_sso.utils.webclient.common import ( |
124 | BaseWebClient, |
125 | HeaderDict, |
126 | - |
127 | UnauthorizedError, |
128 | WebClientError, |
129 | ) |
130 | @@ -378,7 +377,7 @@ |
131 | def test_request_is_oauth_signed(self): |
132 | """The request is oauth signed.""" |
133 | tsc = self.wc.get_timestamp_checker() |
134 | - self.patch(tsc, "get_faithful_time", lambda: defer.succeed(1)) |
135 | + self.patch(tsc, "get_faithful_time", lambda: defer.succeed('1')) |
136 | result = yield self.wc.request(self.base_iri + OAUTHRESOURCE, |
137 | oauth_credentials=SAMPLE_CREDENTIALS) |
138 | self.assertEqual(SAMPLE_RESOURCE, result.content) |
139 | @@ -391,7 +390,7 @@ |
140 | def fake_get_faithful_time(): |
141 | """A fake get_timestamp""" |
142 | called.append(True) |
143 | - return defer.succeed(1) |
144 | + return defer.succeed('1') |
145 | |
146 | tsc = self.wc.get_timestamp_checker() |
147 | self.patch(tsc, "get_faithful_time", fake_get_faithful_time) |
148 | @@ -403,7 +402,7 @@ |
149 | def test_returned_content_are_bytes(self): |
150 | """The returned content are bytes.""" |
151 | tsc = self.wc.get_timestamp_checker() |
152 | - self.patch(tsc, "get_faithful_time", lambda: defer.succeed(1)) |
153 | + self.patch(tsc, "get_faithful_time", lambda: defer.succeed('1')) |
154 | result = yield self.wc.request(self.base_iri + OAUTHRESOURCE, |
155 | oauth_credentials=SAMPLE_CREDENTIALS) |
156 | self.assertTrue(isinstance(result.content, bytes), |
157 | @@ -734,7 +733,7 @@ |
158 | sample_method = "GET" |
159 | sample_url = "http://one.ubuntu.com/" |
160 | sample_params = {'next': 'yadda-yadda-yo', 'foo': 'naranja fanta'} |
161 | - timestamp = 1 |
162 | + timestamp = '1' |
163 | expected_params = { |
164 | "oauth_timestamp": str(timestamp), |
165 | "oauth_consumer_key": SAMPLE_CREDENTIALS["consumer_key"], |
166 | @@ -789,16 +788,17 @@ |
167 | |
168 | def test_build_oauth_request(self, params=None): |
169 | """Check that the oauth request are properly built.""" |
170 | - request = self.wc.build_oauth_request(self.sample_method, |
171 | - self.sample_url, SAMPLE_CREDENTIALS, self.timestamp, |
172 | - parameters=params) |
173 | + url, signed_headers, body = self.wc.build_oauth_request( |
174 | + self.sample_method, |
175 | + self.sample_url, SAMPLE_CREDENTIALS, self.timestamp, |
176 | + parameters=params, as_query=False) |
177 | |
178 | - self.assert_headers_correct(request.to_header()) |
179 | - self.assertEqual(request.http_url, self.sample_url) |
180 | + self.assert_headers_correct(signed_headers) |
181 | + self.assertEqual(url, self.sample_url) |
182 | if params is not None: |
183 | for param, value in params.items(): |
184 | - self.assertIn(param, request.parameters) |
185 | - actual = request.parameters[param] |
186 | + self.assertIn(param, body) |
187 | + actual = body[param] |
188 | self.assertEqual(value, actual) |
189 | |
190 | def test_build_oauth_request_with_params(self): |
191 | @@ -811,23 +811,23 @@ |
192 | self.patch(self.wc, 'get_timestamp', lambda: self.timestamp) |
193 | iri = u'http://foo.bar.baz/ñandú' |
194 | uri = self.wc.iri_to_uri(iri) |
195 | - request = self.wc.build_oauth_request(self.sample_method, |
196 | - uri, SAMPLE_CREDENTIALS, |
197 | - self.timestamp, parameters=params) |
198 | - |
199 | - result = yield self.wc.build_signed_iri(iri, SAMPLE_CREDENTIALS, |
200 | - parameters=params) |
201 | - |
202 | + # request will be a 3-tuple of the url, signed headers, and body. |
203 | + url, signed_headers, body = self.wc.build_oauth_request( |
204 | + self.sample_method, |
205 | + uri, SAMPLE_CREDENTIALS, |
206 | + self.timestamp, parameters=params) |
207 | + scheme, netloc, path, params, query, _ = urlparse(url) |
208 | + expected = dict(parse_qsl(query)) |
209 | + |
210 | + result = yield self.wc.build_signed_iri( |
211 | + iri, SAMPLE_CREDENTIALS, parameters=params) |
212 | scheme, netloc, path, params, query, _ = urlparse(result) |
213 | - |
214 | self.assertEqual(urljoin(scheme + '://' + netloc, path), uri) |
215 | - |
216 | - expected = request.parameters |
217 | actual = dict(parse_qsl(query)) |
218 | + |
219 | for i in ('oauth_nonce', 'oauth_signature'): |
220 | expected.pop(i) |
221 | actual.pop(i) |
222 | - actual['oauth_timestamp'] = int(actual['oauth_timestamp']) |
223 | self.assertEqual(expected, actual) |
224 | |
225 | def test_build_signed_iri_with_params(self, params=None): |
226 | |
227 | === modified file 'ubuntu_sso/utils/webclient/txweb.py' |
228 | --- ubuntu_sso/utils/webclient/txweb.py 2012-07-03 21:23:22 +0000 |
229 | +++ ubuntu_sso/utils/webclient/txweb.py 2013-02-11 21:11:20 +0000 |
230 | @@ -95,6 +95,17 @@ |
231 | @defer.inlineCallbacks |
232 | def raw_request(self, method, uri, headers, postdata): |
233 | """Make a raw http request.""" |
234 | + # Twisted wants headers as bytes, but because of oauthlib, they might |
235 | + # be unicodes. Assume utf-8 and revert the encodings. |
236 | + bytes_headers = {} |
237 | + for key, value in headers.items(): |
238 | + if isinstance(key, unicode): |
239 | + key = key.encode('utf-8') |
240 | + if isinstance(value, unicode): |
241 | + value = value.encode('utf-8') |
242 | + bytes_headers[key] = value |
243 | + headers = bytes_headers |
244 | + |
245 | # delay import, otherwise a default reactor gets installed |
246 | from twisted.web import error |
247 |
This is ready to go, as soon as upstream releases a new version of oauthlib.