Пример #1
0
    def test_adds_query_params_properly(self):
        params1 = {
            b("a2"): b("r b"),
            b("b5"): b("=%3D"),
            b("a3"): [b("a"), b("2 q")],
            b("c2"): [b("")],
            b("c@"): b(""),
        }
        url = b("""HTTP://UserName:[email protected]:8000/result\
;param1?oauth_nonce=7d8f3e4a\
&oauth_signature_method=HMAC-SHA1\
&oauth_timestamp=137131201\
&oauth_token=kkk9d7dh3k39sjv7\
&oauth_consumer_key=9djdj82h48djs9d2\
#fragment""")
        resulting_url = b("""\
http://UserName:[email protected]:8000/result\
;param1?\
a2=r%20b\
&a3=2%20q\
&a3=a\
&b5=%3D%253D\
&c%40=\
&c2=\
&oauth_consumer_key=9djdj82h48djs9d2\
&oauth_nonce=7d8f3e4a\
&oauth_signature_method=HMAC-SHA1\
&oauth_timestamp=137131201\
&oauth_token=kkk9d7dh3k39sjv7\
#fragment""")
        self.assertEqual(url_add_query(url, params1), resulting_url)
Пример #2
0
    def authenticate_redirect(self, callback_uri=None, ax_attrs=None,
                              oauth_scope=None):
        """
        Redirects to the authentication URL for this service.

        After authentication, the service will redirect back to the given
        callback URI.

        We request the given attributes for the authenticated user by default
        (name, email, language, and username). If you don't need all those
        attributes for your app, you can request fewer with the ax_attrs keyword
        argument.

        :param callback_uri:
            The URL to redirect to after authentication.
        :param ax_attrs:
            List of Attribute Exchange attributes to be fetched.
        :returns:
            None
        """
        ax_attrs = ax_attrs or ("name", "email",
                                "language", "username", "country")
        callback_uri = callback_uri or self.adapter_request_path
        args = self._openid_args(callback_uri, ax_attrs, oauth_scope)
        self.adapter_redirect(url_add_query(self._OPENID_ENDPOINT, args))
Пример #3
0
 def test_adds_query_params_properly(self):
     params1 = {
         "a2": "r b",
         "b5": "=%3D",
         "a3": ["a", "2 q"],
         "c2": [""],
         "c@": "",
     }
     url = "HTTP://*****:*****@WWW.EXAMPLE.COM:8000/result;param1?oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7&oauth_consumer_key=9djdj82h48djs9d2#fragment"
     resulting_url = "http://*****:*****@www.example.com:8000/result;param1?a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9djdj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7#fragment"
     assert_equal(url_add_query(url, params1), resulting_url)
Пример #4
0
    def get_authorization_url(self, redirect_uri, **extra_query_params):
        """
        Determines the authorization URL to which the user should redirected
        by client.

        :param redirect_uri:
            The callback URI to which the OAuth 2.0 server will redirect with
            the response attached to the query string.
        :param extra_query_params:
            Keyword arguments for additional query parameters to be added
            to the URL query string.
        :returns:
            Fully-formed URL to which the client can redirect for
            user-authorization.
        """
        query = {
            'response_type': 'code',
            'redirect_uri': redirect_uri,
            'client_id': self._client_credentials.identifier,
        }
        if extra_query_params:
            query.update(extra_query_params)
        url = url_add_query(self._auth_uri, query)
        return url
Пример #5
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)
Пример #6
0
    def _generate_signature(cls, method, url, params,
                            body, headers,
                            oauth_consumer_secret,
                            oauth_token_secret,
                            oauth_params):
        """
        Given the base string parameters, secrets, and protocol parameters,
        calculates a signature for the request.

        :param method:
            HTTP method.
        :param url:
            Request URL.
        :param params:
            Additional query/payload parameters.
        :param body:
            Payload if any.
        :param headers:
            HTTP headers as a dictionary.
        :param oauth_consumer_secret:
            OAuth client shared secret (consumer secret).
        :param oauth_token_secret:
            OAuth token/temporary shared secret if obtained from the OAuth
            server.
        :param oauth_params:
            OAuth parameters generated by
            :func:`OAuthClient._generate_oauth_params`.
        :returns:
            Request signature.
        """
        # Take parameters from the body if the Content-Type is specified
        # as ``application/x-www-form-urlencoded``.
        # http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
        if body:
            try:
                try:
                    content_type = headers[HEADER_CONTENT_TYPE]
                except KeyError:
                    content_type = headers[HEADER_CONTENT_TYPE_CAPS]

                if content_type == CONTENT_TYPE_FORM_URLENCODED:
                    # These parameters must also be included in the signature.
                    # Ignore OAuth-specific parameters. They must be specified
                    # separately.
                    body_params = query_remove_oauth(parse_qs(body))
                    params = query_add(params, body_params)
                else:
                    logging.info(
                        "Entity-body specified but `content-type` header " \
                        "value is not %r: entity-body parameters if " \
                        "present will not be signed: got body %r" % \
                        (CONTENT_TYPE_FORM_URLENCODED, body)
                    )
            except KeyError:
                logging.warning(
                    "Entity-body specified but `content-type` is missing "
                )

        # Make oauth params and sign the request.
        signature_url = url_add_query(url, query_remove_oauth(params))
        # NOTE: We're not explicitly cleaning up because this method
        # expects oauth params generated by _generate_oauth_params.
        base_string = generate_base_string(method, signature_url, oauth_params)

        signature_method = oauth_params[OAUTH_PARAM_SIGNATURE_METHOD]
        cls.check_signature_method(signature_method)
        try:
            sign_func = SIGNATURE_METHOD_MAP[signature_method]
            return sign_func(base_string,
                             oauth_consumer_secret,
                             oauth_token_secret)
        except KeyError:
            raise InvalidSignatureMethodError(
                "unsupported signature method: %r" % signature_method
            )
Пример #7
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)