以aws的iam策略为模型的声明性访问策略/权限。

drf-access-polic的Python项目详细描述


django rest-访问策略

package versionpython versions

该项目为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

文档

语句元素

校长

<表>说明 应通过标识其所属的组或其用户ID来匹配当前请求的用户。 特殊值"*"(任何用户)
"已验证"(任何已验证的用户)
"匿名"(任何未经身份验证的用户) 键入联合[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,)

对于基于函数的视图,将其添加到权限类中

@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自定义子类上定义一次此方法,所有其他访问策略都从该子类继承。

自定义用户组/角色值

如果不使用django的内置auth应用程序,则可能需要定义一种自定义方法来检索用户所属的角色/组名称。只需在策略类上定义一个名为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

自定义主体前缀

默认情况下,pref标识原则类型(用户或组)的ixe分别是"id:"和"group:"。您可以通过在策略类上设置这些属性来自定义此设置:

classArticleAccessPolicy(AccessPolicy):statements=[{"action":["list","retrieve"],"principal":"*","effect":"allow"},{"action":["publish","unpublish"],"principal":["group:editor"],"effect":"allow"}]
2

变更日志

0.4.2(2019年6月)

  • 修复pypy显示的自述文件格式。

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月)

  • 添加特殊的authenticatedanonymous主体密钥,以分别匹配任何经过身份验证的用户和任何未经身份验证的用户。感谢@bogdandm的讨论/建议!

0.1.0(2019年5月)

  • 初始版本

测试

测试可以在/tests文件夹中的简化django项目中找到。安装项目需求,并执行/manage.py test运行它们。

许可证

请参见许可证。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
Java程序运行时错误   JavaAndroidStudio:与往常一样,四舍五入到next.5或.0   apache使用Java以表单数据形式上载文件   带矢量的java Freeflight相机如何正确旋转?   java如何以编程方式检索有关当前项目的语言、操作系统、体系结构等信息   java Twitter4J tweet实体?   java PdfBox编码异常   java在拖动未装饰的舞台时,如何强制光标停留在窗口上   JavaSpring注释扫描优化   java无法通过IntelliJ Idea在tomcat上运行服务   java在生命周期中如何拦截请求?   java中的数组返回错误