2013-11-25 110 views
7

我们正在Flask + Jinja2中编写一个Web应用程序。 该应用程序已注册的用户可以根据他们的角色访问某些页面。为了实现这一目标,在服务器端,我们只是用装饰网页:隐藏Jinja2模板中无法访问的链接

@app.route('/action1') 
@security_requirements(roles=['some_role']) 
def action1(): 
    ... 

的装饰检查登录的用户拥有“some_role”中的角色列表,并决定是否将呼叫传递给装饰功能或者只是将用户重定向到“访问被拒绝”页面。

该应用程序还具有使用引导程序实现的导航栏。导航栏使用基本模板显示在每个页面中。就目前而言,无论当前用户是否可以访问,应用程序中的每个页面在导航栏中都有一个条目。尽管这不是一个安全漏洞,但我想隐藏用户无法访问的页面。此外,我希望在不复制Jinja模板中允许的角色列表的情况下实现此功能。有没有可能通过使用我当前的装饰器以某种方式在Jinja中实现此功能?

+1

是'security_requirements'你的装饰者?是否允许改变它? – twil

+0

@twil - 是的,这是我的 – reish

回答

1

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 %} 

can_access函数在Flask应用程序模块中定义。它接收到一个字符串,它必须将其转换为函数对象。它是通过调用app.view_functions

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

这个功能应该从神社模板直接调用。因此,它需要被添加到神社的全局:

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

最后,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 

这种解决方案意味着,访问控制是在一个地方定义的 - 这是函数装饰。

+0

我不完全理解这个,但是,很好! – 8oh8

+0

你应该提交这个补丁,这将是一个很棒的功能。 – Plasma

5

我使用Flask-Security,它将很多登录/安全模块绑定在一个很好的包中。它配备了瓶校长的角色管理礼貌,这将允许你这样做:我改变了security_requirements装饰,看起来像这样

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

可以see how that's implemented in the source,该current_user代理来自Flask-Login

+0

我想用一个地方来存储每个动作所允许的角色列表。如果我理解正确,即使使用Flask-Security,我也必须为每个操作编写两个列表 - 在服务器端代码中以及在模板中。 – reish

+0

所以你打算做一些事情:'{%if current_user.can_access(url_for('protected_endpoint'))%}仅限管理员{%endif%}'? – Doobeh

+0

是的,正好:) – reish