Merge lp:~mbp/launchpad/701545-oauth into lp:launchpad

Proposed by Martin Pool
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
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.

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://code.launchpad.net/~mbp/launchpad/701545-oauth/+merge/48431> but it failed because the same bug 314507 is in our contrib/oauth.py and in the egg too. The bug has been fixed in Ubuntu's oauth.py since karmic so this in conjunction with <https://code.edge.launchpad.net/~mbp/meta-lp-deps/701545-add-dependency/+merge/49536> will get to just using the packaged version.

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

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote : Posted in a previous version of this proposal

Martin--

This looks good. Thanks!

review: Approve (code*)
Revision history for this message
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/update-copyright? It will update the copyright in all the modified files in your tree. I think this serial date style breaks the update-copyright script, subsequent updates will require the engineer to edit the copyright afterwards. You can either change the dates to ranges or verify that the script will work on these files in 2012.

review: Approve (code)
Revision history for this message
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 :-)

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

lp:~mbp/launchpad/701545-oauth updated
12370. By Martin Pool

Also remove oauth from versions.cfg

Revision history for this message
Curtis Hovey (sinzui) wrote :

This is good to land once the deps are in place.

review: Approve (code)
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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