1# -*- coding: utf-8 -*-
2"""
3oauthlib.oauth1.rfc5849.endpoints.signature_only
4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6This module is an implementation of the signing logic of OAuth 1.0 RFC 5849.
7"""
8
9import logging
10
11from .. import errors
12from .base import BaseEndpoint
13
14log = logging.getLogger(__name__)
15
16
17class SignatureOnlyEndpoint(BaseEndpoint):
18
19 """An endpoint only responsible for verifying an oauth signature."""
20
21 def validate_request(self, uri, http_method='GET',
22 body=None, headers=None):
23 """Validate a signed OAuth request.
24
25 :param uri: The full URI of the token request.
26 :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc.
27 :param body: The request body as a string.
28 :param headers: The request headers as a dict.
29 :returns: A tuple of 2 elements.
30 1. True if valid, False otherwise.
31 2. An oauthlib.common.Request object.
32 """
33 try:
34 request = self._create_request(uri, http_method, body, headers)
35 except errors.OAuth1Error as err:
36 log.info(
37 'Exception caught while validating request, %s.' % err)
38 return False, None
39
40 try:
41 self._check_transport_security(request)
42 self._check_mandatory_parameters(request)
43 except errors.OAuth1Error as err:
44 log.info(
45 'Exception caught while validating request, %s.' % err)
46 return False, request
47
48 if not self.request_validator.validate_timestamp_and_nonce(
49 request.client_key, request.timestamp, request.nonce, request):
50 log.debug('[Failure] verification failed: timestamp/nonce')
51 return False, request
52
53 # The server SHOULD return a 401 (Unauthorized) status code when
54 # receiving a request with invalid client credentials.
55 # Note: This is postponed in order to avoid timing attacks, instead
56 # a dummy client is assigned and used to maintain near constant
57 # time request verification.
58 #
59 # Note that early exit would enable client enumeration
60 valid_client = self.request_validator.validate_client_key(
61 request.client_key, request)
62 if not valid_client:
63 request.client_key = self.request_validator.dummy_client
64
65 valid_signature = self._check_signature(request)
66
67 # log the results to the validator_log
68 # this lets us handle internal reporting and analysis
69 request.validator_log['client'] = valid_client
70 request.validator_log['signature'] = valid_signature
71
72 # We delay checking validity until the very end, using dummy values for
73 # calculations and fetching secrets/keys to ensure the flow of every
74 # request remains almost identical regardless of whether valid values
75 # have been supplied. This ensures near constant time execution and
76 # prevents malicious users from guessing sensitive information
77 v = all((valid_client, valid_signature))
78 if not v:
79 log.info("[Failure] request verification failed.")
80 log.info("Valid client: %s", valid_client)
81 log.info("Valid signature: %s", valid_signature)
82 return v, request