Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

changed folder name

  • Loading branch information...
commit 62bdd176ff6ee59a691ca51ad08cd6a9cc345378 1 parent e017c25
Aaron Brenzel authored
702 liclient/oauth2/__init__.py
... ... @@ -0,0 +1,702 @@
  1 +"""
  2 +The MIT License
  3 +
  4 +Copyright (c) 2007 Leah Culver, Joe Stump, Mark Paschal, Vic Fryzel
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy
  7 +of this software and associated documentation files (the "Software"), to deal
  8 +in the Software without restriction, including without limitation the rights
  9 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 +copies of the Software, and to permit persons to whom the Software is
  11 +furnished to do so, subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in
  14 +all copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 +THE SOFTWARE.
  23 +"""
  24 +
  25 +import urllib
  26 +import time
  27 +import random
  28 +import urlparse
  29 +import hmac
  30 +import binascii
  31 +import httplib2
  32 +
  33 +try:
  34 + from urlparse import parse_qs, parse_qsl
  35 +except ImportError:
  36 + from cgi import parse_qs, parse_qsl
  37 +
  38 +
  39 +VERSION = '1.0' # Hi Blaine!
  40 +HTTP_METHOD = 'GET'
  41 +SIGNATURE_METHOD = 'PLAINTEXT'
  42 +
  43 +
  44 +class Error(RuntimeError):
  45 + """Generic exception class."""
  46 +
  47 + def __init__(self, message='OAuth error occured.'):
  48 + self._message = message
  49 +
  50 + @property
  51 + def message(self):
  52 + """A hack to get around the deprecation errors in 2.6."""
  53 + return self._message
  54 +
  55 + def __str__(self):
  56 + return self._message
  57 +
  58 +class MissingSignature(Error):
  59 + pass
  60 +
  61 +def build_authenticate_header(realm=''):
  62 + """Optional WWW-Authenticate header (401 error)"""
  63 + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
  64 +
  65 +
  66 +def escape(s):
  67 + """Escape a URL including any /."""
  68 + return urllib.quote(s, safe='~')
  69 +
  70 +
  71 +def generate_timestamp():
  72 + """Get seconds since epoch (UTC)."""
  73 + return int(time.time())
  74 +
  75 +
  76 +def generate_nonce(length=8):
  77 + """Generate pseudorandom number."""
  78 + return ''.join([str(random.randint(0, 9)) for i in range(length)])
  79 +
  80 +
  81 +def generate_verifier(length=8):
  82 + """Generate pseudorandom number."""
  83 + return ''.join([str(random.randint(0, 9)) for i in range(length)])
  84 +
  85 +
  86 +class Consumer(object):
  87 + """A consumer of OAuth-protected services.
  88 +The OAuth consumer is a "third-party" service that wants to access
  89 +protected resources from an OAuth service provider on behalf of an end
  90 +user. It's kind of the OAuth client.
  91 +Usually a consumer must be registered with the service provider by the
  92 +developer of the consumer software. As part of that process, the service
  93 +provider gives the consumer a *key* and a *secret* with which the consumer
  94 +software can identify itself to the service. The consumer will include its
  95 +key in each request to identify itself, but will use its secret only when
  96 +signing requests, to prove that the request is from that particular
  97 +registered consumer.
  98 +Once registered, the consumer can then use its consumer credentials to ask
  99 +the service provider for a request token, kicking off the OAuth
  100 +authorization process.
  101 +"""
  102 +
  103 + key = None
  104 + secret = None
  105 +
  106 + def __init__(self, key, secret):
  107 + self.key = key
  108 + self.secret = secret
  109 +
  110 + if self.key is None or self.secret is None:
  111 + raise ValueError("Key and secret must be set.")
  112 +
  113 + def __str__(self):
  114 + data = {
  115 + 'oauth_consumer_key': self.key,
  116 + 'oauth_consumer_secret': self.secret
  117 + }
  118 +
  119 + return urllib.urlencode(data)
  120 +
  121 +
  122 +class Token(object):
  123 + """An OAuth credential used to request authorization or a protected
  124 +resource.
  125 +Tokens in OAuth comprise a *key* and a *secret*. The key is included in
  126 +requests to identify the token being used, but the secret is used only in
  127 +the signature, to prove that the requester is who the server gave the
  128 +token to.
  129 +When first negotiating the authorization, the consumer asks for a *request
  130 +token* that the live user authorizes with the service provider. The
  131 +consumer then exchanges the request token for an *access token* that can
  132 +be used to access protected resources.
  133 +"""
  134 +
  135 + key = None
  136 + secret = None
  137 + callback = None
  138 + callback_confirmed = None
  139 + verifier = None
  140 +
  141 + def __init__(self, key, secret):
  142 + self.key = key
  143 + self.secret = secret
  144 +
  145 + if self.key is None or self.secret is None:
  146 + raise ValueError("Key and secret must be set.")
  147 +
  148 + def set_callback(self, callback):
  149 + self.callback = callback
  150 + self.callback_confirmed = 'true'
  151 +
  152 + def set_verifier(self, verifier=None):
  153 + if verifier is not None:
  154 + self.verifier = verifier
  155 + else:
  156 + self.verifier = generate_verifier()
  157 +
  158 + def get_callback_url(self):
  159 + if self.callback and self.verifier:
  160 + # Append the oauth_verifier.
  161 + parts = urlparse.urlparse(self.callback)
  162 + scheme, netloc, path, params, query, fragment = parts[:6]
  163 + if query:
  164 + query = '%s&oauth_verifier=%s' % (query, self.verifier)
  165 + else:
  166 + query = 'oauth_verifier=%s' % self.verifier
  167 + return urlparse.urlunparse((scheme, netloc, path, params,
  168 + query, fragment))
  169 + return self.callback
  170 +
  171 + def to_string(self):
  172 + """Returns this token as a plain string, suitable for storage.
  173 +The resulting string includes the token's secret, so you should never
  174 +send or store this string where a third party can read it.
  175 +"""
  176 +
  177 + data = {
  178 + 'oauth_token': self.key,
  179 + 'oauth_token_secret': self.secret,
  180 + }
  181 +
  182 + if self.callback_confirmed is not None:
  183 + data['oauth_callback_confirmed'] = self.callback_confirmed
  184 + return urllib.urlencode(data)
  185 +
  186 + @staticmethod
  187 + def from_string(s):
  188 + """Deserializes a token from a string like one returned by
  189 +`to_string()`."""
  190 +
  191 + if not len(s):
  192 + raise ValueError("Invalid parameter string.")
  193 +
  194 + params = parse_qs(s, keep_blank_values=False)
  195 + if not len(params):
  196 + raise ValueError("Invalid parameter string.")
  197 +
  198 + try:
  199 + key = params['oauth_token'][0]
  200 + except Exception:
  201 + raise ValueError("'oauth_token' not found in OAuth request.")
  202 +
  203 + try:
  204 + secret = params['oauth_token_secret'][0]
  205 + except Exception:
  206 + raise ValueError("'oauth_token_secret' not found in "
  207 + "OAuth request.")
  208 +
  209 + token = Token(key, secret)
  210 + try:
  211 + token.callback_confirmed = params['oauth_callback_confirmed'][0]
  212 + except KeyError:
  213 + pass # 1.0, no callback confirmed.
  214 + return token
  215 +
  216 + def __str__(self):
  217 + return self.to_string()
  218 +
  219 +
  220 +def setter(attr):
  221 + name = attr.__name__
  222 +
  223 + def getter(self):
  224 + try:
  225 + return self.__dict__[name]
  226 + except KeyError:
  227 + raise AttributeError(name)
  228 +
  229 + def deleter(self):
  230 + del self.__dict__[name]
  231 +
  232 + return property(getter, attr, deleter)
  233 +
  234 +
  235 +class Request(dict):
  236 +
  237 + """The parameters and information for an HTTP request, suitable for
  238 +authorizing with OAuth credentials.
  239 +When a consumer wants to access a service's protected resources, it does
  240 +so using a signed HTTP request identifying itself (the consumer) with its
  241 +key, and providing an access token authorized by the end user to access
  242 +those resources.
  243 +"""
  244 +
  245 + version = VERSION
  246 +
  247 + def __init__(self, method=HTTP_METHOD, url=None, parameters=None):
  248 + self.method = method
  249 + self.url = url
  250 + if parameters is not None:
  251 + self.update(parameters)
  252 +
  253 + @setter
  254 + def url(self, value):
  255 + self.__dict__['url'] = value
  256 + if value is not None:
  257 + scheme, netloc, path, params, query, fragment = urlparse.urlparse(value)
  258 +
  259 + # Exclude default port numbers.
  260 + if scheme == 'http' and netloc[-3:] == ':80':
  261 + netloc = netloc[:-3]
  262 + elif scheme == 'https' and netloc[-4:] == ':443':
  263 + netloc = netloc[:-4]
  264 + if scheme not in ('http', 'https'):
  265 + raise ValueError("Unsupported URL %s (%s)." % (value, scheme))
  266 +
  267 + # Normalized URL excludes params, query, and fragment.
  268 + self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None))
  269 + else:
  270 + self.normalized_url = None
  271 + self.__dict__['url'] = None
  272 +
  273 + @setter
  274 + def method(self, value):
  275 + self.__dict__['method'] = value.upper()
  276 +
  277 + def _get_timestamp_nonce(self):
  278 + return self['oauth_timestamp'], self['oauth_nonce']
  279 +
  280 + def get_nonoauth_parameters(self):
  281 + """Get any non-OAuth parameters."""
  282 + return dict([(k, v) for k, v in self.iteritems()
  283 + if not k.startswith('oauth_')])
  284 +
  285 + def to_header(self, realm=''):
  286 + """Serialize as a header for an HTTPAuth request."""
  287 + oauth_params = ((k, v) for k, v in self.items()
  288 + if k.startswith('oauth_'))
  289 + stringy_params = ((k, escape(str(v))) for k, v in oauth_params)
  290 + header_params = ('%s="%s"' % (k, v) for k, v in stringy_params)
  291 + params_header = ', '.join(header_params)
  292 +
  293 + auth_header = 'OAuth realm="%s"' % realm
  294 + if params_header:
  295 + auth_header = "%s, %s" % (auth_header, params_header)
  296 +
  297 + return {'Authorization': auth_header}
  298 +
  299 + def to_postdata(self):
  300 + """Serialize as post data for a POST request."""
  301 + # tell urlencode to deal with sequence values and map them correctly
  302 + # to resulting querystring. for example self["k"] = ["v1", "v2"] will
  303 + # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D
  304 + return urllib.urlencode(self, True)
  305 +
  306 + def to_url(self):
  307 + """Serialize as a URL for a GET request."""
  308 + base_url = urlparse.urlparse(self.url)
  309 + query = parse_qs(base_url.query)
  310 + for k, v in self.items():
  311 + query.setdefault(k, []).append(v)
  312 + url = (base_url.scheme, base_url.netloc, base_url.path, base_url.params,
  313 + urllib.urlencode(query, True), base_url.fragment)
  314 + return urlparse.urlunparse(url)
  315 +
  316 + def get_parameter(self, parameter):
  317 + ret = self.get(parameter)
  318 + if ret is None:
  319 + raise Error('Parameter not found: %s' % parameter)
  320 +
  321 + return ret
  322 +
  323 + def get_normalized_parameters(self):
  324 + """Return a string that contains the parameters that must be signed."""
  325 + items = []
  326 + for key, value in self.iteritems():
  327 + if key == 'oauth_signature':
  328 + continue
  329 + # 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
  330 + # so we unpack sequence values into multiple items for sorting.
  331 + if hasattr(value, '__iter__'):
  332 + items.extend((key, item) for item in value)
  333 + else:
  334 + items.append((key, value))
  335 +
  336 + # Include any query string parameters from the provided URL
  337 + query = urlparse.urlparse(self.url)[4]
  338 + items.extend(self._split_url_string(query).items())
  339 +
  340 + encoded_str = urllib.urlencode(sorted(items))
  341 + # Encode signature parameters per Oauth Core 1.0 protocol
  342 + # spec draft 7, section 3.6
  343 + # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
  344 + # Spaces must be encoded with "%20" instead of "+"
  345 + return encoded_str.replace('+', '%20')
  346 +
  347 + def sign_request(self, signature_method, consumer, token):
  348 + """Set the signature parameter to the result of sign."""
  349 +
  350 + if 'oauth_consumer_key' not in self:
  351 + self['oauth_consumer_key'] = consumer.key
  352 +
  353 + if token and 'oauth_token' not in self:
  354 + self['oauth_token'] = token.key
  355 +
  356 + self['oauth_signature_method'] = signature_method.name
  357 + self['oauth_signature'] = signature_method.sign(self, consumer, token)
  358 +
  359 + @classmethod
  360 + def make_timestamp(cls):
  361 + """Get seconds since epoch (UTC)."""
  362 + return str(int(time.time()))
  363 +
  364 + @classmethod
  365 + def make_nonce(cls):
  366 + """Generate pseudorandom number."""
  367 + return str(random.randint(0, 100000000))
  368 +
  369 + @classmethod
  370 + def from_request(cls, http_method, http_url, headers=None, parameters=None,
  371 + query_string=None):
  372 + """Combines multiple parameter sources."""
  373 + if parameters is None:
  374 + parameters = {}
  375 +
  376 + # Headers
  377 + if headers and 'Authorization' in headers:
  378 + auth_header = headers['Authorization']
  379 + # Check that the authorization header is OAuth.
  380 + if auth_header[:6] == 'OAuth ':
  381 + auth_header = auth_header[6:]
  382 + try:
  383 + # Get the parameters from the header.
  384 + header_params = cls._split_header(auth_header)
  385 + parameters.update(header_params)
  386 + except:
  387 + raise Error('Unable to parse OAuth parameters from '
  388 + 'Authorization header.')
  389 +
  390 + # GET or POST query string.
  391 + if query_string:
  392 + query_params = cls._split_url_string(query_string)
  393 + parameters.update(query_params)
  394 +
  395 + # URL parameters.
  396 + param_str = urlparse.urlparse(http_url)[4] # query
  397 + url_params = cls._split_url_string(param_str)
  398 + parameters.update(url_params)
  399 +
  400 + if parameters:
  401 + return cls(http_method, http_url, parameters)
  402 +
  403 + return None
  404 +
  405 + @classmethod
  406 + def from_consumer_and_token(cls, consumer, token=None,
  407 + http_method=HTTP_METHOD, http_url=None, parameters=None):
  408 + if not parameters:
  409 + parameters = {}
  410 +
  411 + defaults = {
  412 + 'oauth_consumer_key': consumer.key,
  413 + 'oauth_timestamp': cls.make_timestamp(),
  414 + 'oauth_nonce': cls.make_nonce(),
  415 + 'oauth_version': cls.version,
  416 + }
  417 +
  418 + defaults.update(parameters)
  419 + parameters = defaults
  420 +
  421 + if token:
  422 + parameters['oauth_token'] = token.key
  423 + if token.verifier:
  424 + parameters['oauth_verifier'] = token.verifier
  425 +
  426 + return Request(http_method, http_url, parameters)
  427 +
  428 + @classmethod
  429 + def from_token_and_callback(cls, token, callback=None,
  430 + http_method=HTTP_METHOD, http_url=None, parameters=None):
  431 +
  432 + if not parameters:
  433 + parameters = {}
  434 +
  435 + parameters['oauth_token'] = token.key
  436 +
  437 + if callback:
  438 + parameters['oauth_callback'] = callback
  439 +
  440 + return cls(http_method, http_url, parameters)
  441 +
  442 + @staticmethod
  443 + def _split_header(header):
  444 + """Turn Authorization: header into parameters."""
  445 + params = {}
  446 + parts = header.split(',')
  447 + for param in parts:
  448 + # Ignore realm parameter.
  449 + if param.find('realm') > -1:
  450 + continue
  451 + # Remove whitespace.
  452 + param = param.strip()
  453 + # Split key-value.
  454 + param_parts = param.split('=', 1)
  455 + # Remove quotes and unescape the value.
  456 + params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
  457 + return params
  458 +
  459 + @staticmethod
  460 + def _split_url_string(param_str):
  461 + """Turn URL string into parameters."""
  462 + parameters = parse_qs(param_str, keep_blank_values=False)
  463 + for k, v in parameters.iteritems():
  464 + parameters[k] = urllib.unquote(v[0])
  465 + return parameters
  466 +
  467 +
  468 +class Server(object):
  469 + """A skeletal implementation of a service provider, providing protected
  470 +resources to requests from authorized consumers.
  471 +This class implements the logic to check requests for authorization. You
  472 +can use it with your web server or web framework to protect certain
  473 +resources with OAuth.
  474 +"""
  475 +
  476 + timestamp_threshold = 300 # In seconds, five minutes.
  477 + version = VERSION
  478 + signature_methods = None
  479 +
  480 + def __init__(self, signature_methods=None):
  481 + self.signature_methods = signature_methods or {}
  482 +
  483 + def add_signature_method(self, signature_method):
  484 + self.signature_methods[signature_method.name] = signature_method
  485 + return self.signature_methods
  486 +
  487 + def verify_request(self, request, consumer, token):
  488 + """Verifies an api call and checks all the parameters."""
  489 +
  490 + version = self._get_version(request)
  491 + self._check_signature(request, consumer, token)
  492 + parameters = request.get_nonoauth_parameters()
  493 + return parameters
  494 +
  495 + def build_authenticate_header(self, realm=''):
  496 + """Optional support for the authenticate header."""
  497 + return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
  498 +
  499 + def _get_version(self, request):
  500 + """Verify the correct version request for this server."""
  501 + try:
  502 + version = request.get_parameter('oauth_version')
  503 + except:
  504 + version = VERSION
  505 +
  506 + if version and version != self.version:
  507 + raise Error('OAuth version %s not supported.' % str(version))
  508 +
  509 + return version
  510 +
  511 + def _get_signature_method(self, request):
  512 + """Figure out the signature with some defaults."""
  513 + try:
  514 + signature_method = request.get_parameter('oauth_signature_method')
  515 + except:
  516 + signature_method = SIGNATURE_METHOD
  517 +
  518 + try:
  519 + # Get the signature method object.
  520 + signature_method = self.signature_methods[signature_method]
  521 + except:
  522 + signature_method_names = ', '.join(self.signature_methods.keys())
  523 + raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names))
  524 +
  525 + return signature_method
  526 +
  527 + def _get_verifier(self, request):
  528 + return request.get_parameter('oauth_verifier')
  529 +
  530 + def _check_signature(self, request, consumer, token):
  531 + timestamp, nonce = request._get_timestamp_nonce()
  532 + self._check_timestamp(timestamp)
  533 + signature_method = self._get_signature_method(request)
  534 +
  535 + try:
  536 + signature = request.get_parameter('oauth_signature')
  537 + except:
  538 + raise MissingSignature('Missing oauth_signature.')
  539 +
  540 + # Validate the signature.
  541 + valid = signature_method.check(request, consumer, token, signature)
  542 +
  543 + if not valid:
  544 + key, base = signature_method.signing_base(request, consumer, token)
  545 +
  546 + raise Error('Invalid signature. Expected signature base '
  547 + 'string: %s' % base)
  548 +
  549 + built = signature_method.sign(request, consumer, token)
  550 +
  551 + def _check_timestamp(self, timestamp):
  552 + """Verify that timestamp is recentish."""
  553 + timestamp = int(timestamp)
  554 + now = int(time.time())
  555 + lapsed = now - timestamp
  556 + if lapsed > self.timestamp_threshold:
  557 + raise Error('Expired timestamp: given %d and now %s has a '
  558 + 'greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold))
  559 +
  560 +
  561 +class Client(httplib2.Http):
  562 + """OAuthClient is a worker to attempt to execute a request."""
  563 +
  564 + def __init__(self, consumer, token=None, cache=None, timeout=None,
  565 + proxy_info=None):
  566 +
  567 + if consumer is not None and not isinstance(consumer, Consumer):
  568 + raise ValueError("Invalid consumer.")
  569 +
  570 + if token is not None and not isinstance(token, Token):
  571 + raise ValueError("Invalid token.")
  572 +
  573 + self.consumer = consumer
  574 + self.token = token
  575 + self.method = SignatureMethod_HMAC_SHA1()
  576 +
  577 + httplib2.Http.__init__(self, cache=cache, timeout=timeout,
  578 + proxy_info=proxy_info)
  579 +
  580 + def set_signature_method(self, method):
  581 + if not isinstance(method, SignatureMethod):
  582 + raise ValueError("Invalid signature method.")
  583 +
  584 + self.method = method
  585 +
  586 + def request(self, uri, method="GET", body=None, headers=None,
  587 + redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None):
  588 + DEFAULT_CONTENT_TYPE = 'application/x-www-form-urlencoded'
  589 +
  590 + if not isinstance(headers, dict):
  591 + headers = {}
  592 +
  593 + is_multipart = method == 'POST' and headers.get('Content-Type', DEFAULT_CONTENT_TYPE) != DEFAULT_CONTENT_TYPE
  594 +
  595 + if body and method == "POST" and not is_multipart:
  596 + parameters = dict(parse_qsl(body))
  597 + else:
  598 + parameters = None
  599 +
  600 + req = Request.from_consumer_and_token(self.consumer, token=self.token,
  601 + http_method=method, http_url=uri, parameters=parameters)
  602 +
  603 + req.sign_request(self.method, self.consumer, self.token)
  604 +
  605 +
  606 + if method == "POST":
  607 + headers['Content-Type'] = headers.get('Content-Type', DEFAULT_CONTENT_TYPE)
  608 + if is_multipart:
  609 + headers.update(req.to_header())
  610 + else:
  611 + body = req.to_postdata()
  612 + elif method == "GET":
  613 + uri = req.to_url()
  614 + else:
  615 + headers.update(req.to_header())
  616 +
  617 + return httplib2.Http.request(self, uri, method=method, body=body,
  618 + headers=headers, redirections=redirections,
  619 + connection_type=connection_type)
  620 +
  621 +
  622 +class SignatureMethod(object):
  623 + """A way of signing requests.
  624 +The OAuth protocol lets consumers and service providers pick a way to sign
  625 +requests. This interface shows the methods expected by the other `oauth`
  626 +modules for signing requests. Subclass it and implement its methods to
  627 +provide a new way to sign requests.
  628 +"""
  629 +
  630 + def signing_base(self, request, consumer, token):
  631 + """Calculates the string that needs to be signed.
  632 +
  633 +This method returns a 2-tuple containing the starting key for the
  634 +signing and the message to be signed. The latter may be used in error
  635 +messages to help clients debug their software.
  636 +
  637 +"""
  638 + raise NotImplementedError
  639 +
  640 + def sign(self, request, consumer, token):
  641 + """Returns the signature for the given request, based on the consumer
  642 +and token also provided.
  643 +
  644 +You should use your implementation of `signing_base()` to build the
  645 +message to sign. Otherwise it may be less useful for debugging.
  646 +
  647 +"""
  648 + raise NotImplementedError
  649 +
  650 + def check(self, request, consumer, token, signature):
  651 + """Returns whether the given signature is the correct signature for
  652 +the given consumer and token signing the given request."""
  653 + built = self.sign(request, consumer, token)
  654 + return built == signature
  655 +
  656 +
  657 +class SignatureMethod_HMAC_SHA1(SignatureMethod):
  658 + name = 'HMAC-SHA1'
  659 +
  660 + def signing_base(self, request, consumer, token):
  661 + sig = (
  662 + escape(request.method),
  663 + escape(request.normalized_url),
  664 + escape(request.get_normalized_parameters()),
  665 + )
  666 +
  667 + key = '%s&' % escape(consumer.secret)
  668 + if token:
  669 + key += escape(token.secret)
  670 + raw = '&'.join(sig)
  671 + return key, raw
  672 +
  673 + def sign(self, request, consumer, token):
  674 + """Builds the base signature string."""
  675 + key, raw = self.signing_base(request, consumer, token)
  676 +
  677 + # HMAC object.
  678 + try:
  679 + from hashlib import sha1 as sha
  680 + except ImportError:
  681 + import sha # Deprecated
  682 +
  683 + hashed = hmac.new(key, raw, sha)
  684 +
  685 + # Calculate the digest base 64.
  686 + return binascii.b2a_base64(hashed.digest())[:-1]
  687 +
  688 +class SignatureMethod_PLAINTEXT(SignatureMethod):
  689 +
  690 + name = 'PLAINTEXT'
  691 +
  692 + def signing_base(self, request, consumer, token):
  693 + """Concatenates the consumer key and secret with the token's
  694 + secret."""
  695 + sig = '%s&' % escape(consumer.secret)
  696 + if token:
  697 + sig = sig + escape(token.secret)
  698 + return sig, sig
  699 +
  700 + def sign(self, request, consumer, token):
  701 + key, raw = self.signing_base(request, consumer, token)
  702 + return raw
BIN  liclient/oauth2/__init__.pyc
Binary file not shown

0 comments on commit 62bdd17

Please sign in to comment.
Something went wrong with that request. Please try again.