para - sistema web en python
Cómo hacer que un decorador de python funcione en Flask con argumentos(para autorización) (1)
Utilicé un fragmento de matraz para mi inicio de sesión en el matraz que verifica que un usuario haya iniciado sesión:
from functools import wraps
def logged_in(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if session.get(''logged_in'') is not None:
return f(*args, **kwargs)
else:
flash(''Please log in first.'', ''error'')
return redirect(url_for(''login''))
return decorated_function
Y decoro vistas así:
@app.route(''/secrets'', methods=[''GET'', ''POST''])
@logged_in
def secrets():
error = None
También me gustaría hacer algo similar para la autorización. En este momento, tengo muchas vistas para comprobar que un usuario posee un recurso, digamos el recurso hotdogs
.
Si el usuario log_in es el propietario de ese hotdog en particular, puede editar y administrar sus hotdogs. Si no lo está, lo echo a la pantalla no autorizada.
@app.route(''/<hotdog>/addmustard/'',methods=["GET"])
@logged_in
def addmustard(hotdog):
if not (authorizeowner(hotdog)):
return redirect(url_for(''unauthorized''))
do_stuff()
authorizeowner()
toma un hotdog como entrada y comprueba que el propietario del hotdog registrado coincida con el nombre del propietario que aparece en la variable de sesión.
Intenté hacer una función wrapper / decorator owns_hotdog similar a mi sesión iniciada, pero se quejó de que no aceptaba argumentos. ¿Cómo puedo lograr algo similar? Algo como...
def owns_hotdog(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not authorizeowner(hotdog):
return f(*args, **kwargs)
else:
flash(''Please log in first.'', ''error'')
return redirect(url_for(''login''))
return decorated_function
En el mensaje de error, el decorador parece no estar recibiendo el argumento de hotdog al que las vistas de Flask tienen acceso desde la variable en la ruta. Mi esperanza es para algo como ...
@app.route(''/<hotdog>/addmustard/'',methods=["GET"])
@logged_in
@owns_hotdog(hotdog)
def addmustard(hotdog):
do_stuff()
Todo funciona con mi función actual de autorizador (hotdog), pero parece más limpio tener esto como un envoltorio en la parte superior de mi ruta, en lugar de ser la primera línea dentro de la ruta.
Algunas otras notas:
- Sé que Flask-Security y Flask-Principal pueden gestionar mi autorización. Desafortunadamente, estoy usando un back-end de base de datos no compatible y no puedo usar estas extensiones. Entonces, me veo obligado a hacer autenticación sin ellos.
- Si ve algún agujero deslumbrante al hacer la autorización de esta manera, ¡hágamelo saber!
Aquí está cómo hacerlo:
from functools import update_wrapper
def owns_hotdog(hotdog):
def decorator(fn):
def wrapped_function(*args, **kwargs):
# First check if user is authenticated.
if not logged_in():
return redirect(url_for(''login''))
# For authorization error it is better to return status code 403
# and handle it in errorhandler separately, because the user could
# be already authenticated, but lack the privileges.
if not authorizeowner(hotdog):
abort(403)
return fn(*args, **kwargs)
return update_wrapper(wrapped_function, fn)
return decorator
@app.errorhandler(403)
def forbidden_403(exception):
return ''No hotdogs for you!'', 403
Cuando el decorador toma argumentos, no es realmente un decorador, sino una función de fábrica que devuelve al decorador real .
Pero si fuera usted, usaría Flask-Login para la autenticación y lo aumentaría con decoradores personalizados y funciones como suyas para manejar la autorización.
Busqué en Flask-Principal, pero me pareció demasiado complicado para mi gusto. No he revisado Flask-Security, pero creo que usa Flask-Principal para su autorización. En general, creo que Frask-Login con un código personalizado es suficiente la mayoría del tiempo.