~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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
= OAuth pages =

The pages used in our implementation of the OAuth protocol.

== +authorize-token ==

This is the page where a user reviews (approving or declining) a
consumer's request to access Launchpad on his behalf.

    # Define some things we're going to use throughout this test.
    >>> from zope.component import getMultiAdapter
    >>> from lp.services.oauth.interfaces import (
    ...     IOAuthConsumerSet, OAuthPermission)
    >>> from lp.services.webapp.interfaces import ILaunchpadRoot
    >>> from lp.services.webapp.servers import LaunchpadTestRequest

    >>> consumer = getUtility(IOAuthConsumerSet).getByKey('launchpad-library')
    >>> root = getUtility(ILaunchpadRoot)
    >>> def get_view_with_fresh_token(form):
    ...     token = consumer.newRequestToken()
    ...     form.update({'oauth_token': token.key})
    ...     request = LaunchpadTestRequest(form=form)
    ...     login('salgado@ubuntu.com', request)
    ...     view = getMultiAdapter((root, request), name="+authorize-token")
    ...     view.initialize()
    ...     return view, token

    >>> from BeautifulSoup import BeautifulSoup, SoupStrainer
    >>> def print_hidden_fields(html):
    ...     soup = BeautifulSoup(html, SoupStrainer(attrs={'type': 'hidden'}))
    ...     for tag in soup.findAll(attrs={'type': 'hidden'}):
    ...         if tag.attrMap['value']:
    ...             print tag.attrMap['name'], tag.attrMap['value']

When the client doesn't specify a duration, the resulting request
token will have no expiration date set.

    >>> from datetime import datetime
    >>> view, token = get_view_with_fresh_token({})
    >>> view.reviewToken(OAuthPermission.READ_PRIVATE, None)
    >>> print token.date_expires
    None

When the client specifies a duration, the resulting request
token will have an appropriate expiration date set.

    >>> from datetime import datetime
    >>> import pytz
    >>> from lp.services.oauth.browser import (
    ...     TemporaryIntegrations)
    >>> view, token = get_view_with_fresh_token({})
    >>> view.reviewToken(
    ...     OAuthPermission.READ_PRIVATE, TemporaryIntegrations.HOUR)
    >>> token.date_expires > datetime.now(pytz.timezone('UTC'))
    True

When the consumer doesn't specify a context, the token will not have a
context either.

    >>> view, token = get_view_with_fresh_token({})
    >>> print view.token_context
    None

    # Note that the token is stored in a hidden field in the HTML so that
    # it's submitted together with the user's chosen permission.
    >>> print_hidden_fields(view())
    loggingout...
    oauth_token...

    >>> view.reviewToken(OAuthPermission.READ_PRIVATE, None)
    >>> token.person.name
    u'salgado'
    >>> print token.context
    None
    >>> token.permission
    <DBItem OAuthPermission.READ_PRIVATE...
    >>> print token.is_reviewed
    True

The context can be a product, and if it's specified it will be carried
over to the token once it's reviewed.

    >>> view, token = get_view_with_fresh_token({'lp.context': 'firefox'})
    >>> view.token_context.name
    u'firefox'

    # The context is also stored in a hidden field in the HTML so that
    # it's submitted together with the user's chosen permission.
    >>> print_hidden_fields(view())
    loggingout...
    oauth_token...
    lp.context firefox

    >>> view.reviewToken(OAuthPermission.READ_PUBLIC, None)
    >>> token.context.name
    u'firefox'

Likewise for a project.

    >>> view, token = get_view_with_fresh_token({'lp.context': 'mozilla'})
    >>> view.token_context.name
    u'mozilla'

    # The context is also stored in a hidden field in the HTML so that
    # it's submitted together with the user's chosen permission.
    >>> print_hidden_fields(view())
    loggingout...
    oauth_token...
    lp.context mozilla

    >>> view.reviewToken(OAuthPermission.READ_PUBLIC, None)
    >>> token.context.name
    u'mozilla'

And a distribution.

    >>> view, token = get_view_with_fresh_token({'lp.context': 'ubuntu'})
    >>> view.token_context.name
    u'ubuntu'

    # The context is also stored in a hidden field in the HTML so that
    # it's submitted together with the user's chosen permission.
    >>> print_hidden_fields(view())
    loggingout...
    oauth_token...
    lp.context ubuntu

    >>> view.reviewToken(OAuthPermission.READ_PUBLIC, None)
    >>> token.context.name
    u'ubuntu'

If the consumer wants to access only things related to a distribution's
package, it must specify the distribution and the package's name.

    >>> view, token = get_view_with_fresh_token(
    ...     {'lp.context': 'ubuntu/evolution'})
    >>> view.token_context.title
    u'...evolution... package in Ubuntu'

    # The context is also stored in a hidden field in the HTML so that
    # it's submitted together with the user's chosen permission.
    >>> print_hidden_fields(view())
    loggingout...
    oauth_token...
    lp.context ubuntu/evolution

    >>> view.reviewToken(OAuthPermission.READ_PUBLIC, None)
    >>> token.context.title
    u'...evolution... package in Ubuntu'

An error is raised if the context is not found.

    >>> view, token = get_view_with_fresh_token({'lp.context': 'fooooo'})
    Traceback (most recent call last):
    ...
    UnexpectedFormData: ...

Or if the user gives us a package in a non-existing distribution.

    >>> view, token = get_view_with_fresh_token(
    ...     {'lp.context': 'firefox/evolution'})
    Traceback (most recent call last):
    ...
    UnexpectedFormData: ...