annotate Yading/7digital-python/lib/oauth.py @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 8c29444cb5fd
children
rev   line source
yading@7 1 """
yading@7 2 The MIT License
yading@7 3
yading@7 4 Copyright (c) 2007 Leah Culver
yading@7 5
yading@7 6 Permission is hereby granted, free of charge, to any person obtaining a copy
yading@7 7 of this software and associated documentation files (the "Software"), to deal
yading@7 8 in the Software without restriction, including without limitation the rights
yading@7 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
yading@7 10 copies of the Software, and to permit persons to whom the Software is
yading@7 11 furnished to do so, subject to the following conditions:
yading@7 12
yading@7 13 The above copyright notice and this permission notice shall be included in
yading@7 14 all copies or substantial portions of the Software.
yading@7 15
yading@7 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
yading@7 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
yading@7 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
yading@7 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
yading@7 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
yading@7 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
yading@7 22 THE SOFTWARE.
yading@7 23 """
yading@7 24
yading@7 25 import cgi
yading@7 26 import urllib
yading@7 27 import time
yading@7 28 import random
yading@7 29 import urlparse
yading@7 30 import hmac
yading@7 31 import binascii
yading@7 32 import re
yading@7 33
yading@7 34
yading@7 35 VERSION = '1.0' # Hi Blaine!
yading@7 36 HTTP_METHOD = 'GET'
yading@7 37 SIGNATURE_METHOD = 'PLAINTEXT'
yading@7 38
yading@7 39
yading@7 40 class OAuthError(RuntimeError):
yading@7 41 """Generic exception class."""
yading@7 42 def __init__(self, message='OAuth error occured.'):
yading@7 43 self.message = message
yading@7 44
yading@7 45 def build_authenticate_header(realm=''):
yading@7 46 """Optional WWW-Authenticate header (401 error)"""
yading@7 47 return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
yading@7 48
yading@7 49 def escape(s):
yading@7 50 """Escape a URL including any /."""
yading@7 51 return urllib.quote(s, safe='~')
yading@7 52
yading@7 53 def _utf8_str(s):
yading@7 54 """Convert unicode to utf-8."""
yading@7 55 if isinstance(s, unicode):
yading@7 56 return s.encode("utf-8")
yading@7 57 else:
yading@7 58 return str(s)
yading@7 59
yading@7 60 def generate_timestamp():
yading@7 61 """Get seconds since epoch (UTC)."""
yading@7 62 return int(time.time())
yading@7 63
yading@7 64 def generate_nonce(length=8):
yading@7 65 """Generate pseudorandom number."""
yading@7 66 return ''.join([str(random.randint(0, 9)) for i in range(length)])
yading@7 67
yading@7 68 def generate_verifier(length=8):
yading@7 69 """Generate pseudorandom number."""
yading@7 70 return ''.join([str(random.randint(0, 9)) for i in range(length)])
yading@7 71
yading@7 72
yading@7 73 class OAuthConsumer(object):
yading@7 74 """Consumer of OAuth authentication.
yading@7 75
yading@7 76 OAuthConsumer is a data type that represents the identity of the Consumer
yading@7 77 via its shared secret with the Service Provider.
yading@7 78
yading@7 79 """
yading@7 80 key = None
yading@7 81 secret = None
yading@7 82
yading@7 83 def __init__(self, key, secret):
yading@7 84 self.key = key
yading@7 85 self.secret = secret
yading@7 86
yading@7 87
yading@7 88 class OAuthToken(object):
yading@7 89 """OAuthToken is a data type that represents an End User via either an access
yading@7 90 or request token.
yading@7 91
yading@7 92 key -- the token
yading@7 93 secret -- the token secret
yading@7 94
yading@7 95 """
yading@7 96 key = None
yading@7 97 secret = None
yading@7 98 callback = None
yading@7 99 callback_confirmed = None
yading@7 100 verifier = None
yading@7 101
yading@7 102 def __init__(self, key, secret):
yading@7 103 self.key = key
yading@7 104 self.secret = secret
yading@7 105
yading@7 106 def set_callback(self, callback):
yading@7 107 self.callback = callback
yading@7 108 self.callback_confirmed = 'true'
yading@7 109
yading@7 110 def set_verifier(self, verifier=None):
yading@7 111 if verifier is not None:
yading@7 112 self.verifier = verifier
yading@7 113 else:
yading@7 114 self.verifier = generate_verifier()
yading@7 115
yading@7 116 def get_callback_url(self):
yading@7 117 if self.callback and self.verifier:
yading@7 118 # Append the oauth_verifier.
yading@7 119 parts = urlparse.urlparse(self.callback)
yading@7 120 scheme, netloc, path, params, query, fragment = parts[:6]
yading@7 121 if query:
yading@7 122 query = '%s&oauth_verifier=%s' % (query, self.verifier)
yading@7 123 else:
yading@7 124 query = 'oauth_verifier=%s' % self.verifier
yading@7 125 return urlparse.urlunparse((scheme, netloc, path, params,
yading@7 126 query, fragment))
yading@7 127 return self.callback
yading@7 128
yading@7 129 def to_string(self):
yading@7 130 data = {
yading@7 131 'oauth_token': self.key,
yading@7 132 'oauth_token_secret': self.secret,
yading@7 133 }
yading@7 134 if self.callback_confirmed is not None:
yading@7 135 data['oauth_callback_confirmed'] = self.callback_confirmed
yading@7 136 return urllib.urlencode(data)
yading@7 137
yading@7 138 def from_string(s):
yading@7 139 """ Returns a token from something like:
yading@7 140 oauth_token_secret=xxx&oauth_token=xxx
yading@7 141 """
yading@7 142 print "******* %s" % s.__class__
yading@7 143 #params = urlparse.parse_qs(s, keep_blank_values=False)
yading@7 144
yading@7 145 key = re.search("<oauth_token>(\w.+)</oauth_token>", s).groups()[0]
yading@7 146 print "@@@@@@ key: %s" %key
yading@7 147 secret = re.search("<oauth_token_secret>(\w.+)</oauth_token_secret>", s).groups()[0]
yading@7 148 print "@@@@@@ secret: %s" % secret
yading@7 149 token = OAuthToken(key, secret)
yading@7 150
yading@7 151 return token
yading@7 152 from_string = staticmethod(from_string)
yading@7 153
yading@7 154 def __str__(self):
yading@7 155 return self.to_string()
yading@7 156
yading@7 157
yading@7 158 class OAuthRequest(object):
yading@7 159 """OAuthRequest represents the request and can be serialized.
yading@7 160
yading@7 161 OAuth parameters:
yading@7 162 - oauth_consumer_key
yading@7 163 - oauth_token
yading@7 164 - oauth_signature_method
yading@7 165 - oauth_signature
yading@7 166 - oauth_timestamp
yading@7 167 - oauth_nonce
yading@7 168 - oauth_version
yading@7 169 - oauth_verifier
yading@7 170 ... any additional parameters, as defined by the Service Provider.
yading@7 171 """
yading@7 172 parameters = None # OAuth parameters.
yading@7 173 http_method = HTTP_METHOD
yading@7 174 http_url = None
yading@7 175 version = VERSION
yading@7 176
yading@7 177 def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
yading@7 178 self.http_method = http_method
yading@7 179 self.http_url = http_url
yading@7 180 self.parameters = parameters or {}
yading@7 181
yading@7 182 def set_parameter(self, parameter, value):
yading@7 183 self.parameters[parameter] = value
yading@7 184
yading@7 185 def get_parameter(self, parameter):
yading@7 186 try:
yading@7 187 return self.parameters[parameter]
yading@7 188 except:
yading@7 189 raise OAuthError('Parameter not found: %s' % parameter)
yading@7 190
yading@7 191 def _get_timestamp_nonce(self):
yading@7 192 return self.get_parameter('oauth_timestamp'), self.get_parameter(
yading@7 193 'oauth_nonce')
yading@7 194
yading@7 195 def get_nonoauth_parameters(self):
yading@7 196 """Get any non-OAuth parameters."""
yading@7 197 parameters = {}
yading@7 198 for k, v in self.parameters.iteritems():
yading@7 199 # Ignore oauth parameters.
yading@7 200 if k.find('oauth_') < 0:
yading@7 201 parameters[k] = v
yading@7 202 return parameters
yading@7 203
yading@7 204 def to_header(self, realm=''):
yading@7 205 """Serialize as a header for an HTTPAuth request."""
yading@7 206 auth_header = 'OAuth realm="%s"' % realm
yading@7 207 # Add the oauth parameters.
yading@7 208 if self.parameters:
yading@7 209 for k, v in self.parameters.iteritems():
yading@7 210 if k[:6] == 'oauth_':
yading@7 211 auth_header += ', %s="%s"' % (k, escape(str(v)))
yading@7 212 return {'Authorization': auth_header}
yading@7 213
yading@7 214 def to_postdata(self):
yading@7 215 """Serialize as post data for a POST request."""
yading@7 216 return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) \
yading@7 217 for k, v in self.parameters.iteritems()])
yading@7 218
yading@7 219 def to_url(self):
yading@7 220 """Serialize as a URL for a GET request."""
yading@7 221 return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
yading@7 222
yading@7 223 def get_normalized_parameters(self):
yading@7 224 """Return a string that contains the parameters that must be signed."""
yading@7 225 params = self.parameters
yading@7 226 try:
yading@7 227 # Exclude the signature if it exists.
yading@7 228 del params['oauth_signature']
yading@7 229 except:
yading@7 230 pass
yading@7 231 # Escape key values before sorting.
yading@7 232 key_values = [(escape(_utf8_str(k)), escape(_utf8_str(v))) \
yading@7 233 for k,v in params.items()]
yading@7 234 # Sort lexicographically, first after key, then after value.
yading@7 235 key_values.sort()
yading@7 236 # Combine key value pairs into a string.
yading@7 237 return '&'.join(['%s=%s' % (k, v) for k, v in key_values])
yading@7 238
yading@7 239 def get_normalized_http_method(self):
yading@7 240 """Uppercases the http method."""
yading@7 241 return self.http_method.upper()
yading@7 242
yading@7 243 def get_normalized_http_url(self):
yading@7 244 """Parses the URL and rebuilds it to be scheme://host/path."""
yading@7 245 parts = urlparse.urlparse(self.http_url)
yading@7 246 scheme, netloc, path = parts[:3]
yading@7 247 # Exclude default port numbers.
yading@7 248 if scheme == 'http' and netloc[-3:] == ':80':
yading@7 249 netloc = netloc[:-3]
yading@7 250 elif scheme == 'https' and netloc[-4:] == ':443':
yading@7 251 netloc = netloc[:-4]
yading@7 252 return '%s://%s%s' % (scheme, netloc, path)
yading@7 253
yading@7 254 def sign_request(self, signature_method, consumer, token):
yading@7 255 """Set the signature parameter to the result of build_signature."""
yading@7 256 # Set the signature method.
yading@7 257 self.set_parameter('oauth_signature_method',
yading@7 258 signature_method.get_name())
yading@7 259 # Set the signature.
yading@7 260 self.set_parameter('oauth_signature',
yading@7 261 self.build_signature(signature_method, consumer, token))
yading@7 262
yading@7 263 def build_signature(self, signature_method, consumer, token):
yading@7 264 """Calls the build signature method within the signature method."""
yading@7 265 return signature_method.build_signature(self, consumer, token)
yading@7 266
yading@7 267 def from_request(http_method, http_url, headers=None, parameters=None,
yading@7 268 query_string=None):
yading@7 269 """Combines multiple parameter sources."""
yading@7 270 if parameters is None:
yading@7 271 parameters = {}
yading@7 272
yading@7 273 # Headers
yading@7 274 if headers and 'Authorization' in headers:
yading@7 275 auth_header = headers['Authorization']
yading@7 276 # Check that the authorization header is OAuth.
yading@7 277 if auth_header[:6] == 'OAuth ':
yading@7 278 auth_header = auth_header[6:]
yading@7 279 try:
yading@7 280 # Get the parameters from the header.
yading@7 281 header_params = OAuthRequest._split_header(auth_header)
yading@7 282 parameters.update(header_params)
yading@7 283 except:
yading@7 284 raise OAuthError('Unable to parse OAuth parameters from '
yading@7 285 'Authorization header.')
yading@7 286
yading@7 287 # GET or POST query string.
yading@7 288 if query_string:
yading@7 289 query_params = OAuthRequest._split_url_string(query_string)
yading@7 290 parameters.update(query_params)
yading@7 291
yading@7 292 # URL parameters.
yading@7 293 param_str = urlparse.urlparse(http_url)[4] # query
yading@7 294 url_params = OAuthRequest._split_url_string(param_str)
yading@7 295 parameters.update(url_params)
yading@7 296
yading@7 297 if parameters:
yading@7 298 return OAuthRequest(http_method, http_url, parameters)
yading@7 299
yading@7 300 return None
yading@7 301 from_request = staticmethod(from_request)
yading@7 302
yading@7 303 def from_consumer_and_token(oauth_consumer, token=None,
yading@7 304 callback=None, verifier=None, http_method=HTTP_METHOD,
yading@7 305 http_url=None, parameters=None):
yading@7 306 if not parameters:
yading@7 307 parameters = {}
yading@7 308
yading@7 309 defaults = {
yading@7 310 'oauth_consumer_key': oauth_consumer.key,
yading@7 311 'oauth_timestamp': generate_timestamp(),
yading@7 312 'oauth_nonce': generate_nonce(),
yading@7 313 'oauth_version': OAuthRequest.version,
yading@7 314 }
yading@7 315
yading@7 316 defaults.update(parameters)
yading@7 317 parameters = defaults
yading@7 318
yading@7 319 if token:
yading@7 320 parameters['oauth_token'] = token.key
yading@7 321 if token.callback:
yading@7 322 parameters['oauth_callback'] = token.callback
yading@7 323 # 1.0a support for verifier.
yading@7 324 if verifier:
yading@7 325 parameters['oauth_verifier'] = verifier
yading@7 326 elif callback:
yading@7 327 # 1.0a support for callback in the request token request.
yading@7 328 parameters['oauth_callback'] = callback
yading@7 329
yading@7 330 return OAuthRequest(http_method, http_url, parameters)
yading@7 331 from_consumer_and_token = staticmethod(from_consumer_and_token)
yading@7 332
yading@7 333 def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD,
yading@7 334 http_url=None, parameters=None):
yading@7 335 if not parameters:
yading@7 336 parameters = {}
yading@7 337
yading@7 338 parameters['oauth_token'] = token.key
yading@7 339
yading@7 340 if callback:
yading@7 341 parameters['oauth_callback'] = callback
yading@7 342
yading@7 343 return OAuthRequest(http_method, http_url, parameters)
yading@7 344 from_token_and_callback = staticmethod(from_token_and_callback)
yading@7 345
yading@7 346 def _split_header(header):
yading@7 347 """Turn Authorization: header into parameters."""
yading@7 348 params = {}
yading@7 349 parts = header.split(',')
yading@7 350 for param in parts:
yading@7 351 # Ignore realm parameter.
yading@7 352 if param.find('realm') > -1:
yading@7 353 continue
yading@7 354 # Remove whitespace.
yading@7 355 param = param.strip()
yading@7 356 # Split key-value.
yading@7 357 param_parts = param.split('=', 1)
yading@7 358 # Remove quotes and unescape the value.
yading@7 359 params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
yading@7 360 return params
yading@7 361 _split_header = staticmethod(_split_header)
yading@7 362
yading@7 363 def _split_url_string(param_str):
yading@7 364 """Turn URL string into parameters."""
yading@7 365 parameters = cgi.parse_qs(param_str, keep_blank_values=False)
yading@7 366 for k, v in parameters.iteritems():
yading@7 367 parameters[k] = urllib.unquote(v[0])
yading@7 368 return parameters
yading@7 369 _split_url_string = staticmethod(_split_url_string)
yading@7 370
yading@7 371 class OAuthServer(object):
yading@7 372 """A worker to check the validity of a request against a data store."""
yading@7 373 timestamp_threshold = 300 # In seconds, five minutes.
yading@7 374 version = VERSION
yading@7 375 signature_methods = None
yading@7 376 data_store = None
yading@7 377
yading@7 378 def __init__(self, data_store=None, signature_methods=None):
yading@7 379 self.data_store = data_store
yading@7 380 self.signature_methods = signature_methods or {}
yading@7 381
yading@7 382 def set_data_store(self, data_store):
yading@7 383 self.data_store = data_store
yading@7 384
yading@7 385 def get_data_store(self):
yading@7 386 return self.data_store
yading@7 387
yading@7 388 def add_signature_method(self, signature_method):
yading@7 389 self.signature_methods[signature_method.get_name()] = signature_method
yading@7 390 return self.signature_methods
yading@7 391
yading@7 392 def fetch_request_token(self, oauth_request):
yading@7 393 """Processes a request_token request and returns the
yading@7 394 request token on success.
yading@7 395 """
yading@7 396 try:
yading@7 397 # Get the request token for authorization.
yading@7 398 token = self._get_token(oauth_request, 'request')
yading@7 399 except OAuthError:
yading@7 400 # No token required for the initial token request.
yading@7 401 version = self._get_version(oauth_request)
yading@7 402 consumer = self._get_consumer(oauth_request)
yading@7 403 try:
yading@7 404 callback = self.get_callback(oauth_request)
yading@7 405 except OAuthError:
yading@7 406 callback = None # 1.0, no callback specified.
yading@7 407 self._check_signature(oauth_request, consumer, None)
yading@7 408 # Fetch a new token.
yading@7 409 token = self.data_store.fetch_request_token(consumer, callback)
yading@7 410 return token
yading@7 411
yading@7 412 def fetch_access_token(self, oauth_request):
yading@7 413 """Processes an access_token request and returns the
yading@7 414 access token on success.
yading@7 415 """
yading@7 416 version = self._get_version(oauth_request)
yading@7 417 consumer = self._get_consumer(oauth_request)
yading@7 418 try:
yading@7 419 verifier = self._get_verifier(oauth_request)
yading@7 420 except OAuthError:
yading@7 421 verifier = None
yading@7 422 # Get the request token.
yading@7 423 token = self._get_token(oauth_request, 'request')
yading@7 424 self._check_signature(oauth_request, consumer, token)
yading@7 425 new_token = self.data_store.fetch_access_token(consumer, token, verifier)
yading@7 426 return new_token
yading@7 427
yading@7 428 def verify_request(self, oauth_request):
yading@7 429 """Verifies an api call and checks all the parameters."""
yading@7 430 # -> consumer and token
yading@7 431 version = self._get_version(oauth_request)
yading@7 432 consumer = self._get_consumer(oauth_request)
yading@7 433 # Get the access token.
yading@7 434 token = self._get_token(oauth_request, 'access')
yading@7 435 self._check_signature(oauth_request, consumer, token)
yading@7 436 parameters = oauth_request.get_nonoauth_parameters()
yading@7 437 return consumer, token, parameters
yading@7 438
yading@7 439 def authorize_token(self, token, user):
yading@7 440 """Authorize a request token."""
yading@7 441 return self.data_store.authorize_request_token(token, user)
yading@7 442
yading@7 443 def get_callback(self, oauth_request):
yading@7 444 """Get the callback URL."""
yading@7 445 return oauth_request.get_parameter('oauth_callback')
yading@7 446
yading@7 447 def build_authenticate_header(self, realm=''):
yading@7 448 """Optional support for the authenticate header."""
yading@7 449 return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
yading@7 450
yading@7 451 def _get_version(self, oauth_request):
yading@7 452 """Verify the correct version request for this server."""
yading@7 453 try:
yading@7 454 version = oauth_request.get_parameter('oauth_version')
yading@7 455 except:
yading@7 456 version = VERSION
yading@7 457 if version and version != self.version:
yading@7 458 raise OAuthError('OAuth version %s not supported.' % str(version))
yading@7 459 return version
yading@7 460
yading@7 461 def _get_signature_method(self, oauth_request):
yading@7 462 """Figure out the signature with some defaults."""
yading@7 463 try:
yading@7 464 signature_method = oauth_request.get_parameter(
yading@7 465 'oauth_signature_method')
yading@7 466 except:
yading@7 467 signature_method = SIGNATURE_METHOD
yading@7 468 try:
yading@7 469 # Get the signature method object.
yading@7 470 signature_method = self.signature_methods[signature_method]
yading@7 471 except:
yading@7 472 signature_method_names = ', '.join(self.signature_methods.keys())
yading@7 473 raise OAuthError('Signature method %s not supported try one of the '
yading@7 474 'following: %s' % (signature_method, signature_method_names))
yading@7 475
yading@7 476 return signature_method
yading@7 477
yading@7 478 def _get_consumer(self, oauth_request):
yading@7 479 consumer_key = oauth_request.get_parameter('oauth_consumer_key')
yading@7 480 consumer = self.data_store.lookup_consumer(consumer_key)
yading@7 481 if not consumer:
yading@7 482 raise OAuthError('Invalid consumer.')
yading@7 483 return consumer
yading@7 484
yading@7 485 def _get_token(self, oauth_request, token_type='access'):
yading@7 486 """Try to find the token for the provided request token key."""
yading@7 487 token_field = oauth_request.get_parameter('oauth_token')
yading@7 488 token = self.data_store.lookup_token(token_type, token_field)
yading@7 489 if not token:
yading@7 490 raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
yading@7 491 return token
yading@7 492
yading@7 493 def _get_verifier(self, oauth_request):
yading@7 494 return oauth_request.get_parameter('oauth_verifier')
yading@7 495
yading@7 496 def _check_signature(self, oauth_request, consumer, token):
yading@7 497 timestamp, nonce = oauth_request._get_timestamp_nonce()
yading@7 498 self._check_timestamp(timestamp)
yading@7 499 self._check_nonce(consumer, token, nonce)
yading@7 500 signature_method = self._get_signature_method(oauth_request)
yading@7 501 try:
yading@7 502 signature = oauth_request.get_parameter('oauth_signature')
yading@7 503 except:
yading@7 504 raise OAuthError('Missing signature.')
yading@7 505 # Validate the signature.
yading@7 506 valid_sig = signature_method.check_signature(oauth_request, consumer,
yading@7 507 token, signature)
yading@7 508 if not valid_sig:
yading@7 509 key, base = signature_method.build_signature_base_string(
yading@7 510 oauth_request, consumer, token)
yading@7 511 raise OAuthError('Invalid signature. Expected signature base '
yading@7 512 'string: %s' % base)
yading@7 513 built = signature_method.build_signature(oauth_request, consumer, token)
yading@7 514
yading@7 515 def _check_timestamp(self, timestamp):
yading@7 516 """Verify that timestamp is recentish."""
yading@7 517 timestamp = int(timestamp)
yading@7 518 now = int(time.time())
yading@7 519 lapsed = abs(now - timestamp)
yading@7 520 if lapsed > self.timestamp_threshold:
yading@7 521 raise OAuthError('Expired timestamp: given %d and now %s has a '
yading@7 522 'greater difference than threshold %d' %
yading@7 523 (timestamp, now, self.timestamp_threshold))
yading@7 524
yading@7 525 def _check_nonce(self, consumer, token, nonce):
yading@7 526 """Verify that the nonce is uniqueish."""
yading@7 527 nonce = self.data_store.lookup_nonce(consumer, token, nonce)
yading@7 528 if nonce:
yading@7 529 raise OAuthError('Nonce already used: %s' % str(nonce))
yading@7 530
yading@7 531
yading@7 532 class OAuthClient(object):
yading@7 533 """OAuthClient is a worker to attempt to execute a request."""
yading@7 534 consumer = None
yading@7 535 token = None
yading@7 536
yading@7 537 def __init__(self, oauth_consumer, oauth_token):
yading@7 538 self.consumer = oauth_consumer
yading@7 539 self.token = oauth_token
yading@7 540
yading@7 541 def get_consumer(self):
yading@7 542 return self.consumer
yading@7 543
yading@7 544 def get_token(self):
yading@7 545 return self.token
yading@7 546
yading@7 547 def fetch_request_token(self, oauth_request):
yading@7 548 """-> OAuthToken."""
yading@7 549 raise NotImplementedError
yading@7 550
yading@7 551 def fetch_access_token(self, oauth_request):
yading@7 552 """-> OAuthToken."""
yading@7 553 raise NotImplementedError
yading@7 554
yading@7 555 def access_resource(self, oauth_request):
yading@7 556 """-> Some protected resource."""
yading@7 557 raise NotImplementedError
yading@7 558
yading@7 559
yading@7 560 class OAuthDataStore(object):
yading@7 561 """A database abstraction used to lookup consumers and tokens."""
yading@7 562
yading@7 563 def lookup_consumer(self, key):
yading@7 564 """-> OAuthConsumer."""
yading@7 565 raise NotImplementedError
yading@7 566
yading@7 567 def lookup_token(self, oauth_consumer, token_type, token_token):
yading@7 568 """-> OAuthToken."""
yading@7 569 raise NotImplementedError
yading@7 570
yading@7 571 def lookup_nonce(self, oauth_consumer, oauth_token, nonce):
yading@7 572 """-> OAuthToken."""
yading@7 573 raise NotImplementedError
yading@7 574
yading@7 575 def fetch_request_token(self, oauth_consumer, oauth_callback):
yading@7 576 """-> OAuthToken."""
yading@7 577 raise NotImplementedError
yading@7 578
yading@7 579 def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier):
yading@7 580 """-> OAuthToken."""
yading@7 581 raise NotImplementedError
yading@7 582
yading@7 583 def authorize_request_token(self, oauth_token, user):
yading@7 584 """-> OAuthToken."""
yading@7 585 raise NotImplementedError
yading@7 586
yading@7 587
yading@7 588 class OAuthSignatureMethod(object):
yading@7 589 """A strategy class that implements a signature method."""
yading@7 590 def get_name(self):
yading@7 591 """-> str."""
yading@7 592 raise NotImplementedError
yading@7 593
yading@7 594 def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
yading@7 595 """-> str key, str raw."""
yading@7 596 raise NotImplementedError
yading@7 597
yading@7 598 def build_signature(self, oauth_request, oauth_consumer, oauth_token):
yading@7 599 """-> str."""
yading@7 600 raise NotImplementedError
yading@7 601
yading@7 602 def check_signature(self, oauth_request, consumer, token, signature):
yading@7 603 built = self.build_signature(oauth_request, consumer, token)
yading@7 604 return built == signature
yading@7 605
yading@7 606
yading@7 607 class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
yading@7 608
yading@7 609 def get_name(self):
yading@7 610 return 'HMAC-SHA1'
yading@7 611
yading@7 612 def build_signature_base_string(self, oauth_request, consumer, token):
yading@7 613 sig = (
yading@7 614 escape(oauth_request.get_normalized_http_method()),
yading@7 615 escape(oauth_request.get_normalized_http_url()),
yading@7 616 escape(oauth_request.get_normalized_parameters()),
yading@7 617 )
yading@7 618
yading@7 619 key = '%s&' % escape(consumer.secret)
yading@7 620 if token:
yading@7 621 key += escape(token.secret)
yading@7 622 raw = '&'.join(sig)
yading@7 623 return key, raw
yading@7 624
yading@7 625 def build_signature(self, oauth_request, consumer, token):
yading@7 626 """Builds the base signature string."""
yading@7 627 key, raw = self.build_signature_base_string(oauth_request, consumer,
yading@7 628 token)
yading@7 629
yading@7 630 # HMAC object.
yading@7 631 try:
yading@7 632 import hashlib # 2.5
yading@7 633 hashed = hmac.new(key, raw, hashlib.sha1)
yading@7 634 except:
yading@7 635 import sha # Deprecated
yading@7 636 hashed = hmac.new(key, raw, sha)
yading@7 637
yading@7 638 # Calculate the digest base 64.
yading@7 639 return binascii.b2a_base64(hashed.digest())[:-1]
yading@7 640
yading@7 641
yading@7 642 class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
yading@7 643
yading@7 644 def get_name(self):
yading@7 645 return 'PLAINTEXT'
yading@7 646
yading@7 647 def build_signature_base_string(self, oauth_request, consumer, token):
yading@7 648 """Concatenates the consumer key and secret."""
yading@7 649 sig = '%s&' % escape(consumer.secret)
yading@7 650 if token:
yading@7 651 sig = sig + escape(token.secret)
yading@7 652 return sig, sig
yading@7 653
yading@7 654 def build_signature(self, oauth_request, consumer, token):
yading@7 655 key, raw = self.build_signature_base_string(oauth_request, consumer,
yading@7 656 token)
yading@7 657 return key