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 / base.py @ 564
History | View | Annotate | Download (10.1 KB)
1 |
# -*- coding: utf-8 -*-
|
---|---|
2 |
"""
|
3 |
oauthlib.oauth1.rfc5849.endpoints.base
|
4 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
5 |
|
6 |
This module is an implementation of various logic needed
|
7 |
for signing and checking OAuth 1.0 RFC 5849 requests.
|
8 |
"""
|
9 |
from __future__ import absolute_import, unicode_literals |
10 |
|
11 |
import time |
12 |
|
13 |
from oauthlib.common import Request, generate_token |
14 |
|
15 |
from .. import signature, utils, errors
|
16 |
from .. import CONTENT_TYPE_FORM_URLENCODED
|
17 |
from .. import SIGNATURE_HMAC, SIGNATURE_RSA
|
18 |
from .. import SIGNATURE_TYPE_AUTH_HEADER
|
19 |
from .. import SIGNATURE_TYPE_QUERY
|
20 |
from .. import SIGNATURE_TYPE_BODY
|
21 |
|
22 |
|
23 |
class BaseEndpoint(object): |
24 |
|
25 |
def __init__(self, request_validator, token_generator=None): |
26 |
self.request_validator = request_validator
|
27 |
self.token_generator = token_generator or generate_token |
28 |
|
29 |
def _get_signature_type_and_params(self, request): |
30 |
"""Extracts parameters from query, headers and body. Signature type
|
31 |
is set to the source in which parameters were found.
|
32 |
"""
|
33 |
# Per RFC5849, only the Authorization header may contain the 'realm'
|
34 |
# optional parameter.
|
35 |
header_params = signature.collect_parameters(headers=request.headers, |
36 |
exclude_oauth_signature=False, with_realm=True) |
37 |
body_params = signature.collect_parameters(body=request.body, |
38 |
exclude_oauth_signature=False)
|
39 |
query_params = signature.collect_parameters(uri_query=request.uri_query, |
40 |
exclude_oauth_signature=False)
|
41 |
|
42 |
params = [] |
43 |
params.extend(header_params) |
44 |
params.extend(body_params) |
45 |
params.extend(query_params) |
46 |
signature_types_with_oauth_params = list(filter(lambda s: s[2], ( |
47 |
(SIGNATURE_TYPE_AUTH_HEADER, params, |
48 |
utils.filter_oauth_params(header_params)), |
49 |
(SIGNATURE_TYPE_BODY, params, |
50 |
utils.filter_oauth_params(body_params)), |
51 |
(SIGNATURE_TYPE_QUERY, params, |
52 |
utils.filter_oauth_params(query_params)) |
53 |
))) |
54 |
|
55 |
if len(signature_types_with_oauth_params) > 1: |
56 |
found_types = [s[0] for s in signature_types_with_oauth_params] |
57 |
raise errors.InvalidRequestError(
|
58 |
description=('oauth_ params must come from only 1 signature'
|
59 |
'type but were found in %s',
|
60 |
', '.join(found_types)))
|
61 |
|
62 |
try:
|
63 |
signature_type, params, oauth_params = signature_types_with_oauth_params[ |
64 |
0]
|
65 |
except IndexError: |
66 |
raise errors.InvalidRequestError(
|
67 |
description='Missing mandatory OAuth parameters.')
|
68 |
|
69 |
return signature_type, params, oauth_params
|
70 |
|
71 |
def _create_request(self, uri, http_method, body, headers): |
72 |
# Only include body data from x-www-form-urlencoded requests
|
73 |
headers = headers or {}
|
74 |
if ("Content-Type" in headers and |
75 |
CONTENT_TYPE_FORM_URLENCODED in headers["Content-Type"]): |
76 |
request = Request(uri, http_method, body, headers) |
77 |
else:
|
78 |
request = Request(uri, http_method, '', headers)
|
79 |
|
80 |
signature_type, params, oauth_params = ( |
81 |
self._get_signature_type_and_params(request))
|
82 |
|
83 |
# The server SHOULD return a 400 (Bad Request) status code when
|
84 |
# receiving a request with duplicated protocol parameters.
|
85 |
if len(dict(oauth_params)) != len(oauth_params): |
86 |
raise errors.InvalidRequestError(
|
87 |
description='Duplicate OAuth1 entries.')
|
88 |
|
89 |
oauth_params = dict(oauth_params)
|
90 |
request.signature = oauth_params.get('oauth_signature')
|
91 |
request.client_key = oauth_params.get('oauth_consumer_key')
|
92 |
request.resource_owner_key = oauth_params.get('oauth_token')
|
93 |
request.nonce = oauth_params.get('oauth_nonce')
|
94 |
request.timestamp = oauth_params.get('oauth_timestamp')
|
95 |
request.redirect_uri = oauth_params.get('oauth_callback')
|
96 |
request.verifier = oauth_params.get('oauth_verifier')
|
97 |
request.signature_method = oauth_params.get('oauth_signature_method')
|
98 |
request.realm = dict(params).get('realm') |
99 |
request.oauth_params = oauth_params |
100 |
|
101 |
# Parameters to Client depend on signature method which may vary
|
102 |
# for each request. Note that HMAC-SHA1 and PLAINTEXT share parameters
|
103 |
request.params = [(k, v) for k, v in params if k != "oauth_signature"] |
104 |
|
105 |
if 'realm' in request.headers.get('Authorization', ''): |
106 |
request.params = [(k, v) |
107 |
for k, v in request.params if k != "realm"] |
108 |
|
109 |
return request
|
110 |
|
111 |
def _check_transport_security(self, request): |
112 |
# TODO: move into oauthlib.common from oauth2.utils
|
113 |
if (self.request_validator.enforce_ssl and |
114 |
not request.uri.lower().startswith("https://")): |
115 |
raise errors.InsecureTransportError()
|
116 |
|
117 |
def _check_mandatory_parameters(self, request): |
118 |
# The server SHOULD return a 400 (Bad Request) status code when
|
119 |
# receiving a request with missing parameters.
|
120 |
if not all((request.signature, request.client_key, |
121 |
request.nonce, request.timestamp, |
122 |
request.signature_method)): |
123 |
raise errors.InvalidRequestError(
|
124 |
description='Missing mandatory OAuth parameters.')
|
125 |
|
126 |
# OAuth does not mandate a particular signature method, as each
|
127 |
# implementation can have its own unique requirements. Servers are
|
128 |
# free to implement and document their own custom methods.
|
129 |
# Recommending any particular method is beyond the scope of this
|
130 |
# specification. Implementers should review the Security
|
131 |
# Considerations section (`Section 4`_) before deciding on which
|
132 |
# method to support.
|
133 |
# .. _`Section 4`: http://tools.ietf.org/html/rfc5849#section-4
|
134 |
if (not request.signature_method in |
135 |
self.request_validator.allowed_signature_methods):
|
136 |
raise errors.InvalidSignatureMethodError(
|
137 |
description="Invalid signature, %s not in %r." % (
|
138 |
request.signature_method, |
139 |
self.request_validator.allowed_signature_methods))
|
140 |
|
141 |
# Servers receiving an authenticated request MUST validate it by:
|
142 |
# If the "oauth_version" parameter is present, ensuring its value is
|
143 |
# "1.0".
|
144 |
if ('oauth_version' in request.oauth_params and |
145 |
request.oauth_params['oauth_version'] != '1.0'): |
146 |
raise errors.InvalidRequestError(
|
147 |
description='Invalid OAuth version.')
|
148 |
|
149 |
# The timestamp value MUST be a positive integer. Unless otherwise
|
150 |
# specified by the server's documentation, the timestamp is expressed
|
151 |
# in the number of seconds since January 1, 1970 00:00:00 GMT.
|
152 |
if len(request.timestamp) != 10: |
153 |
raise errors.InvalidRequestError(
|
154 |
description='Invalid timestamp size')
|
155 |
|
156 |
try:
|
157 |
ts = int(request.timestamp)
|
158 |
|
159 |
except ValueError: |
160 |
raise errors.InvalidRequestError(
|
161 |
description='Timestamp must be an integer.')
|
162 |
|
163 |
else:
|
164 |
# To avoid the need to retain an infinite number of nonce values for
|
165 |
# future checks, servers MAY choose to restrict the time period after
|
166 |
# which a request with an old timestamp is rejected.
|
167 |
if abs(time.time() - ts) > self.request_validator.timestamp_lifetime: |
168 |
raise errors.InvalidRequestError(
|
169 |
description=('Timestamp given is invalid, differ from '
|
170 |
'allowed by over %s seconds.' % (
|
171 |
self.request_validator.timestamp_lifetime)))
|
172 |
|
173 |
# Provider specific validation of parameters, used to enforce
|
174 |
# restrictions such as character set and length.
|
175 |
if not self.request_validator.check_client_key(request.client_key): |
176 |
raise errors.InvalidRequestError(
|
177 |
description='Invalid client key format.')
|
178 |
|
179 |
if not self.request_validator.check_nonce(request.nonce): |
180 |
raise errors.InvalidRequestError(
|
181 |
description='Invalid nonce format.')
|
182 |
|
183 |
def _check_signature(self, request, is_token_request=False): |
184 |
# ---- RSA Signature verification ----
|
185 |
if request.signature_method == SIGNATURE_RSA:
|
186 |
# The server verifies the signature per `[RFC3447] section 8.2.2`_
|
187 |
# .. _`[RFC3447] section 8.2.2`: http://tools.ietf.org/html/rfc3447#section-8.2.1
|
188 |
rsa_key = self.request_validator.get_rsa_key(
|
189 |
request.client_key, request) |
190 |
valid_signature = signature.verify_rsa_sha1(request, rsa_key) |
191 |
|
192 |
# ---- HMAC or Plaintext Signature verification ----
|
193 |
else:
|
194 |
# Servers receiving an authenticated request MUST validate it by:
|
195 |
# Recalculating the request signature independently as described in
|
196 |
# `Section 3.4`_ and comparing it to the value received from the
|
197 |
# client via the "oauth_signature" parameter.
|
198 |
# .. _`Section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
|
199 |
client_secret = self.request_validator.get_client_secret(
|
200 |
request.client_key, request) |
201 |
resource_owner_secret = None
|
202 |
if request.resource_owner_key:
|
203 |
if is_token_request:
|
204 |
resource_owner_secret = self.request_validator.get_request_token_secret(
|
205 |
request.client_key, request.resource_owner_key, request) |
206 |
else:
|
207 |
resource_owner_secret = self.request_validator.get_access_token_secret(
|
208 |
request.client_key, request.resource_owner_key, request) |
209 |
|
210 |
if request.signature_method == SIGNATURE_HMAC:
|
211 |
valid_signature = signature.verify_hmac_sha1(request, |
212 |
client_secret, resource_owner_secret) |
213 |
else:
|
214 |
valid_signature = signature.verify_plaintext(request, |
215 |
client_secret, resource_owner_secret) |
216 |
return valid_signature
|