updates
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# flake8: noqa
|
||||
|
||||
from .authorization_server import AuthorizationServer
|
||||
from .cache import create_exists_nonce_func
|
||||
from .cache import register_nonce_hooks
|
||||
from .cache import register_temporary_credential_hooks
|
||||
from .resource_protector import ResourceProtector
|
||||
from .resource_protector import current_credential
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,168 @@
|
||||
import logging
|
||||
|
||||
from flask import Response
|
||||
from flask import request as flask_req
|
||||
from werkzeug.utils import import_string
|
||||
|
||||
from authlib.common.security import generate_token
|
||||
from authlib.common.urls import url_encode
|
||||
from authlib.oauth1 import AuthorizationServer as _AuthorizationServer
|
||||
from authlib.oauth1 import OAuth1Request
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthorizationServer(_AuthorizationServer):
|
||||
"""Flask implementation of :class:`authlib.rfc5849.AuthorizationServer`.
|
||||
Initialize it with Flask app instance, client model class and cache::
|
||||
|
||||
server = AuthorizationServer(app=app, query_client=query_client)
|
||||
# or initialize lazily
|
||||
server = AuthorizationServer()
|
||||
server.init_app(app, query_client=query_client)
|
||||
|
||||
:param app: A Flask app instance
|
||||
:param query_client: A function to get client by client_id. The client
|
||||
model class MUST implement the methods described by
|
||||
:class:`~authlib.oauth1.rfc5849.ClientMixin`.
|
||||
:param token_generator: A function to generate token
|
||||
"""
|
||||
|
||||
def __init__(self, app=None, query_client=None, token_generator=None):
|
||||
self.app = app
|
||||
self.query_client = query_client
|
||||
self.token_generator = token_generator
|
||||
|
||||
self._hooks = {
|
||||
"exists_nonce": None,
|
||||
"create_temporary_credential": None,
|
||||
"get_temporary_credential": None,
|
||||
"delete_temporary_credential": None,
|
||||
"create_authorization_verifier": None,
|
||||
"create_token_credential": None,
|
||||
}
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app, query_client=None, token_generator=None):
|
||||
if query_client is not None:
|
||||
self.query_client = query_client
|
||||
if token_generator is not None:
|
||||
self.token_generator = token_generator
|
||||
|
||||
if self.token_generator is None:
|
||||
self.token_generator = self.create_token_generator(app)
|
||||
|
||||
methods = app.config.get("OAUTH1_SUPPORTED_SIGNATURE_METHODS")
|
||||
if methods and isinstance(methods, (list, tuple)):
|
||||
self.SUPPORTED_SIGNATURE_METHODS = methods
|
||||
|
||||
self.app = app
|
||||
|
||||
def register_hook(self, name, func):
|
||||
if name not in self._hooks:
|
||||
raise ValueError('Invalid "name" of hook')
|
||||
self._hooks[name] = func
|
||||
|
||||
def create_token_generator(self, app):
|
||||
token_generator = app.config.get("OAUTH1_TOKEN_GENERATOR")
|
||||
|
||||
if isinstance(token_generator, str):
|
||||
token_generator = import_string(token_generator)
|
||||
else:
|
||||
length = app.config.get("OAUTH1_TOKEN_LENGTH", 42)
|
||||
|
||||
def token_generator():
|
||||
return generate_token(length)
|
||||
|
||||
secret_generator = app.config.get("OAUTH1_TOKEN_SECRET_GENERATOR")
|
||||
if isinstance(secret_generator, str):
|
||||
secret_generator = import_string(secret_generator)
|
||||
else:
|
||||
length = app.config.get("OAUTH1_TOKEN_SECRET_LENGTH", 48)
|
||||
|
||||
def secret_generator():
|
||||
return generate_token(length)
|
||||
|
||||
def create_token():
|
||||
return {
|
||||
"oauth_token": token_generator(),
|
||||
"oauth_token_secret": secret_generator(),
|
||||
}
|
||||
|
||||
return create_token
|
||||
|
||||
def get_client_by_id(self, client_id):
|
||||
return self.query_client(client_id)
|
||||
|
||||
def exists_nonce(self, nonce, request):
|
||||
func = self._hooks["exists_nonce"]
|
||||
if callable(func):
|
||||
timestamp = request.timestamp
|
||||
client_id = request.client_id
|
||||
token = request.token
|
||||
return func(nonce, timestamp, client_id, token)
|
||||
|
||||
raise RuntimeError('"exists_nonce" hook is required.')
|
||||
|
||||
def create_temporary_credential(self, request):
|
||||
func = self._hooks["create_temporary_credential"]
|
||||
if callable(func):
|
||||
token = self.token_generator()
|
||||
return func(token, request.client_id, request.redirect_uri)
|
||||
raise RuntimeError('"create_temporary_credential" hook is required.')
|
||||
|
||||
def get_temporary_credential(self, request):
|
||||
func = self._hooks["get_temporary_credential"]
|
||||
if callable(func):
|
||||
return func(request.token)
|
||||
|
||||
raise RuntimeError('"get_temporary_credential" hook is required.')
|
||||
|
||||
def delete_temporary_credential(self, request):
|
||||
func = self._hooks["delete_temporary_credential"]
|
||||
if callable(func):
|
||||
return func(request.token)
|
||||
|
||||
raise RuntimeError('"delete_temporary_credential" hook is required.')
|
||||
|
||||
def create_authorization_verifier(self, request):
|
||||
func = self._hooks["create_authorization_verifier"]
|
||||
if callable(func):
|
||||
verifier = generate_token(36)
|
||||
func(request.credential, request.user, verifier)
|
||||
return verifier
|
||||
|
||||
raise RuntimeError('"create_authorization_verifier" hook is required.')
|
||||
|
||||
def create_token_credential(self, request):
|
||||
func = self._hooks["create_token_credential"]
|
||||
if callable(func):
|
||||
temporary_credential = request.credential
|
||||
token = self.token_generator()
|
||||
return func(token, temporary_credential)
|
||||
|
||||
raise RuntimeError('"create_token_credential" hook is required.')
|
||||
|
||||
def check_authorization_request(self):
|
||||
req = self.create_oauth1_request(None)
|
||||
self.validate_authorization_request(req)
|
||||
return req
|
||||
|
||||
def create_authorization_response(self, request=None, grant_user=None):
|
||||
return super().create_authorization_response(request, grant_user)
|
||||
|
||||
def create_token_response(self, request=None):
|
||||
return super().create_token_response(request)
|
||||
|
||||
def create_oauth1_request(self, request):
|
||||
if request is None:
|
||||
request = flask_req
|
||||
if request.method in ("POST", "PUT"):
|
||||
body = request.form.to_dict(flat=True)
|
||||
else:
|
||||
body = None
|
||||
return OAuth1Request(request.method, request.url, body, request.headers)
|
||||
|
||||
def handle_response(self, status_code, payload, headers):
|
||||
return Response(url_encode(payload), status=status_code, headers=headers)
|
||||
@@ -0,0 +1,88 @@
|
||||
from authlib.oauth1 import TemporaryCredential
|
||||
|
||||
|
||||
def register_temporary_credential_hooks(
|
||||
authorization_server, cache, key_prefix="temporary_credential:"
|
||||
):
|
||||
"""Register temporary credential related hooks to authorization server.
|
||||
|
||||
:param authorization_server: AuthorizationServer instance
|
||||
:param cache: Cache instance
|
||||
:param key_prefix: key prefix for temporary credential
|
||||
"""
|
||||
|
||||
def create_temporary_credential(token, client_id, redirect_uri):
|
||||
key = key_prefix + token["oauth_token"]
|
||||
token["client_id"] = client_id
|
||||
if redirect_uri:
|
||||
token["oauth_callback"] = redirect_uri
|
||||
|
||||
cache.set(key, token, timeout=86400) # cache for one day
|
||||
return TemporaryCredential(token)
|
||||
|
||||
def get_temporary_credential(oauth_token):
|
||||
if not oauth_token:
|
||||
return None
|
||||
key = key_prefix + oauth_token
|
||||
value = cache.get(key)
|
||||
if value:
|
||||
return TemporaryCredential(value)
|
||||
|
||||
def delete_temporary_credential(oauth_token):
|
||||
if oauth_token:
|
||||
key = key_prefix + oauth_token
|
||||
cache.delete(key)
|
||||
|
||||
def create_authorization_verifier(credential, grant_user, verifier):
|
||||
key = key_prefix + credential.get_oauth_token()
|
||||
credential["oauth_verifier"] = verifier
|
||||
credential["user_id"] = grant_user.get_user_id()
|
||||
cache.set(key, credential, timeout=86400)
|
||||
return credential
|
||||
|
||||
authorization_server.register_hook(
|
||||
"create_temporary_credential", create_temporary_credential
|
||||
)
|
||||
authorization_server.register_hook(
|
||||
"get_temporary_credential", get_temporary_credential
|
||||
)
|
||||
authorization_server.register_hook(
|
||||
"delete_temporary_credential", delete_temporary_credential
|
||||
)
|
||||
authorization_server.register_hook(
|
||||
"create_authorization_verifier", create_authorization_verifier
|
||||
)
|
||||
|
||||
|
||||
def create_exists_nonce_func(cache, key_prefix="nonce:", expires=86400):
|
||||
"""Create an ``exists_nonce`` function that can be used in hooks and
|
||||
resource protector.
|
||||
|
||||
:param cache: Cache instance
|
||||
:param key_prefix: key prefix for temporary credential
|
||||
:param expires: Expire time for nonce
|
||||
"""
|
||||
|
||||
def exists_nonce(nonce, timestamp, client_id, oauth_token):
|
||||
key = f"{key_prefix}{nonce}-{timestamp}-{client_id}"
|
||||
if oauth_token:
|
||||
key = f"{key}-{oauth_token}"
|
||||
rv = cache.has(key)
|
||||
cache.set(key, 1, timeout=expires)
|
||||
return rv
|
||||
|
||||
return exists_nonce
|
||||
|
||||
|
||||
def register_nonce_hooks(
|
||||
authorization_server, cache, key_prefix="nonce:", expires=86400
|
||||
):
|
||||
"""Register nonce related hooks to authorization server.
|
||||
|
||||
:param authorization_server: AuthorizationServer instance
|
||||
:param cache: Cache instance
|
||||
:param key_prefix: key prefix for temporary credential
|
||||
:param expires: Expire time for nonce
|
||||
"""
|
||||
exists_nonce = create_exists_nonce_func(cache, key_prefix, expires)
|
||||
authorization_server.register_hook("exists_nonce", exists_nonce)
|
||||
@@ -0,0 +1,121 @@
|
||||
import functools
|
||||
|
||||
from flask import Response
|
||||
from flask import g
|
||||
from flask import json
|
||||
from flask import request as _req
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from authlib.consts import default_json_headers
|
||||
from authlib.oauth1 import ResourceProtector as _ResourceProtector
|
||||
from authlib.oauth1.errors import OAuth1Error
|
||||
|
||||
|
||||
class ResourceProtector(_ResourceProtector):
|
||||
"""A protecting method for resource servers. Initialize a resource
|
||||
protector with the these method:
|
||||
|
||||
1. query_client
|
||||
2. query_token,
|
||||
3. exists_nonce
|
||||
|
||||
Usually, a ``query_client`` method would look like (if using SQLAlchemy)::
|
||||
|
||||
def query_client(client_id):
|
||||
return Client.query.filter_by(client_id=client_id).first()
|
||||
|
||||
A ``query_token`` method accept two parameters, ``client_id`` and ``oauth_token``::
|
||||
|
||||
def query_token(client_id, oauth_token):
|
||||
return Token.query.filter_by(
|
||||
client_id=client_id, oauth_token=oauth_token
|
||||
).first()
|
||||
|
||||
And for ``exists_nonce``, if using cache, we have a built-in hook to create this method::
|
||||
|
||||
from authlib.integrations.flask_oauth1 import create_exists_nonce_func
|
||||
|
||||
exists_nonce = create_exists_nonce_func(cache)
|
||||
|
||||
Then initialize the resource protector with those methods::
|
||||
|
||||
require_oauth = ResourceProtector(
|
||||
app,
|
||||
query_client=query_client,
|
||||
query_token=query_token,
|
||||
exists_nonce=exists_nonce,
|
||||
)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, app=None, query_client=None, query_token=None, exists_nonce=None
|
||||
):
|
||||
self.query_client = query_client
|
||||
self.query_token = query_token
|
||||
self._exists_nonce = exists_nonce
|
||||
|
||||
self.app = app
|
||||
if app:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app, query_client=None, query_token=None, exists_nonce=None):
|
||||
if query_client is not None:
|
||||
self.query_client = query_client
|
||||
if query_token is not None:
|
||||
self.query_token = query_token
|
||||
if exists_nonce is not None:
|
||||
self._exists_nonce = exists_nonce
|
||||
|
||||
methods = app.config.get("OAUTH1_SUPPORTED_SIGNATURE_METHODS")
|
||||
if methods and isinstance(methods, (list, tuple)):
|
||||
self.SUPPORTED_SIGNATURE_METHODS = methods
|
||||
|
||||
self.app = app
|
||||
|
||||
def get_client_by_id(self, client_id):
|
||||
return self.query_client(client_id)
|
||||
|
||||
def get_token_credential(self, request):
|
||||
return self.query_token(request.client_id, request.token)
|
||||
|
||||
def exists_nonce(self, nonce, request):
|
||||
if not self._exists_nonce:
|
||||
raise RuntimeError('"exists_nonce" function is required.')
|
||||
|
||||
timestamp = request.timestamp
|
||||
client_id = request.client_id
|
||||
token = request.token
|
||||
return self._exists_nonce(nonce, timestamp, client_id, token)
|
||||
|
||||
def acquire_credential(self):
|
||||
req = self.validate_request(
|
||||
_req.method, _req.url, _req.form.to_dict(flat=True), _req.headers
|
||||
)
|
||||
g.authlib_server_oauth1_credential = req.credential
|
||||
return req.credential
|
||||
|
||||
def __call__(self, scope=None):
|
||||
def wrapper(f):
|
||||
@functools.wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
try:
|
||||
self.acquire_credential()
|
||||
except OAuth1Error as error:
|
||||
body = dict(error.get_body())
|
||||
return Response(
|
||||
json.dumps(body),
|
||||
status=error.status_code,
|
||||
headers=default_json_headers,
|
||||
)
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def _get_current_credential():
|
||||
return g.get("authlib_server_oauth1_credential")
|
||||
|
||||
|
||||
current_credential = LocalProxy(_get_current_credential)
|
||||
Reference in New Issue
Block a user