summaryrefslogtreecommitdiff
path: root/oauthlib/oauth2/draft25/utils.py
blob: 48b4ea1d11fab08005ffadeb15aabcbbf71683fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
oauthlib.utils
~~~~~~~~~~~~~~

This module contains utility methods used by various parts of the OAuth 2 spec.
"""

import random
import string
import time
import urllib
from urlparse import urlparse, urlunparse, parse_qsl

UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
    string.digits.decode('ascii'))

def add_params_to_qs(query, params):
    """Extend a query with a list of two-tuples.

    :param query: Query string.
    :param params: List of two-tuples.
    :return: extended query
    """
    queryparams = parse_qsl(query, keep_blank_values=True)
    queryparams.extend(params)
    return urlencode(queryparams)


def add_params_to_uri(uri, params):
    """Add a list of two-tuples to the uri query components.

    :param uri: Full URI.
    :param params: List of two-tuples.
    :return: uri with extended query
    """
    sch, net, path, par, query, fra = urlparse(uri)
    query = add_params_to_qs(query, params)
    return urlunparse((sch, net, path, par, query, fra))


def escape(u):
    """Escape a string in an OAuth-compatible fashion.

    Per `section 3.6`_ of the spec.

    .. _`section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6

    """
    if not isinstance(u, unicode):
        raise ValueError('Only unicode objects are escapable.')
    return urllib.quote(u.encode('utf-8'), safe='~')


def generate_nonce():
    """Generate pseudorandom nonce that is unlikely to repeat.

    Per `section 3.2.1`_ of the MAC Access Authentication spec.

    A random 64-bit number is appended to the epoch timestamp for both
    randomness and to decrease the likelihood of collisions.

    .. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
    """
    return unicode(unicode(random.getrandbits(64)) + generate_timestamp())


def generate_timestamp():
    """Get seconds since epoch (UTC).

    Per `section 3.2.1`_ of the MAC Access Authentication spec.

    .. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
    """
    return unicode(int(time.time()))


def generate_token(length=20, chars=UNICODE_ASCII_CHARACTER_SET):
    """Generates a generic OAuth 2 token

    According to `section 1.4`_ and `section 1.5` of the spec, the method of token
    construction is undefined. This implementation is simply a random selection
    of `length` choices from `chars`. SystemRandom is used since it provides
    higher entropy than random.choice. 

    .. _`section 1.4`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-1.4
    .. _`section 1.5`: http://tools.ietf.org/html/draft-ietf-oauth-v2-25#section-1.5
    """
    rand = random.SystemRandom()
    return u''.join(rand.choice(chars) for x in range(length))


def host_from_uri(uri):
    """Extract hostname and port from URI.

    Will use default port for HTTP and HTTPS if none is present in the URI. 

    >>> host_from_uri(u'https://www.example.com/path?query')
    u'www.example.com', u'443'
    >>> host_from_uri(u'http://www.example.com:8080/path?query')
    u'www.example.com', u'8080'

    :param uri: Full URI.
    :param http_method: HTTP request method.
    :return: hostname, port
    """
    default_ports = {
        u'HTTP' : u'80',
        u'HTTPS' : u'443',
    }

    sch, netloc, path, par, query, fra = urlparse(uri)
    if u':' in netloc:
        netloc, port = netloc.split(u':', 1)
    else:
        port = default_ports.get(sch.upper())

    return netloc, port


def urlencode(query):
    """Encode a sequence of two-element tuples or dictionary into a URL query string.

    Operates using an OAuth-safe escape() method, in contrast to urllib.urlenocde.
    """
    # Convert dictionaries to list of tuples
    if isinstance(query, dict):
        query = query.items()
    return "&".join(['='.join([escape(k), escape(v)]) for k, v in query])