Hiding inaccessible links in Jinja2 templates

后端 未结 2 521
我在风中等你
我在风中等你 2021-02-04 16:20

We\'re writing a web application in Flask + Jinja2 at work. The application has registered users which can access certain pages based on their roles. In order to achieve this o

相关标签:
2条回答
  • 2021-02-04 16:49

    I use Flask-Security, which ties a lot of the login/security modules together in a nice package. It comes with role management courtesy of Flask-Principal, which will allow you to do:

    {% if current_user.has_role('admin') %}
        <li><a href="#">Manage Site</a></li>
    {% endif %}
    

    You can see how that's implemented in the source, the current_user proxy comes from Flask-Login

    0 讨论(0)
  • 2021-02-04 17:06

    I changed the security_requirements decorator to look like this:

    def security_requirements(logged_in=True,
                              roles=None):
    def wrapper(f):
        # Store the security attributes as a member of the function object
        f.access_control = dict(logged_in=logged_in, roles=roles)
        @functools.wraps(f)
        def wrapped(*args, **kwargs):
            access_result = _eval_access(logged_in, roles)
            # Redirect the user to the appropriate page (Access denied / Login Required / Actual Page) based on the result
            ...
    

    The only real difference from the previous version of this decorator is the line that stores the security attributes inside the function object. This line is useless from inside the decorator. However, now I can implement the following action to be called from the Jinja template:

    {% if can_access(func) %}
    <li><a>...</a></li>
    {% endif %}
    

    The can_access function is defined in the Flask application module. It receives a string which it must convert to a function object. It does that by calling app.view_functions:

    def can_access(func):
        return auth.can_access(app.view_functions[func])
    

    This function should be called directly from a Jinja template. So it needs to be added to Jinja's globals:

    app.jinja_env.globals.update(can_access=can_access)
    

    Finally, auth.can_access:

    def can_access(f):
        if not hasattr(f, 'access_control'):
            return True
    
        # Use the access_control member set by the decorator
        return _eval_access(**f.access_control) == AccessResult.ALLOWED
    

    This solution means that the access control is defined in a single place - which is the function decorator.

    0 讨论(0)
提交回复
热议问题