2013-11-25 113 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