Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / oauthlib / oauth1 / rfc5849 / endpoints / request_token.py @ 564

History | View | Annotate | Download (9.07 KB)

1
# -*- coding: utf-8 -*-
2
"""
3
oauthlib.oauth1.rfc5849.endpoints.request_token
4
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5

6
This module is an implementation of the request token provider logic of
7
OAuth 1.0 RFC 5849. It validates the correctness of request token requests,
8
creates and persists tokens as well as create the proper response to be
9
returned to the client.
10
"""
11
from __future__ import absolute_import, unicode_literals
12

    
13
import logging
14

    
15
from oauthlib.common import urlencode
16

    
17
from .base import BaseEndpoint
18
from .. import errors
19

    
20
log = logging.getLogger(__name__)
21

    
22

    
23
class RequestTokenEndpoint(BaseEndpoint):
24

    
25
    """An endpoint responsible for providing OAuth 1 request tokens.
26

27
    Typical use is to instantiate with a request validator and invoke the
28
    ``create_request_token_response`` from a view function. The tuple returned
29
    has all information necessary (body, status, headers) to quickly form
30
    and return a proper response. See :doc:`/oauth1/validator` for details on which
31
    validator methods to implement for this endpoint.
32
    """
33

    
34
    def create_request_token(self, request, credentials):
35
        """Create and save a new request token.
36

37
        :param request: An oauthlib.common.Request object.
38
        :param credentials: A dict of extra token credentials.
39
        :returns: The token as an urlencoded string.
40
        """
41
        token = {
42
            'oauth_token': self.token_generator(),
43
            'oauth_token_secret': self.token_generator(),
44
            'oauth_callback_confirmed': 'true'
45
        }
46
        token.update(credentials)
47
        self.request_validator.save_request_token(token, request)
48
        return urlencode(token.items())
49

    
50
    def create_request_token_response(self, uri, http_method='GET', body=None,
51
                                      headers=None, credentials=None):
52
        """Create a request token response, with a new request token if valid.
53

54
        :param uri: The full URI of the token request.
55
        :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
56
        :param body: The request body as a string.
57
        :param headers: The request headers as a dict.
58
        :param credentials: A list of extra credentials to include in the token.
59
        :returns: A tuple of 3 elements.
60
                  1. A dict of headers to set on the response.
61
                  2. The response body as a string.
62
                  3. The response status code as an integer.
63

64
        An example of a valid request::
65

66
            >>> from your_validator import your_validator
67
            >>> from oauthlib.oauth1 import RequestTokenEndpoint
68
            >>> endpoint = RequestTokenEndpoint(your_validator)
69
            >>> h, b, s = endpoint.create_request_token_response(
70
            ...     'https://your.provider/request_token?foo=bar',
71
            ...     headers={
72
            ...         'Authorization': 'OAuth realm=movies user, oauth_....'
73
            ...     },
74
            ...     credentials={
75
            ...         'my_specific': 'argument',
76
            ...     })
77
            >>> h
78
            {'Content-Type': 'application/x-www-form-urlencoded'}
79
            >>> b
80
            'oauth_token=lsdkfol23w54jlksdef&oauth_token_secret=qwe089234lkjsdf&oauth_callback_confirmed=true&my_specific=argument'
81
            >>> s
82
            200
83

84
        An response to invalid request would have a different body and status::
85

86
            >>> b
87
            'error=invalid_request&description=missing+callback+uri'
88
            >>> s
89
            400
90

91
        The same goes for an an unauthorized request:
92

93
            >>> b
94
            ''
95
            >>> s
96
            401
97
        """
98
        resp_headers = {'Content-Type': 'application/x-www-form-urlencoded'}
99
        try:
100
            request = self._create_request(uri, http_method, body, headers)
101
            valid, processed_request = self.validate_request_token_request(
102
                request)
103
            if valid:
104
                token = self.create_request_token(request, credentials or {})
105
                return resp_headers, token, 200
106
            else:
107
                return {}, None, 401
108
        except errors.OAuth1Error as e:
109
            return resp_headers, e.urlencoded, e.status_code
110

    
111
    def validate_request_token_request(self, request):
112
        """Validate a request token request.
113

114
        :param request: An oauthlib.common.Request object.
115
        :raises: OAuth1Error if the request is invalid.
116
        :returns: A tuple of 2 elements.
117
                  1. The validation result (True or False).
118
                  2. The request object.
119
        """
120
        self._check_transport_security(request)
121
        self._check_mandatory_parameters(request)
122

    
123
        if request.realm:
124
            request.realms = request.realm.split(' ')
125
        else:
126
            request.realms = self.request_validator.get_default_realms(
127
                request.client_key, request)
128
        if not self.request_validator.check_realms(request.realms):
129
            raise errors.InvalidRequestError(
130
                description='Invalid realm %s. Allowed are %r.' % (
131
                    request.realms, self.request_validator.realms))
132

    
133
        if not request.redirect_uri:
134
            raise errors.InvalidRequestError(
135
                description='Missing callback URI.')
136

    
137
        if not self.request_validator.validate_timestamp_and_nonce(
138
                request.client_key, request.timestamp, request.nonce, request,
139
                request_token=request.resource_owner_key):
140
            return False, request
141

    
142
        # The server SHOULD return a 401 (Unauthorized) status code when
143
        # receiving a request with invalid client credentials.
144
        # Note: This is postponed in order to avoid timing attacks, instead
145
        # a dummy client is assigned and used to maintain near constant
146
        # time request verification.
147
        #
148
        # Note that early exit would enable client enumeration
149
        valid_client = self.request_validator.validate_client_key(
150
            request.client_key, request)
151
        if not valid_client:
152
            request.client_key = self.request_validator.dummy_client
153

    
154
        # Note that `realm`_ is only used in authorization headers and how
155
        # it should be interepreted is not included in the OAuth spec.
156
        # However they could be seen as a scope or realm to which the
157
        # client has access and as such every client should be checked
158
        # to ensure it is authorized access to that scope or realm.
159
        # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2
160
        #
161
        # Note that early exit would enable client realm access enumeration.
162
        #
163
        # The require_realm indicates this is the first step in the OAuth
164
        # workflow where a client requests access to a specific realm.
165
        # This first step (obtaining request token) need not require a realm
166
        # and can then be identified by checking the require_resource_owner
167
        # flag and abscence of realm.
168
        #
169
        # Clients obtaining an access token will not supply a realm and it will
170
        # not be checked. Instead the previously requested realm should be
171
        # transferred from the request token to the access token.
172
        #
173
        # Access to protected resources will always validate the realm but note
174
        # that the realm is now tied to the access token and not provided by
175
        # the client.
176
        valid_realm = self.request_validator.validate_requested_realms(
177
            request.client_key, request.realms, request)
178

    
179
        # Callback is normally never required, except for requests for
180
        # a Temporary Credential as described in `Section 2.1`_
181
        # .._`Section 2.1`: http://tools.ietf.org/html/rfc5849#section-2.1
182
        valid_redirect = self.request_validator.validate_redirect_uri(
183
            request.client_key, request.redirect_uri, request)
184
        if not request.redirect_uri:
185
            raise NotImplementedError('Redirect URI must either be provided '
186
                                      'or set to a default during validation.')
187

    
188
        valid_signature = self._check_signature(request)
189

    
190
        # log the results to the validator_log
191
        # this lets us handle internal reporting and analysis
192
        request.validator_log['client'] = valid_client
193
        request.validator_log['realm'] = valid_realm
194
        request.validator_log['callback'] = valid_redirect
195
        request.validator_log['signature'] = valid_signature
196

    
197
        # We delay checking validity until the very end, using dummy values for
198
        # calculations and fetching secrets/keys to ensure the flow of every
199
        # request remains almost identical regardless of whether valid values
200
        # have been supplied. This ensures near constant time execution and
201
        # prevents malicious users from guessing sensitive information
202
        v = all((valid_client, valid_realm, valid_redirect, valid_signature))
203
        if not v:
204
            log.info("[Failure] request verification failed.")
205
            log.info("Valid client: %s.", valid_client)
206
            log.info("Valid realm: %s.", valid_realm)
207
            log.info("Valid callback: %s.", valid_redirect)
208
            log.info("Valid signature: %s.", valid_signature)
209
        return v, request