Example #1
0
def _authorization_header_parse_param(param):
    """
    Parses an individual authorization header parameter string.

    ::

        'name="value%201"' -> ("name", "value 1")

    The ``realm`` parameter, if present, will not be percent-decoded.

    :param param:
        The parameter (name=value) pair string.
    :returns:
        A tuple of (percent-decoded name, percent-decoded value)
    """
    # Split into a name, value pair.
    try:
        name, value = param.split("=", 1)
    except ValueError:
        raise InvalidAuthorizationHeaderError("bad parameter field: `%r`" %
                                              param)

    # Get rid of all the whitespace surrounding the name and value.
    # Already done in parent function when splitting into param pairs.
    # name, value = name.strip(), value.strip()

    # Value must be at least ""
    if len(value) < 2:
        raise InvalidAuthorizationHeaderError(
            "bad parameter value: `%r` -- missing quotes?" % param)

    # Value must be quoted between " characters.
    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('"') because that will remove more " characters than
    # necessary. We need to let the user be able to detect problems with the
    # values as well.
    value = value[1:-1]

    # Names and values must be percent-decoded except for the ``realm``
    # parameter.
    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.
        name = "realm"
    else:
        # Percent decode if the parameter is not ``realm``.
        value = percent_decode(value)

    # Hooray! You made it.
    return name, value
    def test_ignores_body_params_if_content_type_is_not_urlencoded(self):
        oauth_params = dict(
            oauth_consumer_key=RFC_CLIENT_IDENTIFIER,
            oauth_token=RFC_TOKEN_IDENTIFIER,
            oauth_signature_method=SIGNATURE_METHOD_HMAC_SHA1,
            oauth_timestamp=RFC_TIMESTAMP_3,
            oauth_nonce=RFC_NONCE_3,
        )
        self.assertEqual(_OAuthClient._generate_signature(
            HTTP_GET,
            # I Know ^ is a GET request and we're specifying a body in this
            # test. _generate_signature does not validate HTTP methods,
            # but only generates signatures. This example must produce
            # the same signature as in the RFC example, hence the test.
            RFC_RESOURCE_FULL_URL,
            params=None,
            body=b("""
body {
    font-family: "Lucida Grande", serif;
}
a:link {
    text-decoration: none;
}
"""),
            headers={
                HEADER_CONTENT_TYPE: CONTENT_TYPE_TEXT_CSS,
            },
            oauth_consumer_secret=RFC_CLIENT_SECRET,
            oauth_token_secret=RFC_TOKEN_SECRET,
            oauth_params=oauth_params,
        ), utf8_encode(percent_decode(RFC_RESOURCE_REQUEST_SIGNATURE_ENCODED)))
Example #3
0
 def test_oauth_test_cases(self):
     # http://wiki.oauth.net/w/page/12238556/TestCases
     ex = [
         ('abcABC123', 'abcABC123'),
         ('-._~', '-._~'),
         ('%', '%25'),
         ('+', '%2B'),
         ('&=*', '%26%3D%2A'),
         (u'\u000A', '%0A'),
         (u'\u0020', '%20'),
         (u'\u007F', '%7F'),
         (u'\u0080', '%C2%80'),
         (u'\u3001', '%E3%80%81'),
     ]
     for k, v in ex:
         assert_equal(percent_decode(v), to_utf8_if_unicode(k))
 def test_generates_signature_including_urlencoded_body(self):
     oauth_params = dict(
         oauth_consumer_key=RFC_CLIENT_IDENTIFIER,
         oauth_token=RFC_TOKEN_IDENTIFIER,
         oauth_signature_method=SIGNATURE_METHOD_HMAC_SHA1,
         oauth_timestamp=RFC_TIMESTAMP_3,
         oauth_nonce=RFC_NONCE_3,
     )
     self.assertEqual(_OAuthClient._generate_signature(
         HTTP_GET,
         # I Know ^ is a GET request and we're specifying a body in this
         # test. _generate_signature does not validate HTTP methods,
         # but only generates signatures. This example must produce
         # the same signature as in the RFC example, hence the test.
         RFC_RESOURCE_URI,
         params=None,
         body=b("file=vacation.jpg&size=original&oauth_ignored=IGNORED"),
         headers={
             HEADER_CONTENT_TYPE: CONTENT_TYPE_FORM_URLENCODED,
         },
         oauth_consumer_secret=RFC_CLIENT_SECRET,
         oauth_token_secret=RFC_TOKEN_SECRET,
         oauth_params=oauth_params,
     ), utf8_encode(percent_decode(RFC_RESOURCE_REQUEST_SIGNATURE_ENCODED)))
Example #5
0
 def test_oauth_test_cases(self):
     # http://wiki.oauth.net/w/page/12238556/TestCases
     ex = constants.percent_decode_test_cases
     for decoded, encoded in ex:
         self.assertEqual(percent_decode(encoded), decoded)
Example #6
0
 def test_plus_is_treated_as_space_character(self):
     self.assertEqual(
         percent_decode(b('+')), " ",
         "Plus character in encoding is not treated as space character.")
Example #7
0
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
Example #8
0
 def test_plus_is_treated_as_space_character(self):
     assert_equal(percent_decode('+'), ' ', "Plus character in encoding is not treated as space character.")
Example #9
0
 def test_percent_encoded_unicode_input(self):
     assert_equal(percent_decode("%FF%FE%E5%00%E9%00%EE%00%F8%00%FC%00"),
                  u'åéîøü'.encode("utf-16"))