Python 在Jinja2模板中隐藏不可访问的链接

Python 在Jinja2模板中隐藏不可访问的链接,python,web,permissions,flask,jinja2,Python,Web,Permissions,Flask,Jinja2,我们正在用Flask+Jinja2编写一个web应用程序。 应用程序拥有注册用户,这些用户可以根据其角色访问某些页面。为了在服务器端实现这一点,我们仅使用装饰页面: @app.route('/action1') @security_requirements(roles=['some_role']) def action1(): ... 装饰程序检查登录用户的角色列表中是否有“some_role”,并决定是将调用传递给装饰函数,还是将用户重定向到“拒绝访问”页面 该应用程序还有一个使用引

我们正在用Flask+Jinja2编写一个web应用程序。 应用程序拥有注册用户,这些用户可以根据其角色访问某些页面。为了在服务器端实现这一点,我们仅使用装饰页面:

@app.route('/action1')
@security_requirements(roles=['some_role'])
def action1():
    ...
装饰程序检查登录用户的角色列表中是否有“some_role”,并决定是将调用传递给装饰函数,还是将用户重定向到“拒绝访问”页面

该应用程序还有一个使用引导实现的导航栏。导航栏使用基本模板显示在每个页面中。现在,应用程序中的每个页面都在导航栏中有一个条目,不管当前用户是否可以访问它。尽管这不是一个安全漏洞,我还是想对用户隐藏他们无法访问的页面。此外,我希望在不复制Jinja模板中允许的角色列表的情况下实现此功能。在Jinja中,使用我当前的decorator是否可以实现此功能?

我使用的decorator将许多登录/安全模块绑定在一个漂亮的包中。它具有Flask Principal提供的角色管理功能,允许您执行以下操作:

{% if current_user.has_role('admin') %}
    <li><a href="#">Manage Site</a></li>
{% endif %}
{%如果当前用户具有\角色('admin')%}
  • {%endif%}

    您可以,当前用户的代理来自于我将
    安全性要求修改为如下:

    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
            ...
    
    与此装饰器的上一个版本唯一的真正区别是在函数对象内部存储安全属性的行。从装饰器内部看,这条线是无用的。但是,现在我可以实现从Jinja模板调用的以下操作:

    {% if can_access(func) %}
    <li><a>...</a></li>
    {% endif %}
    
    应该直接从Jinja模板调用此函数。因此,需要将其添加到Jinja的globals中:

    app.jinja_env.globals.update(can_access=can_access)
    
    最后,
    auth.can\u 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
    

    这个解决方案意味着访问控制是在一个地方定义的,那就是功能装饰器。

    安全要求吗
    你的装饰器?允许更改吗?@twil-是的,它是我的。我想用一个地方来存储每个操作允许的角色列表。如果我理解正确,即使使用Flask安全性,我也必须为每个操作编写两个列表-在服务器端代码和模板中。因此,您的目标是:
    {%If current_user.can_access(url_for('protected_endpoint'))%}仅管理{%endif%}
    ?理想的做法是扩展flask的
    url\u映射
    本身,并在其中实际保存权限信息,因此当您实际定义app.route时,可以在其中指定角色,例如:
    @app.route('/',methods=['post','get'],roles=['admin'])
    。通过查看url_地图,使用起来会更好,并且很容易从jinja2中查找。这是一个有趣的问题,当我有空闲时间时,我将对此进行研究。为了获得一个可能更简单的解决方案(但不是那么漂亮),您可以创建一个将端点链接到角色的存储。然后,它不再在decorator中指定角色,而是在数据存储中查找。你的jinja2模板过滤器也可以做同样的事情(当前的用户角色与数据存储列为所需角色的内容)!您应该将其作为补丁提交,这将是一个很好的特性。我被告知Python生态系统具有满足各种需求的pip库,但我发现自己每天都必须实现这样的基本功能。干得好,你真的应该接受Flask安全或Flask负责人并提交此文件。
    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