Update oauthlib-3.1.1

This commit is contained in:
JonnyWong16 2021-10-14 22:34:45 -07:00
parent e58aa40099
commit d76838a607
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
64 changed files with 4329 additions and 1421 deletions

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1
~~~~~~~~~~~~~~
@ -6,14 +5,24 @@ oauthlib.oauth1
This module is a wrapper for the most recent implementation of OAuth 1.0 Client
and Server classes.
"""
from __future__ import absolute_import, unicode_literals
from .rfc5849 import Client
from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
from .rfc5849 import (SIGNATURE_HMAC,
SIGNATURE_HMAC_SHA1,
SIGNATURE_HMAC_SHA256,
SIGNATURE_HMAC_SHA512,
SIGNATURE_RSA,
SIGNATURE_RSA_SHA1,
SIGNATURE_RSA_SHA256,
SIGNATURE_RSA_SHA512,
SIGNATURE_PLAINTEXT)
from .rfc5849 import SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_QUERY
from .rfc5849 import SIGNATURE_TYPE_BODY
from .rfc5849.request_validator import RequestValidator
from .rfc5849.endpoints import RequestTokenEndpoint, AuthorizationEndpoint
from .rfc5849.endpoints import AccessTokenEndpoint, ResourceEndpoint
from .rfc5849.endpoints import SignatureOnlyEndpoint, WebApplicationServer
from .rfc5849.errors import *
from .rfc5849.errors import (InsecureTransportError,
InvalidClientError,
InvalidRequestError,
InvalidSignatureMethodError,
OAuth1Error)

View file

@ -1,36 +1,68 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1.rfc5849
~~~~~~~~~~~~~~
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
It supports all three standard signature methods defined in RFC 5849:
- HMAC-SHA1
- RSA-SHA1
- PLAINTEXT
It also supports signature methods that are not defined in RFC 5849. These are
based on the standard ones but replace SHA-1 with the more secure SHA-256:
- HMAC-SHA256
- RSA-SHA256
"""
from __future__ import absolute_import, unicode_literals
import base64
import hashlib
import logging
log = logging.getLogger(__name__)
import urllib.parse as urlparse
import sys
try:
import urlparse
except ImportError:
import urllib.parse as urlparse
from oauthlib.common import (
Request, generate_nonce, generate_timestamp, to_unicode, urlencode,
)
if sys.version_info[0] == 3:
bytes_type = bytes
else:
bytes_type = str
from oauthlib.common import Request, urlencode, generate_nonce
from oauthlib.common import generate_timestamp, to_unicode
from . import parameters, signature
SIGNATURE_HMAC = "HMAC-SHA1"
SIGNATURE_RSA = "RSA-SHA1"
log = logging.getLogger(__name__)
# Available signature methods
#
# Note: SIGNATURE_HMAC and SIGNATURE_RSA are kept for backward compatibility
# with previous versions of this library, when it the only HMAC-based and
# RSA-based signature methods were HMAC-SHA1 and RSA-SHA1. But now that it
# supports other hashing algorithms besides SHA1, explicitly identifying which
# hashing algorithm is being used is recommended.
#
# Note: if additional values are defined here, don't forget to update the
# imports in "../__init__.py" so they are available outside this module.
SIGNATURE_HMAC_SHA1 = "HMAC-SHA1"
SIGNATURE_HMAC_SHA256 = "HMAC-SHA256"
SIGNATURE_HMAC_SHA512 = "HMAC-SHA512"
SIGNATURE_HMAC = SIGNATURE_HMAC_SHA1 # deprecated variable for HMAC-SHA1
SIGNATURE_RSA_SHA1 = "RSA-SHA1"
SIGNATURE_RSA_SHA256 = "RSA-SHA256"
SIGNATURE_RSA_SHA512 = "RSA-SHA512"
SIGNATURE_RSA = SIGNATURE_RSA_SHA1 # deprecated variable for RSA-SHA1
SIGNATURE_PLAINTEXT = "PLAINTEXT"
SIGNATURE_METHODS = (SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_PLAINTEXT)
SIGNATURE_METHODS = (
SIGNATURE_HMAC_SHA1,
SIGNATURE_HMAC_SHA256,
SIGNATURE_HMAC_SHA512,
SIGNATURE_RSA_SHA1,
SIGNATURE_RSA_SHA256,
SIGNATURE_RSA_SHA512,
SIGNATURE_PLAINTEXT
)
SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER'
SIGNATURE_TYPE_QUERY = 'QUERY'
@ -39,12 +71,16 @@ SIGNATURE_TYPE_BODY = 'BODY'
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'
class Client(object):
class Client:
"""A client used to sign OAuth 1.0 RFC 5849 requests."""
SIGNATURE_METHODS = {
SIGNATURE_HMAC: signature.sign_hmac_sha1_with_client,
SIGNATURE_RSA: signature.sign_rsa_sha1_with_client,
SIGNATURE_HMAC_SHA1: signature.sign_hmac_sha1_with_client,
SIGNATURE_HMAC_SHA256: signature.sign_hmac_sha256_with_client,
SIGNATURE_HMAC_SHA512: signature.sign_hmac_sha512_with_client,
SIGNATURE_RSA_SHA1: signature.sign_rsa_sha1_with_client,
SIGNATURE_RSA_SHA256: signature.sign_rsa_sha256_with_client,
SIGNATURE_RSA_SHA512: signature.sign_rsa_sha512_with_client,
SIGNATURE_PLAINTEXT: signature.sign_plaintext_with_client
}
@ -57,7 +93,7 @@ class Client(object):
resource_owner_key=None,
resource_owner_secret=None,
callback_uri=None,
signature_method=SIGNATURE_HMAC,
signature_method=SIGNATURE_HMAC_SHA1,
signature_type=SIGNATURE_TYPE_AUTH_HEADER,
rsa_key=None, verifier=None, realm=None,
encoding='utf-8', decoding=None,
@ -105,10 +141,11 @@ class Client(object):
def __repr__(self):
attrs = vars(self).copy()
attrs['client_secret'] = '****' if attrs['client_secret'] else None
attrs['rsa_key'] = '****' if attrs['rsa_key'] else None
attrs[
'resource_owner_secret'] = '****' if attrs['resource_owner_secret'] else None
attribute_str = ', '.join('%s=%s' % (k, v) for k, v in attrs.items())
return '<%s %s>' % (self.__class__.__name__, attribute_str)
attribute_str = ', '.join('{}={}'.format(k, v) for k, v in attrs.items())
return '<{} {}>'.format(self.__class__.__name__, attribute_str)
def get_oauth_signature(self, request):
"""Get an OAuth signature to be used in signing a request
@ -118,7 +155,7 @@ class Client(object):
replace any netloc part of the request argument's uri attribute
value.
.. _`section 3.4.1.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.2
.. _`section 3.4.1.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.2
"""
if self.signature_method == SIGNATURE_PLAINTEXT:
# fast-path
@ -131,25 +168,24 @@ class Client(object):
uri_query=urlparse.urlparse(uri).query,
body=body,
headers=headers)
log.debug("Collected params: {0}".format(collected_params))
log.debug("Collected params: {}".format(collected_params))
normalized_params = signature.normalize_parameters(collected_params)
normalized_uri = signature.normalize_base_string_uri(uri,
headers.get('Host', None))
log.debug("Normalized params: {0}".format(normalized_params))
log.debug("Normalized URI: {0}".format(normalized_uri))
normalized_uri = signature.base_string_uri(uri, headers.get('Host', None))
log.debug("Normalized params: {}".format(normalized_params))
log.debug("Normalized URI: {}".format(normalized_uri))
base_string = signature.construct_base_string(request.http_method,
base_string = signature.signature_base_string(request.http_method,
normalized_uri, normalized_params)
log.debug("Base signing string: {0}".format(base_string))
log.debug("Signing: signature base string: {}".format(base_string))
if self.signature_method not in self.SIGNATURE_METHODS:
raise ValueError('Invalid signature method.')
sig = self.SIGNATURE_METHODS[self.signature_method](base_string, self)
log.debug("Signature: {0}".format(sig))
log.debug("Signature: {}".format(sig))
return sig
def get_oauth_params(self, request):
@ -174,10 +210,12 @@ class Client(object):
params.append(('oauth_verifier', self.verifier))
# providing body hash for requests other than x-www-form-urlencoded
# as described in http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html
# as described in https://tools.ietf.org/html/draft-eaton-oauth-bodyhash-00#section-4.1.1
# 4.1.1. When to include the body hash
# * [...] MUST NOT include an oauth_body_hash parameter on requests with form-encoded request bodies
# * [...] SHOULD include the oauth_body_hash parameter on all other requests.
# Note that SHA-1 is vulnerable. The spec acknowledges that in https://tools.ietf.org/html/draft-eaton-oauth-bodyhash-00#section-6.2
# At this time, no further effort has been made to replace SHA-1 for the OAuth Request Body Hash extension.
content_type = request.headers.get('Content-Type', None)
content_type_eligible = content_type and content_type.find('application/x-www-form-urlencoded') < 0
if request.body is not None and content_type_eligible:
@ -278,8 +316,8 @@ class Client(object):
# header field set to "application/x-www-form-urlencoded".
elif not should_have_params and has_params:
raise ValueError(
"Body contains parameters but Content-Type header was {0} "
"instead of {1}".format(content_type or "not set",
"Body contains parameters but Content-Type header was {} "
"instead of {}".format(content_type or "not set",
CONTENT_TYPE_FORM_URLENCODED))
# 3.5.2. Form-Encoded Body
@ -296,7 +334,7 @@ class Client(object):
raise ValueError(
'Body signatures may only be used with form-urlencoded content')
# We amend http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
# We amend https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
# with the clause that parameters from body should only be included
# in non GET or HEAD requests. Extracting the request body parameters
# and including them in the signature base string would give semantic

View file

@ -1,9 +1,8 @@
from __future__ import absolute_import
from .access_token import AccessTokenEndpoint
from .authorization import AuthorizationEndpoint
from .base import BaseEndpoint
from .request_token import RequestTokenEndpoint
from .authorization import AuthorizationEndpoint
from .access_token import AccessTokenEndpoint
from .resource import ResourceEndpoint
from .signature_only import SignatureOnlyEndpoint
from .pre_configured import WebApplicationServer
from .pre_configured import WebApplicationServer # isort:skip

View file

@ -8,14 +8,12 @@ OAuth 1.0 RFC 5849. It validates the correctness of access token requests,
creates and persists tokens as well as create the proper response to be
returned to the client.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import urlencode
from .base import BaseEndpoint
from .. import errors
from .base import BaseEndpoint
log = logging.getLogger(__name__)
@ -37,7 +35,8 @@ class AccessTokenEndpoint(BaseEndpoint):
Similar to OAuth 2, indication of granted scopes will be included as a
space separated list in ``oauth_authorized_realms``.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The token as an urlencoded string.
"""
request.realms = self.request_validator.get_realms(
@ -120,7 +119,8 @@ class AccessTokenEndpoint(BaseEndpoint):
def validate_access_token_request(self, request):
"""Validate an access token request.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:raises: OAuth1Error if the request is invalid.
:returns: A tuple of 2 elements.
1. The validation result (True or False).
@ -180,7 +180,7 @@ class AccessTokenEndpoint(BaseEndpoint):
# 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
# .. _`Section 3.2`: https://tools.ietf.org/html/rfc5849#section-3.2
#
# Note that early exit would enable resource owner authorization
# verifier enumertion.

View file

@ -6,16 +6,12 @@ oauthlib.oauth1.rfc5849.endpoints.authorization
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
from urllib.parse import urlencode
from oauthlib.common import Request, add_params_to_uri
from oauthlib.common import add_params_to_uri
from .base import BaseEndpoint
from .. import errors
try:
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
from .base import BaseEndpoint
class AuthorizationEndpoint(BaseEndpoint):
@ -41,7 +37,8 @@ class AuthorizationEndpoint(BaseEndpoint):
def create_verifier(self, request, credentials):
"""Create and save a new request token.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:param credentials: A dict of extra token credentials.
:returns: The verifier as a dict.
"""

View file

@ -6,21 +6,20 @@ oauthlib.oauth1.rfc5849.endpoints.base
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
import time
from oauthlib.common import Request, generate_token
from oauthlib.common import CaseInsensitiveDict, Request, generate_token
from .. import signature, utils, errors
from .. import CONTENT_TYPE_FORM_URLENCODED
from .. import SIGNATURE_HMAC, SIGNATURE_RSA
from .. import SIGNATURE_TYPE_AUTH_HEADER
from .. import SIGNATURE_TYPE_QUERY
from .. import SIGNATURE_TYPE_BODY
from .. import (
CONTENT_TYPE_FORM_URLENCODED,
SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_HMAC_SHA512,
SIGNATURE_RSA_SHA1, SIGNATURE_RSA_SHA256, SIGNATURE_RSA_SHA512,
SIGNATURE_PLAINTEXT,
SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY,
SIGNATURE_TYPE_QUERY, errors, signature, utils)
class BaseEndpoint(object):
class BaseEndpoint:
def __init__(self, request_validator, token_generator=None):
self.request_validator = request_validator
@ -70,7 +69,7 @@ class BaseEndpoint(object):
def _create_request(self, uri, http_method, body, headers):
# Only include body data from x-www-form-urlencoded requests
headers = headers or {}
headers = CaseInsensitiveDict(headers or {})
if ("Content-Type" in headers and
CONTENT_TYPE_FORM_URLENCODED in headers["Content-Type"]):
request = Request(uri, http_method, body, headers)
@ -130,11 +129,11 @@ class BaseEndpoint(object):
# 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
# .. _`Section 4`: https://tools.ietf.org/html/rfc5849#section-4
if (not request.signature_method in
self.request_validator.allowed_signature_methods):
raise errors.InvalidSignatureMethodError(
description="Invalid signature, %s not in %r." % (
description="Invalid signature, {} not in {!r}.".format(
request.signature_method,
self.request_validator.allowed_signature_methods))
@ -182,35 +181,65 @@ class BaseEndpoint(object):
def _check_signature(self, request, is_token_request=False):
# ---- RSA Signature verification ----
if request.signature_method == SIGNATURE_RSA:
if request.signature_method == SIGNATURE_RSA_SHA1 or \
request.signature_method == SIGNATURE_RSA_SHA256 or \
request.signature_method == SIGNATURE_RSA_SHA512:
# RSA-based signature method
# 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
# .. _`[RFC3447] section 8.2.2`: https://tools.ietf.org/html/rfc3447#section-8.2.1
rsa_key = self.request_validator.get_rsa_key(
request.client_key, request)
valid_signature = signature.verify_rsa_sha1(request, rsa_key)
if request.signature_method == SIGNATURE_RSA_SHA1:
valid_signature = signature.verify_rsa_sha1(request, rsa_key)
elif request.signature_method == SIGNATURE_RSA_SHA256:
valid_signature = signature.verify_rsa_sha256(request, rsa_key)
elif request.signature_method == SIGNATURE_RSA_SHA512:
valid_signature = signature.verify_rsa_sha512(request, rsa_key)
else:
valid_signature = False
# ---- HMAC or Plaintext Signature verification ----
else:
# Non-RSA based signature method
# 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
# .. _`Section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4
client_secret = self.request_validator.get_client_secret(
request.client_key, request)
resource_owner_secret = None
if request.resource_owner_key:
if is_token_request:
resource_owner_secret = self.request_validator.get_request_token_secret(
request.client_key, request.resource_owner_key, request)
resource_owner_secret = \
self.request_validator.get_request_token_secret(
request.client_key, request.resource_owner_key,
request)
else:
resource_owner_secret = self.request_validator.get_access_token_secret(
request.client_key, request.resource_owner_key, request)
resource_owner_secret = \
self.request_validator.get_access_token_secret(
request.client_key, request.resource_owner_key,
request)
if request.signature_method == SIGNATURE_HMAC:
valid_signature = signature.verify_hmac_sha1(request,
client_secret, resource_owner_secret)
if request.signature_method == SIGNATURE_HMAC_SHA1:
valid_signature = signature.verify_hmac_sha1(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_HMAC_SHA256:
valid_signature = signature.verify_hmac_sha256(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_HMAC_SHA512:
valid_signature = signature.verify_hmac_sha512(
request, client_secret, resource_owner_secret)
elif request.signature_method == SIGNATURE_PLAINTEXT:
valid_signature = signature.verify_plaintext(
request, client_secret, resource_owner_secret)
else:
valid_signature = signature.verify_plaintext(request,
client_secret, resource_owner_secret)
valid_signature = False
return valid_signature

View file

@ -1,7 +1,7 @@
from __future__ import absolute_import, unicode_literals
from . import RequestTokenEndpoint, AuthorizationEndpoint
from . import AccessTokenEndpoint, ResourceEndpoint
from . import (
AccessTokenEndpoint, AuthorizationEndpoint, RequestTokenEndpoint,
ResourceEndpoint,
)
class WebApplicationServer(RequestTokenEndpoint, AuthorizationEndpoint,

View file

@ -8,14 +8,12 @@ OAuth 1.0 RFC 5849. It validates the correctness of request token requests,
creates and persists tokens as well as create the proper response to be
returned to the client.
"""
from __future__ import absolute_import, unicode_literals
import logging
from oauthlib.common import urlencode
from .base import BaseEndpoint
from .. import errors
from .base import BaseEndpoint
log = logging.getLogger(__name__)
@ -34,7 +32,8 @@ class RequestTokenEndpoint(BaseEndpoint):
def create_request_token(self, request, credentials):
"""Create and save a new request token.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:param credentials: A dict of extra token credentials.
:returns: The token as an urlencoded string.
"""
@ -111,7 +110,8 @@ class RequestTokenEndpoint(BaseEndpoint):
def validate_request_token_request(self, request):
"""Validate a request token request.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:raises: OAuth1Error if the request is invalid.
:returns: A tuple of 2 elements.
1. The validation result (True or False).
@ -127,7 +127,7 @@ class RequestTokenEndpoint(BaseEndpoint):
request.client_key, request)
if not self.request_validator.check_realms(request.realms):
raise errors.InvalidRequestError(
description='Invalid realm %s. Allowed are %r.' % (
description='Invalid realm {}. Allowed are {!r}.'.format(
request.realms, self.request_validator.realms))
if not request.redirect_uri:
@ -156,7 +156,7 @@ class RequestTokenEndpoint(BaseEndpoint):
# 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
# .. _`realm`: https://tools.ietf.org/html/rfc2617#section-1.2
#
# Note that early exit would enable client realm access enumeration.
#
@ -178,7 +178,7 @@ class RequestTokenEndpoint(BaseEndpoint):
# 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
# .._`Section 2.1`: https://tools.ietf.org/html/rfc5849#section-2.1
valid_redirect = self.request_validator.validate_redirect_uri(
request.client_key, request.redirect_uri, request)
if not request.redirect_uri:

View file

@ -6,12 +6,10 @@ oauthlib.oauth1.rfc5849.endpoints.resource
This module is an implementation of the resource protection provider logic of
OAuth 1.0 RFC 5849.
"""
from __future__ import absolute_import, unicode_literals
import logging
from .base import BaseEndpoint
from .. import errors
from .base import BaseEndpoint
log = logging.getLogger(__name__)
@ -119,7 +117,7 @@ class ResourceEndpoint(BaseEndpoint):
# 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
# .. _`realm`: https://tools.ietf.org/html/rfc2617#section-1.2
#
# Note that early exit would enable client realm access enumeration.
#

View file

@ -6,12 +6,10 @@ oauthlib.oauth1.rfc5849.endpoints.signature_only
This module is an implementation of the signing logic of OAuth 1.0 RFC 5849.
"""
from __future__ import absolute_import, unicode_literals
import logging
from .base import BaseEndpoint
from .. import errors
from .base import BaseEndpoint
log = logging.getLogger(__name__)
@ -34,17 +32,22 @@ class SignatureOnlyEndpoint(BaseEndpoint):
"""
try:
request = self._create_request(uri, http_method, body, headers)
except errors.OAuth1Error:
except errors.OAuth1Error as err:
log.info(
'Exception caught while validating request, %s.' % err)
return False, None
try:
self._check_transport_security(request)
self._check_mandatory_parameters(request)
except errors.OAuth1Error:
except errors.OAuth1Error as err:
log.info(
'Exception caught while validating request, %s.' % err)
return False, request
if not self.request_validator.validate_timestamp_and_nonce(
request.client_key, request.timestamp, request.nonce, request):
log.debug('[Failure] verification failed: timestamp/nonce')
return False, request
# The server SHOULD return a 401 (Unauthorized) status code when

View file

@ -1,4 +1,3 @@
# coding=utf-8
"""
oauthlib.oauth1.rfc5849.errors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -6,9 +5,7 @@ oauthlib.oauth1.rfc5849.errors
Error used both by OAuth 1 clients and provicers to represent the spec
defined error responses for all four core grant types.
"""
from __future__ import unicode_literals
from oauthlib.common import urlencode, add_params_to_uri
from oauthlib.common import add_params_to_uri, urlencode
class OAuth1Error(Exception):
@ -37,10 +34,10 @@ class OAuth1Error(Exception):
request: Oauthlib Request object
"""
self.description = description or self.description
message = '(%s) %s' % (self.error, self.description)
message = '({}) {}'.format(self.error, self.description)
if request:
message += ' ' + repr(request)
super(OAuth1Error, self).__init__(message)
super().__init__(message)
self.uri = uri
self.status_code = status_code

View file

@ -1,21 +1,17 @@
# -*- coding: utf-8 -*-
"""
oauthlib.parameters
~~~~~~~~~~~~~~~~~~~
This module contains methods related to `section 3.5`_ of the OAuth 1.0a spec.
.. _`section 3.5`: http://tools.ietf.org/html/rfc5849#section-3.5
.. _`section 3.5`: https://tools.ietf.org/html/rfc5849#section-3.5
"""
from __future__ import absolute_import, unicode_literals
from urllib.parse import urlparse, urlunparse
try:
from urlparse import urlparse, urlunparse
except ImportError:
from urllib.parse import urlparse, urlunparse
from . import utils
from oauthlib.common import extract_params, urlencode
from . import utils
# TODO: do we need filter_params now that oauth_params are handled by Request?
# We can easily pass in just oauth protocol params.
@ -40,8 +36,8 @@ def prepare_headers(oauth_params, headers=None, realm=None):
oauth_version="1.0"
.. _`section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1
.. _`RFC2617`: http://tools.ietf.org/html/rfc2617
.. _`section 3.5.1`: https://tools.ietf.org/html/rfc5849#section-3.5.1
.. _`RFC2617`: https://tools.ietf.org/html/rfc2617
"""
headers = headers or {}
@ -52,28 +48,28 @@ def prepare_headers(oauth_params, headers=None, realm=None):
# 1. Parameter names and values are encoded per Parameter Encoding
# (`Section 3.6`_)
#
# .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
# .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6
escaped_name = utils.escape(oauth_parameter_name)
escaped_value = utils.escape(value)
# 2. Each parameter's name is immediately followed by an "=" character
# (ASCII code 61), a """ character (ASCII code 34), the parameter
# value (MAY be empty), and another """ character (ASCII code 34).
part = '{0}="{1}"'.format(escaped_name, escaped_value)
part = '{}="{}"'.format(escaped_name, escaped_value)
authorization_header_parameters_parts.append(part)
# 3. Parameters are separated by a "," character (ASCII code 44) and
# OPTIONAL linear whitespace per `RFC2617`_.
#
# .. _`RFC2617`: http://tools.ietf.org/html/rfc2617
# .. _`RFC2617`: https://tools.ietf.org/html/rfc2617
authorization_header_parameters = ', '.join(
authorization_header_parameters_parts)
# 4. The OPTIONAL "realm" parameter MAY be added and interpreted per
# `RFC2617 section 1.2`_.
#
# .. _`RFC2617 section 1.2`: http://tools.ietf.org/html/rfc2617#section-1.2
# .. _`RFC2617 section 1.2`: https://tools.ietf.org/html/rfc2617#section-1.2
if realm:
# NOTE: realm should *not* be escaped
authorization_header_parameters = ('realm="%s", ' % realm +
@ -96,8 +92,8 @@ def _append_params(oauth_params, params):
Per `section 3.5.2`_ and `3.5.3`_ of the spec.
.. _`section 3.5.2`: http://tools.ietf.org/html/rfc5849#section-3.5.2
.. _`3.5.3`: http://tools.ietf.org/html/rfc5849#section-3.5.3
.. _`section 3.5.2`: https://tools.ietf.org/html/rfc5849#section-3.5.2
.. _`3.5.3`: https://tools.ietf.org/html/rfc5849#section-3.5.3
"""
merged = list(params)
@ -115,7 +111,7 @@ def prepare_form_encoded_body(oauth_params, body):
Per `section 3.5.2`_ of the spec.
.. _`section 3.5.2`: http://tools.ietf.org/html/rfc5849#section-3.5.2
.. _`section 3.5.2`: https://tools.ietf.org/html/rfc5849#section-3.5.2
"""
# append OAuth params to the existing body
@ -127,7 +123,7 @@ def prepare_request_uri_query(oauth_params, uri):
Per `section 3.5.3`_ of the spec.
.. _`section 3.5.3`: http://tools.ietf.org/html/rfc5849#section-3.5.3
.. _`section 3.5.3`: https://tools.ietf.org/html/rfc5849#section-3.5.3
"""
# append OAuth params to the existing set of query components

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.oauth1.rfc5849
~~~~~~~~~~~~~~
@ -6,12 +5,10 @@ oauthlib.oauth1.rfc5849
This module is an implementation of various logic needed
for signing and checking OAuth 1.0 RFC 5849 requests.
"""
from __future__ import absolute_import, unicode_literals
from . import SIGNATURE_METHODS, utils
class RequestValidator(object):
class RequestValidator:
"""A validator/datastore interaction base class for OAuth 1 providers.
@ -107,7 +104,7 @@ class RequestValidator(object):
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
.. _`whitelisting or blacklisting`: https://www.schneier.com/blog/archives/2011/01/whitelisting_vs.html
"""
def __init__(self):
@ -195,7 +192,15 @@ class RequestValidator(object):
def check_realms(self, realms):
"""Check that the realm is one of a set allowed realms."""
return all((r in self.realms for r in realms))
return all(r in self.realms for r in realms)
def _subclass_must_implement(self, fn):
"""
Returns a NotImplementedError for a function that should be implemented.
:param fn: name of the function
"""
m = "Missing function implementation in {}: {}".format(type(self), fn)
return NotImplementedError(m)
@property
def dummy_client(self):
@ -219,7 +224,7 @@ class RequestValidator(object):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_client")
@property
def dummy_request_token(self):
@ -235,7 +240,7 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_request_token")
@property
def dummy_access_token(self):
@ -251,13 +256,14 @@ class RequestValidator(object):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("dummy_access_token")
def get_client_secret(self, client_key, request):
"""Retrieves the client secret associated with the client key.
:param client_key: The client/consumer key.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The client secret as a string.
This method must allow the use of a dummy client_key value.
@ -286,14 +292,15 @@ class RequestValidator(object):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement('get_client_secret')
def get_request_token_secret(self, client_key, token, request):
"""Retrieves the shared secret associated with the request token.
:param client_key: The client/consumer key.
:param token: The request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The token secret as a string.
This method must allow the use of a dummy values and the running time
@ -318,14 +325,15 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement('get_request_token_secret')
def get_access_token_secret(self, client_key, token, request):
"""Retrieves the shared secret associated with the access token.
:param client_key: The client/consumer key.
:param token: The access token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The token secret as a string.
This method must allow the use of a dummy values and the running time
@ -350,13 +358,14 @@ class RequestValidator(object):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_access_token_secret")
def get_default_realms(self, client_key, request):
"""Get the default realms for a client.
:param client_key: The client/consumer key.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The list of default realms associated with the client.
The list of default realms will be set during client registration and
@ -366,13 +375,14 @@ class RequestValidator(object):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_default_realms")
def get_realms(self, token, request):
"""Get realms associated with a request token.
:param token: The request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The list of realms associated with the request token.
This method is used by
@ -380,13 +390,14 @@ class RequestValidator(object):
* AuthorizationEndpoint
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_realms")
def get_redirect_uri(self, token, request):
"""Get the redirect URI associated with a request token.
:param token: The request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The redirect URI associated with the request token.
It may be desirable to return a custom URI if the redirect is set to "oob".
@ -397,13 +408,14 @@ class RequestValidator(object):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_redirect_uri")
def get_rsa_key(self, client_key, request):
"""Retrieves a previously stored client provided RSA key.
:param client_key: The client/consumer key.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: The rsa public key as a string.
This method must allow the use of a dummy client_key value. Fetching
@ -420,14 +432,15 @@ class RequestValidator(object):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("get_rsa_key")
def invalidate_request_token(self, client_key, request_token, request):
"""Invalidates a used request token.
:param client_key: The client/consumer key.
:param request_token: The request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: None
Per `Section 2.3`__ of the spec:
@ -435,7 +448,7 @@ class RequestValidator(object):
"The server MUST (...) ensure that the temporary
credentials have not expired or been used before."
.. _`Section 2.3`: http://tools.ietf.org/html/rfc5849#section-2.3
.. _`Section 2.3`: https://tools.ietf.org/html/rfc5849#section-2.3
This method should ensure that provided token won't validate anymore.
It can be simply removing RequestToken from storage or setting
@ -446,13 +459,14 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("invalidate_request_token")
def validate_client_key(self, client_key, request):
"""Validates that supplied client key is a registered and valid client.
:param client_key: The client/consumer key.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
Note that if the dummy client is supplied it should validate in same
@ -482,14 +496,15 @@ class RequestValidator(object):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_client_key")
def validate_request_token(self, client_key, token, request):
"""Validates that supplied request token is registered and valid.
:param client_key: The client/consumer key.
:param token: The request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
Note that if the dummy request_token is supplied it should validate in
@ -516,14 +531,15 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_request_token")
def validate_access_token(self, client_key, token, request):
"""Validates that supplied access token is registered and valid.
:param client_key: The client/consumer key.
:param token: The access token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
Note that if the dummy access token is supplied it should validate in
@ -550,7 +566,7 @@ class RequestValidator(object):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_access_token")
def validate_timestamp_and_nonce(self, client_key, timestamp, nonce,
request, request_token=None, access_token=None):
@ -561,7 +577,8 @@ class RequestValidator(object):
:param nonce: The ``oauth_nonce`` parameter.
:param request_token: Request token string, if any.
:param access_token: Access token string, if any.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
Per `Section 3.3`_ of the spec.
@ -572,7 +589,7 @@ class RequestValidator(object):
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
.. _`Section 3.3`: https://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
@ -600,7 +617,7 @@ class RequestValidator(object):
* ResourceEndpoint
* SignatureOnlyEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_timestamp_and_nonce")
def validate_redirect_uri(self, client_key, redirect_uri, request):
"""Validates the client supplied redirection URI.
@ -608,7 +625,8 @@ class RequestValidator(object):
:param client_key: The client/consumer key.
:param redirect_uri: The URI the client which to redirect back to after
authorization is successful.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
It is highly recommended that OAuth providers require their clients
@ -633,14 +651,15 @@ class RequestValidator(object):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_redirect_uri")
def validate_requested_realms(self, client_key, realms, request):
"""Validates that the client may request access to the realm.
:param client_key: The client/consumer key.
:param realms: The list of realms that client is requesting access to.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
This method is invoked when obtaining a request token and should
@ -651,7 +670,7 @@ class RequestValidator(object):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_requested_realms")
def validate_realms(self, client_key, token, request, uri=None,
realms=None):
@ -659,7 +678,8 @@ class RequestValidator(object):
:param client_key: The client/consumer key.
:param token: A request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:param uri: The URI the realms is protecting.
:param realms: A list of realms that must have been granted to
the access token.
@ -685,7 +705,7 @@ class RequestValidator(object):
* ResourceEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_realms")
def validate_verifier(self, client_key, token, verifier, request):
"""Validates a verification code.
@ -693,7 +713,8 @@ class RequestValidator(object):
:param client_key: The client/consumer key.
:param token: A request token string.
:param verifier: The authorization verifier string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
OAuth providers issue a verification code to clients after the
@ -716,13 +737,14 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("validate_verifier")
def verify_request_token(self, token, request):
"""Verify that the given OAuth1 request token is valid.
:param token: A request token string.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
This method is used only in AuthorizationEndpoint to check whether the
@ -734,14 +756,15 @@ class RequestValidator(object):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("verify_request_token")
def verify_realms(self, token, realms, request):
"""Verify authorized realms to see if they match those given to token.
:param token: An access token string.
:param realms: A list of realms the client attempts to access.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
:returns: True or False
This prevents the list of authorized realms sent by the client during
@ -757,13 +780,14 @@ class RequestValidator(object):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("verify_realms")
def save_access_token(self, token, request):
"""Save an OAuth1 access token.
:param token: A dict with token credentials.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
The token dictionary will at minimum include
@ -780,13 +804,14 @@ class RequestValidator(object):
* AccessTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_access_token")
def save_request_token(self, token, request):
"""Save an OAuth1 request token.
:param token: A dict with token credentials.
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
The token dictionary will at minimum include
@ -800,7 +825,7 @@ class RequestValidator(object):
* RequestTokenEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_request_token")
def save_verifier(self, token, verifier, request):
"""Associate an authorization verifier with a request token.
@ -808,7 +833,8 @@ class RequestValidator(object):
:param token: A request token string.
:param verifier A dictionary containing the oauth_verifier and
oauth_token
:param request: An oauthlib.common.Request object.
:param request: OAuthlib request.
:type request: oauthlib.common.Request
We need to associate verifiers with tokens for validation during the
access token request.
@ -820,4 +846,4 @@ class RequestValidator(object):
* AuthorizationEndpoint
"""
raise NotImplementedError("Subclasses must implement this function.")
raise self._subclass_must_implement("save_verifier")

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
oauthlib.utils
~~~~~~~~~~~~~~
@ -6,14 +5,9 @@ oauthlib.utils
This module contains utility methods used by various parts of the OAuth
spec.
"""
from __future__ import absolute_import, unicode_literals
import urllib.request as urllib2
try:
import urllib2
except ImportError:
import urllib.request as urllib2
from oauthlib.common import quote, unquote, bytes_type, unicode_type
from oauthlib.common import quote, unquote
UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@ -48,19 +42,19 @@ def escape(u):
Per `section 3.6`_ of the spec.
.. _`section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
.. _`section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6
"""
if not isinstance(u, unicode_type):
if not isinstance(u, str):
raise ValueError('Only unicode objects are escapable. ' +
'Got %s of type %s.' % (u, type(u)))
'Got {!r} of type {}.'.format(u, type(u)))
# Letters, digits, and the characters '_.-' are already treated as safe
# by urllib.quote(). We need to add '~' to fully support rfc5849.
return quote(u, safe=b'~')
def unescape(u):
if not isinstance(u, unicode_type):
if not isinstance(u, str):
raise ValueError('Only unicode objects are unescapable.')
return unquote(u)