1# -*- coding: utf-8 -*- 
    2""" 
    3oauthlib.oauth1.rfc5849.endpoints.resource 
    4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    5 
    6This module is an implementation of the resource protection provider logic of 
    7OAuth 1.0 RFC 5849. 
    8""" 
    9import logging 
    10 
    11from .. import errors 
    12from .base import BaseEndpoint 
    13 
    14log = logging.getLogger(__name__) 
    15 
    16 
    17class ResourceEndpoint(BaseEndpoint): 
    18 
    19    """An endpoint responsible for protecting resources. 
    20 
    21    Typical use is to instantiate with a request validator and invoke the 
    22    ``validate_protected_resource_request`` in a decorator around a view 
    23    function. If the request is valid, invoke and return the response of the 
    24    view. If invalid create and return an error response directly from the 
    25    decorator. 
    26 
    27    See :doc:`/oauth1/validator` for details on which validator methods to implement 
    28    for this endpoint. 
    29 
    30    An example decorator:: 
    31 
    32        from functools import wraps 
    33        from your_validator import your_validator 
    34        from oauthlib.oauth1 import ResourceEndpoint 
    35        endpoint = ResourceEndpoint(your_validator) 
    36 
    37        def require_oauth(realms=None): 
    38            def decorator(f): 
    39                @wraps(f) 
    40                def wrapper(request, *args, **kwargs): 
    41                    v, r = provider.validate_protected_resource_request( 
    42                            request.url, 
    43                            http_method=request.method, 
    44                            body=request.data, 
    45                            headers=request.headers, 
    46                            realms=realms or []) 
    47                    if v: 
    48                        return f(*args, **kwargs) 
    49                    else: 
    50                        return abort(403) 
    51    """ 
    52 
    53    def validate_protected_resource_request(self, uri, http_method='GET', 
    54                                            body=None, headers=None, realms=None): 
    55        """Create a request token response, with a new request token if valid. 
    56 
    57        :param uri: The full URI of the token request. 
    58        :param http_method: A valid HTTP verb, i.e. GET, POST, PUT, HEAD, etc. 
    59        :param body: The request body as a string. 
    60        :param headers: The request headers as a dict. 
    61        :param realms: A list of realms the resource is protected under. 
    62                       This will be supplied to the ``validate_realms`` 
    63                       method of the request validator. 
    64        :returns: A tuple of 2 elements. 
    65                  1. True if valid, False otherwise. 
    66                  2. An oauthlib.common.Request object. 
    67        """ 
    68        try: 
    69            request = self._create_request(uri, http_method, body, headers) 
    70        except errors.OAuth1Error: 
    71            return False, None 
    72 
    73        try: 
    74            self._check_transport_security(request) 
    75            self._check_mandatory_parameters(request) 
    76        except errors.OAuth1Error: 
    77            return False, request 
    78 
    79        if not request.resource_owner_key: 
    80            return False, request 
    81 
    82        if not self.request_validator.check_access_token( 
    83                request.resource_owner_key): 
    84            return False, request 
    85 
    86        if not self.request_validator.validate_timestamp_and_nonce( 
    87                request.client_key, request.timestamp, request.nonce, request, 
    88                access_token=request.resource_owner_key): 
    89            return False, request 
    90 
    91        # The server SHOULD return a 401 (Unauthorized) status code when 
    92        # receiving a request with invalid client credentials. 
    93        # Note: This is postponed in order to avoid timing attacks, instead 
    94        # a dummy client is assigned and used to maintain near constant 
    95        # time request verification. 
    96        # 
    97        # Note that early exit would enable client enumeration 
    98        valid_client = self.request_validator.validate_client_key( 
    99            request.client_key, request) 
    100        if not valid_client: 
    101            request.client_key = self.request_validator.dummy_client 
    102 
    103        # The server SHOULD return a 401 (Unauthorized) status code when 
    104        # receiving a request with invalid or expired token. 
    105        # Note: This is postponed in order to avoid timing attacks, instead 
    106        # a dummy token is assigned and used to maintain near constant 
    107        # time request verification. 
    108        # 
    109        # Note that early exit would enable resource owner enumeration 
    110        valid_resource_owner = self.request_validator.validate_access_token( 
    111            request.client_key, request.resource_owner_key, request) 
    112        if not valid_resource_owner: 
    113            request.resource_owner_key = self.request_validator.dummy_access_token 
    114 
    115        # Note that `realm`_ is only used in authorization headers and how 
    116        # it should be interpreted is not included in the OAuth spec. 
    117        # However they could be seen as a scope or realm to which the 
    118        # client has access and as such every client should be checked 
    119        # to ensure it is authorized access to that scope or realm. 
    120        # .. _`realm`: https://tools.ietf.org/html/rfc2617#section-1.2 
    121        # 
    122        # Note that early exit would enable client realm access enumeration. 
    123        # 
    124        # The require_realm indicates this is the first step in the OAuth 
    125        # workflow where a client requests access to a specific realm. 
    126        # This first step (obtaining request token) need not require a realm 
    127        # and can then be identified by checking the require_resource_owner 
    128        # flag and absence of realm. 
    129        # 
    130        # Clients obtaining an access token will not supply a realm and it will 
    131        # not be checked. Instead the previously requested realm should be 
    132        # transferred from the request token to the access token. 
    133        # 
    134        # Access to protected resources will always validate the realm but note 
    135        # that the realm is now tied to the access token and not provided by 
    136        # the client. 
    137        valid_realm = self.request_validator.validate_realms(request.client_key, 
    138                                                             request.resource_owner_key, request, uri=request.uri, 
    139                                                             realms=realms) 
    140 
    141        valid_signature = self._check_signature(request) 
    142 
    143        # log the results to the validator_log 
    144        # this lets us handle internal reporting and analysis 
    145        request.validator_log['client'] = valid_client 
    146        request.validator_log['resource_owner'] = valid_resource_owner 
    147        request.validator_log['realm'] = valid_realm 
    148        request.validator_log['signature'] = valid_signature 
    149 
    150        # We delay checking validity until the very end, using dummy values for 
    151        # calculations and fetching secrets/keys to ensure the flow of every 
    152        # request remains almost identical regardless of whether valid values 
    153        # have been supplied. This ensures near constant time execution and 
    154        # prevents malicious users from guessing sensitive information 
    155        v = all((valid_client, valid_resource_owner, valid_realm, 
    156                 valid_signature)) 
    157        if not v: 
    158            log.info("[Failure] request verification failed.") 
    159            log.info("Valid client: %s", valid_client) 
    160            log.info("Valid token: %s", valid_resource_owner) 
    161            log.info("Valid realm: %s", valid_realm) 
    162            log.info("Valid signature: %s", valid_signature) 
    163        return v, request