Jak zrobić w Flask funkcję dekoratora Pythona z argumentami (dla autoryzacji)

Użyłem fragmentu kolby dla mojej kolby-login, który sprawdza, czy użytkownik jest zalogowany:

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

A ja tak dekoruję widoki:

@app.route('/secrets', methods=['GET', 'POST'])
@logged_in
def secrets():
    error = None
Ja też chciałbym zrobić coś podobnego dla autoryzacji. W tej chwili mam wiele widoków, aby sprawdzić, czy użytkownik jest właścicielem zasobu, powiedzmy zasobu hotdogs.

Jeśli użytkownik logged_in jest właścicielem danego hotdoga, może edytować i zarządzać swoimi hotdogami. Jeśli nie, wyrzucę go do nieautoryzowanego ekran.

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

authorizeowner() pobiera hotdoga jako dane wejściowe i sprawdza, czy zarejestrowany właściciel hotdoga odpowiada nazwie właściciela wymienionej w zmiennej sesji.

Próbowałem zrobić funkcję owns_hotdog wrapper / decorator podobną do mojej zalogowanej, ale narzekał, że nie akceptuje argumentów. Jak mogę osiągnąć coś podobnego? Coś w tym stylu...

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

Z komunikatu o błędzie dekorator zdaje się nie odbierać argumentu hotdoga, do którego widoki Flask mają dostęp z zmienna na trasie. Mam nadzieję na coś takiego...

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

Wszystko działa z moją aktualną funkcją authorizeowner (hotdog), ale wydaje się czystsze, aby mieć to na swoim miejscu jako owijkę na górze mojej trasy, a nie jako pierwszą linię wewnątrz trasy.

Inne uwagi:

  • wiem, że Flask-Security i Flask-Principal mogą zarządzać autoryzacja dla mnie. Niestety, używam nieobsługiwanego back-end bazy danych i nie mogę korzystać z tych rozszerzeń. Więc jestem zmuszeni do uwierzytelniania bez nich.
  • jeśli widzisz jakieś rażące dziury w robieniu autoryzacji w ten sposób, proszę daj mi znać!
Author: Mittenchops, 2012-12-16

1 answers

Oto Jak to zrobić:

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

Kiedy dekorator pobiera argumenty, nie jest to tak naprawdę dekorator, ale funkcja factory , która zwraca prawdziwy dekorator.

Ale gdybym był tobą, użyłbym Flask-Login do uwierzytelniania i rozszerzył go o niestandardowe dekoratory i funkcje jako twoje do obsługi autoryzacji.

Przyjrzałam się Dyrektorowi, ale uznałam, że to zbyt skomplikowane jak na mój gust. Nie sprawdzałem kolby-zabezpieczenia, ale uważam, że używa Kolba-główna do autoryzacji. Ogólnie rzecz biorąc, myślę, że Flask-Login z jakiegoś niestandardowego kodu wystarcza większość czasu.
 20
Author: Audrius Kažukauskas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-16 09:56:24