venv added, updated

This commit is contained in:
Norbert
2024-09-13 09:46:28 +02:00
parent 577596d9f3
commit 82af8c809a
4812 changed files with 640223 additions and 2 deletions

View File

@@ -0,0 +1,26 @@
"""
authlib.oauth2.rfc6750
~~~~~~~~~~~~~~~~~~~~~~
This module represents a direct implementation of
The OAuth 2.0 Authorization Framework: Bearer Token Usage.
https://tools.ietf.org/html/rfc6750
"""
from .errors import InvalidTokenError, InsufficientScopeError
from .parameters import add_bearer_token
from .token import BearerTokenGenerator
from .validator import BearerTokenValidator
# TODO: add deprecation
BearerToken = BearerTokenGenerator
__all__ = [
'InvalidTokenError', 'InsufficientScopeError',
'add_bearer_token',
'BearerToken',
'BearerTokenGenerator',
'BearerTokenValidator',
]

View File

@@ -0,0 +1,80 @@
"""
authlib.rfc6750.errors
~~~~~~~~~~~~~~~~~~~~~~
OAuth Extensions Error Registration. When a request fails,
the resource server responds using the appropriate HTTP
status code and includes one of the following error codes
in the response.
https://tools.ietf.org/html/rfc6750#section-6.2
:copyright: (c) 2017 by Hsiaoming Yang.
"""
from ..base import OAuth2Error
__all__ = [
'InvalidTokenError', 'InsufficientScopeError'
]
class InvalidTokenError(OAuth2Error):
"""The access token provided is expired, revoked, malformed, or
invalid for other reasons. The resource SHOULD respond with
the HTTP 401 (Unauthorized) status code. The client MAY
request a new access token and retry the protected resource
request.
https://tools.ietf.org/html/rfc6750#section-3.1
"""
error = 'invalid_token'
description = (
'The access token provided is expired, revoked, malformed, '
'or invalid for other reasons.'
)
status_code = 401
def __init__(self, description=None, uri=None, status_code=None,
state=None, realm=None, **extra_attributes):
super().__init__(
description, uri, status_code, state)
self.realm = realm
self.extra_attributes = extra_attributes
def get_headers(self):
"""If the protected resource request does not include authentication
credentials or does not contain an access token that enables access
to the protected resource, the resource server MUST include the HTTP
"WWW-Authenticate" response header field; it MAY include it in
response to other conditions as well.
https://tools.ietf.org/html/rfc6750#section-3
"""
headers = super().get_headers()
extras = []
if self.realm:
extras.append(f'realm="{self.realm}"')
if self.extra_attributes:
extras.extend([f'{k}="{self.extra_attributes[k]}"' for k in self.extra_attributes])
extras.append(f'error="{self.error}"')
error_description = self.get_error_description()
extras.append(f'error_description="{error_description}"')
headers.append(
('WWW-Authenticate', 'Bearer ' + ', '.join(extras))
)
return headers
class InsufficientScopeError(OAuth2Error):
"""The request requires higher privileges than provided by the
access token. The resource server SHOULD respond with the HTTP
403 (Forbidden) status code and MAY include the "scope"
attribute with the scope necessary to access the protected
resource.
https://tools.ietf.org/html/rfc6750#section-3.1
"""
error = 'insufficient_scope'
description = 'The request requires higher privileges than provided by the access token.'
status_code = 403

View File

@@ -0,0 +1,41 @@
from authlib.common.urls import add_params_to_qs, add_params_to_uri
def add_to_uri(token, uri):
"""Add a Bearer Token to the request URI.
Not recommended, use only if client can't use authorization header or body.
http://www.example.com/path?access_token=h480djs93hd8
"""
return add_params_to_uri(uri, [('access_token', token)])
def add_to_headers(token, headers=None):
"""Add a Bearer Token to the request URI.
Recommended method of passing bearer tokens.
Authorization: Bearer h480djs93hd8
"""
headers = headers or {}
headers['Authorization'] = f'Bearer {token}'
return headers
def add_to_body(token, body=None):
"""Add a Bearer Token to the request body.
access_token=h480djs93hd8
"""
if body is None:
body = ''
return add_params_to_qs(body, [('access_token', token)])
def add_bearer_token(token, uri, headers, body, placement='header'):
if placement in ('uri', 'url', 'query'):
uri = add_to_uri(token, uri)
elif placement in ('header', 'headers'):
headers = add_to_headers(token, headers)
elif placement == 'body':
body = add_to_body(token, body)
return uri, headers, body

View File

@@ -0,0 +1,88 @@
class BearerTokenGenerator:
"""Bearer token generator which can create the payload for token response
by OAuth 2 server. A typical token response would be:
.. code-block:: http
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"mF_9.B5f-4.1JqM",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
"""
#: default expires_in value
DEFAULT_EXPIRES_IN = 3600
#: default expires_in value differentiate by grant_type
GRANT_TYPES_EXPIRES_IN = {
'authorization_code': 864000,
'implicit': 3600,
'password': 864000,
'client_credentials': 864000
}
def __init__(self, access_token_generator,
refresh_token_generator=None,
expires_generator=None):
self.access_token_generator = access_token_generator
self.refresh_token_generator = refresh_token_generator
self.expires_generator = expires_generator
def _get_expires_in(self, client, grant_type):
if self.expires_generator is None:
expires_in = self.GRANT_TYPES_EXPIRES_IN.get(
grant_type, self.DEFAULT_EXPIRES_IN)
elif callable(self.expires_generator):
expires_in = self.expires_generator(client, grant_type)
elif isinstance(self.expires_generator, int):
expires_in = self.expires_generator
else:
expires_in = self.DEFAULT_EXPIRES_IN
return expires_in
@staticmethod
def get_allowed_scope(client, scope):
if scope:
scope = client.get_allowed_scope(scope)
return scope
def generate(self, grant_type, client, user=None, scope=None,
expires_in=None, include_refresh_token=True):
"""Generate a bearer token for OAuth 2.0 authorization token endpoint.
:param client: the client that making the request.
:param grant_type: current requested grant_type.
:param user: current authorized user.
:param expires_in: if provided, use this value as expires_in.
:param scope: current requested scope.
:param include_refresh_token: should refresh_token be included.
:return: Token dict
"""
scope = self.get_allowed_scope(client, scope)
access_token = self.access_token_generator(
client=client, grant_type=grant_type, user=user, scope=scope)
if expires_in is None:
expires_in = self._get_expires_in(client, grant_type)
token = {
'token_type': 'Bearer',
'access_token': access_token,
}
if expires_in:
token['expires_in'] = expires_in
if include_refresh_token and self.refresh_token_generator:
token['refresh_token'] = self.refresh_token_generator(
client=client, grant_type=grant_type, user=user, scope=scope)
if scope:
token['scope'] = scope
return token
def __call__(self, grant_type, client, user=None, scope=None,
expires_in=None, include_refresh_token=True):
return self.generate(grant_type, client, user, scope, expires_in, include_refresh_token)

View File

@@ -0,0 +1,39 @@
"""
authlib.oauth2.rfc6750.validator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Validate Bearer Token for in request, scope and token.
"""
from ..rfc6749 import TokenValidator
from .errors import (
InvalidTokenError,
InsufficientScopeError
)
class BearerTokenValidator(TokenValidator):
TOKEN_TYPE = 'bearer'
def authenticate_token(self, token_string):
"""A method to query token from database with the given token string.
Developers MUST re-implement this method. For instance::
def authenticate_token(self, token_string):
return get_token_from_database(token_string)
:param token_string: A string to represent the access_token.
:return: token
"""
raise NotImplementedError()
def validate_token(self, token, scopes, request):
"""Check if token is active and matches the requested scopes."""
if not token:
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
if token.is_expired():
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
if token.is_revoked():
raise InvalidTokenError(realm=self.realm, extra_attributes=self.extra_attributes)
if self.scope_insufficient(token.get_scope(), scopes):
raise InsufficientScopeError()