def test_returns_bytes_and_None_unchanged_and_converts_unicode(self): assert_equal(unicode_to_utf8(unicode_string), utf8_bytes) assert_equal(unicode_to_utf8(None), None) assert_equal(unicode_to_utf8(utf8_bytes), utf8_bytes) assert_raises(AssertionError, unicode_to_utf8, 5) assert_raises(AssertionError, unicode_to_utf8, False) assert_raises(AssertionError, unicode_to_utf8, True) assert_raises(AssertionError, unicode_to_utf8, []) assert_raises(AssertionError, unicode_to_utf8, ()) assert_raises(AssertionError, unicode_to_utf8, {}) assert_raises(AssertionError, unicode_to_utf8, object)
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 percent_decode(value): """ Percent-decodes according to the OAuth spec. :see: Percent Encoding (http://tools.ietf.org/html/rfc5849#section-3.6) :param value: Value to percent-decode. Value will be UTF-8 encoded if it is a Unicode string. '+' is treated as a ' ' character. :returns: Percent-decoded value. """ return unquote_plus(unicode_to_utf8(value))
def urlencode_sl(query_params, allow_func=None): """ Serializes a dictionary of query parameters into a list of query parameters, ``(name, value)`` pairs, sorted first by ``name`` then by ``value`` based on the OAuth percent-encoding rules and specification. Behaves like :func:`urllib.urlencode` with ``doseq=1``. :param query_params: Dictionary of query parameters. :param allow_func: A callback that will be called for each query parameter and should return ``False`` or a falsy value if that parameter should not be included. By default, all query parameters are included. The function takes the following method signature:: def allow_func(name, value): return is_name_allowed(name) and is_value_allowed(value) :returns: A list of query parameters, ``(name, value)`` pairs, sorted first by ``name`` and then by ``value`` based on the OAuth percent-encoding rules and specification. """ query_params = query_params or {} encoded_pairs = [] for k, v in query_params.items(): # Keys are also percent-encoded according to OAuth spec. k = percent_encode(unicode_to_utf8(k)) if allow_func and not allow_func(k, v): continue elif is_bytes_or_unicode(v): encoded_pairs.append((k, percent_encode(v),)) else: if is_sequence(v): # Loop over the sequence. if len(v) > 0: for i in v: encoded_pairs.append((k, percent_encode(i), )) # ``urllib.urlencode()`` doesn't preserve blank lists. # Therefore, we're discarding them. #else: # # Preserve blank list values. # encoded_pairs.append((k, "", )) else: encoded_pairs.append((k, percent_encode(v),)) # Sort after encoding according to the OAuth spec. return sorted(encoded_pairs)
def _parse_authorization_header_value_l(header_value, param_delimiter=",", strict=True): """ Parses the OAuth Authorization header preserving the order of the parameters as in the header value. :see: Authorization Header http://tools.ietf.org/html/rfc5849#section-3.5.1 :param header_value: Header value. Non protocol parameters will be ignored. :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`` (default), 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: Tuple: (list of parameter name value pairs in order or appearance, realm) realm will be ``None`` if the authorization header does not have a realm parameter. """ # Remove the auth-scheme from the value. header_value = unicode_to_utf8(header_value) if strict: if "\n" in header_value: raise ValueError("Header value must be on a single line: got `%r`" % (header_value, )) if param_delimiter != ",": raise ValueError("The param delimiter must be a comma: got `%r`" % (param_delimiter, )) pattern = re.compile(r"(^OAuth[\s]+)", re.IGNORECASE) header_value = re.sub(pattern, "", header_value.strip(), 1) realm = None pairs = [param_pair.strip() for param_pair in header_value.split(param_delimiter)] decoded_pairs = [] for param in pairs: if not param: if header_value.endswith(param_delimiter): raise InvalidAuthorizationHeaderError("Malformed `Authorization` header value -- found trailing `%r` character" % param_delimiter) #else: # continue nv = param.split("=", 1) if len(nv) != 2: raise InvalidAuthorizationHeaderError("bad parameter field: `%r`" % (param, )) name, value = nv[0].strip(), nv[1].strip() if len(value) < 2: raise InvalidAuthorizationHeaderError("bad parameter value: `%r` -- missing quotes?" % (param, )) if value[0] != '"' or value[-1] != '"': raise InvalidAuthorizationHeaderError("missing quotes around parameter value: `%r` -- values must be quoted using (\")" % (param, )) # We only need to remove a single pair of quotes. Do not use str.strip('"'). # We need to be able to detect problems with the values too. value = value[1:-1] name = percent_decode(name) if name.lower() == "realm": # "realm" is case-insensitive. # The realm parameter value is a simple quoted string. # It is neither percent-encoded nor percent-decoded in OAuth. # realm is ignored from the protocol parameters list. realm = value else: value = percent_decode(value) decoded_pairs.append((name, value)) return decoded_pairs, realm