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)))
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)))
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)
def test_plus_is_treated_as_space_character(self): self.assertEqual( percent_decode(b('+')), " ", "Plus character in encoding is not treated as space character.")
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
def test_plus_is_treated_as_space_character(self): assert_equal(percent_decode('+'), ' ', "Plus character in encoding is not treated as space character.")
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"))