""" Parts derived from socialregistration and authorized by: alen, pinda Inspired by: http://github.com/leah/python-oauth/blob/master/oauth/example/client.py http://github.com/facebook/tornado/blob/master/tornado/auth.py """ import requests from urllib.parse import parse_qsl, urlparse from django.http import HttpResponseRedirect from django.utils.http import urlencode from django.utils.translation import gettext as _ from requests_oauthlib import OAuth1 from allauth.utils import build_absolute_uri, get_request_param def get_token_prefix(url): """ Returns a prefix for the token to store in the session so we can hold more than one single oauth provider's access key in the session. Example: The request token url ``http://twitter.com/oauth/request_token`` returns ``twitter.com`` """ return urlparse(url).netloc class OAuthError(Exception): pass class OAuthClient(object): def __init__( self, request, consumer_key, consumer_secret, request_token_url, access_token_url, callback_url, parameters=None, provider=None, ): self.request = request self.request_token_url = request_token_url self.access_token_url = access_token_url self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.parameters = parameters self.callback_url = callback_url self.provider = provider self.errors = [] self.request_token = None self.access_token = None def _get_request_token(self): """ Obtain a temporary request token to authorize an access token and to sign the request to obtain the access token """ if self.request_token is None: get_params = {} if self.parameters: get_params.update(self.parameters) get_params["oauth_callback"] = build_absolute_uri( self.request, self.callback_url ) rt_url = self.request_token_url + "?" + urlencode(get_params) oauth = OAuth1(self.consumer_key, client_secret=self.consumer_secret) response = requests.post(url=rt_url, auth=oauth) if response.status_code not in [200, 201]: raise OAuthError( _( "Invalid response while obtaining request token" ' from "%s". Response was: %s.' ) % (get_token_prefix(self.request_token_url), response.text) ) self.request_token = dict(parse_qsl(response.text)) self.request.session[ "oauth_%s_request_token" % get_token_prefix(self.request_token_url) ] = self.request_token return self.request_token def get_access_token(self): """ Obtain the access token to access private resources at the API endpoint. """ if self.access_token is None: request_token = self._get_rt_from_session() oauth = OAuth1( self.consumer_key, client_secret=self.consumer_secret, resource_owner_key=request_token["oauth_token"], resource_owner_secret=request_token["oauth_token_secret"], ) at_url = self.access_token_url # Passing along oauth_verifier is required according to: # http://groups.google.com/group/twitter-development-talk/browse_frm/thread/472500cfe9e7cdb9# # Though, the custom oauth_callback seems to work without it? oauth_verifier = get_request_param(self.request, "oauth_verifier") if oauth_verifier: at_url = at_url + "?" + urlencode({"oauth_verifier": oauth_verifier}) response = requests.post(url=at_url, auth=oauth) if response.status_code not in [200, 201]: raise OAuthError( _("Invalid response while obtaining access token" ' from "%s".') % get_token_prefix(self.request_token_url) ) self.access_token = dict(parse_qsl(response.text)) self.request.session[ "oauth_%s_access_token" % get_token_prefix(self.request_token_url) ] = self.access_token return self.access_token def _get_rt_from_session(self): """ Returns the request token cached in the session by ``_get_request_token`` """ try: return self.request.session[ "oauth_%s_request_token" % get_token_prefix(self.request_token_url) ] except KeyError: raise OAuthError( _('No request token saved for "%s".') % get_token_prefix(self.request_token_url) ) def is_valid(self): try: self._get_rt_from_session() self.get_access_token() except OAuthError as e: self.errors.append(e.args[0]) return False return True def get_redirect(self, authorization_url, extra_params): """ Returns a ``HttpResponseRedirect`` object to redirect the user to the URL the OAuth provider handles authorization. """ request_token = self._get_request_token() params = { "oauth_token": request_token["oauth_token"], "oauth_callback": self.request.build_absolute_uri(self.callback_url), } params.update(extra_params) url = authorization_url + "?" + urlencode(params) return HttpResponseRedirect(url) class OAuth(object): """ Base class to perform oauth signed requests from access keys saved in a user's session. See the ``OAuthTwitter`` class below for an example. """ def __init__(self, request, consumer_key, secret_key, request_token_url): self.request = request self.consumer_key = consumer_key self.secret_key = secret_key self.request_token_url = request_token_url def _get_at_from_session(self): """ Get the saved access token for private resources from the session. """ try: return self.request.session[ "oauth_%s_access_token" % get_token_prefix(self.request_token_url) ] except KeyError: raise OAuthError( _('No access token saved for "%s".') % get_token_prefix(self.request_token_url) ) def query(self, url, method="GET", params=None, headers=None): """ Request a API endpoint at ``url`` with ``params`` being either the POST or GET data. """ access_token = self._get_at_from_session() oauth = OAuth1( self.consumer_key, client_secret=self.secret_key, resource_owner_key=access_token["oauth_token"], resource_owner_secret=access_token["oauth_token_secret"], ) response = getattr(requests, method.lower())( url, auth=oauth, headers=headers, params=params ) if response.status_code != 200: raise OAuthError( _('No access to private resources at "%s".') % get_token_prefix(self.request_token_url) ) return response.text