以aws的iam策略为模型的声明性访问策略/权限。
drf-access-polic的Python项目详细描述
django rest-访问策略
该项目为django rest框架项目中的访问控制管理带来了一种去极化的、有组织的方法。每个视图集或基于函数的视图都可以为公开的资源分配显式策略。不再需要深入查看视图或seralizer来理解访问逻辑——这都是一种不太技术涉众能够理解的格式。如果您熟悉其他去极化访问模型,例如aws的iam,那么语法将非常熟悉。
简而言之,您可以开始这样表达访问规则:
classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"}]
这个项目有完整的测试覆盖范围,基本的accesspolicy类只有大约150行代码:这里没有魔法。
目录:
设置
pip install drf-access-policy
要定义策略,请导入accesspolicy
并将其子类化:
fromrest_access_policyimportAccessPolicyclassShoppingCartAccessPolicy(AccessPolicy):statements=[]# Now read on...
示例1:viewset的策略
简而言之,策略由声明"主体"可以或不能在资源上执行什么"操作"的"语句"组成,并带有可选的自定义检查,可以检查当前请求的任何细节。
下面还有两个关键点需要记住:
- 默认情况下,所有访问都被隐式拒绝
- 任何具有"deny"效果的语句都将覆盖任何和所有"allow"语句
现在让我们看看文章端点下面的策略,它是通过视图集提供的。
classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"},{"action":["delete"],"principal":["*"],"effect":"allow","condition":"is_author"},{"action":["*"],"principal":["*"],"effect":"deny","condition":"is_happy_hour"}]defis_author(self,request,view,action)->bool:article=view.get_object()returnrequest.user==article.authordefis_happy_hour(self,request,view,action)->bool:now=datetime.datetime.now()returnnow.hour>=17andnow.hour<=18:@classmethoddefscope_queryset(cls,request,queryset):ifrequest.user.groups.filter(name='editor').exists():returnquerysetreturnqueryset.filter(status='published')classArticleViewSet(ModelViewSet):# Just stick the policy here, as you would do with# regular DRF "permissions"permission_classes=(ArticleAccessPolicy,)# Helper property here to make get_queryset logic# more explicit@propertydefaccess_policy(self):returnself.permission_classes[0]# Ensure that current user can only see the models # they are allowed to seedefget_queryset(self):returnself.access_policy.scope_queryset(self.request,Articles.objects.all())@action(method="POST")defpublish(self,request,*args,**kwargs):pass@action(method="POST")defunpublish(self,request,*args,**kwargs):pass# the rest of you view set definition...
这些操作对应于视图集中方法的名称。
在上面的示例中,设置了以下规则:
- 任何人都可以列出和检索文章
- 编辑组中的用户可以发布和取消发布文章
- 为了删除一篇文章,用户必须是文章的作者。注意condition方法
是如何在视图上调用
get_object()
来获取当前文章的。 - 如果条件
是"快乐时光"
,计算结果为true
,则不允许任何人执行任何操作。
此外,我们在scope\u queryset
方法中有一些逻辑,用于筛选哪些模型对当前用户可见。在这里,我们希望用户只看到已发布的文章,除非他们是一个编辑器,在这种情况下,他们可以看到任何状态的文章。您必须记住从视图中调用此方法,因此我建议将其作为安全审核清单的一部分进行查看。
示例2:基于功能的视图的策略
您还可以使用基于功能的视图制定策略。要在策略语句中引用的操作是函数的名称。您还可以将多个功能捆绑到同一策略中,如下例所示。
classAuditLogsAccessPolicy(AccessPolicy):statements=[{"action":["search_logs"],"principal":"group:it_staff","effect":"allow"},{"action":["download_logs"],"principal":["group:it_admin"],"effect":"allow"}]@api_view(["GET"])@permission_classes((AuditLogsAccessPolicy,))defsearch_logs(request):## you logic here...pass@api_view(["GET"])@permission_classes((AuditLogsAccessPolicy,))defdownload_logs(request):## you logic here...pass
文档
语句元素
校长
<表>"*"
(任何用户)"已验证"
(任何已验证的用户)"匿名"
(任何未经身份验证的用户)
联合[str,list[str]
"组:{name}"
按id与
"id:{id}"匹配
["group:admins","id:9322"]
["id:5352"]
["匿名"]
"*"
动作
<表>联合[str,list[str]
"*"
(任何操作)"<;安全方法"
(只读http请求:head、get、options)
["list","delete","create]
["*"]
["<;安全方法"]
效果
<表>allow
语句时,请使用deny
。
str
条件
<表>条件(请求、视图、操作:str、自定义参数:str=none)
。如果要将自定义参数传递给条件的方法,请将值格式化为{method{name}:{value}
,例如,用户必须是:所有者
将调用名为用户必须是
的方法,并将字符串"所有者"
作为最终参数传递给它。如果是真的,P奥利西将生效。用于强制对象级权限。如果给定条件列表,则所有条件的计算结果都必须为true
。
联合[str,list[str]
"是帐户的管理员"
"是文章的作者"
["余额为正","账户未冻结"]`
< BR>"用户必须是:帐户管理器"
政策评估逻辑
为了确定是否授予对请求的访问权,首先过滤所有适用的语句。如果以下所有条件均为真,则语句适用于当前请求(1)请求用户与语句的一个主体匹配,(2)方法/函数的名称与其一个操作匹配,以及(3)任何自定义条件的计算结果均为真。
如果任何语句具有"允许"的效果,而没有任何语句具有"拒绝"的效果,则允许该请求。默认情况下,所有请求都被拒绝。
对象级权限/条件
什么对象级权限?您可以轻松地检查自定义条件中的对象级访问,该条件的计算结果将确定语句是否生效。此条件传递给视图
实例,因此可以通过调用view.get_object()
来获取模型实例。您甚至可以引用多个条件,以保持访问方法的集中性和可测试性,并使用参数对这些条件进行参数化。
classAccountAccessPolicy(AccessPolicy):statements=[## ... other statements ...{"action":["withdraw"],"principal":["*"],"effect":"allow","condition":["balance_is_positive","user_must_be:owner"]},{"action":["upgrade_to_gold_status"],"principal":["*"],"effect":"allow","condition":["user_must_be:account_advisor"]}## ... other statements ...]defbalance_is_positive(self,request,view,action)->bool:account=view.get_object()returnaccount.balance>0defuser_must_be(self,request,view,action,field:str)->bool:account=view.get_object()returngetattr(account,field)==request.user
请注意,我们是如何使用user_must_be
方法的,方法是使用应该等于请求用户的model字段对其进行参数化:该语句只有在该条件通过时才有效。
多租户数据/限制查询集
您可以在策略类上定义一个类方法,该方法接受queryset和当前请求,并返回一个安全作用域的queryset,该queryset仅表示当前用户应具有访问权限的数据库行。这有助于多租户情况,或者更一般地,当用户不应该对模型实例具有完全可见性时。当然,您可以在代码的其他地方执行此操作,但是将此方法放在policy类中会将所有访问逻辑放在一个地方。
classPhotoAlbumAccessPolicy(AccessPolicy):# ... statements, etc ...# Users can only access albums they have created@classmethoddefscope_queryset(cls,request,qs):returnqs.filter(creator=request.user)classTodoListAccessPolicy(AccessPolicy):# ... statements, etc ...# Users can only access todo lists owned by their organization@classmethoddefscope_queryset(cls,request,qs):user_orgs=request.user.organizations.all()returnqs.filter(org__id__in=user_orgs)
附加到视图集和基于函数的视图
您附加访问策略的方式与常规drf权限相同。
对于视图集,将其添加到权限
属性:
classArticleViewSet(ModelViewSet):permission_classes=(ArticleAccessPolicy,)
对于基于函数的视图,将其添加到 如果您不想将策略语句硬编码到类中,可以从外部数据源加载它们:这是一个很好的步骤,因为您可以在不重新部署代码的情况下更改访问规则。 只需在策略类上定义一个名为 示例: 您可能只想在自己的 如果不使用django的内置auth应用程序,则可能需要定义一种自定义方法来检索用户所属的角色/组名称。只需在策略类上定义一个名为 默认情况下,pref标识原则类型(用户或组)的ixe分别是"id:"和"group:"。您可以通过在策略类上设置这些属性来自定义此设置: 测试可以在 请参见许可证。
权限类中
@api_view(["GET"])@permission_classes((ArticleAccessPolicy,))defcreate_article(request):## you logic here...pass
从外部源加载语句
get_policy_statements
的方法,该方法具有以下签名:
获取策略语句(self、request、view)->;list[dict]
classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"}]
0
accesspolicy
自定义子类上定义一次此方法,所有其他访问策略都从该子类继承。自定义用户组/角色值
get_user_group_values
的方法。它被传递一个参数:当前请求的用户。在下面的示例中,用户模型与"roles"有一个to-many关系,roles在一个名为"title"的字段中有其"name"值。classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"}]
1
自定义主体前缀
classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"}]
2
变更日志
0.4.2(2019年6月)
0.4.0(2019年6月)
{method\u name}:{arg\u value}
的条件值向条件方法传递参数
0.3.0(2019年5月)
<;safe_methods>;
操作键,当当前请求是http只读方法时,该键与之匹配:head、get、options。0.2.0(2019年5月)
authenticated
和anonymous
主体密钥,以分别匹配任何经过身份验证的用户和任何未经身份验证的用户。感谢@bogdandm的讨论/建议!0.1.0(2019年5月)
测试
/tests
文件夹中的简化django项目中找到。安装项目需求,并执行/manage.py test
运行它们。许可证
推荐PyPI第三方库