summaryrefslogtreecommitdiff
path: root/oauthlib/oauth1
diff options
context:
space:
mode:
authorIb Lundgren <ib.lundgren@gmail.com>2013-06-18 21:21:59 +0100
committerIb Lundgren <ib.lundgren@gmail.com>2013-06-18 21:21:59 +0100
commit2261e99ae65fafd03aed337bf100faa6942108e3 (patch)
tree54135b5d7c30d7b8ca055fa92892aec645f53c61 /oauthlib/oauth1
parentb9ef07ca18ec56ef8dfe24344c81ef1ba3a59e34 (diff)
downloadoauthlib-2261e99ae65fafd03aed337bf100faa6942108e3.tar.gz
Remove old OAuth 1 provider and make way for the new one. #95.
Diffstat (limited to 'oauthlib/oauth1')
-rw-r--r--oauthlib/oauth1/rfc5849/__init__.py814
-rw-r--r--oauthlib/oauth1/rfc5849/endpoints/__init__.py8
2 files changed, 9 insertions, 813 deletions
diff --git a/oauthlib/oauth1/rfc5849/__init__.py b/oauthlib/oauth1/rfc5849/__init__.py
index a009016..42683c7 100644
--- a/oauthlib/oauth1/rfc5849/__init__.py
+++ b/oauthlib/oauth1/rfc5849/__init__.py
@@ -13,7 +13,6 @@ import logging
log = logging.getLogger("oauthlib")
import sys
-import time
try:
import urlparse
except ImportError:
@@ -26,7 +25,7 @@ else:
from oauthlib.common import Request, urlencode, generate_nonce
from oauthlib.common import generate_timestamp, to_unicode
-from . import parameters, signature, utils
+from . import parameters, signature
SIGNATURE_HMAC = "HMAC-SHA1"
SIGNATURE_RSA = "RSA-SHA1"
@@ -294,814 +293,3 @@ class Client(object):
new_headers[k.encode(self.decoding)] = v.encode(self.decoding)
headers = new_headers
return uri, headers, body
-
-
-class Server(object):
- """A server base class used to verify OAuth 1.0 RFC 5849 requests
-
- OAuth providers should inherit from Server and implement the methods
- and properties outlined below. Further details are provided in the
- documentation for each method and property.
-
- Methods used to check the format of input parameters. Common tests include
- length, character set, membership, range or pattern. These tests are
- referred to as `whitelisting or blacklisting`_. Whitelisting is better
- but blacklisting can be usefull to spot malicious activity.
- The following have methods a default implementation:
-
- - check_client_key
- - check_request_token
- - check_access_token
- - check_nonce
- - check_verifier
- - check_realm
-
- The methods above default to whitelist input parameters, checking that they
- are alphanumerical and between a minimum and maximum length. Rather than
- overloading the methods a few properties can be used to configure these
- methods.
-
- * @safe_characters -> (character set)
- * @client_key_length -> (min, max)
- * @request_token_length -> (min, max)
- * @access_token_length -> (min, max)
- * @nonce_length -> (min, max)
- * @verifier_length -> (min, max)
- * @realms -> [list, of, realms]
-
- Methods used to validate input parameters. These checks usually hit either
- persistent or temporary storage such as databases or the filesystem. See
- each methods documentation for detailed usage.
- The following methods must be implemented:
-
- - validate_client_key
- - validate_request_token
- - validate_access_token
- - validate_timestamp_and_nonce
- - validate_redirect_uri
- - validate_requested_realm
- - validate_realm
- - validate_verifier
-
- Method used to retrieve sensitive information from storage.
- The following methods must be implemented:
-
- - get_client_secret
- - get_request_token_secret
- - get_access_token_secret
- - get_rsa_key
-
- To prevent timing attacks it is necessary to not exit early even if the
- client key or resource owner key is invalid. Instead dummy values should
- be used during the remaining verification process. It is very important
- that the dummy client and token are valid input parameters to the methods
- get_client_secret, get_rsa_key and get_(access/request)_token_secret and
- that the running time of those methods when given a dummy value remain
- equivalent to the running time when given a valid client/resource owner.
- The following properties must be implemented:
-
- * @dummy_client
- * @dummy_request_token
- * @dummy_access_token
-
- Example implementations have been provided, note that the database used is
- a simple dictionary and serves only an illustrative purpose. Use whichever
- database suits your project and how to access it is entirely up to you.
- The methods are introduced in an order which should make understanding
- their use more straightforward and as such it could be worth reading what
- follows in chronological order.
-
- .. _`whitelisting or blacklisting`: http://www.schneier.com/blog/archives/2011/01/whitelisting_vs.html
- """
-
- def __init__(self):
- pass
-
- @property
- def allowed_signature_methods(self):
- return SIGNATURE_METHODS
-
- @property
- def safe_characters(self):
- return set(utils.UNICODE_ASCII_CHARACTER_SET)
-
- @property
- def client_key_length(self):
- return 20, 30
-
- @property
- def request_token_length(self):
- return 20, 30
-
- @property
- def access_token_length(self):
- return 20, 30
-
- @property
- def timestamp_lifetime(self):
- return 600
-
- @property
- def nonce_length(self):
- return 20, 30
-
- @property
- def verifier_length(self):
- return 20, 30
-
- @property
- def realms(self):
- return []
-
- @property
- def enforce_ssl(self):
- return True
-
- def check_client_key(self, client_key):
- """Check that the client key only contains safe characters
- and is no shorter than lower and no longer than upper.
- """
- lower, upper = self.client_key_length
- return (set(client_key) <= self.safe_characters and
- lower <= len(client_key) <= upper)
-
- def check_request_token(self, request_token):
- """Checks that the request token contains only safe characters
- and is no shorter than lower and no longer than upper.
- """
- lower, upper = self.request_token_length
- return (set(request_token) <= self.safe_characters and
- lower <= len(request_token) <= upper)
-
- def check_access_token(self, request_token):
- """Checks that the token contains only safe characters
- and is no shorter than lower and no longer than upper.
- """
- lower, upper = self.access_token_length
- return (set(request_token) <= self.safe_characters and
- lower <= len(request_token) <= upper)
-
- def check_nonce(self, nonce):
- """Checks that the nonce only contains only safe characters
- and is no shorter than lower and no longer than upper.
- """
- lower, upper = self.nonce_length
- return (set(nonce) <= self.safe_characters and
- lower <= len(nonce) <= upper)
-
- def check_verifier(self, verifier):
- """Checks that the verifier contains only safe characters
- and is no shorter than lower and no longer than upper.
- """
- lower, upper = self.verifier_length
- return (set(verifier) <= self.safe_characters and
- lower <= len(verifier) <= upper)
-
- def check_realm(self, realm):
- """Check that the realm is one of a set allowed realms.
- """
- return realm in self.realms
-
- def get_client_secret(self, client_key):
- """Retrieves the client secret associated with the client key.
-
- This method must allow the use of a dummy client_key value.
- Fetching the secret using the dummy key must take the same amount of
- time as fetching a secret for a valid client::
-
- # Unlikely to be near constant time as it uses two database
- # lookups for a valid client, and only one for an invalid.
- from your_datastore import ClientSecret
- if ClientSecret.has(client_key):
- return ClientSecret.get(client_key)
- else:
- return 'dummy'
-
- # Aim to mimic number of latency inducing operations no matter
- # whether the client is valid or not.
- from your_datastore import ClientSecret
- return ClientSecret.get(client_key, 'dummy')
-
- Note that the returned key must be in plaintext.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- @property
- def dummy_client(self):
- """Dummy client used when an invalid client key is supplied.
-
- The dummy client should be associated with either a client secret,
- a rsa key or both depending on which signature methods are supported.
- Providers should make sure that
-
- get_client_secret(dummy_client)
- get_rsa_key(dummy_client)
-
- return a valid secret or key for the dummy client.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def get_request_token_secret(self, client_key, request_token):
- """Retrieves the shared secret associated with the request token.
-
- This method must allow the use of a dummy values and the running time
- must be roughly equivalent to that of the running time of valid values::
-
- # Unlikely to be near constant time as it uses two database
- # lookups for a valid client, and only one for an invalid.
- from your_datastore import RequestTokenSecret
- if RequestTokenSecret.has(client_key):
- return RequestTokenSecret.get((client_key, request_token))
- else:
- return 'dummy'
-
- # Aim to mimic number of latency inducing operations no matter
- # whether the client is valid or not.
- from your_datastore import RequestTokenSecret
- return ClientSecret.get((client_key, request_token), 'dummy')
-
- Note that the returned key must be in plaintext.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def get_access_token_secret(self, client_key, access_token):
- """Retrieves the shared secret associated with the access token.
-
- This method must allow the use of a dummy values and the running time
- must be roughly equivalent to that of the running time of valid values::
-
- # Unlikely to be near constant time as it uses two database
- # lookups for a valid client, and only one for an invalid.
- from your_datastore import AccessTokenSecret
- if AccessTokenSecret.has(client_key):
- return AccessTokenSecret.get((client_key, request_token))
- else:
- return 'dummy'
-
- # Aim to mimic number of latency inducing operations no matter
- # whether the client is valid or not.
- from your_datastore import AccessTokenSecret
- return ClientSecret.get((client_key, request_token), 'dummy')
-
- Note that the returned key must be in plaintext.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- @property
- def dummy_request_token(self):
- """Dummy request token used when an invalid token was supplied.
-
- The dummy request token should be associated with a request token
- secret such that get_request_token_secret(.., dummy_request_token)
- returns a valid secret.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- @property
- def dummy_access_token(self):
- """Dummy access token used when an invalid token was supplied.
-
- The dummy access token should be associated with an access token
- secret such that get_access_token_secret(.., dummy_access_token)
- returns a valid secret.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def get_rsa_key(self, client_key):
- """Retrieves a previously stored client provided RSA key.
-
- This method must allow the use of a dummy client_key value. Fetching
- the rsa key using the dummy key must take the same amount of time
- as fetching a key for a valid client. The dummy key must also be of
- the same bit length as client keys.
-
- Note that the key must be returned in plaintext.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def _get_signature_type_and_params(self, request):
- """Extracts parameters from query, headers and body. Signature type
- is set to the source in which parameters were found.
- """
- # Per RFC5849, only the Authorization header may contain the 'realm' optional parameter.
- header_params = signature.collect_parameters(headers=request.headers,
- exclude_oauth_signature=False, with_realm=True)
- body_params = signature.collect_parameters(body=request.body,
- exclude_oauth_signature=False)
- query_params = signature.collect_parameters(uri_query=request.uri_query,
- exclude_oauth_signature=False)
-
- params = []
- params.extend(header_params)
- params.extend(body_params)
- params.extend(query_params)
- signature_types_with_oauth_params = list(filter(lambda s: s[2], (
- (SIGNATURE_TYPE_AUTH_HEADER, params,
- utils.filter_oauth_params(header_params)),
- (SIGNATURE_TYPE_BODY, params,
- utils.filter_oauth_params(body_params)),
- (SIGNATURE_TYPE_QUERY, params,
- utils.filter_oauth_params(query_params))
- )))
-
- if len(signature_types_with_oauth_params) > 1:
- raise ValueError('oauth_ params must come from only 1 signature type but were found in %s' % ', '.join(
- [s[0] for s in signature_types_with_oauth_params]))
- try:
- signature_type, params, oauth_params = signature_types_with_oauth_params[0]
- except IndexError:
- raise ValueError('oauth_ params are missing. Could not determine signature type.')
-
- return signature_type, params, oauth_params
-
- def validate_client_key(self, client_key):
- """Validates that supplied client key is a registered and valid client.
-
- Note that if the dummy client is supplied it should validate in same
- or nearly the same amount of time as a valid one.
-
- Ensure latency inducing tasks are mimiced even for dummy clients.
- For example, use::
-
- from your_datastore import Client
- try:
- return Client.exists(client_key, access_token)
- except DoesNotExist:
- return False
-
- Rather than::
-
- from your_datastore import Client
- if access_token == self.dummy_access_token:
- return False
- else:
- return Client.exists(client_key, access_token)
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_request_token(self, client_key, request_token):
- """Validates that supplied request token is registered and valid.
-
- Note that if the dummy request_token is supplied it should validate in
- the same nearly the same amount of time as a valid one.
-
- Ensure latency inducing tasks are mimiced even for dummy clients.
- For example, use::
-
- from your_datastore import RequestToken
- try:
- return RequestToken.exists(client_key, access_token)
- except DoesNotExist:
- return False
-
- Rather than::
-
- from your_datastore import RequestToken
- if access_token == self.dummy_access_token:
- return False
- else:
- return RequestToken.exists(client_key, access_token)
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_access_token(self, client_key, access_token):
- """Validates that supplied access token is registered and valid.
-
- Note that if the dummy access token is supplied it should validate in
- the same or nearly the same amount of time as a valid one.
-
- Ensure latency inducing tasks are mimiced even for dummy clients.
- For example, use::
-
- from your_datastore import AccessToken
- try:
- return AccessToken.exists(client_key, access_token)
- except DoesNotExist:
- return False
-
- Rather than::
-
- from your_datastore import AccessToken
- if access_token == self.dummy_access_token:
- return False
- else:
- return AccessToken.exists(client_key, access_token)
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_timestamp_and_nonce(self, client_key, timestamp, nonce,
- request_token=None, access_token=None):
- """Validates that the nonce has not been used before.
-
- Per `Section 3.3`_ of the spec.
-
- "A nonce is a random string, uniquely generated by the client to allow
- the server to verify that a request has never been made before and
- helps prevent replay attacks when requests are made over a non-secure
- channel. The nonce value MUST be unique across all requests with the
- same timestamp, client credentials, and token combinations."
-
- .. _`Section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
-
- One of the first validation checks that will be made is for the validity
- of the nonce and timestamp, which are associated with a client key and
- possibly a token. If invalid then immediately fail the request
- by returning False. If the nonce/timestamp pair has been used before and
- you may just have detected a replay attack. Therefore it is an essential
- part of OAuth security that you not allow nonce/timestamp reuse.
- Note that this validation check is done before checking the validity of
- the client and token.::
-
- nonces_and_timestamps_database = [
- (u'foo', 1234567890, u'rannoMstrInghere', u'bar')
- ]
-
- def validate_timestamp_and_nonce(self, client_key, timestamp, nonce,
- request_token=None, access_token=None):
-
- return ((client_key, timestamp, nonce, request_token or access_token)
- not in self.nonces_and_timestamps_database)
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_redirect_uri(self, client_key, redirect_uri):
- """Validates the client supplied redirection URI.
-
- It is highly recommended that OAuth providers require their clients
- to register all redirection URIs prior to using them in requests and
- register them as absolute URIs. See `CWE-601`_ for more information
- about open redirection attacks.
-
- By requiring registration of all redirection URIs it should be
- straightforward for the provider to verify whether the supplied
- redirect_uri is valid or not.
-
- Alternatively per `Section 2.1`_ of the spec:
-
- "If the client is unable to receive callbacks or a callback URI has
- been established via other means, the parameter value MUST be set to
- "oob" (case sensitive), to indicate an out-of-band configuration."
-
- .. _`CWE-601`: http://cwe.mitre.org/top25/index.html#CWE-601
- .. _`Section 2.1`: https://tools.ietf.org/html/rfc5849#section-2.1
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_requested_realm(self, client_key, realm):
- """Validates that the client may request access to the realm.
-
- This method is invoked when obtaining a request token and should
- tie a realm to the request token and after user authorization
- this realm restriction should transfer to the access token.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_realm(self, client_key, access_token, uri=None,
- required_realm=None):
- """Validates access to the request realm.
-
- How providers choose to use the realm parameter is outside the OAuth
- specification but it is commonly used to restrict access to a subset
- of protected resources such as "photos".
-
- required_realm is a convenience parameter which can be used to provide
- a per view method pre-defined list of allowed realms.
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def validate_verifier(self, client_key, request_token, verifier):
- """Validates a verification code.
-
- OAuth providers issue a verification code to clients after the
- resource owner authorizes access. This code is used by the client to
- obtain token credentials and the provider must verify that the
- verifier is valid and associated with the client as well as the
- resource owner.
-
- Verifier validation should be done in near constant time
- (to avoid verifier enumeration). To achieve this we need a
- constant time string comparison which is provided by OAuthLib
- in ``oauthlib.common.safe_string_equals``::
-
- from your_datastore import Verifier
- correct_verifier = Verifier.get(client_key, request_token)
- from oauthlib.common import safe_string_equals
- return safe_string_equals(verifier, correct_verifier)
- """
- raise NotImplementedError("Subclasses must implement this function.")
-
- def verify_request_token_request(self, uri, http_method='GET', body=None,
- headers=None):
- """Verify the initial request in the OAuth workflow.
-
- During this step the client obtains a request token for use during
- resource owner authorization (which is outside the scope of oauthlib).
- """
- return self.verify_request(uri, http_method=http_method, body=body,
- headers=headers, require_resource_owner=False,
- require_realm=True, require_callback=True)
-
- def verify_access_token_request(self, uri, http_method='GET', body=None,
- headers=None):
- """Verify the second request in the OAuth workflow.
-
- During this step the client obtains the access token for use when
- accessing protected resources.
- """
- return self.verify_request(uri, http_method=http_method, body=body,
- headers=headers, require_verifier=True)
-
- def verify_request(self, uri, http_method='GET', body=None,
- headers=None, require_resource_owner=True, require_verifier=False,
- require_realm=False, required_realm=None, require_callback=False):
- """Verifies a request ensuring that the following is true:
-
- Per `section 3.2`_ of the spec.
-
- - all mandated OAuth parameters are supplied
- - parameters are only supplied in one source which may be the URI
- query, the Authorization header or the body
- - all parameters are checked and validated, see comments and the
- methods and properties of this class for further details.
- - the supplied signature is verified against a recalculated one
-
- A ValueError will be raised if any parameter is missing,
- supplied twice or invalid. A HTTP 400 Response should be returned
- upon catching an exception.
-
- A HTTP 401 Response should be returned if verify_request returns False.
-
- `Timing attacks`_ are prevented through the use of dummy credentials to
- create near constant time verification even if an invalid credential
- is used. Early exit on invalid credentials would enable attackers
- to perform `enumeration attacks`_. Near constant time string comparison
- is used to prevent secret key guessing. Note that timing attacks can
- only be prevented through near constant time execution, not by adding
- a random delay which would only require more samples to be gathered.
-
- .. _`section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
- .. _`Timing attacks`: http://rdist.root.org/2010/07/19/exploiting-remote-timing-attacks/
- .. _`enumeration attacks`: http://www.sans.edu/research/security-laboratory/article/attacks-browsing
- """
- # Only include body data from x-www-form-urlencoded requests
- headers = headers or {}
- if ("Content-Type" in headers and
- headers["Content-Type"] == CONTENT_TYPE_FORM_URLENCODED):
- request = Request(uri, http_method, body, headers)
- else:
- request = Request(uri, http_method, '', headers)
-
- if self.enforce_ssl and not request.uri.lower().startswith("https://"):
- raise ValueError("Insecure transport, only HTTPS is allowed.")
-
- signature_type, params, oauth_params = self._get_signature_type_and_params(request)
-
- # The server SHOULD return a 400 (Bad Request) status code when
- # receiving a request with duplicated protocol parameters.
- if len(dict(oauth_params)) != len(oauth_params):
- raise ValueError("Duplicate OAuth entries.")
-
- oauth_params = dict(oauth_params)
- request.signature = oauth_params.get('oauth_signature')
- request.client_key = oauth_params.get('oauth_consumer_key')
- request.resource_owner_key = oauth_params.get('oauth_token')
- request.nonce = oauth_params.get('oauth_nonce')
- request.timestamp = oauth_params.get('oauth_timestamp')
- request.callback_uri = oauth_params.get('oauth_callback')
- request.verifier = oauth_params.get('oauth_verifier')
- request.signature_method = oauth_params.get('oauth_signature_method')
- request.realm = dict(params).get('realm')
-
- # The server SHOULD return a 400 (Bad Request) status code when
- # receiving a request with missing parameters.
- if not all((request.signature, request.client_key,
- request.nonce, request.timestamp,
- request.signature_method)):
- raise ValueError("Missing OAuth parameters.")
-
- # OAuth does not mandate a particular signature method, as each
- # implementation can have its own unique requirements. Servers are
- # free to implement and document their own custom methods.
- # Recommending any particular method is beyond the scope of this
- # specification. Implementers should review the Security
- # Considerations section (`Section 4`_) before deciding on which
- # method to support.
- # .. _`Section 4`: http://tools.ietf.org/html/rfc5849#section-4
- if not request.signature_method in self.allowed_signature_methods:
- raise ValueError("Invalid signature method.")
-
- # Servers receiving an authenticated request MUST validate it by:
- # If the "oauth_version" parameter is present, ensuring its value is
- # "1.0".
- if ('oauth_version' in request.oauth_params and
- request.oauth_params['oauth_version'] != '1.0'):
- raise ValueError("Invalid OAuth version.")
-
- # The timestamp value MUST be a positive integer. Unless otherwise
- # specified by the server's documentation, the timestamp is expressed
- # in the number of seconds since January 1, 1970 00:00:00 GMT.
- if len(request.timestamp) != 10:
- raise ValueError("Invalid timestamp size")
- try:
- ts = int(request.timestamp)
-
- except ValueError:
- raise ValueError("Timestamp must be an integer")
-
- else:
- # To avoid the need to retain an infinite number of nonce values for
- # future checks, servers MAY choose to restrict the time period after
- # which a request with an old timestamp is rejected.
- if time.time() - ts > self.timestamp_lifetime:
- raise ValueError("Request too old, over 10 minutes.")
-
- # Provider specific validation of parameters, used to enforce
- # restrictions such as character set and length.
- if not self.check_client_key(request.client_key):
- raise ValueError("Invalid client key.")
-
- if not request.resource_owner_key and require_resource_owner:
- raise ValueError("Missing resource owner.")
-
- if (require_resource_owner and not require_verifier and
- not self.check_access_token(request.resource_owner_key)):
- raise ValueError("Invalid resource owner key.")
-
- if (require_resource_owner and require_verifier and
- not self.check_request_token(request.resource_owner_key)):
- raise ValueError("Invalid resource owner key.")
-
- if not self.check_nonce(request.nonce):
- raise ValueError("Invalid nonce.")
-
- if request.realm and not self.check_realm(request.realm):
- raise ValueError("Invalid realm. Allowed are %s" % self.realms)
-
- if not request.verifier and require_verifier:
- raise ValueError("Missing verifier.")
-
- if require_verifier and not self.check_verifier(request.verifier):
- raise ValueError("Invalid verifier.")
-
- if require_callback and not request.callback_uri:
- raise ValueError("Missing callback URI.")
-
- # Servers receiving an authenticated request MUST validate it by:
- # If using the "HMAC-SHA1" or "RSA-SHA1" signature methods, ensuring
- # that the combination of nonce/timestamp/token (if present)
- # received from the client has not been used before in a previous
- # request (the server MAY reject requests with stale timestamps as
- # described in `Section 3.3`_).
- # .._`Section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
- #
- # We check this before validating client and resource owner for
- # increased security and performance, both gained by doing less work.
- if require_verifier:
- token = {"request_token": request.resource_owner_key}
- else:
- token = {"access_token": request.resource_owner_key}
- if not self.validate_timestamp_and_nonce(request.client_key,
- request.timestamp, request.nonce, **token):
- return False, request
-
- # The server SHOULD return a 401 (Unauthorized) status code when
- # receiving a request with invalid client credentials.
- # Note: This is postponed in order to avoid timing attacks, instead
- # a dummy client is assigned and used to maintain near constant
- # time request verification.
- #
- # Note that early exit would enable client enumeration
- valid_client = self.validate_client_key(request.client_key)
- if not valid_client:
- request.client_key = self.dummy_client
-
- # Callback is normally never required, except for requests for
- # a Temporary Credential as described in `Section 2.1`_
- # .._`Section 2.1`: http://tools.ietf.org/html/rfc5849#section-2.1
- if require_callback:
- valid_redirect = self.validate_redirect_uri(request.client_key,
- request.callback_uri)
- else:
- valid_redirect = True
-
- # The server SHOULD return a 401 (Unauthorized) status code when
- # receiving a request with invalid or expired token.
- # Note: This is postponed in order to avoid timing attacks, instead
- # a dummy token is assigned and used to maintain near constant
- # time request verification.
- #
- # Note that early exit would enable resource owner enumeration
- if request.resource_owner_key:
- if require_verifier:
- valid_resource_owner = self.validate_request_token(
- request.client_key, request.resource_owner_key)
- if not valid_resource_owner:
- request.resource_owner_key = self.dummy_request_token
- else:
- valid_resource_owner = self.validate_access_token(
- request.client_key, request.resource_owner_key)
- if not valid_resource_owner:
- request.resource_owner_key = self.dummy_access_token
- else:
- valid_resource_owner = True
-
- # Note that `realm`_ is only used in authorization headers and how
- # it should be interepreted is not included in the OAuth spec.
- # However they could be seen as a scope or realm to which the
- # client has access and as such every client should be checked
- # to ensure it is authorized access to that scope or realm.
- # .. _`realm`: http://tools.ietf.org/html/rfc2617#section-1.2
- #
- # Note that early exit would enable client realm access enumeration.
- #
- # The require_realm indicates this is the first step in the OAuth
- # workflow where a client requests access to a specific realm.
- # This first step (obtaining request token) need not require a realm
- # and can then be identified by checking the require_resource_owner
- # flag and abscence of realm.
- #
- # Clients obtaining an access token will not supply a realm and it will
- # not be checked. Instead the previously requested realm should be
- # transferred from the request token to the access token.
- #
- # Access to protected resources will always validate the realm but note
- # that the realm is now tied to the access token and not provided by
- # the client.
- if ((require_realm and not request.resource_owner_key) or
- (not require_resource_owner and not request.realm)):
- valid_realm = self.validate_requested_realm(request.client_key,
- request.realm)
- elif require_verifier:
- valid_realm = True
- else:
- valid_realm = self.validate_realm(request.client_key,
- request.resource_owner_key, uri=request.uri,
- required_realm=required_realm)
-
- # The server MUST verify (Section 3.2) the validity of the request,
- # ensure that the resource owner has authorized the provisioning of
- # token credentials to the client, and ensure that the temporary
- # credentials have not expired or been used before. The server MUST
- # also verify the verification code received from the client.
- # .. _`Section 3.2`: http://tools.ietf.org/html/rfc5849#section-3.2
- #
- # Note that early exit would enable resource owner authorization
- # verifier enumertion.
- if request.verifier:
- valid_verifier = self.validate_verifier(request.client_key,
- request.resource_owner_key, request.verifier)
- else:
- valid_verifier = True
-
- # Parameters to Client depend on signature method which may vary
- # for each request. Note that HMAC-SHA1 and PLAINTEXT share parameters
-
- request.params = filter(lambda x: x[0] not in ("oauth_signature", "realm"), params)
-
- # ---- RSA Signature verification ----
- if request.signature_method == SIGNATURE_RSA:
- # The server verifies the signature per `[RFC3447] section 8.2.2`_
- # .. _`[RFC3447] section 8.2.2`: http://tools.ietf.org/html/rfc3447#section-8.2.1
- rsa_key = self.get_rsa_key(request.client_key)
- valid_signature = signature.verify_rsa_sha1(request, rsa_key)
-
- # ---- HMAC or Plaintext Signature verification ----
- else:
- # Servers receiving an authenticated request MUST validate it by:
- # Recalculating the request signature independently as described in
- # `Section 3.4`_ and comparing it to the value received from the
- # client via the "oauth_signature" parameter.
- # .. _`Section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
- client_secret = self.get_client_secret(request.client_key)
- resource_owner_secret = None
- if require_resource_owner:
- if require_verifier:
- resource_owner_secret = self.get_request_token_secret(
- request.client_key, request.resource_owner_key)
- else:
- resource_owner_secret = self.get_access_token_secret(
- request.client_key, request.resource_owner_key)
-
- if request.signature_method == SIGNATURE_HMAC:
- valid_signature = signature.verify_hmac_sha1(request,
- client_secret, resource_owner_secret)
- else:
- valid_signature = signature.verify_plaintext(request,
- client_secret, resource_owner_secret)
-
- # We delay checking validity until the very end, using dummy values for
- # calculations and fetching secrets/keys to ensure the flow of every
- # request remains almost identical regardless of whether valid values
- # have been supplied. This ensures near constant time execution and
- # prevents malicious users from guessing sensitive information
- v = all((valid_client, valid_resource_owner, valid_realm,
- valid_redirect, valid_verifier, valid_signature))
- if not v:
- log.info("[Failure] OAuthLib request verification failed.")
- log.info("Valid client:\t%s" % valid_client)
- log.info("Valid token:\t%s\t(Required: %s" % (valid_resource_owner, require_resource_owner))
- log.info("Valid realm:\t%s\t(Required: %s)" % (valid_realm, require_realm))
- log.info("Valid callback:\t%s" % valid_redirect)
- log.info("Valid verifier:\t%s\t(Required: %s)" % (valid_verifier, require_verifier))
- log.info("Valid signature:\t%s" % valid_signature)
- return v, request
diff --git a/oauthlib/oauth1/rfc5849/endpoints/__init__.py b/oauthlib/oauth1/rfc5849/endpoints/__init__.py
new file mode 100644
index 0000000..c1b57d6
--- /dev/null
+++ b/oauthlib/oauth1/rfc5849/endpoints/__init__.py
@@ -0,0 +1,8 @@
+from __future__ import absolute_import
+
+from .base import BaseEndpoint
+from .request_token import RequestTokenEndpoint
+from .authorization import AuthorizationEndpoint
+from .access_token import AccessTokenEndpoint
+from .resource import ResourceEndpoint
+from .pre_configured import WebApplicationServer