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