# 用于view的装饰器 | |
from ..utils.permissions import VisitorPermission | |
@bp.route('/signin', methods=['GET', 'POST']) | |
@VisitorPermission() | |
def signin(): | |
"""登陆""" | |
form = SigninForm() | |
if form.validate_on_submit(): | |
signin_user(form.user) | |
return redirect(url_for('site.index')) | |
return render_template('account/signin.html', form=form) | |
# 用于view代码中 | |
from ..utils.permissions import QuestionAdminPermission | |
@bp.route('/question/<int:uid>/delete') | |
def delete_question(uid): | |
"""删除问题""" | |
question = Question.query.get_or_404(uid) | |
permission = QuestionAdminPermission(uid) | |
if not permission.check(): | |
return permission.deny() | |
db.session.delete(question) | |
db.session.commit() | |
return redirect(url_for('site.index')) | |
# 用于Jinja2中 | |
def register_jinja(app): | |
"""注册Jinja2全局变量""" | |
from .utils import permissions | |
# inject vars into template context | |
@app.context_processor | |
def inject_vars(): | |
return dict( | |
g_permissions=permissions, | |
) | |
""" | |
{% if g_permissions.QuestionAdminPermission(question.id).check() %} | |
<a class="btn btn-default confirm" data-confirm="确认删除此问题?" | |
href="{{ url_for('qa.delete_question', uid=question.id) }}"> | |
<span class="fa fa-trash-o"></span> 删除 | |
</a> | |
{% endif %} | |
""" |
# coding: utf-8 | |
from functools import wraps | |
class Permission(object): | |
def __call__(self, func): | |
"""提供view装饰器能力""" | |
@wraps(func) | |
def decorator(*args, **kwargs): | |
if not self.check(): | |
return self.deny() | |
return func(*args, **kwargs) | |
return decorator | |
def check(self): | |
"""运行规则""" | |
result, self.deny = self.rule.run() | |
return result | |
def show(self): | |
"""显示self.rule的规则结构,调试用""" | |
self.rule.show() | |
class Rule(object): | |
def __init__(self): | |
self.rules_list = [[(self.check, self.deny)]] | |
# 若子类实现了base方法,则将其返回的rule实例的rules_list串联到self.rules_list上游 | |
base_rule = self.base() | |
if base_rule: | |
self.rules_list = Rule._and(base_rule.rules_list, self.rules_list) | |
def __and__(self, other): | |
"""逻辑与操作(&) | |
将other.rules_list串联到self.rules_list的下游, | |
并返回当前实例。""" | |
self.rules_list = Rule._and(self.rules_list, other.rules_list) | |
return self | |
def __or__(self, other): | |
"""逻辑或操作(|) | |
将self.rules_list与other.rules_list并联起来, | |
并返回当前实例""" | |
for rule in other.rules_list: | |
self.rules_list.append(rule) | |
return self | |
def show(self): | |
"""显示rules_list的结构""" | |
for rule in self.rules_list: | |
result = ", ".join([check.__repr__() for check, deny in rule]) | |
print(result) | |
def base(self): | |
"""提供rule规则继承能力(串联)""" | |
return None | |
def run(self): | |
"""运行rules_list。 | |
若某一通道check通过,则返回成功状态 | |
若所有通道都无法check通过,则返回失败状态(包含最后运行失败的rule的deny方法)""" | |
failed_result = None | |
for rule in self.rules_list: | |
for check, deny in rule: | |
if not check(): | |
failed_result = (False, deny) | |
break | |
else: | |
return (True, None) | |
return failed_result | |
def check(self): | |
"""当前rule的测试方法,强制子类overload""" | |
raise NotImplementedError() | |
def deny(self): | |
"""当前rule测试失败后需要执行的方法,强制子类overload""" | |
raise NotImplementedError() | |
@staticmethod | |
def _and(rules_list_pre, rules_list_pro): | |
"""将rule_list_pre串联到rule_list_pro上游""" | |
return [rule_pre + rule_pro | |
for rule_pre in rules_list_pre | |
for rule_pro in rules_list_pro] |
# coding: utf-8 | |
from .flask_permission import Permission | |
from .rules import VisitorRule, UserRule, AdminRule, QuestionOwnerRule | |
class VisitorPermission(Permission): | |
"""游客许可""" | |
def __init__(self): | |
self.rule = VisitorRule() | |
class UserPermission(Permission): | |
"""注册用户许可""" | |
def __init__(self): | |
self.rule = UserRule() | |
class AdminPermission(Permission): | |
"""管理员许可""" | |
def __init__(self): | |
self.rule = AdminRule() | |
class QuestionAdminPermission(Permission): | |
"""Question管理许可 | |
Question拥有者/管理员均有该许可 | |
""" | |
def __init__(self, question_id): | |
self.rule = AdminRule() | QuestionOwnerRule(question_id) |
# coding: utf-8 | |
from flask import redirect, url_for, abort, flash, g | |
from ..models import Question | |
from .flask_permission import Rule | |
class VisitorRule(Rule): | |
"""游客""" | |
def check(self): | |
return not g.user | |
def deny(self): | |
return redirect(url_for('site.index')) | |
class UserRule(Rule): | |
"""注册用户""" | |
def check(self): | |
return g.user | |
def deny(self): | |
flash('此操作需要登录账户') | |
return redirect(url_for('account.signin')) | |
class AdminRule(Rule): | |
"""管理员""" | |
def base(self): | |
return UserRule() | |
def check(self): | |
return g.user.is_admin | |
def deny(self): | |
abort(403) | |
class QuestionOwnerRule(Rule): | |
"""问题拥有者""" | |
def __init__(self, question_id): | |
self.question_id = question_id | |
Rule.__init__(self) | |
def base(self): | |
return UserRule() | |
def check(self): | |
question = Question.query.filter(Question.id == self.question_id).first() | |
return question and question.user_id == g.user.id | |
def deny(self): | |
abort(403) |
![]() |
1
loading 2014-09-09 21:02:02 +08:00 via Android ![]() 感谢
|
![]() |
2
humiaozuzu 2014-09-09 21:19:43 +08:00 via iPhone ![]() @loading 还有lz,有没有兴趣建立一个群或者邮件列表交流flask相关的技术,官方的邮件列表已经不怎么活跃了
|
![]() |
3
hustlzp OP @humiaozuzu 有兴趣啊,形式大家可以考虑一下。qq很少上...因为总觉得qq有点干扰...
|
![]() |
4
humiaozuzu 2014-09-09 21:52:01 +08:00
@hustlzp 不如就用QQ吧,slack 之类的不太方便,邮件列表参与度挺难起来。 QQ群的话,当做异步交流的工具也未尝不可。
|
5
tolbkni 2014-09-09 22:07:15 +08:00
@humiaozuzu 没办法异步,除非一直开着 QQ,否则还是会丢失一堆信息的,而且不能按主题索引
|
![]() |
6
humiaozuzu 2014-09-09 22:11:51 +08:00
@tolbkni 有方法异步,手机 QQ 里面可以设置不提醒,可以看到历史。我更希望讨论的结果能写成Blog,不仅仅是邮件列表的主题。而且 flask 的方向并不会太多,简单的讨论会更多些。
|
![]() |
7
hustlzp OP @humiaozuzu 可以试试 :)
|
![]() |
8
humiaozuzu 2014-09-09 22:53:56 +08:00
|
![]() |
9
hustlzp OP @humiaozuzu 724475543
|
![]() |
10
gonjay 2014-09-09 23:02:59 +08:00 ![]() 可以参考下rails的cancan,那个gem质量还是相当不错的
|
12
prowayne 2015-05-18 19:22:36 +08:00
装饰器模式怎么传参数进去呢
QuestionAdminPermission(question.id) |
![]() |
13
hustlzp OP ![]() @prowayne 这是个问题...
目前只能在代码内部这样做: permission = QuestionAdminPermission(uid) if not permission.check(): return permission.deny() |
![]() |
14
elvis_w 2015-11-30 08:44:15 +08:00
我是一直在用 Flask-Security
https://pythonhosted.org/Flask-Security/ |
![]() |
15
fhefh 2015-12-16 00:11:55 +08:00
mark
|
![]() |
16
hsluoyz 2019-01-26 09:39:12 +08:00
现在新推出了一个权限框架,叫 PyCasbin。PyCasbin 采用了元模型的设计思想,支持多种经典的访问控制方案,如 ACL、RBAC、ABAC,还支持对 RESTful API 的控制。现在已经支持 Django、Flask 等 Web 框架了。需要中文文档的话,可以在百度搜索:PyCasbin
|
![]() |
17
hsluoyz 2019-01-26 09:39:28 +08:00
现在新推出了一个权限框架叫 PyCasbin。PyCasbin 采用了元模型的设计思想,支持多种经典的访问控制方案,如 ACL、RBAC、ABAC,还支持对 RESTful API 的控制。现在已经支持 Django、Flask 等 Web 框架了。需要中文文档的话,可以在百度搜索:PyCasbin
|