示例#1
0
    def __init__(self, url, http_method='GET', params=None, headers={},
            version=OAUTH_VERSION, timestamp_threshold=TIMESTAMP_THRESHOLD,
            nonce_length=NONCE_LENGTH):
        if params and not isinstance(params, collections.Mapping):
            # if its not a mapping, it must be a string
            params = parse_qs(params)
        elif not params:
            params = {}

        if 'Authorization' in headers:
            auth_header = headers['Authorization']
            # check that the authorization header is OAuth
            if auth_header.index('OAuth') > -1:
                try:
                    header_params = OAuthRequest._parse_auth_header(auth_header)
                    params.update(header_params)
                except ValueError:
                    raise OAuthError('Unable to parse OAuth parameters from Authorization header.')

        # URL parameters
        parts = urlparse.urlparse(url)
        url = '%s://%s%s' % (parts.scheme, parts.netloc, parts.path)
        params.update(parse_qs(parts.query)) #FIXME should this be a merge?

        self.http_method = http_method.upper()
        self.url = url
        self.params = params.copy()
        self.version = version
        self.timestamp_threshold = timestamp_threshold
        self.nonce_length = nonce_length
示例#2
0
    def need_authorization(self, request):
        """
        The view that triggers the access token flow by sending the user to the
        authorization url. If you wish to show the user a message, you may
        provide a template named:

            `django_oauth_consumer/{NAME}/need_authorization.html`

        The template will be provided an `authorization_url` in the context.

        If you do not provide a template, the user will be redirected there
        immediately.

        """

        response = self.make_signed_req(self.request_token_url)
        body = unicode(response.read(), 'utf8').strip()
        request_token = urlencoding.parse_qs(body)
        request.session[self.name + '_request_token'] = request_token
        qs = urlencoding.compose_qs({
            'oauth_token':
            request_token['oauth_token'],
            'oauth_callback':
            request.build_absolute_uri(
                reverse(self.SUCCESS_VIEW_NAME,
                        kwargs={'oauth_token': request_token['oauth_token']})),
        })
        url = self.authorization_url
        if '?' in url:
            if url[-1] == '&':
                url += qs
            else:
                url += '&' + qs
        else:
            url += '?' + qs

        if self._has_needs_auth_template:
            try:
                return self._render('need_authorization', request,
                                    {'authorization_url': url})
            except TemplateDoesNotExist:
                self._has_needs_auth_template = False
        return HttpResponseRedirect(url)
    def need_authorization(self, request):
        """
        The view that triggers the access token flow by sending the user to the
        authorization url. If you wish to show the user a message, you may
        provide a template named:

            `django_oauth_consumer/{NAME}/need_authorization.html`

        The template will be provided an `authorization_url` in the context.

        If you do not provide a template, the user will be redirected there
        immediately.

        """

        response = self.make_signed_req(self.request_token_url)
        body = unicode(response.read(), "utf8").strip()
        request_token = urlencoding.parse_qs(body)
        request.session[self.name + "_request_token"] = request_token
        qs = urlencoding.compose_qs(
            {
                "oauth_token": request_token["oauth_token"],
                "oauth_callback": request.build_absolute_uri(
                    reverse(self.SUCCESS_VIEW_NAME, kwargs={"oauth_token": request_token["oauth_token"]})
                ),
            }
        )
        url = self.authorization_url
        if "?" in url:
            if url[-1] == "&":
                url += qs
            else:
                url += "&" + qs
        else:
            url += "?" + qs

        if self._has_needs_auth_template:
            try:
                return self._render("need_authorization", request, {"authorization_url": url})
            except TemplateDoesNotExist:
                self._has_needs_auth_template = False
        return HttpResponseRedirect(url)
示例#4
0
    def success_auth(self, request, oauth_token=None):
        """
        The view that handles a successful OAuth Authorization flow from the
        user's side. The Service Provider redirect returns the user here.
        If you wish to show the user a message here before continuing to the
        original URL that triggered the access token flow, you may provide a
        template named:

            `django_oauth_consumer/{NAME}/successful_authorization.html`

        The template will be provided the `access_token` and the `next_url`
        (the original URL the user visited that triggered the access token
        flow).

        If you do not provide a template, the view will simply redirect the
        user back to the original URL.

        """
        request_token = request.session[self.REQUEST_TOKEN_NAME]
        if request_token['oauth_token'] != oauth_token:
            logging.error('request token in session and url dont match')
        response = self.make_signed_req(self.access_token_url,
                                        token=request_token)
        body = unicode(response.read(), 'utf8').strip()
        access_token = urlencoding.parse_qs(body)
        self.store_access_token(request, access_token)
        del request.session[self.REQUEST_TOKEN_NAME]
        try:
            next_url = request.session.pop(self.NEXT_URL_NAME)
        except KeyError:
            next_url = '/'

        if self._has_success_auth_template:
            try:
                return self._render('successful_authorization', request, {
                    'access_token': access_token,
                    'next_url': next_url,
                })
            except TemplateDoesNotExist:
                self._has_success_auth_template = False
        return HttpResponseRedirect(next_url)
示例#5
0
    def success_auth(self, request, oauth_token=None):
        """
        The view that handles a successful OAuth Authorization flow from the
        user's side. The Service Provider redirect returns the user here.
        If you wish to show the user a message here before continuing to the
        original URL that triggered the access token flow, you may provide a
        template named:

            `django_oauth_consumer/{NAME}/successful_authorization.html`

        The template will be provided the `access_token` and the `next_url`
        (the original URL the user visited that triggered the access token
        flow).

        If you do not provide a template, the view will simply redirect the
        user back to the original URL.

        """
        request_token = request.session[self.REQUEST_TOKEN_NAME]
        if request_token['oauth_token'] != oauth_token:
            logging.error('request token in session and url dont match')
        response = self.make_signed_req(self.access_token_url, token=request_token)
        body = unicode(response.read(), 'utf8').strip()
        access_token = urlencoding.parse_qs(body)
        self.store_access_token(request, access_token)
        del request.session[self.REQUEST_TOKEN_NAME]
        try:
            next_url = request.session.pop(self.NEXT_URL_NAME)
        except KeyError:
            next_url = '/'

        if self._has_success_auth_template:
            try:
                return self._render('successful_authorization', request, {
                    'access_token': access_token,
                    'next_url': next_url,
                })
            except TemplateDoesNotExist:
                self._has_success_auth_template = False
        return HttpResponseRedirect(next_url)
示例#6
0
    def make_signed_req(self, url, method='GET', content={}, headers={}, token=None, request=None):
        """
        Identical to the make_request API, and accepts an additional (optional)
        token parameter and request object (required if dealing with Scalable
        OAuth service providers). It adds the OAuth Authorization header based
        on the consumer set on this instance. If content not a Mapping object,
        it will be ignored with respect to signing. This means you need to
        either pass the query parameters as a dict/Mapping object, or pass a
        encoded query string as part of the URL which will be extracted and
        included in the signature.

        http://oauth.net/core/1.0/#rfc.section.7

        Arguments:

            `url`
                The URL - query parameters will be parsed out.

            `method`
                The HTTP method to use.

            `content`
                A dict of key/values or string/unicode value.

            `headers`
                A dict of headers.

            `token`
                An optional access token. If this is provided, you will be
                making a 3-legged request. If it is missing, you will be making
                a 2-legged request.

            `request`
                *Optional*. Needed if using Scalable OAuth in order to
                transparently handle access token renewal.

                http://wiki.oauth.net/ScalableOAuth#AccessTokenRenewal

        """

        if isinstance(content, collections.Mapping):
            params = content
        else:
            params = {}

        orequest = oauth.OAuthRequest(url, method, params)
        orequest.sign_request(self.sig_method, self.consumer, token)
        headers['Authorization'] = orequest.to_header(self.realm)
        response = make_request(url, method=method, content=content, headers=headers)

        # check if we got a scalable oauth token_expired error
        # we will fetch a new access token using the oauth_session_handle in
        # the current token and redo the request in this case.
        # FIXME: infinite loop
        www_auth = response.getheader('www-authenticate', None)
        if www_auth and response.status == 401 and 'token_expired' in www_auth:
            response = self.make_signed_req(
                self.access_token_url,
                content={'oauth_session_handle': token['oauth_session_handle']},
                token=token,
                request=request
            )
            body = unicode(response.read(), 'utf8').strip()
            new_token = urlencoding.parse_qs(body)
            self.store_access_token(request, new_token)

            return self.make_signed_req(url, method, content, headers, new_token, request)
        else:
            return response
示例#7
0
 def test_simple(self):
     self.assertEqual(urlencoding.parse_qs('a=1'), {'a': '1'})
示例#8
0
 def test_array_value(self):
     self.assertEqual(
         urlencoding.parse_qs('a=1&a=2'),
         {'a': ['1', '2']}
     )
示例#9
0
 def test_square_index(self):
     self.assertEqual(
         urlencoding.parse_qs('a%5Bb%5D=1&a%5Bc%5D=2'),
         {'a[b]': '1', 'a[c]': '2'}
     )
示例#10
0
 def test_space(self):
     self.assertEqual(
         urlencoding.parse_qs('a=1&b=2&c=%20d'),
         {'a': '1', 'b': '2', 'c': ' d'}
     )
示例#11
0
 def test_multi(self):
     self.assertEqual(urlencoding.parse_qs('a=1&b=2'), {'a': '1', 'b': '2'})
示例#12
0
    def make_signed_req(self,
                        url,
                        method='GET',
                        content={},
                        headers={},
                        token=None,
                        request=None):
        """
        Identical to the make_request API, and accepts an additional (optional)
        token parameter and request object (required if dealing with Scalable
        OAuth service providers). It adds the OAuth Authorization header based
        on the consumer set on this instance. If content not a Mapping object,
        it will be ignored with respect to signing. This means you need to
        either pass the query parameters as a dict/Mapping object, or pass a
        encoded query string as part of the URL which will be extracted and
        included in the signature.

        http://oauth.net/core/1.0/#rfc.section.7

        Arguments:

            `url`
                The URL - query parameters will be parsed out.

            `method`
                The HTTP method to use.

            `content`
                A dict of key/values or string/unicode value.

            `headers`
                A dict of headers.

            `token`
                An optional access token. If this is provided, you will be
                making a 3-legged request. If it is missing, you will be making
                a 2-legged request.

            `request`
                *Optional*. Needed if using Scalable OAuth in order to
                transparently handle access token renewal.

                http://wiki.oauth.net/ScalableOAuth#AccessTokenRenewal

        """

        if isinstance(content, collections.Mapping):
            params = content
        else:
            params = {}

        orequest = oauth.OAuthRequest(url, method, params)
        orequest.sign_request(self.sig_method, self.consumer, token)
        headers['Authorization'] = orequest.to_header(self.realm)
        response = make_request(url,
                                method=method,
                                content=content,
                                headers=headers)

        # check if we got a scalable oauth token_expired error
        # we will fetch a new access token using the oauth_session_handle in
        # the current token and redo the request in this case.
        # FIXME: infinite loop
        www_auth = response.getheader('www-authenticate', None)
        if www_auth and response.status == 401 and 'token_expired' in www_auth:
            response = self.make_signed_req(self.access_token_url,
                                            content={
                                                'oauth_session_handle':
                                                token['oauth_session_handle']
                                            },
                                            token=token,
                                            request=request)
            body = unicode(response.read(), 'utf8').strip()
            new_token = urlencoding.parse_qs(body)
            self.store_access_token(request, new_token)

            return self.make_signed_req(url, method, content, headers,
                                        new_token, request)
        else:
            return response
示例#13
0
def prepare_request(url, method='GET', content=None, headers={}):
    """
    This is the generic processing logic used by the various APIs.

    If content is a Mapping object, parameters will be processed. In this case,
    query parameters from the URL will be processed and merged with the content
    dict. They will then be appended to the URL or sent as the body based on
    the method.

    Arguments:

        `url`
            The URL - query parameters will be parsed out.

        `method`
            The HTTP method to use.

        `content`
            A dict of key/values or string/unicode value.

        `headers`
            A dict of headers.

    """
    # we potentially modify them
    headers = headers.copy()

    parts = urlparse(url)
    url = parts.path
    if parts.params:
        url += ';' + parts.params

    # we dont do much with the url/body unless content is a Mapping object
    if isinstance(content, collections.Mapping):
        # merge the parameters in the query string
        if parts.query:
            qs_params = parse_qs(parts.query)
            qs_params.update(content)
            content = qs_params

        # put the content in the url or convert to string body
        if content:
            content = compose_qs(content)
            if method in ('HEAD', 'GET'):
                url += '?' + content
                content = None
            else:
                if 'Content-Type' not in headers:
                    headers['Content-Type'] = 'application/x-www-form-urlencoded'
    else:
        if parts.query:
            url += '?' + parts.query

    # add Content-Length if needed
    if content and 'Content-Length' not in headers:
        headers['Content-Length'] = len(content)

    return {
        'scheme': parts.scheme,
        'netloc': parts.netloc,
        'url': url,
        'method': method,
        'content': content,
        'headers': headers,
    }
示例#14
0
def prepare_request(url, method='GET', content=None, headers={}):
    """
    This is the generic processing logic used by the various APIs.

    If content is a Mapping object, parameters will be processed. In this case,
    query parameters from the URL will be processed and merged with the content
    dict. They will then be appended to the URL or sent as the body based on
    the method.

    Arguments:

        `url`
            The URL - query parameters will be parsed out.

        `method`
            The HTTP method to use.

        `content`
            A dict of key/values or string/unicode value.

        `headers`
            A dict of headers.

    """
    # we potentially modify them
    headers = headers.copy()

    parts = urlparse(url)
    url = parts.path
    if parts.params:
        url += ';' + parts.params

    # we dont do much with the url/body unless content is a Mapping object
    if isinstance(content, collections.Mapping):
        # merge the parameters in the query string
        if parts.query:
            qs_params = parse_qs(parts.query)
            qs_params.update(content)
            content = qs_params

        # put the content in the url or convert to string body
        if content:
            content = compose_qs(content)
            if method in ('HEAD', 'GET'):
                url += '?' + content
                content = None
            else:
                if 'Content-Type' not in headers:
                    headers[
                        'Content-Type'] = 'application/x-www-form-urlencoded'
    else:
        if parts.query:
            url += '?' + parts.query

    # add Content-Length if needed
    if content and 'Content-Length' not in headers:
        headers['Content-Length'] = len(content)

    return {
        'scheme': parts.scheme,
        'netloc': parts.netloc,
        'url': url,
        'method': method,
        'content': content,
        'headers': headers,
    }