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)
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)
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)
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)