def test_filter(self): params = { "a2": ["r b"], "b5": ["=%3D"], "a3": ["a", "2 q"], "c@": [""], "c2": [""], "oauth_consumer_key": ["9djdj82h48djs9d2"], "oauth_token": ["kkk9d7dh3k39sjv7"], "oauth_signature_method": ["HMAC-SHA1"], "oauth_timestamp": ["137131201"], "oauth_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 = { "oauth_consumer_key": ["9djdj82h48djs9d2"], "oauth_token": ["kkk9d7dh3k39sjv7"], "oauth_signature_method": ["HMAC-SHA1"], "oauth_timestamp": ["137131201"], "oauth_nonce": ["7d8f3e4a"], } expected_result = urlencode_s(expected_params) assert_equal(urlencode_s(request_protocol_params_sanitize(params)), expected_result) assert_equal(urlencode_s(request_protocol_params_sanitize(query_string)), expected_result)
def generate_normalized_authorization_header_value(oauth_params, realm=None, param_delimiter=","): """ Builds the Authorization header value. Please note that the generated authorization header value MUST be on a SINGLE line. :see: Authorization Header http://tools.ietf.org/html/rfc5849#section-3.5.1 :param oauth_params: Protocol-specific parameters excluding the ``realm`` parameter. :param realm: If specified, the realm is included into the Authorization header. The realm is never percent-encoded according to the OAuth spec. :param param_delimiter: The delimiter used to separate header value parameters. According to the Specification, this must be a comma ",". However, certain services like Yahoo! use "&" instead. Comma is default. See https://github.com/oauth/oauth-ruby/pull/12 :returns: A properly formatted Authorization header value. """ if realm: s = 'OAuth realm="' + unicode_to_utf8(realm) + '"' + param_delimiter else: s = 'OAuth ' oauth_params = request_protocol_params_sanitize(oauth_params) normalized_param_pairs = urlencode_sl(oauth_params) s += param_delimiter.join([k + '="' + v + '"' for k, v in normalized_param_pairs]) return s
def _generate_signature_base_string_query(url_query_params, 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_params: 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_params = query_params_sanitize(url_query_params) oauth_params = request_protocol_params_sanitize(oauth_params) query_params = {} query_params.update(url_query_params) query_params.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, value): return name not in ("oauth_signature", #"oauth_consumer_secret", # Already filtered by protocol parameter sanitization above. #"oauth_token_secret", # Already filtered by protocol parameter sanitization above. ) query = urlencode_s(query_params, allow_func=allow_func) return query
def parse_authorization_header_value(header_value, param_delimiter=",", strict=True): """ Parses the OAuth Authorization header. :see: Authorization Header http://tools.ietf.org/html/rfc5849#section-3.5.1 :param header_value: Header value. :param param_delimiter: The delimiter used to separate header value parameters. According to the Specification, this must be a comma ",". However, certain services like Yahoo! use "&" instead. Comma is default. If you want to use another delimiter character, the ``strict`` argument to this function must also be set to ``False``. See https://github.com/oauth/oauth-ruby/pull/12 :param strict: When ``True``, more strict checking will be performed. The authorization header value must be on a single line. The param delimiter MUST be a comma. When ``False``, the parser is a bit lenient. :returns: Dictionary of parameter name value pairs. """ d = {} param_list, realm = \ _parse_authorization_header_value_l(header_value, param_delimiter=param_delimiter, strict=strict) for name, value in param_list: #d[name] = [value] # We do keep track of multiple values because they will be # detected by the sanitization below and flagged as an error # in the Authorization header value. if name in d: d[name].append(value) else: d[name] = [value] d = request_protocol_params_sanitize(d) return d, realm
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)