~launchpad-pqm/launchpad/devel

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
= Asking for a request token =

Our sample consumer (whose key is 'foobar123451432') asks Launchpad for
a request token which may later be exchanged for an access token.

    >>> import time
    >>> from urllib import urlencode
    >>> data = dict(
    ...     oauth_consumer_key='foobar123451432',
    ...     oauth_version='1.0',
    ...     oauth_signature_method='PLAINTEXT',
    ...     oauth_signature='&',
    ...     oauth_timestamp=time.time(),
    ...     oauth_nonce='4572616e48616d6d65724c61686176')
    >>> anon_browser.open(
    ...     'http://launchpad.dev/+request-token', data=urlencode(data))

    >>> print anon_browser.contents
    oauth_token=...&oauth_token_secret=...

The consumer can ask for a JSON representation of the request token,
which will also include information about the available permission
levels.

    >>> import simplejson
    >>> from lp.testing.pages import setupBrowser

    >>> json_browser = setupBrowser()
    >>> json_browser.addHeader('Accept', 'application/json')
    >>> json_browser.open(
    ...     'http://launchpad.dev/+request-token', data=urlencode(data))
    >>> token = simplejson.loads(json_browser.contents)
    >>> sorted(token.keys())
    ['access_levels', 'oauth_token', 'oauth_token_consumer',
     'oauth_token_secret']

    >>> sorted((level['value'], level['title'])
    ...        for level in token['access_levels'])
    [('READ_PRIVATE', 'Read Anything'),
     ('READ_PUBLIC', 'Read Non-Private Data'),
     ('UNAUTHORIZED', 'No Access'),
     ('WRITE_PRIVATE', 'Change Anything'),
     ('WRITE_PUBLIC', 'Change Non-Private Data')]

If the consumer key is not yet registered, we register it automatically
so that the application can proceed.

    >>> from zope.component import getUtility
    >>> from lp.testing import login, logout
    >>> from lp.services.oauth.interfaces import IOAuthConsumerSet
    >>> login('salgado@ubuntu.com')
    >>> print getUtility(IOAuthConsumerSet).getByKey('joe-feed-reader')
    None

    >>> logout()
    >>> data2 = data.copy()
    >>> data2['oauth_consumer_key'] = 'joe-feed-reader'
    >>> anon_browser.open(
    ...     'http://launchpad.dev/+request-token', data=urlencode(data2))

    >>> print anon_browser.contents
    oauth_token=...&oauth_token_secret=...

    >>> login('salgado@ubuntu.com')
    >>> getUtility(IOAuthConsumerSet).getByKey('joe-feed-reader')
    <...OAuthConsumer...
    >>> logout()

If the consumer key is empty, we respond with a 401 status.

    >>> data2 = data.copy()
    >>> data2['oauth_consumer_key'] = ''
    >>> print http(r"""
    ... GET /+request-token?%s HTTP/1.1
    ... Host: launchpad.dev
    ... """ % urlencode(data2))
    HTTP/1.1 401 Unauthorized
    ...
    WWW-Authenticate: OAuth realm="https://api.launchpad.net"
    <BLANKLINE>

The only signature method supported is PLAINTEXT, which consists of the
concatenated values of the consumer secret and token secret, separated
by a & character. That means, in our case, the signature should be only
an '&', since there's no token yet and the consumer secret is empty.

    >>> data['oauth_signature']
    '&'
    >>> data2 = data.copy()
    >>> data2['oauth_signature'] = '&somesecret'
    >>> print http(r"""
    ... GET /+request-token?%s HTTP/1.1
    ... Host: launchpad.dev
    ... """ % urlencode(data2))
    HTTP/1.1 401 Unauthorized
    ...

If the consumer tries to sign a request with a different method, it will
get a 400 response.

    >>> data2 = data.copy()
    >>> data2['oauth_signature_method'] = 'HMAC-SHA1'
    >>> print http(r"""
    ... GET /+request-token?%s HTTP/1.1
    ... Host: launchpad.dev
    ... """ % urlencode(data2))
    HTTP/1.1 400 Bad Request
    ...