Flask按需提供主要的粒度资源

时间:2013-08-19 17:20:35

标签: python flask flask-principal

我一直在看这篇文章: http://pythonhosted.org/Flask-Principal/#granular-resource-protection

现在虽然目前的工作方式没有任何问题,但我看不出它是非常实用的,因为在登录时会读取所有帖子并将EditBlogPostNeed添加到身份中。

想象一下,如果我写的数量超过了正常数量的帖子,那么从长远来看这不是一个非常好的策略,因为我想在访问视图/posts/<post_id>时查看帖子。

有没有办法使用Flask Principal检查每个视图请求?

我当然可以通过懒惰的关系查询和过滤器轻松解决它,但我想使用Flask Principal

3 个答案:

答案 0 :(得分:1)

我不确定我完全理解你的问题,但这可能有所帮助。在Flask应用程序中,我使用Flask-Principal作为角色权限,例如admin和editor,我也使用它来进行粒度资源保护,如Flask-Principal docs中所述。在我的情况下,我正在检查用户是否有权访问特定帐户。在每个视图中加载标识并检查权限。

视图

@login_required
def call_list(id, page=1):

    dept = models.Department.query.get_or_404(id)
    view_permission = auth.ViewAccountPermission(dept.office.account_id)

    if view_permission.can():
        # do something

自定义权限

ViewAccount = namedtuple('View', ['method', 'value'])
ViewAccountNeed = partial(ViewAccount, 'view')

class ViewAccountPermission(Permission):
    def __init__(self, account_id):
        need = ViewAccountNeed(unicode(account_id))
        super(ViewAccountPermission, self).__init__(need)

身份加载器功能

if hasattr(current_user, 'assigned_accounts'):
    for account_id in current_user.assigned_accounts():
        identity.provides.add(auth.ViewAccountNeed(unicode(account_id)))

答案 1 :(得分:1)

虽然Flask-Principal是最受欢迎的插件,但它不必要复杂,它在我需要它的大多数情况下都不起作用。我一直试图强迫它以我喜欢的方式工作,但我从未成功过。幸运的是,我发现了一个非常简单明了的模块 - permission

用法

首先,您需要通过继承Rule来定义自己的规则 覆盖check()deny()

# rules.py
from flask import session, flash, redirect, url_for
from permission import Rule

class UserRule(Rule):
    def check(self):
        """Check if there is a user signed in."""
        return 'user_id' in session

    def deny(self):
        """When no user signed in, redirect to signin page."""
        flash('Sign in first.')
        return redirect(url_for('signin'))

然后通过继承Permission并覆盖rule()

来定义权限
# permissions.py
from permission import Permission
from .rules import UserRule

class UserPermission(Permission):
    """Only signin user has this permission."""
    def rule(self):
        return UserRule()

有四种方法可以使用上面定义的UserPermission

<强> 1。用作视图装饰器

from .permissions import UserPermission

@app.route('/settings')
@UserPermission()
def settings():
    """User settings page, only accessable for sign-in user."""
    return render_template('settings.html')

<强> 2。在视图代码中使用

from .permissions import UserPermission

@app.route('/settions')
def settings():
    permission = UserPermission()
    if not permission.check()
        return permission.deny()
    return render_template('settings.html')

第3。在视图代码中使用(使用with声明)

from .permissions import UserPermission

@app.route('/settions')
def settings():
    with UserPermission():
        return render_template('settings.html')

<强> 4。在Jinja2模板中使用

首先,您需要将已定义的权限注入模板上下文:

from . import permissions

@app.context_processor
def inject_vars():
    return dict(
        permissions=permissions
    )

然后在模板中:

{% if permissions.UserPermission().check() %}
    <a href="{{ url_for('new') }}">New</a>
{% endif %}

答案 2 :(得分:0)

我的回答是基于这样的假设:您已经知道了如何使用flask主体,以及它如何与数据库集成。

首先,我们只需将Needs存储在数据库中, 如果您不知道原因,我建议您不要阅读下面的答案

然后,回到你的问题,我们需要编辑一篇文章,如何控制它?

@app.route('/article/edit/<id>'):
@Permission(Need('edit', 'article')).require()
def article(id):
    pass

用户身份

id = identity(user.id)
id.provide.add(Need('edit','article'))

然后用户有权编辑文章。@Permission(Need('edit', 'article')).require()将为每篇文章返回true,即使用户不是文章的作者,这是你的问题,对吧?

以下是我解决此问题的方法

因为默认的Permission.require()没有提供任何传递的args,所以我定义了自己的Permisson和IdentityContext并传入了文章ID和文章Model,然后我检查文章的user_id with flask login's current_user.id

class MyPermission(Permission):
    pass


class MyIdentityContext():

    pass

如果用户是文章的作者,那么我返回True,用户可以编辑文章,如果没有,则返回False,然后就可以了。

--------我稍后会更新更多细节------------