Example #1
0
 def test_appends_query_params_properly(self):
     params1 = {
         "a2": "r b",
         "b5": "=%3D",
         "a3": ["a"],
         "c2": [""],
     }
     params2 = {
         "a3": ["2 q"],
         "c@": "",
     }
     params3 = "oauth_nonce=7d8f3e4a"
     resulting_query_string = "a2=r%20b&a3=a&b5=%3D%253D&c2=&a3=2%20q&c%40=&oauth_nonce=7d8f3e4a"
     assert_equal(query_append(params1, params2, params3), resulting_query_string)
Example #2
0
    def test_appends_query_params_properly(self):
        params1 = {
            b("a2"): b("r b"),
            b("b5"): b("=%3D"),
            b("a3"): [b("a")],
            b("c2"): [b("")],
        }
        params2 = {
            b("a3"): [b("2 q")],
            b("c@"): b(""),
        }
        params3 = b("oauth_nonce=7d8f3e4a")
        resulting_query_string = b("""\
a2=r%20b\
&a3=a\
&b5=%3D%253D\
&c2=\
&a3=2%20q\
&c%40=\
&oauth_nonce=7d8f3e4a""")
        self.assertEqual(query_append(params1, params2, params3),
                         resulting_query_string)
Example #3
0
    def _build_request(cls, method, url, params, body, headers,
                       oauth_params, realm, use_authorization_header):
        """
        Builds a request based on the HTTP arguments and OAuth protocol
        parameters.

        :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.
        :param oauth_params:
            Protocol-specific parameters.
        :param realm:
            OAuth authorization realm.
        :param use_authorization_header:
            ``True`` if the Authorization HTTP header should be used;
            ``False`` otherwise.
        :returns:
            An instance of :class:`pyoauth.http.RequestAdapter`.
        """
        # http://tools.ietf.org/html/rfc5849#section-3.6
        if HEADER_AUTHORIZATION_CAPS in headers or \
           HEADER_AUTHORIZATION in headers:
            raise InvalidAuthorizationHeaderError(
                "Authorization field is already present in headers: %r" % \
                headers
            )
        if use_authorization_header:
            headers[HEADER_AUTHORIZATION_CAPS] = \
                generate_authorization_header(oauth_params, realm)
            # Empty oauth params so that they are not included again below.
            oauth_params = None

        # OAuth requests can contain payloads.
        if body or method == HTTP_GET:
            # Append params to query string.
            url = url_append_query(url_add_query(url, params), oauth_params)
            if body and method == HTTP_GET:
                raise InvalidHttpRequestError(
                    "HTTP method GET does not take an entity body"
                )
            if body and \
               HEADER_CONTENT_LENGTH not in headers and \
               HEADER_CONTENT_LENGTH_CAPS not in headers:
                raise ValueError("You must set the `content-length` header.")
        else:
            if params or oauth_params:
                # Append to payload and set content type.
                body = utf8_encode(query_append(params, oauth_params))
                headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_FORM_URLENCODED
                headers[HEADER_CONTENT_LENGTH] = str(len(body)).encode("ascii")
            else:
                # Zero-length body.
                body = SYMBOL_EMPTY_BYTES
                headers[HEADER_CONTENT_LENGTH] = SYMBOL_ZERO
        return RequestAdapter(method, url, body, headers)
Example #4
0
    def _build_request(self,
                      method,
                      url,
                      payload_params=None,
                      headers=None,
                      token_or_temporary_credentials=None,
                      realm=None,
                      oauth_signature_method=SIGNATURE_METHOD_HMAC_SHA1,
                      **extra_oauth_params):
        """
        Builds an OAuth request.

        :param method:
            HTTP request method.
        :param url:
            The OAuth request URI.
        :param payload_params:
            A dictionary of payload parameters. These will be serialized
            into the URL or the entity-body depending on the HTTP request method.
            These must not include any parameters starting with ``oauth_``. Any
            of these parameters with names starting with the ``oauth_`` prefix
            will be ignored.
        :param headers:
            A dictionary of headers that will be passed along with the request.
            Must not include the "Authorization" header.
        :param realm:
            The value to use for the realm parameter in the Authorization HTTP
            header. It will be excluded from the request signature.
        :param oauth_signature_method:
            One of:
            1. :attr:`pyoauth.oauth1.SIGNATURE_METHOD_HMAC_SHA1`
            2. :attr:`pyoauth.oauth1.SIGNATURE_METHOD_RSA_SHA1`
            3. :attr:`pyoauth.oauth1.SIGNATURE_METHOD_PLAINTEXT`
        :param extra_oauth_params:
            Any additional oauth parameters you would like to include.
            The parameter names must begin with ``oauth_``. Any other parameters
            with names that do not begin with this prefix will be ignored.
        :returns:
            An instance of :class:`pyoauth.http.Request`.
        """
        method = method.upper()
        headers = headers or {}
        realm = realm or ""

        if oauth_signature_method not in SIGNATURE_METHOD_MAP:
            raise InvalidSignatureMethodError("Invalid signature method specified: `%r`" % (oauth_signature_method,))

        # Required OAuth protocol parameters.
        # See Making Requests (http://tools.ietf.org/html/rfc5849#section-3.1)
        oauth_params = dict(
            oauth_consumer_key=self._client_credentials.identifier,
            oauth_signature_method=oauth_signature_method,
            oauth_timestamp=generate_timestamp(),
            oauth_nonce=generate_nonce(),
            oauth_version=self.oauth_version,
        )
        if token_or_temporary_credentials:
            oauth_params["oauth_token"] = token_or_temporary_credentials.identifier

        if "_test_force_exclude_oauth_version" in extra_oauth_params:
            del oauth_params["oauth_version"]

        # Filter and add additional OAuth parameters.
        _force_override_reserved_oauth_params_for_tests = "_test_force_override_reserved_oauth_params" in extra_oauth_params
        extra_oauth_params = request_protocol_params_sanitize(extra_oauth_params)
        reserved_oauth_params = (
            "oauth_signature",     # Calculated from given parameters.
            "oauth_nonce",         # System-generated.
            "oauth_timestamp",     # System-generated.
            "oauth_consumer_key",  # Provided when creating the client instance.
            "oauth_version",       # Optional but MUST be set to "1.0" according to spec.
            "oauth_token",         # Determined from the token or temporary credentials.
        )
        for k, v in extra_oauth_params.items():
            if not _force_override_reserved_oauth_params_for_tests and k in reserved_oauth_params:
                # Don't override these required system-generated protocol parameters.
                raise IllegalArgumentError("Cannot override system-generated protocol parameter `%r`." % k)
            else:
                if k in oauth_params:
                    # Warn when an existing protocol parameter is being
                    # overridden.
                    logging.warning("Overriding existing protocol parameter `%r`=`%r` with `%r`=`%r`",
                                    k, oauth_params[k], k, v[0])
                oauth_params[k] = v[0]

        # Filter payload parameters for the request.
        payload_params = query_params_sanitize(payload_params)

        # I was not entirely certain about whether PUT payload
        # params should be included in the signature or not.
        # Here is why:
        #
        #    http://groups.google.com/group/oauth/browse_thread/thread/fdc0b11f2c4a8dc3/
        #
        # http://tools.ietf.org/html/rfc5849#appendix-A
        # However, Appendix A in the RFC specification clarifies this point.
        # form URL encoded entity bodies in a request using any HTTP verb
        # must be part of the base string used for the signature.
        # Therefore, do NOT exclude payload params from the signature URL
        # when the PUT HTTP method is used.
        #
        #if method == "PUT":
        #     signature_url = url
        #else:
        signature_url = url_add_query(url, payload_params)

        # Determine the request's OAuth signature.
        oauth_params["oauth_signature"] = self._sign_request_data(oauth_signature_method,
                                                                  method, signature_url, oauth_params,
                                                                  token_or_temporary_credentials)

        # Build request data now.
        # OAuth parameters and any parameters starting with the ``oauth_``
        # must be included only in ONE of these three locations:
        #
        # 1. Authorization header.
        # 2. Request URI query string.
        # 3. Request entity body.
        #
        # See Parameter Transmission (http://tools.ietf.org/html/rfc5849#section-3.6)
        if "Authorization" in headers:
            raise InvalidAuthorizationHeaderError("Authorization field is already present in headers: `%r`" % (headers, ))
        if self._use_authorization_header:
            auth_header_value = \
                generate_normalized_authorization_header_value(oauth_params,
                                                          realm=realm,
                                                          param_delimiter=self._authorization_header_param_delimiter)
            headers["Authorization"] = auth_header_value
            # Empty the params if using authorization so that they are not
            # included multiple times in a request below.
            oauth_params = None

        if method == "GET":
            request_url = url_add_query(url, payload_params)
            request_url = url_append_query(request_url, oauth_params)
            payload = ""
        else:
            # The payload params are not appended to the OAuth request URL
            # in this case but added to the payload instead.
            request_url = url
            headers["Content-Type"] = CONTENT_TYPE_FORM_URLENCODED
            payload = query_append(payload_params, oauth_params)

        return RequestProxy(method,
                            url=request_url,
                            body=payload,
                            headers=headers)