Подтвердить что ты не робот

Как сделать функцию декоратора python в Flask с аргументами (для авторизации)

Я использовал флеш-фрагмент для моего флеш-входа, который проверяет, что пользователь вошел в систему:

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

И я украшаю такие взгляды:

@app.route('/secrets', methods=['GET', 'POST'])
@logged_in
def secrets():
    error = None

Я хотел бы сделать что-то подобное для авторизации. Сейчас у меня много просмотров, чтобы проверить, что пользователь владеет ресурсом, скажем, ресурсом hotdogs.

Если пользователь logged_in является владельцем этого конкретного хот-дога, он может редактировать и управлять своими хот-догами. Если нет, я выталкиваю его на несанкционированный экран.

@app.route('/<hotdog>/addmustard/',methods=["GET"])
@logged_in
def addmustard(hotdog):
    if not (authorizeowner(hotdog)):
        return redirect(url_for('unauthorized'))
    do_stuff()

authorizeowner() принимает хот-дог как ввод и проверяет, что зарегистрированный владелец хот-дога совпадает с именем владельца, указанным в переменной сеанса.

Я попытался создать функцию-оберщик/декоратор owns_hotdog, аналогичный моей регистрации в одном, но он жаловался, что не принимает аргументы. Как я могу достичь чего-то подобного? Что-то вроде...

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

Из сообщения об ошибке, декоратор, похоже, не получает аргумент hotdog, доступ к которому имеет вид Flask из переменной в маршруте. Я надеюсь на что-то вроде...

@app.route('/<hotdog>/addmustard/',methods=["GET"])
@logged_in
@owns_hotdog(hotdog)
def addmustard(hotdog):
    do_stuff()

Все работает с моей текущей функцией authorizeowner (hotdog), но просто кажется, что это чище иметь это место как обертка поверх моего маршрута, а не как первая строка внутри маршрута.

Некоторые другие примечания:

  • Я знаю, что Flask-Security и Flask-Principal могут управлять разрешение для меня. К сожалению, я использую неподдерживаемый базы данных и я не могу использовать эти расширения. Так что я вынуждены выполнять аутентификацию без них.
  • Если вы видите какие-либо яркие дыры при авторизации таким образом, пожалуйста, дайте мне знать!
4b9b3361

Ответ 1

Вот как это сделать:

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

Когда декоратор принимает аргументы, это не действительно декоратор, а функция factory, которая возвращает реальный декоратор.

Но если бы я был вами, я бы использовал Flask-Login для аутентификации и увеличил его с помощью пользовательских декораторов и функций, как ваш, чтобы обрабатывать авторизацию.

Я заглянул в Flask-Principal, но нашел это слишком сложным для моих вкусов. Не проверял Flask-Security, но я считаю, что для авторизации использует Flask-Principal. В целом я думаю, что Flask-Login с некоторым пользовательским кодом достаточно времени.