mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-15 01:32:57 -07:00
Update oauthlib-3.1.1
This commit is contained in:
parent
e58aa40099
commit
d76838a607
64 changed files with 4329 additions and 1421 deletions
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -6,14 +5,13 @@ oauthlib.oauth2.rfc6749
|
|||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .authorization import AuthorizationEndpoint
|
||||
from .token import TokenEndpoint
|
||||
from .introspect import IntrospectEndpoint
|
||||
from .metadata import MetadataEndpoint
|
||||
from .pre_configured import (
|
||||
BackendApplicationServer, LegacyApplicationServer, MobileApplicationServer,
|
||||
Server, WebApplicationServer,
|
||||
)
|
||||
from .resource import ResourceEndpoint
|
||||
from .revocation import RevocationEndpoint
|
||||
from .pre_configured import Server
|
||||
from .pre_configured import WebApplicationServer
|
||||
from .pre_configured import MobileApplicationServer
|
||||
from .pre_configured import LegacyApplicationServer
|
||||
from .pre_configured import BackendApplicationServer
|
||||
from .token import TokenEndpoint
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -6,11 +5,10 @@ oauthlib.oauth2.rfc6749
|
|||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from oauthlib.common import Request
|
||||
from oauthlib.oauth2.rfc6749 import utils
|
||||
|
||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||
|
||||
|
@ -58,7 +56,7 @@ class AuthorizationEndpoint(BaseEndpoint):
|
|||
|
||||
# Enforced through the design of oauthlib.common.Request
|
||||
|
||||
.. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
|
||||
.. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
|
||||
"""
|
||||
|
||||
def __init__(self, default_response_type, default_token_type,
|
||||
|
@ -108,7 +106,9 @@ class AuthorizationEndpoint(BaseEndpoint):
|
|||
"""Extract response_type and route to the designated handler."""
|
||||
request = Request(
|
||||
uri, http_method=http_method, body=body, headers=headers)
|
||||
request.scopes = None
|
||||
|
||||
request.scopes = utils.scope_to_list(request.scope)
|
||||
|
||||
response_type_handler = self.response_types.get(
|
||||
request.response_type, self.default_response_type_handler)
|
||||
return response_type_handler.validate_authorization_request(request)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -6,22 +5,34 @@ oauthlib.oauth2.rfc6749
|
|||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import functools
|
||||
import logging
|
||||
|
||||
from ..errors import TemporarilyUnavailableError, ServerError
|
||||
from ..errors import FatalClientError, OAuth2Error
|
||||
from ..errors import (
|
||||
FatalClientError, InvalidClientError, InvalidRequestError, OAuth2Error,
|
||||
ServerError, TemporarilyUnavailableError, UnsupportedTokenTypeError,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseEndpoint(object):
|
||||
class BaseEndpoint:
|
||||
|
||||
def __init__(self):
|
||||
self._available = True
|
||||
self._catch_errors = False
|
||||
self._valid_request_methods = None
|
||||
|
||||
@property
|
||||
def valid_request_methods(self):
|
||||
return self._valid_request_methods
|
||||
|
||||
@valid_request_methods.setter
|
||||
def valid_request_methods(self, valid_request_methods):
|
||||
if valid_request_methods is not None:
|
||||
valid_request_methods = [x.upper() for x in valid_request_methods]
|
||||
self._valid_request_methods = valid_request_methods
|
||||
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
|
@ -29,7 +40,7 @@ class BaseEndpoint(object):
|
|||
|
||||
@available.setter
|
||||
def available(self, available):
|
||||
self._available = available
|
||||
self._available = available
|
||||
|
||||
@property
|
||||
def catch_errors(self):
|
||||
|
@ -39,6 +50,43 @@ class BaseEndpoint(object):
|
|||
def catch_errors(self, catch_errors):
|
||||
self._catch_errors = catch_errors
|
||||
|
||||
def _raise_on_missing_token(self, request):
|
||||
"""Raise error on missing token."""
|
||||
if not request.token:
|
||||
raise InvalidRequestError(request=request,
|
||||
description='Missing token parameter.')
|
||||
def _raise_on_invalid_client(self, request):
|
||||
"""Raise on failed client authentication."""
|
||||
if self.request_validator.client_authentication_required(request):
|
||||
if not self.request_validator.authenticate_client(request):
|
||||
log.debug('Client authentication failed, %r.', request)
|
||||
raise InvalidClientError(request=request)
|
||||
elif not self.request_validator.authenticate_client_id(request.client_id, request):
|
||||
log.debug('Client authentication failed, %r.', request)
|
||||
raise InvalidClientError(request=request)
|
||||
|
||||
def _raise_on_unsupported_token(self, request):
|
||||
"""Raise on unsupported tokens."""
|
||||
if (request.token_type_hint and
|
||||
request.token_type_hint in self.valid_token_types and
|
||||
request.token_type_hint not in self.supported_token_types):
|
||||
raise UnsupportedTokenTypeError(request=request)
|
||||
|
||||
def _raise_on_bad_method(self, request):
|
||||
if self.valid_request_methods is None:
|
||||
raise ValueError('Configure "valid_request_methods" property first')
|
||||
if request.http_method.upper() not in self.valid_request_methods:
|
||||
raise InvalidRequestError(request=request,
|
||||
description=('Unsupported request method %s' % request.http_method.upper()))
|
||||
|
||||
def _raise_on_bad_post_request(self, request):
|
||||
"""Raise if invalid POST request received
|
||||
"""
|
||||
if request.http_method.upper() == 'POST':
|
||||
query_params = request.uri_query or ""
|
||||
if query_params:
|
||||
raise InvalidRequestError(request=request,
|
||||
description=('URL query parameters are not allowed'))
|
||||
|
||||
def catch_errors_and_unavailability(f):
|
||||
@functools.wraps(f)
|
||||
|
|
122
lib/oauthlib/oauth2/rfc6749/endpoints/introspect.py
Normal file
122
lib/oauthlib/oauth2/rfc6749/endpoints/introspect.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
"""
|
||||
oauthlib.oauth2.rfc6749.endpoint.introspect
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An implementation of the OAuth 2.0 `Token Introspection`.
|
||||
|
||||
.. _`Token Introspection`: https://tools.ietf.org/html/rfc7662
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
|
||||
from oauthlib.common import Request
|
||||
|
||||
from ..errors import OAuth2Error
|
||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IntrospectEndpoint(BaseEndpoint):
|
||||
|
||||
"""Introspect token endpoint.
|
||||
|
||||
This endpoint defines a method to query an OAuth 2.0 authorization
|
||||
server to determine the active state of an OAuth 2.0 token and to
|
||||
determine meta-information about this token. OAuth 2.0 deployments
|
||||
can use this method to convey information about the authorization
|
||||
context of the token from the authorization server to the protected
|
||||
resource.
|
||||
|
||||
To prevent the values of access tokens from leaking into
|
||||
server-side logs via query parameters, an authorization server
|
||||
offering token introspection MAY disallow the use of HTTP GET on
|
||||
the introspection endpoint and instead require the HTTP POST method
|
||||
to be used at the introspection endpoint.
|
||||
"""
|
||||
|
||||
valid_token_types = ('access_token', 'refresh_token')
|
||||
valid_request_methods = ('POST',)
|
||||
|
||||
def __init__(self, request_validator, supported_token_types=None):
|
||||
BaseEndpoint.__init__(self)
|
||||
self.request_validator = request_validator
|
||||
self.supported_token_types = (
|
||||
supported_token_types or self.valid_token_types)
|
||||
|
||||
@catch_errors_and_unavailability
|
||||
def create_introspect_response(self, uri, http_method='POST', body=None,
|
||||
headers=None):
|
||||
"""Create introspect valid or invalid response
|
||||
|
||||
If the authorization server is unable to determine the state
|
||||
of the token without additional information, it SHOULD return
|
||||
an introspection response indicating the token is not active
|
||||
as described in Section 2.2.
|
||||
"""
|
||||
resp_headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-store',
|
||||
'Pragma': 'no-cache',
|
||||
}
|
||||
request = Request(uri, http_method, body, headers)
|
||||
try:
|
||||
self.validate_introspect_request(request)
|
||||
log.debug('Token introspect valid for %r.', request)
|
||||
except OAuth2Error as e:
|
||||
log.debug('Client error during validation of %r. %r.', request, e)
|
||||
resp_headers.update(e.headers)
|
||||
return resp_headers, e.json, e.status_code
|
||||
|
||||
claims = self.request_validator.introspect_token(
|
||||
request.token,
|
||||
request.token_type_hint,
|
||||
request
|
||||
)
|
||||
if claims is None:
|
||||
return resp_headers, json.dumps(dict(active=False)), 200
|
||||
if "active" in claims:
|
||||
claims.pop("active")
|
||||
return resp_headers, json.dumps(dict(active=True, **claims)), 200
|
||||
|
||||
def validate_introspect_request(self, request):
|
||||
"""Ensure the request is valid.
|
||||
|
||||
The protected resource calls the introspection endpoint using
|
||||
an HTTP POST request with parameters sent as
|
||||
"application/x-www-form-urlencoded".
|
||||
|
||||
token REQUIRED. The string value of the token.
|
||||
|
||||
token_type_hint OPTIONAL.
|
||||
A hint about the type of the token submitted for
|
||||
introspection. The protected resource MAY pass this parameter to
|
||||
help the authorization server optimize the token lookup. If the
|
||||
server is unable to locate the token using the given hint, it MUST
|
||||
extend its search across all of its supported token types. An
|
||||
authorization server MAY ignore this parameter, particularly if it
|
||||
is able to detect the token type automatically.
|
||||
* access_token: An Access Token as defined in [`RFC6749`],
|
||||
`section 1.4`_
|
||||
|
||||
* refresh_token: A Refresh Token as defined in [`RFC6749`],
|
||||
`section 1.5`_
|
||||
|
||||
The introspection endpoint MAY accept other OPTIONAL
|
||||
parameters to provide further context to the query. For
|
||||
instance, an authorization server may desire to know the IP
|
||||
address of the client accessing the protected resource to
|
||||
determine if the correct client is likely to be presenting the
|
||||
token. The definition of this or any other parameters are
|
||||
outside the scope of this specification, to be defined by
|
||||
service documentation or extensions to this specification.
|
||||
|
||||
.. _`section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4
|
||||
.. _`section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5
|
||||
.. _`RFC6749`: http://tools.ietf.org/html/rfc6749
|
||||
"""
|
||||
self._raise_on_bad_method(request)
|
||||
self._raise_on_bad_post_request(request)
|
||||
self._raise_on_missing_token(request)
|
||||
self._raise_on_invalid_client(request)
|
||||
self._raise_on_unsupported_token(request)
|
237
lib/oauthlib/oauth2/rfc6749/endpoints/metadata.py
Normal file
237
lib/oauthlib/oauth2/rfc6749/endpoints/metadata.py
Normal file
|
@ -0,0 +1,237 @@
|
|||
"""
|
||||
oauthlib.oauth2.rfc6749.endpoint.metadata
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An implementation of the `OAuth 2.0 Authorization Server Metadata`.
|
||||
|
||||
.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414
|
||||
"""
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
|
||||
from .. import grant_types
|
||||
from .authorization import AuthorizationEndpoint
|
||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||
from .introspect import IntrospectEndpoint
|
||||
from .revocation import RevocationEndpoint
|
||||
from .token import TokenEndpoint
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MetadataEndpoint(BaseEndpoint):
|
||||
|
||||
"""OAuth2.0 Authorization Server Metadata endpoint.
|
||||
|
||||
This specification generalizes the metadata format defined by
|
||||
`OpenID Connect Discovery 1.0` in a way that is compatible
|
||||
with OpenID Connect Discovery while being applicable to a wider set
|
||||
of OAuth 2.0 use cases. This is intentionally parallel to the way
|
||||
that OAuth 2.0 Dynamic Client Registration Protocol [`RFC7591`_]
|
||||
generalized the dynamic client registration mechanisms defined by
|
||||
OpenID Connect Dynamic Client Registration 1.0
|
||||
in a way that is compatible with it.
|
||||
|
||||
.. _`OpenID Connect Discovery 1.0`: https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||
.. _`RFC7591`: https://tools.ietf.org/html/rfc7591
|
||||
"""
|
||||
|
||||
def __init__(self, endpoints, claims={}, raise_errors=True):
|
||||
assert isinstance(claims, dict)
|
||||
for endpoint in endpoints:
|
||||
assert isinstance(endpoint, BaseEndpoint)
|
||||
|
||||
BaseEndpoint.__init__(self)
|
||||
self.raise_errors = raise_errors
|
||||
self.endpoints = endpoints
|
||||
self.initial_claims = claims
|
||||
self.claims = self.validate_metadata_server()
|
||||
|
||||
@catch_errors_and_unavailability
|
||||
def create_metadata_response(self, uri, http_method='GET', body=None,
|
||||
headers=None):
|
||||
"""Create metadata response
|
||||
"""
|
||||
headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
return headers, json.dumps(self.claims), 200
|
||||
|
||||
def validate_metadata(self, array, key, is_required=False, is_list=False, is_url=False, is_issuer=False):
|
||||
if not self.raise_errors:
|
||||
return
|
||||
|
||||
if key not in array:
|
||||
if is_required:
|
||||
raise ValueError("key {} is a mandatory metadata.".format(key))
|
||||
|
||||
elif is_issuer:
|
||||
if not array[key].startswith("https"):
|
||||
raise ValueError("key {}: {} must be an HTTPS URL".format(key, array[key]))
|
||||
if "?" in array[key] or "&" in array[key] or "#" in array[key]:
|
||||
raise ValueError("key {}: {} must not contain query or fragment components".format(key, array[key]))
|
||||
|
||||
elif is_url:
|
||||
if not array[key].startswith("http"):
|
||||
raise ValueError("key {}: {} must be an URL".format(key, array[key]))
|
||||
|
||||
elif is_list:
|
||||
if not isinstance(array[key], list):
|
||||
raise ValueError("key {}: {} must be an Array".format(key, array[key]))
|
||||
for elem in array[key]:
|
||||
if not isinstance(elem, str):
|
||||
raise ValueError("array {}: {} must contains only string (not {})".format(key, array[key], elem))
|
||||
|
||||
def validate_metadata_token(self, claims, endpoint):
|
||||
"""
|
||||
If the token endpoint is used in the grant type, the value of this
|
||||
parameter MUST be the same as the value of the "grant_type"
|
||||
parameter passed to the token endpoint defined in the grant type
|
||||
definition.
|
||||
"""
|
||||
self._grant_types.extend(endpoint._grant_types.keys())
|
||||
claims.setdefault("token_endpoint_auth_methods_supported", ["client_secret_post", "client_secret_basic"])
|
||||
|
||||
self.validate_metadata(claims, "token_endpoint_auth_methods_supported", is_list=True)
|
||||
self.validate_metadata(claims, "token_endpoint_auth_signing_alg_values_supported", is_list=True)
|
||||
self.validate_metadata(claims, "token_endpoint", is_required=True, is_url=True)
|
||||
|
||||
def validate_metadata_authorization(self, claims, endpoint):
|
||||
claims.setdefault("response_types_supported",
|
||||
list(filter(lambda x: x != "none", endpoint._response_types.keys())))
|
||||
claims.setdefault("response_modes_supported", ["query", "fragment"])
|
||||
|
||||
# The OAuth2.0 Implicit flow is defined as a "grant type" but it is not
|
||||
# using the "token" endpoint, as such, we have to add it explicitly to
|
||||
# the list of "grant_types_supported" when enabled.
|
||||
if "token" in claims["response_types_supported"]:
|
||||
self._grant_types.append("implicit")
|
||||
|
||||
self.validate_metadata(claims, "response_types_supported", is_required=True, is_list=True)
|
||||
self.validate_metadata(claims, "response_modes_supported", is_list=True)
|
||||
if "code" in claims["response_types_supported"]:
|
||||
code_grant = endpoint._response_types["code"]
|
||||
if not isinstance(code_grant, grant_types.AuthorizationCodeGrant) and hasattr(code_grant, "default_grant"):
|
||||
code_grant = code_grant.default_grant
|
||||
|
||||
claims.setdefault("code_challenge_methods_supported",
|
||||
list(code_grant._code_challenge_methods.keys()))
|
||||
self.validate_metadata(claims, "code_challenge_methods_supported", is_list=True)
|
||||
self.validate_metadata(claims, "authorization_endpoint", is_required=True, is_url=True)
|
||||
|
||||
def validate_metadata_revocation(self, claims, endpoint):
|
||||
claims.setdefault("revocation_endpoint_auth_methods_supported",
|
||||
["client_secret_post", "client_secret_basic"])
|
||||
|
||||
self.validate_metadata(claims, "revocation_endpoint_auth_methods_supported", is_list=True)
|
||||
self.validate_metadata(claims, "revocation_endpoint_auth_signing_alg_values_supported", is_list=True)
|
||||
self.validate_metadata(claims, "revocation_endpoint", is_required=True, is_url=True)
|
||||
|
||||
def validate_metadata_introspection(self, claims, endpoint):
|
||||
claims.setdefault("introspection_endpoint_auth_methods_supported",
|
||||
["client_secret_post", "client_secret_basic"])
|
||||
|
||||
self.validate_metadata(claims, "introspection_endpoint_auth_methods_supported", is_list=True)
|
||||
self.validate_metadata(claims, "introspection_endpoint_auth_signing_alg_values_supported", is_list=True)
|
||||
self.validate_metadata(claims, "introspection_endpoint", is_required=True, is_url=True)
|
||||
|
||||
def validate_metadata_server(self):
|
||||
"""
|
||||
Authorization servers can have metadata describing their
|
||||
configuration. The following authorization server metadata values
|
||||
are used by this specification. More details can be found in
|
||||
`RFC8414 section 2`_ :
|
||||
|
||||
issuer
|
||||
REQUIRED
|
||||
|
||||
authorization_endpoint
|
||||
URL of the authorization server's authorization endpoint
|
||||
[`RFC6749#Authorization`_]. This is REQUIRED unless no grant types are supported
|
||||
that use the authorization endpoint.
|
||||
|
||||
token_endpoint
|
||||
URL of the authorization server's token endpoint [`RFC6749#Token`_]. This
|
||||
is REQUIRED unless only the implicit grant type is supported.
|
||||
|
||||
scopes_supported
|
||||
RECOMMENDED.
|
||||
|
||||
response_types_supported
|
||||
REQUIRED.
|
||||
|
||||
Other OPTIONAL fields:
|
||||
jwks_uri,
|
||||
registration_endpoint,
|
||||
response_modes_supported
|
||||
|
||||
grant_types_supported
|
||||
OPTIONAL. JSON array containing a list of the OAuth 2.0 grant
|
||||
type values that this authorization server supports. The array
|
||||
values used are the same as those used with the "grant_types"
|
||||
parameter defined by "OAuth 2.0 Dynamic Client Registration
|
||||
Protocol" [`RFC7591`_]. If omitted, the default value is
|
||||
"["authorization_code", "implicit"]".
|
||||
|
||||
token_endpoint_auth_methods_supported
|
||||
|
||||
token_endpoint_auth_signing_alg_values_supported
|
||||
|
||||
service_documentation
|
||||
|
||||
ui_locales_supported
|
||||
|
||||
op_policy_uri
|
||||
|
||||
op_tos_uri
|
||||
|
||||
revocation_endpoint
|
||||
|
||||
revocation_endpoint_auth_methods_supported
|
||||
|
||||
revocation_endpoint_auth_signing_alg_values_supported
|
||||
|
||||
introspection_endpoint
|
||||
|
||||
introspection_endpoint_auth_methods_supported
|
||||
|
||||
introspection_endpoint_auth_signing_alg_values_supported
|
||||
|
||||
code_challenge_methods_supported
|
||||
|
||||
Additional authorization server metadata parameters MAY also be used.
|
||||
Some are defined by other specifications, such as OpenID Connect
|
||||
Discovery 1.0 [`OpenID.Discovery`_].
|
||||
|
||||
.. _`RFC8414 section 2`: https://tools.ietf.org/html/rfc8414#section-2
|
||||
.. _`RFC6749#Authorization`: https://tools.ietf.org/html/rfc6749#section-3.1
|
||||
.. _`RFC6749#Token`: https://tools.ietf.org/html/rfc6749#section-3.2
|
||||
.. _`RFC7591`: https://tools.ietf.org/html/rfc7591
|
||||
.. _`OpenID.Discovery`: https://openid.net/specs/openid-connect-discovery-1_0.html
|
||||
"""
|
||||
claims = copy.deepcopy(self.initial_claims)
|
||||
self.validate_metadata(claims, "issuer", is_required=True, is_issuer=True)
|
||||
self.validate_metadata(claims, "jwks_uri", is_url=True)
|
||||
self.validate_metadata(claims, "scopes_supported", is_list=True)
|
||||
self.validate_metadata(claims, "service_documentation", is_url=True)
|
||||
self.validate_metadata(claims, "ui_locales_supported", is_list=True)
|
||||
self.validate_metadata(claims, "op_policy_uri", is_url=True)
|
||||
self.validate_metadata(claims, "op_tos_uri", is_url=True)
|
||||
|
||||
self._grant_types = []
|
||||
for endpoint in self.endpoints:
|
||||
if isinstance(endpoint, TokenEndpoint):
|
||||
self.validate_metadata_token(claims, endpoint)
|
||||
if isinstance(endpoint, AuthorizationEndpoint):
|
||||
self.validate_metadata_authorization(claims, endpoint)
|
||||
if isinstance(endpoint, RevocationEndpoint):
|
||||
self.validate_metadata_revocation(claims, endpoint)
|
||||
if isinstance(endpoint, IntrospectEndpoint):
|
||||
self.validate_metadata_introspection(claims, endpoint)
|
||||
|
||||
# "grant_types_supported" is a combination of all OAuth2 grant types
|
||||
# allowed in the current provider implementation.
|
||||
claims.setdefault("grant_types_supported", self._grant_types)
|
||||
self.validate_metadata(claims, "grant_types_supported", is_list=True)
|
||||
return claims
|
|
@ -1,28 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
oauthlib.oauth2.rfc6749.endpoints.pre_configured
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
This module is an implementation of various endpoints needed
|
||||
for providing OAuth 2.0 RFC6749 servers.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from ..grant_types import (
|
||||
AuthorizationCodeGrant, ClientCredentialsGrant, ImplicitGrant,
|
||||
RefreshTokenGrant, ResourceOwnerPasswordCredentialsGrant,
|
||||
)
|
||||
from ..tokens import BearerToken
|
||||
from ..grant_types import AuthorizationCodeGrant
|
||||
from ..grant_types import ImplicitGrant
|
||||
from ..grant_types import ResourceOwnerPasswordCredentialsGrant
|
||||
from ..grant_types import ClientCredentialsGrant
|
||||
from ..grant_types import RefreshTokenGrant
|
||||
|
||||
from .authorization import AuthorizationEndpoint
|
||||
from .token import TokenEndpoint
|
||||
from .introspect import IntrospectEndpoint
|
||||
from .resource import ResourceEndpoint
|
||||
from .revocation import RevocationEndpoint
|
||||
from .token import TokenEndpoint
|
||||
|
||||
|
||||
class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
|
||||
RevocationEndpoint):
|
||||
class Server(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
|
||||
ResourceEndpoint, RevocationEndpoint):
|
||||
|
||||
"""An all-in-one endpoint featuring all four major grant types."""
|
||||
|
||||
|
@ -42,35 +38,40 @@ class Server(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
|
|||
:param kwargs: Extra parameters to pass to authorization-,
|
||||
token-, resource-, and revocation-endpoint constructors.
|
||||
"""
|
||||
auth_grant = AuthorizationCodeGrant(request_validator)
|
||||
implicit_grant = ImplicitGrant(request_validator)
|
||||
password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||
self.auth_grant = AuthorizationCodeGrant(request_validator)
|
||||
self.implicit_grant = ImplicitGrant(request_validator)
|
||||
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||
request_validator)
|
||||
credentials_grant = ClientCredentialsGrant(request_validator)
|
||||
refresh_grant = RefreshTokenGrant(request_validator)
|
||||
bearer = BearerToken(request_validator, token_generator,
|
||||
self.credentials_grant = ClientCredentialsGrant(request_validator)
|
||||
self.refresh_grant = RefreshTokenGrant(request_validator)
|
||||
|
||||
self.bearer = BearerToken(request_validator, token_generator,
|
||||
token_expires_in, refresh_token_generator)
|
||||
|
||||
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
||||
response_types={
|
||||
'code': auth_grant,
|
||||
'token': implicit_grant,
|
||||
'code': self.auth_grant,
|
||||
'token': self.implicit_grant,
|
||||
'none': self.auth_grant
|
||||
},
|
||||
default_token_type=bearer)
|
||||
default_token_type=self.bearer)
|
||||
|
||||
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
||||
grant_types={
|
||||
'authorization_code': auth_grant,
|
||||
'password': password_grant,
|
||||
'client_credentials': credentials_grant,
|
||||
'refresh_token': refresh_grant,
|
||||
'authorization_code': self.auth_grant,
|
||||
'password': self.password_grant,
|
||||
'client_credentials': self.credentials_grant,
|
||||
'refresh_token': self.refresh_grant,
|
||||
},
|
||||
default_token_type=bearer)
|
||||
default_token_type=self.bearer)
|
||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||
token_types={'Bearer': bearer})
|
||||
token_types={'Bearer': self.bearer})
|
||||
RevocationEndpoint.__init__(self, request_validator)
|
||||
IntrospectEndpoint.__init__(self, request_validator)
|
||||
|
||||
|
||||
class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
|
||||
RevocationEndpoint):
|
||||
class WebApplicationServer(AuthorizationEndpoint, IntrospectEndpoint, TokenEndpoint,
|
||||
ResourceEndpoint, RevocationEndpoint):
|
||||
|
||||
"""An all-in-one endpoint featuring Authorization code grant and Bearer tokens."""
|
||||
|
||||
|
@ -89,26 +90,27 @@ class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoin
|
|||
:param kwargs: Extra parameters to pass to authorization-,
|
||||
token-, resource-, and revocation-endpoint constructors.
|
||||
"""
|
||||
auth_grant = AuthorizationCodeGrant(request_validator)
|
||||
refresh_grant = RefreshTokenGrant(request_validator)
|
||||
bearer = BearerToken(request_validator, token_generator,
|
||||
self.auth_grant = AuthorizationCodeGrant(request_validator)
|
||||
self.refresh_grant = RefreshTokenGrant(request_validator)
|
||||
self.bearer = BearerToken(request_validator, token_generator,
|
||||
token_expires_in, refresh_token_generator)
|
||||
AuthorizationEndpoint.__init__(self, default_response_type='code',
|
||||
response_types={'code': auth_grant},
|
||||
default_token_type=bearer)
|
||||
response_types={'code': self.auth_grant},
|
||||
default_token_type=self.bearer)
|
||||
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
|
||||
grant_types={
|
||||
'authorization_code': auth_grant,
|
||||
'refresh_token': refresh_grant,
|
||||
'authorization_code': self.auth_grant,
|
||||
'refresh_token': self.refresh_grant,
|
||||
},
|
||||
default_token_type=bearer)
|
||||
default_token_type=self.bearer)
|
||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||
token_types={'Bearer': bearer})
|
||||
token_types={'Bearer': self.bearer})
|
||||
RevocationEndpoint.__init__(self, request_validator)
|
||||
IntrospectEndpoint.__init__(self, request_validator)
|
||||
|
||||
|
||||
class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint,
|
||||
RevocationEndpoint):
|
||||
class MobileApplicationServer(AuthorizationEndpoint, IntrospectEndpoint,
|
||||
ResourceEndpoint, RevocationEndpoint):
|
||||
|
||||
"""An all-in-one endpoint featuring Implicit code grant and Bearer tokens."""
|
||||
|
||||
|
@ -127,21 +129,23 @@ class MobileApplicationServer(AuthorizationEndpoint, ResourceEndpoint,
|
|||
:param kwargs: Extra parameters to pass to authorization-,
|
||||
token-, resource-, and revocation-endpoint constructors.
|
||||
"""
|
||||
implicit_grant = ImplicitGrant(request_validator)
|
||||
bearer = BearerToken(request_validator, token_generator,
|
||||
self.implicit_grant = ImplicitGrant(request_validator)
|
||||
self.bearer = BearerToken(request_validator, token_generator,
|
||||
token_expires_in, refresh_token_generator)
|
||||
AuthorizationEndpoint.__init__(self, default_response_type='token',
|
||||
response_types={
|
||||
'token': implicit_grant},
|
||||
default_token_type=bearer)
|
||||
'token': self.implicit_grant},
|
||||
default_token_type=self.bearer)
|
||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||
token_types={'Bearer': bearer})
|
||||
token_types={'Bearer': self.bearer})
|
||||
RevocationEndpoint.__init__(self, request_validator,
|
||||
supported_token_types=['access_token'])
|
||||
IntrospectEndpoint.__init__(self, request_validator,
|
||||
supported_token_types=['access_token'])
|
||||
|
||||
|
||||
class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint,
|
||||
RevocationEndpoint):
|
||||
class LegacyApplicationServer(TokenEndpoint, IntrospectEndpoint,
|
||||
ResourceEndpoint, RevocationEndpoint):
|
||||
|
||||
"""An all-in-one endpoint featuring Resource Owner Password Credentials grant and Bearer tokens."""
|
||||
|
||||
|
@ -160,24 +164,25 @@ class LegacyApplicationServer(TokenEndpoint, ResourceEndpoint,
|
|||
:param kwargs: Extra parameters to pass to authorization-,
|
||||
token-, resource-, and revocation-endpoint constructors.
|
||||
"""
|
||||
password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||
self.password_grant = ResourceOwnerPasswordCredentialsGrant(
|
||||
request_validator)
|
||||
refresh_grant = RefreshTokenGrant(request_validator)
|
||||
bearer = BearerToken(request_validator, token_generator,
|
||||
self.refresh_grant = RefreshTokenGrant(request_validator)
|
||||
self.bearer = BearerToken(request_validator, token_generator,
|
||||
token_expires_in, refresh_token_generator)
|
||||
TokenEndpoint.__init__(self, default_grant_type='password',
|
||||
grant_types={
|
||||
'password': password_grant,
|
||||
'refresh_token': refresh_grant,
|
||||
'password': self.password_grant,
|
||||
'refresh_token': self.refresh_grant,
|
||||
},
|
||||
default_token_type=bearer)
|
||||
default_token_type=self.bearer)
|
||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||
token_types={'Bearer': bearer})
|
||||
token_types={'Bearer': self.bearer})
|
||||
RevocationEndpoint.__init__(self, request_validator)
|
||||
IntrospectEndpoint.__init__(self, request_validator)
|
||||
|
||||
|
||||
class BackendApplicationServer(TokenEndpoint, ResourceEndpoint,
|
||||
RevocationEndpoint):
|
||||
class BackendApplicationServer(TokenEndpoint, IntrospectEndpoint,
|
||||
ResourceEndpoint, RevocationEndpoint):
|
||||
|
||||
"""An all-in-one endpoint featuring Client Credentials grant and Bearer tokens."""
|
||||
|
||||
|
@ -196,14 +201,16 @@ class BackendApplicationServer(TokenEndpoint, ResourceEndpoint,
|
|||
:param kwargs: Extra parameters to pass to authorization-,
|
||||
token-, resource-, and revocation-endpoint constructors.
|
||||
"""
|
||||
credentials_grant = ClientCredentialsGrant(request_validator)
|
||||
bearer = BearerToken(request_validator, token_generator,
|
||||
self.credentials_grant = ClientCredentialsGrant(request_validator)
|
||||
self.bearer = BearerToken(request_validator, token_generator,
|
||||
token_expires_in, refresh_token_generator)
|
||||
TokenEndpoint.__init__(self, default_grant_type='client_credentials',
|
||||
grant_types={
|
||||
'client_credentials': credentials_grant},
|
||||
default_token_type=bearer)
|
||||
'client_credentials': self.credentials_grant},
|
||||
default_token_type=self.bearer)
|
||||
ResourceEndpoint.__init__(self, default_token='Bearer',
|
||||
token_types={'Bearer': bearer})
|
||||
token_types={'Bearer': self.bearer})
|
||||
RevocationEndpoint.__init__(self, request_validator,
|
||||
supported_token_types=['access_token'])
|
||||
IntrospectEndpoint.__init__(self, request_validator,
|
||||
supported_token_types=['access_token'])
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -6,8 +5,6 @@ oauthlib.oauth2.rfc6749
|
|||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from oauthlib.common import Request
|
||||
|
@ -83,5 +80,5 @@ class ResourceEndpoint(BaseEndpoint):
|
|||
to give an estimation based on the request.
|
||||
"""
|
||||
estimates = sorted(((t.estimate_type(request), n)
|
||||
for n, t in self.tokens.items()))
|
||||
for n, t in self.tokens.items()), reverse=True)
|
||||
return estimates[0][1] if len(estimates) else None
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749.endpoint.revocation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An implementation of the OAuth 2 `Token Revocation`_ spec (draft 11).
|
||||
|
||||
.. _`Token Revocation`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11
|
||||
.. _`Token Revocation`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from oauthlib.common import Request
|
||||
|
||||
from ..errors import OAuth2Error
|
||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||
from ..errors import InvalidClientError, UnsupportedTokenTypeError
|
||||
from ..errors import InvalidRequestError, OAuth2Error
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -29,6 +25,7 @@ class RevocationEndpoint(BaseEndpoint):
|
|||
"""
|
||||
|
||||
valid_token_types = ('access_token', 'refresh_token')
|
||||
valid_request_methods = ('POST',)
|
||||
|
||||
def __init__(self, request_validator, supported_token_types=None,
|
||||
enable_jsonp=False):
|
||||
|
@ -59,6 +56,11 @@ class RevocationEndpoint(BaseEndpoint):
|
|||
An invalid token type hint value is ignored by the authorization server
|
||||
and does not influence the revocation response.
|
||||
"""
|
||||
resp_headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-store',
|
||||
'Pragma': 'no-cache',
|
||||
}
|
||||
request = Request(
|
||||
uri, http_method=http_method, body=body, headers=headers)
|
||||
try:
|
||||
|
@ -68,8 +70,9 @@ class RevocationEndpoint(BaseEndpoint):
|
|||
log.debug('Client error during validation of %r. %r.', request, e)
|
||||
response_body = e.json
|
||||
if self.enable_jsonp and request.callback:
|
||||
response_body = '%s(%s);' % (request.callback, response_body)
|
||||
return {}, response_body, e.status_code
|
||||
response_body = '{}({});'.format(request.callback, response_body)
|
||||
resp_headers.update(e.headers)
|
||||
return resp_headers, response_body, e.status_code
|
||||
|
||||
self.request_validator.revoke_token(request.token,
|
||||
request.token_type_hint, request)
|
||||
|
@ -110,21 +113,14 @@ class RevocationEndpoint(BaseEndpoint):
|
|||
The client also includes its authentication credentials as described in
|
||||
`Section 2.3`_. of [`RFC6749`_].
|
||||
|
||||
.. _`section 1.4`: http://tools.ietf.org/html/rfc6749#section-1.4
|
||||
.. _`section 1.5`: http://tools.ietf.org/html/rfc6749#section-1.5
|
||||
.. _`section 2.3`: http://tools.ietf.org/html/rfc6749#section-2.3
|
||||
.. _`Section 4.1.2`: http://tools.ietf.org/html/draft-ietf-oauth-revocation-11#section-4.1.2
|
||||
.. _`RFC6749`: http://tools.ietf.org/html/rfc6749
|
||||
.. _`section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4
|
||||
.. _`section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5
|
||||
.. _`section 2.3`: https://tools.ietf.org/html/rfc6749#section-2.3
|
||||
.. _`Section 4.1.2`: https://tools.ietf.org/html/draft-ietf-oauth-revocation-11#section-4.1.2
|
||||
.. _`RFC6749`: https://tools.ietf.org/html/rfc6749
|
||||
"""
|
||||
if not request.token:
|
||||
raise InvalidRequestError(request=request,
|
||||
description='Missing token parameter.')
|
||||
|
||||
if self.request_validator.client_authentication_required(request):
|
||||
if not self.request_validator.authenticate_client(request):
|
||||
raise InvalidClientError(request=request)
|
||||
|
||||
if (request.token_type_hint and
|
||||
request.token_type_hint in self.valid_token_types and
|
||||
request.token_type_hint not in self.supported_token_types):
|
||||
raise UnsupportedTokenTypeError(request=request)
|
||||
self._raise_on_bad_method(request)
|
||||
self._raise_on_bad_post_request(request)
|
||||
self._raise_on_missing_token(request)
|
||||
self._raise_on_invalid_client(request)
|
||||
self._raise_on_unsupported_token(request)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
oauthlib.oauth2.rfc6749
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -6,15 +5,13 @@ oauthlib.oauth2.rfc6749
|
|||
This module is an implementation of various logic needed
|
||||
for consuming and providing OAuth 2.0 RFC6749.
|
||||
"""
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from oauthlib.common import Request
|
||||
from oauthlib.oauth2.rfc6749 import utils
|
||||
|
||||
from .base import BaseEndpoint, catch_errors_and_unavailability
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -39,7 +36,6 @@ class TokenEndpoint(BaseEndpoint):
|
|||
https://example.com/path?query=component # OK
|
||||
https://example.com/path?query=component#fragment # Not OK
|
||||
|
||||
Since requests to the authorization endpoint result in user
|
||||
Since requests to the token endpoint result in the transmission of
|
||||
clear-text credentials (in the HTTP request and response), the
|
||||
authorization server MUST require the use of TLS as described in
|
||||
|
@ -59,9 +55,11 @@ class TokenEndpoint(BaseEndpoint):
|
|||
|
||||
# Delegated to each grant type.
|
||||
|
||||
.. _`Appendix B`: http://tools.ietf.org/html/rfc6749#appendix-B
|
||||
.. _`Appendix B`: https://tools.ietf.org/html/rfc6749#appendix-B
|
||||
"""
|
||||
|
||||
valid_request_methods = ('POST',)
|
||||
|
||||
def __init__(self, default_grant_type, default_token_type, grant_types):
|
||||
BaseEndpoint.__init__(self)
|
||||
self._grant_types = grant_types
|
||||
|
@ -85,16 +83,37 @@ class TokenEndpoint(BaseEndpoint):
|
|||
return self._default_token_type
|
||||
|
||||
@catch_errors_and_unavailability
|
||||
def create_token_response(self, uri, http_method='GET', body=None,
|
||||
headers=None, credentials=None):
|
||||
def create_token_response(self, uri, http_method='POST', body=None,
|
||||
headers=None, credentials=None, grant_type_for_scope=None,
|
||||
claims=None):
|
||||
"""Extract grant_type and route to the designated handler."""
|
||||
request = Request(
|
||||
uri, http_method=http_method, body=body, headers=headers)
|
||||
request.scopes = None
|
||||
self.validate_token_request(request)
|
||||
# 'scope' is an allowed Token Request param in both the "Resource Owner Password Credentials Grant"
|
||||
# and "Client Credentials Grant" flows
|
||||
# https://tools.ietf.org/html/rfc6749#section-4.3.2
|
||||
# https://tools.ietf.org/html/rfc6749#section-4.4.2
|
||||
request.scopes = utils.scope_to_list(request.scope)
|
||||
|
||||
request.extra_credentials = credentials
|
||||
if grant_type_for_scope:
|
||||
request.grant_type = grant_type_for_scope
|
||||
|
||||
# OpenID Connect claims, if provided. The server using oauthlib might choose
|
||||
# to implement the claims parameter of the Authorization Request. In this case
|
||||
# it should retrieve those claims and pass them via the claims argument here,
|
||||
# as a dict.
|
||||
if claims:
|
||||
request.claims = claims
|
||||
|
||||
grant_type_handler = self.grant_types.get(request.grant_type,
|
||||
self.default_grant_type_handler)
|
||||
log.debug('Dispatching grant_type %s request to %r.',
|
||||
request.grant_type, grant_type_handler)
|
||||
return grant_type_handler.create_token_response(
|
||||
request, self.default_token_type)
|
||||
|
||||
def validate_token_request(self, request):
|
||||
self._raise_on_bad_method(request)
|
||||
self._raise_on_bad_post_request(request)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue