예제 #1
0
    def get_authentication_url(self, temporary_credentials, **query_params):
        """
        Calculates the automatic authentication redirect URL to which the
        user will be (re)directed. Some providers support automatic
        authorization URLs if the user is already signed in. You can use
        this method with such URLs.

        :param temporary_credentials:
            Temporary credentials obtained after parsing the response to
            the temporary credentials request.
        :param query_params:
            Additional query parameters that you would like to include
            into the authorization URL. Parameters beginning with the ``oauth_``
            prefix will be ignored.
        """
        url = self._authentication_uri
        if not url:
            raise NotImplementedError(
                "Service does not support automatic authentication redirects.")
        if query_params:
            query_params = query_remove_oauth(query_params)
            url = url_append_query(url, query_params)

        # So that the "oauth_token" appears LAST.
        return url_append_query(url, {
            OAUTH_PARAM_TOKEN: temporary_credentials.identifier,
            })
예제 #2
0
    def get_authorization_url(self, temporary_credentials, **query_params):
        """
        Calculates the authorization URL to which the user will be (re)directed.

        :param temporary_credentials:
            Temporary credentials obtained after parsing the response to
            the temporary credentials request.
        :param query_params:
            Additional query parameters that you would like to include
            into the authorization URL. Parameters beginning with the ``oauth_``
            prefix will be ignored.
        """
        url = self._resource_owner_authorization_uri
        if query_params:
            query_params = query_params_sanitize(query_params)
            url = url_append_query(url, query_params)
        # So that the "oauth_token" appears LAST.
        return url_append_query(url, {
            "oauth_token": temporary_credentials.identifier,
        })
예제 #3
0
    def get_authorization_url(self, temporary_credentials, **query_params):
        """
        Calculates the authorization URL to which the user will be (re)directed.

        :param temporary_credentials:
            Temporary credentials obtained after parsing the response to
            the temporary credentials request.
        :param query_params:
            Additional query parameters that you would like to include
            into the authorization URL. Parameters beginning with the ``oauth_``
            prefix will be ignored.
        """
        url = self._authorization_uri
        if query_params:
            query_params = query_remove_oauth(query_params)
            url = url_append_query(url, query_params)

        # `oauth_token` must appear last.
        return url_append_query(url, {
            OAUTH_PARAM_TOKEN: temporary_credentials.identifier,
        })
예제 #4
0
 def test_append_to_url_preserving_fragment_doesnt_change_order(self):
     url = b("http://www.example.com/request?b=1#fragment")
     expected_url = b("http://www.example.com/request?b=1&a=1#fragment")
     self.assertEqual(url_append_query(url, {"a": 1}), expected_url)
     self.assertEqual(url_append_query(url, "a=1"), expected_url)
예제 #5
0
 def test_returns_url_unchanged_if_no_query_params(self):
     url = b("http://www.example.com/request?a=b")
     self.assertEqual(url_append_query(url, None), url)
예제 #6
0
 def test_does_not_prefix_with_ampersand_when_url_has_no_query_params(self):
     url = b("https://www.example.com/authorize")
     self.assertEqual(url_append_query(url, dict(a=1)),
                      b("https://www.example.com/authorize?a=1"))
     self.assertNotEqual(url_append_query(url, dict(a=1)),
                         b("https://www.example.com/authorize?&a=1"))
예제 #7
0
    def _build_request(cls, method, url, params, body, headers,
                       oauth_params, realm, use_authorization_header):
        """
        Builds a request based on the HTTP arguments and OAuth protocol
        parameters.

        :param method:
            HTTP method.
        :param url:
            Request URL
        :param params:
            Additional query/payload parameters.
            If a `body` argument to this function is specified,
            the parameters are appended to the URL query string.
            If a `body` is not specified and a method other than GET is used
            the parameters will be added to the entity body.
        :param body:
            Entity body.
        :param oauth_params:
            Protocol-specific parameters.
        :param realm:
            OAuth authorization realm.
        :param use_authorization_header:
            ``True`` if the Authorization HTTP header should be used;
            ``False`` otherwise.
        :returns:
            An instance of :class:`pyoauth.http.RequestAdapter`.
        """
        # http://tools.ietf.org/html/rfc5849#section-3.6
        if HEADER_AUTHORIZATION_CAPS in headers or \
           HEADER_AUTHORIZATION in headers:
            raise InvalidAuthorizationHeaderError(
                "Authorization field is already present in headers: %r" % \
                headers
            )
        if use_authorization_header:
            headers[HEADER_AUTHORIZATION_CAPS] = \
                generate_authorization_header(oauth_params, realm)
            # Empty oauth params so that they are not included again below.
            oauth_params = None

        # OAuth requests can contain payloads.
        if body or method == HTTP_GET:
            # Append params to query string.
            url = url_append_query(url_add_query(url, params), oauth_params)
            if body and method == HTTP_GET:
                raise InvalidHttpRequestError(
                    "HTTP method GET does not take an entity body"
                )
            if body and \
               HEADER_CONTENT_LENGTH not in headers and \
               HEADER_CONTENT_LENGTH_CAPS not in headers:
                raise ValueError("You must set the `content-length` header.")
        else:
            if params or oauth_params:
                # Append to payload and set content type.
                body = utf8_encode(query_append(params, oauth_params))
                headers[HEADER_CONTENT_TYPE] = CONTENT_TYPE_FORM_URLENCODED
                headers[HEADER_CONTENT_LENGTH] = str(len(body)).encode("ascii")
            else:
                # Zero-length body.
                body = SYMBOL_EMPTY_BYTES
                headers[HEADER_CONTENT_LENGTH] = SYMBOL_ZERO
        return RequestAdapter(method, url, body, headers)
예제 #8
0
 def test_appends_to_url_preserving_fragments_and_does_not_change_append_order(self):
     url = "http://www.example.com/request?b=1#fragment"
     expected_url = "http://www.example.com/request?b=1&a=1#fragment"
     assert_equal(url_append_query(url, {"a": 1}), expected_url)
     assert_equal(url_append_query(url, "a=1"), expected_url)
예제 #9
0
    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)