Example #1
0
    def get_authentication_url(self, temporary_credentials, **query_params):
        """
        Calculates the automatic authentication redirect URL to which the
        user will be (re)directed. Some providers support automatic
        authorization URLs if the user is already signed in. You can use
        this method with such URLs.

        :param temporary_credentials:
            Temporary credentials obtained after parsing the response to
            the temporary credentials request.
        :param query_params:
            Additional query parameters that you would like to include
            into the authorization URL. Parameters beginning with the ``oauth_``
            prefix will be ignored.
        """
        url = self._authentication_uri
        if not url:
            raise NotImplementedError(
                "Service does not support automatic authentication redirects.")
        if query_params:
            query_params = query_remove_oauth(query_params)
            url = url_append_query(url, query_params)

        # So that the "oauth_token" appears LAST.
        return url_append_query(url, {
            OAUTH_PARAM_TOKEN: temporary_credentials.identifier,
            })
Example #2
0
    def test_filter(self):
        params = {
            "a2": ["r b"],
            "b5": ["=%3D"],
            "a3": ["a", "2 q"],
            "[email protected]": [""],
            "c2": [""],
            OAUTH_PARAM_CONSUMER_KEY: ["9djdj82h48djs9d2"],
            OAUTH_PARAM_TOKEN: ["kkk9d7dh3k39sjv7"],
            OAUTH_PARAM_SIGNATURE_METHOD: ["HMAC-SHA1"],
            OAUTH_PARAM_TIMESTAMP: ["137131201"],
            OAUTH_PARAM_NONCE: ["7d8f3e4a"],
        }
        query_string = """\
?a2=r%20b\
&a3=2%20q\
&a3=a\
&b5=%3D%253D\
&c%40=\
&c2=\
&oauth_consumer_key=9djdj82h48djs9d2\
&oauth_nonce=7d8f3e4a\
&oauth_signature_method=HMAC-SHA1\
&oauth_timestamp=137131201\
&oauth_token=kkk9d7dh3k39sjv7"""
        expected_params = {
            "a2": ["r b"],
            "b5": ["=%3D"],
            "a3": ["a", "2 q"],
            "[email protected]": [""],
            "c2": [""],
        }
        expected_result = urlencode_s(expected_params)

        self.assertEqual(urlencode_s(query_remove_oauth(params)),
                         expected_result)
        self.assertEqual(urlencode_s(query_remove_oauth(query_string)),
                         expected_result)
Example #3
0
def generate_base_string_query(url_query, oauth_params):
    """
    Serializes URL query parameters and OAuth protocol parameters into a valid
    OAuth base string URI query string.

    :see: Parameter Normalization
          (http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2)
    :param url_query:
        A dictionary or string of URL query parameters. Any parameters starting
        with ``oauth_`` will be ignored.
    :param oauth_params:
        A dictionary or string of protocol-specific query parameters. Any
        parameter names that do not begin with ``oauth_`` will be excluded
        from the normalized query string. ``oauth_signature``,
        ``oauth_consumer_secret``, and ``oauth_token_secret`` are also
        specifically excluded.
    :returns:
        Normalized string of query parameters.
    """
    url_query = query_remove_oauth(url_query)
    oauth_params = request_query_remove_non_oauth(oauth_params)

    query_d = {}
    query_d.update(url_query)
    query_d.update(oauth_params)

    # Now encode the parameters, while ignoring 'oauth_signature' and obviously,
    # the secrets from the entire list of parameters.
    def allow_func(name, _):
        """Allows only protocol parameters that must be included into
        the signature.

        :param name:
            The name of the parameter.
        :returns:
            ``True`` if the parameter can be included; ``False`` otherwise.
        """
        return name not in (OAUTH_PARAM_SIGNATURE,
                            #OAUTH_PARAM_CONSUMER_SECRET, # Sanitized above.
                            #OAUTH_PARAM_TOKEN_SECRET,    # Sanitized above.
                            )
    query = urlencode_s(query_d, allow_func)
    return query
Example #4
0
    def get_authorization_url(self, temporary_credentials, **query_params):
        """
        Calculates the authorization URL to which the user will be (re)directed.

        :param temporary_credentials:
            Temporary credentials obtained after parsing the response to
            the temporary credentials request.
        :param query_params:
            Additional query parameters that you would like to include
            into the authorization URL. Parameters beginning with the ``oauth_``
            prefix will be ignored.
        """
        url = self._authorization_uri
        if query_params:
            query_params = query_remove_oauth(query_params)
            url = url_append_query(url, query_params)

        # `oauth_token` must appear last.
        return url_append_query(url, {
            OAUTH_PARAM_TOKEN: temporary_credentials.identifier,
        })
Example #5
0
    def _request(cls,
                 client_credentials,
                 method, url, params=None, body=None, headers=None,
                 realm=None, use_authorization_header=True,
                 auth_credentials=None,
                 oauth_signature_method=SIGNATURE_METHOD_HMAC_SHA1,
                 oauth_version=OAUTH_VERSION_1,
                 **kwargs):
        """
        Makes an OAuth request.

        :param client_credentials:
            Client credentials (consumer key and secret).
        :param method:
            HTTP method.
        :param url:
            Request URL
        :param params:
            Additional query/payload parameters.
            If a `body` argument to this function is specified,
            the parameters are appended to the URL query string.
            If a `body` is not specified and a method other than GET is used
            the parameters will be added to the entity body.
        :param body:
            Entity body string.
        :param headers:
            Request headers dictionary.
        :param realm:
            Authorization realm.
        :param use_authorization_header:
            ``True`` if we should; ``False`` otherwise.
        :param auth_credentials:
            OAuth token/temporary credentials (if available).
        :param oauth_signature_method:
            Signature method.
        :param kwargs:
            Additional parameters including those that may begin with
            ``oauth_``.
        :returns:
            HTTP response (:class:`pyoauth.http.ResponseAdapter`) if
            ``async_callback`` is not specified;
            otherwise, ``async_callback`` is called with the response as its
            argument.
        """
        method = method.upper()
        body = body or SYMBOL_EMPTY_BYTES
        headers = headers or {}

        # Split all the oauth parameters and function parameters.
        extra_oauth_params, kwargs = \
            partition_dict(lambda k, v: k.startswith(OAUTH_PARAM_PREFIX),
                           kwargs)

        # Query/payload parameters must not contain OAuth-specific parameters.
        params = query_remove_oauth(params) if params else {}

        # The URL must not contain OAuth-specific parameters.
        url = oauth_url_sanitize(url, force_secure=False)

        # Temporary credentials requests don't have ``oauth_token``.
        if auth_credentials:
            oauth_token = auth_credentials.identifier
            oauth_token_secret = auth_credentials.shared_secret
        else:
            oauth_token = oauth_token_secret = None

        # Make OAuth-specific parameter dictionary.

        oauth_params = cls._generate_oauth_params(
            oauth_consumer_key=client_credentials.identifier,
            oauth_signature_method=oauth_signature_method,
            oauth_version=oauth_version,
            oauth_timestamp=cls.generate_timestamp(),
            oauth_nonce=cls.generate_nonce(),
            oauth_token=oauth_token,
            **extra_oauth_params
        )

        # Sign the request.
        signature = cls._generate_signature(method, url, params, body, headers,
                                            client_credentials.shared_secret,
                                            oauth_token_secret,
                                            oauth_params)
        oauth_params[OAUTH_PARAM_SIGNATURE] = signature

        # Now build the request.
        return cls._build_request(
            method, url, params, body, headers,
            oauth_params, realm, use_authorization_header
        )
Example #6
0
    def _generate_signature(cls, method, url, params,
                            body, headers,
                            oauth_consumer_secret,
                            oauth_token_secret,
                            oauth_params):
        """
        Given the base string parameters, secrets, and protocol parameters,
        calculates a signature for the request.

        :param method:
            HTTP method.
        :param url:
            Request URL.
        :param params:
            Additional query/payload parameters.
        :param body:
            Payload if any.
        :param headers:
            HTTP headers as a dictionary.
        :param oauth_consumer_secret:
            OAuth client shared secret (consumer secret).
        :param oauth_token_secret:
            OAuth token/temporary shared secret if obtained from the OAuth
            server.
        :param oauth_params:
            OAuth parameters generated by
            :func:`OAuthClient._generate_oauth_params`.
        :returns:
            Request signature.
        """
        # Take parameters from the body if the Content-Type is specified
        # as ``application/x-www-form-urlencoded``.
        # http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
        if body:
            try:
                try:
                    content_type = headers[HEADER_CONTENT_TYPE]
                except KeyError:
                    content_type = headers[HEADER_CONTENT_TYPE_CAPS]

                if content_type == CONTENT_TYPE_FORM_URLENCODED:
                    # These parameters must also be included in the signature.
                    # Ignore OAuth-specific parameters. They must be specified
                    # separately.
                    body_params = query_remove_oauth(parse_qs(body))
                    params = query_add(params, body_params)
                else:
                    logging.info(
                        "Entity-body specified but `content-type` header " \
                        "value is not %r: entity-body parameters if " \
                        "present will not be signed: got body %r" % \
                        (CONTENT_TYPE_FORM_URLENCODED, body)
                    )
            except KeyError:
                logging.warning(
                    "Entity-body specified but `content-type` is missing "
                )

        # Make oauth params and sign the request.
        signature_url = url_add_query(url, query_remove_oauth(params))
        # NOTE: We're not explicitly cleaning up because this method
        # expects oauth params generated by _generate_oauth_params.
        base_string = generate_base_string(method, signature_url, oauth_params)

        signature_method = oauth_params[OAUTH_PARAM_SIGNATURE_METHOD]
        cls.check_signature_method(signature_method)
        try:
            sign_func = SIGNATURE_METHOD_MAP[signature_method]
            return sign_func(base_string,
                             oauth_consumer_secret,
                             oauth_token_secret)
        except KeyError:
            raise InvalidSignatureMethodError(
                "unsupported signature method: %r" % signature_method
            )