base.py 11 KB

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