Answer a question

I need to introduce role-based authorization in existing Flask application. Because of that I can't just swap currently used flask-login package with flask-user for example. Nevertheless I have to restrict access to some endpoints to "admin" users without rebuilding entire code base.

I came up with a decorator like this:

def admin_required(func):
    """
    Modified login_required decorator to restrict access to admin group.
    """

    @wraps(func)
    def decorated_view(*args, **kwargs):
        if current_user.group != 0:        # zero means admin, one and up are other groups
            flash("You don't have permission to access this resource.", "warning")
            return redirect(url_for("main.home"))
        return func(*args, **kwargs)
    return decorated_view

I use it with original login_required decorator like so:

@app.route("/admin-restricted"):
@login_required
@admin_required
def admin_resource():
    return "Hello admin"

Everything works as expected BUT I have two concerns:

  1. Is my method safe? Have I missed something which is potential security flaw? Unfortunately I have limited knowledge about Flask internals.
  2. Is there more simple/safe/pythonic way to do that? It just doesn't feel right to me.

Answers

The more common method of dealing with role based permissions is to use Flask Principal.

In the main app:

from flask_principal import Principal

# load the extension
principals = Principal(app)

@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = current_user
    # Add the UserNeed to the identity
    if hasattr(current_user, 'employee_id'):
        identity.provides.add(UserNeed(current_user.employee_id))

    # Assuming the User model has a list of roles, update the
    # identity with the roles that the user provides
    if hasattr(current_user, 'position'):
        # for role in current_user.role:
        identity.provides.add(RoleNeed(str(current_user.position)))

   admin_permission = Permission(RoleNeed('admin'))

And then in the route:

@app.route("/admin-restricted"):
@login_required
@admin_permission.require(http_exception=403)
def admin_resource():
    return "Hello admin"

Docs: https://pythonhosted.org/Flask-Principal/

Logo

学AI,认准AI Studio!GPU算力,限时免费领,邀请好友解锁更多惊喜福利 >>>

更多推荐