web-dev-qa-db-ja.com

pythonデコレータ関数をFlask引数付き(認証用)で作成する方法

ユーザーがログインしていることを確認するフラスコログインに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リソースを所有していることを確認するための多くのビューがあります。

Login_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
_

エラーメッセージから、デコレータはFlaskビューがルート内の変数からアクセスできるというホットドッグ引数を受信して​​いないようです。私の希望は...

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

すべてが私の現在のauthorizeowner(hotdog)関数で機能しますが、ルート内の最初の行としてではなく、ルートの上部にラッパーとして配置する方がクリーンなようです。

その他の注意事項:

  • Flask-SecurityとFlask-Principalが承認を管理できることを知っています。残念ながら、サポートされていないデータベースバックエンドを使用しており、これらの拡張機能を使用できません。だから、私はそれらなしで認証をすることを余儀なくされています。
  • この方法で認証を行う際に明らかな穴が見つかった場合は、お知らせください。
24
Mittenchops

方法は次のとおりです。

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

デコレータが引数を取る場合、それは実際にはデコレータではありませんが、デコレータを返すファクトリ関数です。

しかし、私があなたなら、認証にFlask-Loginを使用し、カスタムデコレータで拡張し、承認を処理するためにあなたのものとして機能します。

Flask-Principalを調べましたが、好みが複雑すぎることがわかりました。 Flask-Securityをチェックしていませんが、認証にFlask-Principalを使用していると思います。全体として、ほとんどの場合、カスタムコードを使用したFlask-Loginで十分だと思います。

20