Exemple #1
0
def parse_authorization_header(header_value,
                               param_delimiter=',',
                               strict=True):
    """
    Parses the OAuth Authorization header.

    ::

        'OAuth realm="example.com",oauth_nonce="123456789",..."
        -> ({"oauth_nonce": ["123456789"], ...}, "example.com")

    :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:
        A tuple of (Dictionary of parameter name value pairs, realm).

        realm will be ``None`` if the authorization header does not have
        a ``realm`` parameter.
    """
    header_value = utf8_decode_if_bytes(header_value)
    realm = None
    params = {}
    for name, value \
        in _parse_authorization_header_l(header_value,
                                        param_delimiter=param_delimiter,
                                        strict=strict):
        # 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.
        #
        #params[name] = [value]
        if not name == OAUTH_PARAM_REALM:
            # The ``realm`` parameter is not included into the protocol
            # parameters list.
            if name in params:
                params[name].append(value)
            else:
                params[name] = [value]
        else:
            realm = value

    # Sanitize and check parameters. Raises an error if
    # multiple values are found. Should help with debugging
    # clients.
    params = request_query_remove_non_oauth(params)
    return params, realm
Exemple #2
0
    def _parse_credentials_response(cls, response, strict=True):
        """
        Parses the entity-body of the OAuth server response to an OAuth
        credential request.

        :param response:
            An instance of :class:`pyoauth.http.ResponseAdapter`.
        :param strict:
            ``True`` (default) for string response parsing; ``False`` to be a
            bit lenient. Some non-compliant OAuth servers return credentials
            without setting the content-type.

            Setting this to ``False`` will not raise an error, but will
            still warn you that the response content-type is not valid.
        :returns:
            A tuple of the form::

                (pyoauth.oauth1.Credentials instance, other parameters)
        """
        if not response.status:
            raise InvalidHttpResponseError(
                "Invalid status code: `%r`" % response.status)
        if not response.reason:
            raise InvalidHttpResponseError(
                "Invalid status message: `%r`" % response.reason)
        if not response.body:
            raise InvalidHttpResponseError(
                "Body is invalid or empty: `%r`" % response.body)
        if not response.headers:
            raise InvalidHttpResponseError(
                "Headers are invalid or not specified: `%r`" % \
                response.headers)

        if response.error:
            raise HttpError("Could not fetch credentials: HTTP %d - %s" \
            % (response.status, response.reason,))

        # The response body must be form URL-encoded.
        if not response.is_body_form_urlencoded():
            if strict:
                raise InvalidContentTypeError(
                    "OAuth credentials server response must " \
                    "have Content-Type: `%s`; got %r" %
                    (CONTENT_TYPE_FORM_URLENCODED, response.content_type))
            else:
                logging.warning(
                    "Response parsing strict-mode disabled -- " \
                    "OAuth server credentials response specifies invalid " \
                    "Content-Type: expected %r; got %r",
                    CONTENT_TYPE_FORM_URLENCODED, response.content_type)

        params = parse_qs(response.body)
        # Ensure the keys to this dictionary are unicode strings in Python 3.x.
        params = map_dict(lambda k, v: (utf8_decode_if_bytes(k), v), params)
        credentials = Credentials(identifier=params[OAUTH_PARAM_TOKEN][0],
                            shared_secret=params[OAUTH_PARAM_TOKEN_SECRET][0])
        return credentials, params
Exemple #3
0
def percent_decode(value):
    """
    Percent-decodes according to the OAuth spec::

        percent_decode('c%40') -> 'c@'

    :see: Percent Encoding (http://tools.ietf.org/html/rfc5849#section-3.6)
    :param value:
        Value to percent-decode. '+' is treated as a ' ' character.
    :returns:
        Percent-decoded value.
    """
    return utf8_decode_if_bytes(unquote_plus(value))
Exemple #4
0
    def predicate(name, _):
        """
        Removes any parameters beginning with ``oauth_``.

        :param name:
            The parameter name.
        :returns:
            ``True`` if should be included; ``False`` otherwise.
        """
        # This gets rid of any params beginning with "oauth_"
        name = utf8_decode_if_bytes(name)
        if not name.startswith(OAUTH_PARAM_PREFIX):
            return True
        else:
            logging.warning(
                "Protocol parameter ignored from URL query parameters: `%r`",
                name)
            return False
Exemple #5
0
    def predicate(name, value):
        """
        Allows only valid oauth parameters.

        Raises an error if multiple values are specified.

        :param name:
            Protocol parameter name.
        :param value:
            Protocol parameter value.
        :returns:
            ``True`` if the parameter should be included; ``False`` otherwise.
        """
        name = utf8_decode_if_bytes(name)
        if name.startswith(OAUTH_PARAM_PREFIX):
            # This gets rid of "realm" or any non-OAuth param.
            if len(value) > 1:
                # Multiple values for a protocol parameter are not allowed.
                # We don't silently discard values because failing fast
                # is better than simply logging and waiting for the user
                # to figure it out all by herself.
                #
                # See Making Requests
                # (http://tools.ietf.org/html/rfc5849#section-3.1)
                # Point 2. Each parameter MUST NOT appear more than once per
                # request, so we disallow multiple values for a protocol
                # parameter.
                raise InvalidOAuthParametersError(
                    "Multiple protocol parameter values found %r=%r" \
                    % (name, value))
            elif name in (OAUTH_PARAM_CONSUMER_SECRET,
                          OAUTH_PARAM_TOKEN_SECRET, ):
                raise InsecureOAuthParametersError(
                    "[SECURITY-ISSUE] Client attempting to transmit "\
                    "confidential protocol parameter `%r`. Communication "\
                    "is insecure if this is in your server logs." % (name, ))
            else:
                return True
        else:
            logging.warning("Invalid protocol parameter ignored: `%r`", name)
            return False
Exemple #6
0
  def test_does_not_encode_else_to_unicode(self):
    self.assertEqual(text.utf8_decode_if_bytes(constants.UNICODE_STRING), constants.UNICODE_STRING)
    self.assertTrue(builtins.is_unicode(text.utf8_decode_if_bytes(constants.UNICODE_STRING)))

    self.assertEqual(text.utf8_decode_if_bytes(constants.UNICODE_STRING2), constants.UNICODE_STRING2)
    self.assertTrue(builtins.is_unicode(text.utf8_decode_if_bytes(constants.UNICODE_STRING2)))

    self.assertEqual(text.utf8_decode_if_bytes(None), None)
    self.assertEqual(text.utf8_decode_if_bytes(False), False)
    self.assertEqual(text.utf8_decode_if_bytes(5), 5)
    self.assertEqual(text.utf8_decode_if_bytes([]), [])
    self.assertEqual(text.utf8_decode_if_bytes(()), ())
    self.assertEqual(text.utf8_decode_if_bytes({}), {})
    self.assertEqual(text.utf8_decode_if_bytes(object), object)
Exemple #7
0
  def test_encodes_bytes_to_unicode(self):
    self.assertEqual(text.utf8_decode_if_bytes(constants.UTF8_BYTES), constants.UNICODE_STRING)
    self.assertTrue(builtins.is_unicode(text.utf8_decode_if_bytes(constants.UTF8_BYTES)))

    self.assertEqual(text.utf8_decode_if_bytes(constants.UTF8_BYTES2), constants.UNICODE_STRING2)
    self.assertTrue(builtins.is_unicode(text.utf8_decode_if_bytes(constants.UTF8_BYTES2)))
Exemple #8
0
  def test_does_not_encode_else_to_unicode(self):
    self.assertEqual(utf8_decode_if_bytes(unicode_string), unicode_string)
    self.assertTrue(is_unicode(utf8_decode_if_bytes(unicode_string)))

    self.assertEqual(utf8_decode_if_bytes(unicode_string2), unicode_string2)
    self.assertTrue(is_unicode(utf8_decode_if_bytes(unicode_string2)))

    self.assertEqual(utf8_decode_if_bytes(None), None)
    self.assertEqual(utf8_decode_if_bytes(False), False)
    self.assertEqual(utf8_decode_if_bytes(5), 5)
    self.assertEqual(utf8_decode_if_bytes([]), [])
    self.assertEqual(utf8_decode_if_bytes(()), ())
    self.assertEqual(utf8_decode_if_bytes({}), {})
    self.assertEqual(utf8_decode_if_bytes(object), object)
Exemple #9
0
  def test_encodes_bytes_to_unicode(self):
    self.assertEqual(utf8_decode_if_bytes(utf8_bytes), unicode_string)
    self.assertTrue(is_unicode(utf8_decode_if_bytes(utf8_bytes)))

    self.assertEqual(utf8_decode_if_bytes(utf8_bytes2), unicode_string2)
    self.assertTrue(is_unicode(utf8_decode_if_bytes(utf8_bytes2)))