Merge lp:~mbp/launchpad/701545-oauth into lp:launchpad
- 701545-oauth
- Merge into devel
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Martin Pool | ||||
Proposed branch: | lp:~mbp/launchpad/701545-oauth | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
624 lines (+9/-537) 6 files modified
lib/canonical/launchpad/webapp/authentication.py (+2/-2) lib/canonical/launchpad/webapp/tests/test_authentication.py (+5/-2) lib/canonical/launchpad/webapp/tests/test_publication.py (+2/-2) lib/contrib/oauth.py (+0/-529) setup.py (+0/-1) versions.cfg (+0/-1) |
||||
To merge this branch: | bzr merge lp:~mbp/launchpad/701545-oauth | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
j.c.sackett | code* | Pending | |
Review via email: mp+49541@code.launchpad.net |
This proposal supersedes a proposal from 2011-02-03.
Commit message
Description of the change
Remove redundant copy of oauth.py.
Launchpad contains a copy of oauth.py, which is a bit out of date. It also has a proper copy of the python-oauth module in lp-sourcedeps.
This was previously attempted in <https:/
This can't/shouldn't land until the meta-lp-deps landing has been pushed all the way through.
The actual patch is similar to what was previously submitted with the addition of removing the setuptools dependency
j.c.sackett (jcsackett) wrote : Posted in a previous version of this proposal | # |
Curtis Hovey (sinzui) wrote : Posted in a previous version of this proposal | # |
Hi Martin.
Thank you for cleaning up the code. We normally write copyrights as a range. Are you aware of utilities/
Francis J. Lacoste (flacoste) wrote : Posted in a previous version of this proposal | # |
On February 3, 2011, Martin Pool wrote:
> If this is acceptable would someone please sponsor landing of it?
All Canonical Bazaar Developers can land branches directly through PQM
nowawayds :-)
Martin Pool (mbp) wrote : Posted in a previous version of this proposal | # |
Thanks, Curtis. I was not aware of that. I will send this to pqm.
Martin Pool (mbp) wrote : Posted in a previous version of this proposal | # |
On 4 February 2011 03:52, Francis J. Lacoste <email address hidden> wrote:
> On February 3, 2011, Martin Pool wrote:
>> If this is acceptable would someone please sponsor landing of it?
>
> All Canonical Bazaar Developers can land branches directly through PQM
> nowawayds :-)
No, I still get a gpgv error from pqm.
Martin Pool (mbp) wrote : Posted in a previous version of this proposal | # |
spm helped me sort this out and it's in the queue now.
- Martin
On 04/02/2011 11:35 AM, "Martin Pool" <email address hidden> wrote:
> On 4 February 2011 03:52, Francis J. Lacoste <email address hidden>
wrote:
>> On February 3, 2011, Martin Pool wrote:
>>> If this is acceptable would someone please sponsor landing of it?
>>
>> All Canonical Bazaar Developers can land branches directly through PQM
>> nowawayds :-)
>
> No, I still get a gpgv error from pqm.
- 12370. By Martin Pool
-
Also remove oauth from versions.cfg
Curtis Hovey (sinzui) wrote : | # |
This is good to land once the deps are in place.
Martin Pool (mbp) wrote : | # |
Per my comments on bug 701545, getting the dependencies to work is too hard to be worthwhile.
Unmerged revisions
- 12370. By Martin Pool
-
Also remove oauth from versions.cfg
- 12369. By Martin Pool
-
Use external python-oauth and remove contrib/oauth.py
- 12368. By Martin Pool
-
Remove dependency on oauth egg
Preview Diff
1 | === modified file 'lib/canonical/launchpad/webapp/authentication.py' |
2 | --- lib/canonical/launchpad/webapp/authentication.py 2011-02-04 14:41:18 +0000 |
3 | +++ lib/canonical/launchpad/webapp/authentication.py 2011-02-13 12:17:26 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
6 | +# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
7 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | |
9 | __metaclass__ = type |
10 | @@ -18,7 +18,7 @@ |
11 | import random |
12 | from UserDict import UserDict |
13 | |
14 | -from contrib.oauth import OAuthRequest |
15 | +from oauth.oauth import OAuthRequest |
16 | from zope.annotation.interfaces import IAnnotations |
17 | from zope.app.security.interfaces import ILoginPassword |
18 | from zope.app.security.principalregistry import UnauthenticatedPrincipal |
19 | |
20 | === modified file 'lib/canonical/launchpad/webapp/tests/test_authentication.py' |
21 | --- lib/canonical/launchpad/webapp/tests/test_authentication.py 2011-02-04 14:41:18 +0000 |
22 | +++ lib/canonical/launchpad/webapp/tests/test_authentication.py 2011-02-13 12:17:26 +0000 |
23 | @@ -1,4 +1,4 @@ |
24 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
25 | +# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
26 | # GNU Affero General Public License version 3 (see the file LICENSE). |
27 | |
28 | """Tests authentication.py""" |
29 | @@ -10,7 +10,7 @@ |
30 | |
31 | from zope.app.security.principalregistry import UnauthenticatedPrincipal |
32 | |
33 | -from contrib.oauth import OAuthRequest |
34 | +from oauth.oauth import OAuthRequest |
35 | |
36 | from canonical.config import config |
37 | from canonical.launchpad.ftests import login |
38 | @@ -67,6 +67,9 @@ |
39 | # |
40 | # Note that the 'realm' parameter is not returned, because it's not |
41 | # included in the OAuth calculations. |
42 | + # |
43 | + # Now we use the separate oauth module (bug 701545), and |
44 | + # this has been fixed upstream, but we might as well keep the test. |
45 | headers = OAuthRequest._split_header( |
46 | 'OAuth realm="foo", oauth_consumer_key="justtesting"') |
47 | self.assertEquals(headers, |
48 | |
49 | === modified file 'lib/canonical/launchpad/webapp/tests/test_publication.py' |
50 | --- lib/canonical/launchpad/webapp/tests/test_publication.py 2011-02-04 14:41:18 +0000 |
51 | +++ lib/canonical/launchpad/webapp/tests/test_publication.py 2011-02-13 12:17:26 +0000 |
52 | @@ -1,4 +1,4 @@ |
53 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
54 | +# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
55 | # GNU Affero General Public License version 3 (see the file LICENSE). |
56 | |
57 | """Tests publication.py""" |
58 | @@ -9,7 +9,7 @@ |
59 | import sys |
60 | import unittest |
61 | |
62 | -from contrib.oauth import ( |
63 | +from oauth.oauth import ( |
64 | OAuthRequest, |
65 | OAuthSignatureMethod_PLAINTEXT, |
66 | ) |
67 | |
68 | === removed file 'lib/contrib/oauth.py' |
69 | --- lib/contrib/oauth.py 2011-02-04 14:41:18 +0000 |
70 | +++ lib/contrib/oauth.py 1970-01-01 00:00:00 +0000 |
71 | @@ -1,529 +0,0 @@ |
72 | -# pylint: disable-msg=C0301,E0602,E0211,E0213,W0105,W0231,W0702 |
73 | - |
74 | -import cgi |
75 | -import urllib |
76 | -import time |
77 | -import random |
78 | -import urlparse |
79 | -import hmac |
80 | -import base64 |
81 | - |
82 | -VERSION = '1.0' # Hi Blaine! |
83 | -HTTP_METHOD = 'GET' |
84 | -SIGNATURE_METHOD = 'PLAINTEXT' |
85 | - |
86 | -# Generic exception class |
87 | -class OAuthError(RuntimeError): |
88 | - def __init__(self, message='OAuth error occured'): |
89 | - self.message = message |
90 | - |
91 | -# optional WWW-Authenticate header (401 error) |
92 | -def build_authenticate_header(realm=''): |
93 | - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} |
94 | - |
95 | -# url escape |
96 | -def escape(s): |
97 | - # escape '/' too |
98 | - return urllib.quote(s, safe='~') |
99 | - |
100 | -# util function: current timestamp |
101 | -# seconds since epoch (UTC) |
102 | -def generate_timestamp(): |
103 | - return int(time.time()) |
104 | - |
105 | -# util function: nonce |
106 | -# pseudorandom number |
107 | -def generate_nonce(length=8): |
108 | - return ''.join(str(random.randint(0, 9)) for i in range(length)) |
109 | - |
110 | -# OAuthConsumer is a data type that represents the identity of the Consumer |
111 | -# via its shared secret with the Service Provider. |
112 | -class OAuthConsumer(object): |
113 | - key = None |
114 | - secret = None |
115 | - |
116 | - def __init__(self, key, secret): |
117 | - self.key = key |
118 | - self.secret = secret |
119 | - |
120 | -# OAuthToken is a data type that represents an End User via either an access |
121 | -# or request token. |
122 | -class OAuthToken(object): |
123 | - # access tokens and request tokens |
124 | - key = None |
125 | - secret = None |
126 | - |
127 | - ''' |
128 | - key = the token |
129 | - secret = the token secret |
130 | - ''' |
131 | - def __init__(self, key, secret): |
132 | - self.key = key |
133 | - self.secret = secret |
134 | - |
135 | - def to_string(self): |
136 | - return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) |
137 | - |
138 | - # return a token from something like: |
139 | - # oauth_token_secret=digg&oauth_token=digg |
140 | - @staticmethod |
141 | - def from_string(s): |
142 | - params = cgi.parse_qs(s, keep_blank_values=False) |
143 | - key = params['oauth_token'][0] |
144 | - secret = params['oauth_token_secret'][0] |
145 | - return OAuthToken(key, secret) |
146 | - |
147 | - def __str__(self): |
148 | - return self.to_string() |
149 | - |
150 | -# OAuthRequest represents the request and can be serialized |
151 | -class OAuthRequest(object): |
152 | - ''' |
153 | - OAuth parameters: |
154 | - - oauth_consumer_key |
155 | - - oauth_token |
156 | - - oauth_signature_method |
157 | - - oauth_signature |
158 | - - oauth_timestamp |
159 | - - oauth_nonce |
160 | - - oauth_version |
161 | - ... any additional parameters, as defined by the Service Provider. |
162 | - ''' |
163 | - parameters = None # oauth parameters |
164 | - http_method = HTTP_METHOD |
165 | - http_url = None |
166 | - version = VERSION |
167 | - |
168 | - def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): |
169 | - self.http_method = http_method |
170 | - self.http_url = http_url |
171 | - self.parameters = parameters or {} |
172 | - |
173 | - def set_parameter(self, parameter, value): |
174 | - self.parameters[parameter] = value |
175 | - |
176 | - def get_parameter(self, parameter): |
177 | - try: |
178 | - return self.parameters[parameter] |
179 | - except: |
180 | - raise OAuthError('Parameter not found: %s' % parameter) |
181 | - |
182 | - def _get_timestamp_nonce(self): |
183 | - return self.get_parameter('oauth_timestamp'), self.get_parameter('oauth_nonce') |
184 | - |
185 | - # get any non-oauth parameters |
186 | - def get_nonoauth_parameters(self): |
187 | - parameters = {} |
188 | - for k, v in self.parameters.iteritems(): |
189 | - # ignore oauth parameters |
190 | - if k.find('oauth_') < 0: |
191 | - parameters[k] = v |
192 | - return parameters |
193 | - |
194 | - # serialize as a header for an HTTPAuth request |
195 | - def to_header(self, realm=''): |
196 | - auth_header = 'OAuth realm="%s"' % realm |
197 | - # add the oauth parameters |
198 | - if self.parameters: |
199 | - for k, v in self.parameters.iteritems(): |
200 | - auth_header += ', %s="%s"' % (k, v) |
201 | - return {'Authorization': auth_header} |
202 | - |
203 | - # serialize as post data for a POST request |
204 | - def to_postdata(self): |
205 | - return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems()) |
206 | - |
207 | - # serialize as a url for a GET request |
208 | - def to_url(self): |
209 | - return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) |
210 | - |
211 | - # return a string that consists of all the parameters that need to be signed |
212 | - def get_normalized_parameters(self): |
213 | - params = self.parameters |
214 | - try: |
215 | - # exclude the signature if it exists |
216 | - del params['oauth_signature'] |
217 | - except: |
218 | - pass |
219 | - key_values = params.items() |
220 | - # sort lexicographically, first after key, then after value |
221 | - key_values.sort() |
222 | - # combine key value pairs in string and escape |
223 | - return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in key_values) |
224 | - |
225 | - # just uppercases the http method |
226 | - def get_normalized_http_method(self): |
227 | - return self.http_method.upper() |
228 | - |
229 | - # parses the url and rebuilds it to be scheme://host/path |
230 | - def get_normalized_http_url(self): |
231 | - parts = urlparse.urlparse(self.http_url) |
232 | - url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path |
233 | - return url_string |
234 | - |
235 | - # set the signature parameter to the result of build_signature |
236 | - def sign_request(self, signature_method, consumer, token): |
237 | - # set the signature method |
238 | - self.set_parameter('oauth_signature_method', signature_method.get_name()) |
239 | - # set the signature |
240 | - self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) |
241 | - |
242 | - def build_signature(self, signature_method, consumer, token): |
243 | - # call the build signature method within the signature method |
244 | - return signature_method.build_signature(self, consumer, token) |
245 | - |
246 | - @staticmethod |
247 | - def from_request(http_method, http_url, headers=None, postdata=None, parameters=None): |
248 | - |
249 | - # let the library user override things however they'd like, if they know |
250 | - # which parameters to use then go for it, for example XMLRPC might want to |
251 | - # do this |
252 | - if parameters is not None: |
253 | - return OAuthRequest(http_method, http_url, parameters) |
254 | - |
255 | - # from the headers |
256 | - if headers is not None: |
257 | - try: |
258 | - auth_header = headers['Authorization'] |
259 | - # check that the authorization header is OAuth |
260 | - auth_header.index('OAuth') |
261 | - # get the parameters from the header |
262 | - parameters = OAuthRequest._split_header(auth_header) |
263 | - return OAuthRequest(http_method, http_url, parameters) |
264 | - except: |
265 | - raise OAuthError('Unable to parse OAuth parameters from Authorization header.') |
266 | - |
267 | - # from the parameter string (post body) |
268 | - if http_method == 'POST' and postdata is not None: |
269 | - parameters = OAuthRequest._split_url_string(postdata) |
270 | - |
271 | - # from the url string |
272 | - elif http_method == 'GET': |
273 | - param_str = urlparse.urlparse(http_url).query |
274 | - parameters = OAuthRequest._split_url_string(param_str) |
275 | - |
276 | - if parameters: |
277 | - return OAuthRequest(http_method, http_url, parameters) |
278 | - |
279 | - raise OAuthError('Missing all OAuth parameters. OAuth parameters must be in the headers, post body, or url.') |
280 | - |
281 | - @staticmethod |
282 | - def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None): |
283 | - if not parameters: |
284 | - parameters = {} |
285 | - |
286 | - defaults = { |
287 | - 'oauth_consumer_key': oauth_consumer.key, |
288 | - 'oauth_timestamp': generate_timestamp(), |
289 | - 'oauth_nonce': generate_nonce(), |
290 | - 'oauth_version': OAuthRequest.version, |
291 | - } |
292 | - |
293 | - defaults.update(parameters) |
294 | - parameters = defaults |
295 | - |
296 | - if token: |
297 | - parameters['oauth_token'] = token.key |
298 | - |
299 | - return OAuthRequest(http_method, http_url, parameters) |
300 | - |
301 | - @staticmethod |
302 | - def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): |
303 | - if not parameters: |
304 | - parameters = {} |
305 | - |
306 | - parameters['oauth_token'] = token.key |
307 | - |
308 | - if callback: |
309 | - parameters['oauth_callback'] = escape(callback) |
310 | - |
311 | - return OAuthRequest(http_method, http_url, parameters) |
312 | - |
313 | - # util function: turn Authorization: header into parameters, has to do some unescaping |
314 | - @staticmethod |
315 | - def _split_header(header): |
316 | - params = {} |
317 | - header = header.lstrip() |
318 | - if not header.startswith('OAuth '): |
319 | - raise ValueError("not an OAuth header: %r" % header) |
320 | - header = header[6:] |
321 | - parts = header.split(',') |
322 | - for param in parts: |
323 | - # remove whitespace |
324 | - param = param.strip() |
325 | - # split key-value |
326 | - param_parts = param.split('=', 1) |
327 | - if param_parts[0] == 'realm': |
328 | - # Realm header is not an OAuth parameter according to rfc5849 |
329 | - # section 3.4.1.3.1. |
330 | - continue |
331 | - # remove quotes and unescape the value |
332 | - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) |
333 | - return params |
334 | - |
335 | - # util function: turn url string into parameters, has to do some unescaping |
336 | - @staticmethod |
337 | - def _split_url_string(param_str): |
338 | - parameters = cgi.parse_qs(param_str, keep_blank_values=False) |
339 | - for k, v in parameters.iteritems(): |
340 | - parameters[k] = urllib.unquote(v[0]) |
341 | - return parameters |
342 | - |
343 | -# OAuthServer is a worker to check a requests validity against a data store |
344 | -class OAuthServer(object): |
345 | - timestamp_threshold = 300 # in seconds, five minutes |
346 | - version = VERSION |
347 | - signature_methods = None |
348 | - data_store = None |
349 | - |
350 | - def __init__(self, data_store=None, signature_methods=None): |
351 | - self.data_store = data_store |
352 | - self.signature_methods = signature_methods or {} |
353 | - |
354 | - def set_data_store(self, oauth_data_store): |
355 | - self.data_store = oauth_data_store |
356 | - |
357 | - def get_data_store(self): |
358 | - return self.data_store |
359 | - |
360 | - def add_signature_method(self, signature_method): |
361 | - self.signature_methods[signature_method.get_name()] = signature_method |
362 | - return self.signature_methods |
363 | - |
364 | - # process a request_token request |
365 | - # returns the request token on success |
366 | - def fetch_request_token(self, oauth_request): |
367 | - try: |
368 | - # get the request token for authorization |
369 | - token = self._get_token(oauth_request, 'request') |
370 | - except: |
371 | - # no token required for the initial token request |
372 | - version = self._get_version(oauth_request) |
373 | - consumer = self._get_consumer(oauth_request) |
374 | - self._check_signature(oauth_request, consumer, None) |
375 | - # fetch a new token |
376 | - token = self.data_store.fetch_request_token(consumer) |
377 | - return token |
378 | - |
379 | - # process an access_token request |
380 | - # returns the access token on success |
381 | - def fetch_access_token(self, oauth_request): |
382 | - version = self._get_version(oauth_request) |
383 | - consumer = self._get_consumer(oauth_request) |
384 | - # get the request token |
385 | - token = self._get_token(oauth_request, 'request') |
386 | - self._check_signature(oauth_request, consumer, token) |
387 | - new_token = self.data_store.fetch_access_token(consumer, token) |
388 | - return new_token |
389 | - |
390 | - # verify an api call, checks all the parameters |
391 | - def verify_request(self, oauth_request): |
392 | - # -> consumer and token |
393 | - version = self._get_version(oauth_request) |
394 | - consumer = self._get_consumer(oauth_request) |
395 | - # get the access token |
396 | - token = self._get_token(oauth_request, 'access') |
397 | - self._check_signature(oauth_request, consumer, token) |
398 | - parameters = oauth_request.get_nonoauth_parameters() |
399 | - return consumer, token, parameters |
400 | - |
401 | - # authorize a request token |
402 | - def authorize_token(self, token, user): |
403 | - return self.data_store.authorize_request_token(token, user) |
404 | - |
405 | - # get the callback url |
406 | - def get_callback(self, oauth_request): |
407 | - return oauth_request.get_parameter('oauth_callback') |
408 | - |
409 | - # optional support for the authenticate header |
410 | - def build_authenticate_header(self, realm=''): |
411 | - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} |
412 | - |
413 | - # verify the correct version request for this server |
414 | - def _get_version(self, oauth_request): |
415 | - try: |
416 | - version = oauth_request.get_parameter('oauth_version') |
417 | - except: |
418 | - version = VERSION |
419 | - if version and version != self.version: |
420 | - raise OAuthError('OAuth version %s not supported' % str(version)) |
421 | - return version |
422 | - |
423 | - # figure out the signature with some defaults |
424 | - def _get_signature_method(self, oauth_request): |
425 | - try: |
426 | - signature_method = oauth_request.get_parameter('oauth_signature_method') |
427 | - except: |
428 | - signature_method = SIGNATURE_METHOD |
429 | - try: |
430 | - # get the signature method object |
431 | - signature_method = self.signature_methods[signature_method] |
432 | - except: |
433 | - signature_method_names = ', '.join(self.signature_methods.keys()) |
434 | - raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) |
435 | - |
436 | - return signature_method |
437 | - |
438 | - def _get_consumer(self, oauth_request): |
439 | - consumer_key = oauth_request.get_parameter('oauth_consumer_key') |
440 | - if not consumer_key: |
441 | - raise OAuthError('Invalid consumer key') |
442 | - consumer = self.data_store.lookup_consumer(consumer_key) |
443 | - if not consumer: |
444 | - raise OAuthError('Invalid consumer') |
445 | - return consumer |
446 | - |
447 | - # try to find the token for the provided request token key |
448 | - def _get_token(self, oauth_request, token_type='access'): |
449 | - token_field = oauth_request.get_parameter('oauth_token') |
450 | - token = self.data_store.lookup_token(token_type, token_field) |
451 | - if not token: |
452 | - raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) |
453 | - return token |
454 | - |
455 | - def _check_signature(self, oauth_request, consumer, token): |
456 | - timestamp, nonce = oauth_request._get_timestamp_nonce() |
457 | - self._check_timestamp(timestamp) |
458 | - self._check_nonce(consumer, token, nonce) |
459 | - signature_method = self._get_signature_method(oauth_request) |
460 | - try: |
461 | - signature = oauth_request.get_parameter('oauth_signature') |
462 | - except: |
463 | - raise OAuthError('Missing signature') |
464 | - # attempt to construct the same signature |
465 | - built = signature_method.build_signature(oauth_request, consumer, token) |
466 | - if signature != built: |
467 | - key, base = signature_method.build_signature_base_string(oauth_request, consumer, token) |
468 | - raise OAuthError('Signature does not match. Expected: %s Got: %s Expected signature base string: %s' % (built, signature, base)) |
469 | - |
470 | - def _check_timestamp(self, timestamp): |
471 | - # verify that timestamp is recentish |
472 | - timestamp = int(timestamp) |
473 | - now = int(time.time()) |
474 | - lapsed = now - timestamp |
475 | - if lapsed > self.timestamp_threshold: |
476 | - raise OAuthError('Expired timestamp: given %d and now %s has a greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) |
477 | - |
478 | - def _check_nonce(self, consumer, token, nonce): |
479 | - # verify that the nonce is uniqueish |
480 | - try: |
481 | - self.data_store.lookup_nonce(consumer, token, nonce) |
482 | - raise OAuthError('Nonce already used: %s' % str(nonce)) |
483 | - except: |
484 | - pass |
485 | - |
486 | -# OAuthClient is a worker to attempt to execute a request |
487 | -class OAuthClient(object): |
488 | - consumer = None |
489 | - token = None |
490 | - |
491 | - def __init__(self, oauth_consumer, oauth_token): |
492 | - self.consumer = oauth_consumer |
493 | - self.token = oauth_token |
494 | - |
495 | - def get_consumer(self): |
496 | - return self.consumer |
497 | - |
498 | - def get_token(self): |
499 | - return self.token |
500 | - |
501 | - def fetch_request_token(self, oauth_request): |
502 | - # -> OAuthToken |
503 | - raise NotImplementedError |
504 | - |
505 | - def fetch_access_token(self, oauth_request): |
506 | - # -> OAuthToken |
507 | - raise NotImplementedError |
508 | - |
509 | - def access_resource(self, oauth_request): |
510 | - # -> some protected resource |
511 | - raise NotImplementedError |
512 | - |
513 | -# OAuthDataStore is a database abstraction used to lookup consumers and tokens |
514 | -class OAuthDataStore(object): |
515 | - |
516 | - def lookup_consumer(self, key): |
517 | - # -> OAuthConsumer |
518 | - raise NotImplementedError |
519 | - |
520 | - def lookup_token(self, oauth_consumer, token_type, token_token): |
521 | - # -> OAuthToken |
522 | - raise NotImplementedError |
523 | - |
524 | - def lookup_nonce(self, oauth_consumer, oauth_token, nonce, timestamp): |
525 | - # -> OAuthToken |
526 | - raise NotImplementedError |
527 | - |
528 | - def fetch_request_token(self, oauth_consumer): |
529 | - # -> OAuthToken |
530 | - raise NotImplementedError |
531 | - |
532 | - def fetch_access_token(self, oauth_consumer, oauth_token): |
533 | - # -> OAuthToken |
534 | - raise NotImplementedError |
535 | - |
536 | - def authorize_request_token(self, oauth_token, user): |
537 | - # -> OAuthToken |
538 | - raise NotImplementedError |
539 | - |
540 | -# OAuthSignatureMethod is a strategy class that implements a signature method |
541 | -class OAuthSignatureMethod(object): |
542 | - def get_name(): |
543 | - # -> str |
544 | - raise NotImplementedError |
545 | - |
546 | - def build_signature_base_string(oauth_request, oauth_consumer, oauth_token): |
547 | - # -> str key, str raw |
548 | - raise NotImplementedError |
549 | - |
550 | - def build_signature(oauth_request, oauth_consumer, oauth_token): |
551 | - # -> str |
552 | - raise NotImplementedError |
553 | - |
554 | -class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): |
555 | - |
556 | - def get_name(self): |
557 | - return 'HMAC-SHA1' |
558 | - |
559 | - def build_signature_base_string(self, oauth_request, consumer, token): |
560 | - sig = ( |
561 | - escape(oauth_request.get_normalized_http_method()), |
562 | - escape(oauth_request.get_normalized_http_url()), |
563 | - escape(oauth_request.get_normalized_parameters()), |
564 | - ) |
565 | - |
566 | - key = '%s&' % escape(consumer.secret) |
567 | - if token: |
568 | - key += escape(token.secret) |
569 | - raw = '&'.join(sig) |
570 | - return key, raw |
571 | - |
572 | - def build_signature(self, oauth_request, consumer, token): |
573 | - # build the base signature string |
574 | - key, raw = self.build_signature_base_string(oauth_request, consumer, token) |
575 | - |
576 | - # hmac object |
577 | - try: |
578 | - import hashlib # 2.5 |
579 | - hashed = hmac.new(key, raw, hashlib.sha1) |
580 | - except: |
581 | - import sha # deprecated |
582 | - hashed = hmac.new(key, raw, sha) |
583 | - |
584 | - # calculate the digest base 64 |
585 | - return base64.b64encode(hashed.digest()) |
586 | - |
587 | -class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): |
588 | - |
589 | - def get_name(self): |
590 | - return 'PLAINTEXT' |
591 | - |
592 | - def build_signature_base_string(self, oauth_request, consumer, token): |
593 | - # concatenate the consumer key and secret |
594 | - sig = escape(consumer.secret) |
595 | - if token: |
596 | - sig = '&'.join((sig, escape(token.secret))) |
597 | - return sig |
598 | - |
599 | - def build_signature(self, oauth_request, consumer, token): |
600 | - return self.build_signature_base_string(oauth_request, consumer, token) |
601 | |
602 | === modified file 'setup.py' |
603 | --- setup.py 2011-01-06 22:21:02 +0000 |
604 | +++ setup.py 2011-02-13 12:17:26 +0000 |
605 | @@ -56,7 +56,6 @@ |
606 | 'meliae', |
607 | 'mercurial', |
608 | 'mocker', |
609 | - 'oauth', |
610 | 'paramiko', |
611 | 'psycopg2', |
612 | 'python-memcached', |
613 | |
614 | === modified file 'versions.cfg' |
615 | --- versions.cfg 2011-02-09 15:25:57 +0000 |
616 | +++ versions.cfg 2011-02-13 12:17:26 +0000 |
617 | @@ -46,7 +46,6 @@ |
618 | mercurial = 1.6.2 |
619 | mocker = 0.10.1 |
620 | mozrunner = 1.3.4 |
621 | -oauth = 1.0 |
622 | paramiko = 1.7.4 |
623 | Paste = 1.7.2 |
624 | PasteDeploy = 1.3.3 |
Martin--
This looks good. Thanks!