Using flask-jwt-extended callbacks with flask-restful and create_app - python

I'm trying to create API tokens for my flask API with flask-jwt-extended. I'm trying to initialize the token_in_blacklist_loader but can't figure out the right way to do that.
The problem is that token_in_blacklist_loader is implemented as a decorator. It is supposed to be used in the following way:
#jwt.token_in_blacklist_loader
def check_if_token_in_blacklist(decrypted_token):
jti = decrypted_token['jti']
return jti in blacklist
^ from the docs here
Where jwt is defined as:
jwt = JWTManager(app)
But if using the create_app pattern, then jwt variable is hidden inside a function, and cannot be used in the global scope for decorators.
What is the right way to fix this / work around this?

What I ended up doing was putting the handler inside of create_app like so:
def create_app(name: str, settings_override: dict = {}):
app = Flask(name, ...)
...
jwt = JWTManager(app)
#jwt.token_in_blacklist_loader
def check_token_in_blacklist(token_dict: dict) -> bool:
...

Put the JWTManager in a different file, and initialize it with the jwt.init_app function
As an example, see:
https://github.com/vimalloc/flask-jwt-extended/blob/master/examples/database_blacklist/extensions.py
and
https://github.com/vimalloc/flask-jwt-extended/blob/master/examples/database_blacklist/app.py

Related

Dynamically call function from another file in python while in runtime using flask

I would like to use flask to run some functions. Assume you have a file called myapp.py with a function run
def run():
return 'special routed hello world'
and this main flask file, something like this
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'hello world'
#app.route('/<myapp>')
def open_app(myapp):
from myapp import run
return run()
So obvisouly that doesn't work, but how can I dynamically call these run functions when flask is already running. Is this even possible?
In other words: when someone opens for example .../foobar, the function open_app with parameter foobar is begin called. In that function, from the file foobar.py (let's assume that file exists) import function run, run it and return the result from that function.
In fact, it is possible to use importlib, especially import_modulein combination with getattr, to dynamically call up functions of a module. And I have security concerns too.
The following two examples show a kind of simple RPC implementation.
The first example uses a dictionary for modules. If a module with the name is available, the run function is called. It allows a strong restriction. Optimization is certainly possible and probably necessary.
The second example shows a possibility of calling different functions within different modules with parameters. In spite of everything, as with the previous version, all modules are in one package called "actions" to ensure that calls can be limited. I also think a variant with POST is more suitable for this purpose than using variable rules.
Remember these are strong simplifications. Protocols such as JSON-RPC will certainly help as a guide during implementation.
from flask import Flask
from flask import jsonify, request, jsonify
from importlib import import_module
from actions import *
app = Flask(__name__)
app.secret_key = 'your secret here'
#app.route('/exec/<string:action>', methods=['POST'])
def exec(action):
result = cmddict[action].run()
return jsonify(result=result)
#app.route('/call', methods=['POST'])
def call():
data = request.get_json()
module = data.get('module')
method = data.get('method')
params = data.get('params')
try:
# import module by name
m = import_module(f'actions.{module}', __name__)
# get function by name
f = getattr(m, method)
# call function with params
result = f(**params) if isinstance(params, dict) else f(*params)
return jsonify(result=result, error=None)
except Exception as err:
return jsonify(result=None, error=f'{err}')
# ./actions/__init__py
__all__ = ['demo']
from importlib import import_module
cmddict = {}
for _ in __all__:
cmddict[_] = import_module(f'actions.{_}', __name__)
__all__.append('cmddict')
# ./actions/demo.py
def run():
return f'hello world'
def func(*args, **kwargs):
print('func', args, kwargs)
``

Global dependency as an argument to each handler

Looking at the docs I got to use my app settings in this way:
import config
...
#router.post('')
async def my_handler(
...
settings: config.SettingsCommon = fastapi.Depends(config.get_settings),
):
...
But I am not satisfied with repeating everywhere import config, config.get_settings.
Is there a way to use settings in my handlers without repeating myself?
Because FastAPI cares about helping you minimize code repetition.
You can use Class Based Views from the fastapi_utils package.
As an example:
from fastapi import APIRouter, Depends, FastAPI
from fastapi_utils.cbv import cbv
from starlette import requests
from logging import Logger
import config
from auth import my_auth
router = APIRouter(
tags=['Settings test'],
dependencies=[Depends(my_auth)] # injected into each query, but my_auth return values are ignored, throw Exceptions
)
#cbv(router)
class MyQueryCBV:
settings: config.SettingsCommon = Depends(config.get_settings) # you can get settings here
def __init__(self, r: requests.Request): # called for each query, after their dependencies have been evaluated
self.logger: Logger = self.settings.logger
self.logger.warning(str(r.headers))
#router.get("/cbv/{test}")
def test_cbv(self, test: str):
self.logger.warning(f"test_cbv: {test}")
return "test_cbv"
#router.get("/cbv2")
def test_cbv2(self):
self.logger.warning(f"test_cbv2")
return "test_cbv2"
It's not currently possible to inject global dependencies. You can still declare them and the code inside the dependencies will run as normal.
Docs on global dependencies for reference.
Without any external dependency, I can think of three ways of using global dependencies. You can set a private variable with your dependency and get that dependency using a function.
You can also use the same approach without a global private variable, but instead using a cache decorator (docs here).
Finally, you can implement the singleton pattern if using a class as a dependency.
Something like:
class Animal:
_singleton = None
#classmethod
def singleton(cls) -> "Animal":
if cls._singleton is None:
cls._singleton = Animal()
return cls._singleton

Flask list of last used pages with sessions TypeError

I am trying to make a list of pages that a user has visited recently, but I keep getting TypeError: store_visited_urls() takes no arguments (1 given).
I don't know how an argument is given with it.
Python/Flask code:
app.secret_key = '/r/xd8}q/xde/x13/xe5F0/xe5/x8b/x96A64/xf2/xf8MK/xb1/xfdA7x8c'
def recentsites():
session['urls'] = []
#app.after_request
def store_visited_urls():
session['urls'].append(request.url)
if(len[session['urls']]) > 3:
session['urls'].pop(0)
session.modified = True
#app.route('/')
def index():
data = []
if 'urls' in session:
data = session['urls']
return render_template('index.html', data=data)
I think that as a function, it automatically includes self as an argument. This argument is included as part of the call when you create a class.
make the definition def store_visited_urls(self): but continue to call it without an argument.
From the way it looks session must be defined in the class. Thus, you would reference self.session so that it will be picked up when the class is instantiated.
See What is the purpose of self? for an explanation.
I see that you are using a decorator See Decorators I: Introduction to Python Decorators and A guide to Python's function decorators or Python Decorators or Primer on Python Decorators

How to use correctly importlib in a flask controller?

I am trying to load a module according to some settings. I have found a working solution but I need a confirmation from an advanced python developer that this solution is the best performance wise as the API endpoint which will use it will be under heavy load.
The idea is to change the working of an endpoint based on parameters from the user and other systems configuration. I am loading the correct handler class based on these settings. The goal is to be able to easily create new handlers without having to modify the code calling the handlers.
This is a working example :
./run.py :
from flask import Flask, abort
import importlib
import handlers
app = Flask(__name__)
#app.route('/')
def api_endpoint():
try:
endpoint = "simple" # Custom logic to choose the right handler
handlerClass = getattr(importlib.import_module('.'+str(endpoint), 'handlers'), 'Handler')
handler = handlerClass()
except Exception as e:
print(e)
abort(404)
print(handlerClass, handler, handler.value, handler.name())
# Handler processing. Not yet implemented
return "Hello World"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)
One "simple" handler example. A handler is a module which needs to define an Handler class :
./handlers/simple.py :
import os
class Handler:
def __init__(self):
self.value = os.urandom(5)
def name(self):
return "simple"
If I understand correctly, the import is done on each query to the endpoint. It means IO in the filesystem with lookup for the modules, ...
Is it the correct/"pythonic" way to implement this strategy ?
Question moved to codereview. Thanks all for your help : https://codereview.stackexchange.com/questions/96533/extension-pattern-in-a-flask-controller-using-importlib
I am closing this thread.

How do you break Python Flask controller flow when calling return inside a function?

I'm using Python Flask and I got defined the following function:
def verify_session():
if not 'logged_in' in session:
flash("You are not logged in.<br/>Please, log in to use our application.", "warning")
return redirect(url_for('login_path'))
This is meant to be called in every controller. I know that I could use #app.before_request but on some places I dont want this function to be called (in the same Login page, for example) and I really dont want the function to check the excluded paths.
My problem is this: inside any Flask controller for each of the paths the first lines look like this:
#app.route('/web/account', methods=["GET"], endpoint="account_path")
def account():
verify_session()
return render_template('account')
But, despite I get the "You need to login" flashed message, it still returns the Account page, because the return inside verify_session just returns for this function.
What I need is something like return verify_session() but that doesn't end the controller flow unless it actually is necessary to return it.
Maybe this problem is only Python/Programming specific rather than Flask so I excuse myself and ask moderators to edit my Question accordingly. I mention Flask because it's the enviroment where I'm having the trouble.
IF more information is needed I'm willing to update. Thank you in advance.
result = verify_session()
if not result:
result = render_template("account")
return result
but really you should be using a decorator for this
def verify_session(fn):
def inner(*args,**kwargs):
if not 'logged_in' in session:
flash("You are not logged in.<br/>Please, log in to use our application.", "warning")
return redirect(url_for('login_path'))
return fn(*args,**kwargs)
return inner
then you would use it like
#app.route('/web/account', methods=["GET"], endpoint="account_path")
#verify_session #make sure to use any decorators after the route decorator
def account():
return render_template('account')
lastly ... I hope you are aware that you are reinventing the wheel and you should probably just use flask-login to manage this stuff(unless you have a pretty compelling reason not to)
To add to Joran's excellent answer. Instead of using #app.before_request, you can put your login required routes in a blueprint and then use #blueprint.before_request:
account = Blueprint('account', __name__)
#account.before_request
def verify_session():
...
#account.route('/')
def index():
...
Then in your app:
app = Flask(__name__)
app.register_blueprint(account, url_prefix='/web/account')

Categories