SYMBOL_INVERTED_DOUBLE_QUOTE = b('"') SYMBOL_EQUAL = b("=") OAUTH_AUTH_SCHEME_PATTERN = re.compile(r"(^OAuth[\s]+)", re.IGNORECASE) SYMBOL_EMPTY_BYTES = b("") OAUTH_PARAM_SIGNATURE = "oauth_signature" SYMBOL_AMPERSAND = b("&") OAUTH_PARAM_REALM = "realm" def unquote_plus(v): HTTP_METHODS = tuple(map(ascii_encode, ("POST", "GET", "PUT", "DELETE",
               "OPTIONS", "TRACE", "HEAD", "CONNECT",
               "PATCH"))) def percent_encode(value):
    """
    Percent-encodes according to the OAuth spec.

    Used in constructing the signature base string and the "Authorization"
    header field::

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

    :see: Percent Encoding (http://tools.ietf.org/html/rfc5849#section-3.6)
    :param value:
        Query string parameter value to escape. If the value is a Unicode
        string, it will be encoded to UTF-8. A byte string is considered
        exactly that, a byte string and will not be UTF-8 encoded—however, it
        will be percent-encoded.
    :returns:
        Percent-encoded string.
   """
    # Escapes '/' too
    if not is_bytes(value):
        value = utf8_encode(str(value))
    return quote(value, safe="~").encode("ascii") 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)) def urlencode_sl(query_params, predicate=None):
    """
    Serializes a dictionary of query parameters into a list of query
    parameters, ``(name, value)`` pairs, sorted first by ``name`` then by
    ``value`` based on the OAuth percent-encoding rules and specification.

    Behaves like :func:`urllib.urlencode` with ``doseq=1``.

    :param query_params:
        Dictionary of query parameters.
    :param predicate:
        A callback that will be called for each query parameter and should
        return ``False`` or a falsy value if that parameter should not be
        included. By default, all query parameters are included. The function
        takes the following method signature::

            def predicate(name, value):
                return is_name_allowed(name) and is_value_allowed(value)
    :returns:
        A list of query parameters, ``(name, value)`` pairs, sorted first by
        ``name`` and then by ``value`` based on the OAuth percent-encoding rules
        and specification.
    """
    query_params = query_params or {}
    encoded_pairs = []
    for k, value in query_params.items():
        # Keys are also percent-encoded according to OAuth spec.
        key = percent_encode(k)
        if predicate and not predicate(k, value):
            continue
        elif is_bytes_or_unicode(value):
            encoded_pairs.append((key, percent_encode(value),))
        elif is_sequence(value):
            # Loop over the sequence.
            if len(value) > 0:
                for i in value:
                    encoded_pairs.append((key, percent_encode(i), ))
            # ``urllib.urlencode()`` doesn't preserve blank lists.
            # Therefore, we're discarding them.
            #else:
            #    # Preserve blank list values.
            #    encoded_pairs.append((k, "", ))
        else:
            encoded_pairs.append((key, percent_encode(value),))
    # Sort after encoding according to the OAuth spec.
    return sorted(encoded_pairs) def urlencode_s(query_params, predicate=None):
    """
    Serializes a dictionary of query parameters into a string of query
    parameters, ``name=value`` pairs separated by ``&``, sorted first by
    ``name`` then by ``value`` based on the OAuth percent-encoding
    rules and specification.

    Behaves like :func:`urllib.urlencode` with ``doseq=1``.

    :param query_params:
        Dictionary of query parameters.
    :param predicate:
        A callback that will be called for each query parameter and should
        return ``False`` or a falsy value if that parameter should not be
        included. By default, all query parameters are included. The function
        takes the following method signature::

            def predicate(name, value):
                return is_name_allowed(name) and is_value_allowed(value)
    :returns:
        A string of query parameters, ``name=value`` pairs separated by ``&``,
        sorted first by ``name`` and then by ``value`` based on the OAuth
        percent-encoding rules and specification.
    """
    return SYMBOL_AMPERSAND.join(
        key + SYMBOL_EQUAL + value
        for key, value in urlencode_sl(query_params, predicate)) def urlparse_normalized(url):
    """
    Like :func:`urlparse.urlparse` but also normalizes scheme, netloc, port,
    and the path.

    Use with OAuth URLs.

    :see: Base String URI (http://tools.ietf.org/html/rfc5849#section-3.4.1.2)
    :param url:
        The URL to split and normalize.
    :returns:
        Tuple that contains these elements:
        ``(scheme, netloc, path, params, query, fragment)``
    """
    if not url:
        raise InvalidUrlError("Invalid URL `%r`" % (url,))

    parts = urlparse(url)

    scheme      = parts.scheme.lower()
    # Netloc.
    username    = parts.username or SYMBOL_EMPTY_BYTES
    password    = (b(":") + parts.password) if parts.password \
                  else SYMBOL_EMPTY_BYTES
    credentials = username + password
    credentials = (credentials + b("@")) if credentials else SYMBOL_EMPTY_BYTES
    hostname = utf8_encode_if_unicode(parts.hostname.lower())

    # Exclude default port numbers.
    # See:
    if parts.port:
        if (scheme == b("http") and parts.port == 80) \
        or (scheme == b("https") and parts.port == 443):
            port = SYMBOL_EMPTY_BYTES
        else:
            port = (b(":") + str(parts.port).encode("ascii")) if parts.port \
                   else SYMBOL_EMPTY_BYTES
    else:
        port = SYMBOL_EMPTY_BYTES

    netloc        = credentials + hostname + port
    # http://tools.ietf.org/html/rfc3986#section-3
    # and http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.2
    path          = parts.path or b("/")
    matrix_params = parts.params or SYMBOL_EMPTY_BYTES
    fragment      = parts.fragment or SYMBOL_EMPTY_BYTES
    query         = parts.query or SYMBOL_EMPTY_BYTES

    return scheme, netloc, path, matrix_params, query, fragment def request_query_remove_non_oauth(query):
    """
    Removes non-OAuth and non-transmittable OAuth parameters from the
    request query parameters.

    .. WARNING:: Do NOT use this function with responses.

        Use ONLY with requests.

        Specifically used ONLY in base string construction, Authorization
        headers construction and parsing, and OAuth requests.

    :param query:
        Query string or query parameter dictionary. Does not filter out
        ``oauth_signature``, but DOES filter out ``oauth_consumer_secret`` and
        ``oauth_token_secret``. These secret parameters must never be
        transmitted.
    :returns:
        Filtered protocol parameters dictionary.
    """
    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
    return query_select(query, predicate) def query_remove_oauth(query):
    """
    Removes protocol parameters from the query parameters.

    Used only in base string construction, Authorization headers construction
    and parsing, and OAuth requests.

    :param query:
        Query string or query parameter dictionary.
    :returns:
        Filtered URL query parameter dictionary.
    """
    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
    return query_select(query, predicate) class InvalidHttpMethodError(OAuthError):
    """
    Raised when an invalid HTTP method is used.
    """
    pass class InvalidUrlError(OAuthError):
    """
    Raised when a specified URL is invalid for consumption by a routine.
    """
    pass class InvalidOAuthParametersError(OAuthError):
    """
    Raised when invalid protocol parameters are detected.
    """
    pass class InvalidAuthorizationHeaderError(OAuthError):
    """
    Raised when an invalid Authorization header is detected.
    """
    pass
    from itertools import imap as map
from mom.builtins import b
from mom.codec import decimal_encode, \
    base64_encode, base64_decode, base58_encode
from mom.codec.text import utf8_encode, utf8_decode_if_bytes, \
    utf8_encode_if_unicode
from mom.security.hash import hmac_sha1_base64_digest, sha1_digest
from mom.security.random import \
    generate_random_bits, \
    generate_random_hex_string
from pyoauth.constants import \
    SYMBOL_INVERTED_DOUBLE_QUOTE, \
    SYMBOL_EQUAL, \
    OAUTH_AUTH_SCHEME_PATTERN, \
    SYMBOL_EMPTY_BYTES, \
    OAUTH_PARAM_SIGNATURE, \
    SYMBOL_AMPERSAND, OAUTH_PARAM_REALM
from pyoauth._compat import urlunparse
from pyoauth.http import HTTP_METHODS
from pyoauth.url import percent_encode, percent_decode, \
    urlencode_sl, urlencode_s, urlparse_normalized, \
    request_query_remove_non_oauth, query_remove_oauth
from pyoauth.error import InvalidHttpMethodError, \
    InvalidUrlError, \
    InvalidOAuthParametersError, \
    InvalidAuthorizationHeaderError
    from mom.security.rsa import parse_private_key
    from mom.security.rsa import parse_public_key
import re
import time

    :param client_shared_secret:
        Client (consumer) shared secret.
    :param token_shared_secret:
        Token/temporary credentials shared secret if available.
    :param _percent_encode:
        (DEBUG)

        Must be ``True`` to be compatible with OAuth 1.0 RFC5849 & OAuth 1.0a;
        We have added this parameter to enable better debugging by the
        signature verification routines. If this is set to ``False``, the
        signature elements will not be percent-encoded before the plaintext
        signature is generated.
    :returns:
        PLAINTEXT signature.
    """
    client_shared_secret = client_shared_secret or SYMBOL_EMPTY_BYTES
    token_shared_secret = token_shared_secret or SYMBOL_EMPTY_BYTES
    if _percent_encode:
        return SYMBOL_AMPERSAND.join(map(percent_encode,
                            (client_shared_secret, token_shared_secret)))
    else:
        # User clients can forget to do this and this has been fixed
        # by OAuth 1.0a, so we use this piece of code to detect whether
        # the user's OAuth client library complies with the specification
        # when in debugging mode.
        return SYMBOL_AMPERSAND.join((client_shared_secret,
                                      token_shared_secret))


def generate_base_string(method, url, oauth_params):
    """
    Calculates a signature base string based on the URL, method, and
    oauth parameters.

    Any query parameter by the name "oauth_signature" will be excluded
    from the base string.

    :see: Signature base string
    (http://tools.ietf.org/html/rfc5849#section-3.4.1)

    :param method:
        HTTP request method.
    :param url:
        The URL. If this includes a query string, query parameters are first
        extracted and encoded as well. All protocol-specific parameters
        will be ignored from the query string.
    :param oauth_params:
        Protocol-specific parameters must be specified in this dictionary.
        All non-protocol parameters will be ignored.
    :returns:
        Base string.
    """
    allowed_methods = HTTP_METHODS
    method_normalized = method.upper()
    if method_normalized not in allowed_methods:
        raise InvalidHttpMethodError(
            "Method must be one of the HTTP methods %s: "\
            "got `%s` instead" % (allowed_methods, method))
    if not url:
        raise InvalidUrlError("URL must be specified: got `%r`" % url)
    if not isinstance(oauth_params, dict):
        raise InvalidOAuthParametersError("Dictionary required: got `%r`" %
                                          oauth_params)

    scheme, netloc, path, matrix_params, query, _ = urlparse_normalized(url)
    query_string = generate_base_string_query(query, oauth_params)
    normalized_url = urlunparse((
        scheme,
        netloc,
        path,
        matrix_params,
        None,
        None
    ))
    return SYMBOL_AMPERSAND.join(map(percent_encode,
                           (method_normalized, normalized_url, query_string)))


def generate_base_string_query(url_query, oauth_params):
    """
    Serializes URL query parameters and OAuth protocol parameters into a valid
    OAuth base string URI query string.

    :see: Parameter Normalization
          (http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2)
    :param url_query:
        A dictionary or string of URL query parameters. Any parameters starting
        with ``oauth_`` will be ignored.
    :param oauth_params:
        A dictionary or string of protocol-specific query parameters. Any
        parameter names that do not begin with ``oauth_`` will be excluded
        from the normalized query string. ``oauth_signature``,
        ``oauth_consumer_secret``, and ``oauth_token_secret`` are also
        specifically excluded.
    :returns:
        Normalized string of query parameters.
    """
    url_query = query_remove_oauth(url_query)
    oauth_params = request_query_remove_non_oauth(oauth_params)

    query_d = {}
    query_d.update(url_query)
    query_d.update(oauth_params)

    # Now encode the parameters, while ignoring 'oauth_signature' and obviously,
    # the secrets from the entire list of parameters.
    def allow_func(name, _):
        """Allows only protocol parameters that must be included into
        the signature.

        :param name:
            The name of the parameter.
        :returns:
            ``True`` if the parameter can be included; ``False`` otherwise.
        """
        return name not in (OAUTH_PARAM_SIGNATURE,
                            #OAUTH_PARAM_CONSUMER_SECRET, # Sanitized above.
                            #OAUTH_PARAM_TOKEN_SECRET,    # Sanitized above.
                            )

    query = urlencode_s(query_d, allow_func)