1. 管理员向导
  2. 管理数据访问权限

权限引擎 示意图

权限计算-查询 示意图华炎魔方基于权限集为每一个对象设置权限,支持对象级、分部级和字段级的权限控制,还可以通过在对象上配置共享规则和限制规则,或者编写 beforeFind 触发器 来实现记录级的数据查看权限。

权限集&简档

华炎魔方通过给每个用户配置“权限集”的方式来标识用户所属权限范围;只要基于权限集为每一个对象设置权限,就可以给每个用户分配不同的查看、创建、编辑、删除记录的权限;

简档是一种特殊的权限集,它与其他权限集一样,都是一种基于权限来对用户进行归类的标识。两者的区别在于,简档好比是每个用户的基本身份,每个用户必须且只能属于一个简档,比如一个用户可能是管理员或普通用户,但不可能同时有两种身份。而权限集是用于标识某种特殊权限的集体,能够为用户额外叠加更多的权限身份,一个用户只能属于一个简档,但是可以属于多个权限集,比如可以为某个财务人员分配一个财务管理员的简档,同时把他加到合同管理员权限集中,这样这个财务人员就额外拥有了管理合同的权限。

我们可以在“设置”应用的“公司设置→权限集/简档”界面查看目前系统中已经存在的”权限集/简档“列表,以下是可以为”权限集”配置的属性:

  • API名称 唯一标识名称,需要符合API命名规范。
  • 显示名称 界面上的显示名称。
  • 类别 权限集/简档。
  • 许可证 platform/community,如果是内部用户简档,请配置为前者,反之外部用户简档,比如供应商和客户应该配置为后者;该属性对于简档类型的权限集必填,普通权限集不用填写。
  • 授权应用 当需要限制某个权限集/简档下的用户只能看到部分应用的话,请在这里选择授权应用,如果设置为空表示授权全部应用。
  • 成员 权限集适用的成员,简档不需要配置该选项,因为简档的成员是通过给相关人员设置其简档属性来配置的。
  • 密码策略 只有简档才需要配置相关策略,详细请参考后续相关小节中的描述。
  • 登录策略 只有简档才需要配置相关策略,详细请参考后续相关小节中的描述。

简档

我们可以且必须为每个用户分配一个简档来标识其基本身份。

标准简档

华炎魔方内置了以下几个标准简档,如果需要,可以另外添加自定义简档来扩展它们。

API 名称名称备注
admin管理员工作区/公司管理员
user用户普通用户
supplier供应商外部供应商用户
customer客户外部客户用户

自定义简档

可以在“设置”应用的“公司设置→简档”界面,点击简档名称进入某个简档详细页,然后点击页面右上角的“自定义”按钮来自定义某个标准简档的属性配置。

除了可以按实际需求通过自定义按钮来修改某个标准简档的属性配置外,还可以新建一个自定义简档来扩展已有简档,比如可以新建一个财务管理员,该简档下的用户默认被分配了财务管理相关功能。

配置用户简档

请在“设置”应用的“公司设置→用户”界面,新建或修改人员信息时,为每个人员配置“简档”属性来配置用户的简档。

密码策略

在华炎魔方中可以为简档配置以下密码策略来极大提高相关简档下用户账户的安全性:

  • 强制密码历史:密码的重复使用是一个重要问题,在确定了新密码与具体数量的旧密码不同之后才可以重复使用旧密码。 许多用户希望在很长一段时间内重复使用其帐户的相同密码。 一个特定帐户使用的密码越久, 被攻击破解的可能性就越大。 如果用户在更改密码时, 使用了旧密码, 则安全性将降低。
  • 最大无效登录尝试次数:设置确定将导致用户帐户被锁定的登录失败次数。
  • 锁定有效期间:设置确定锁定帐户在自动解锁之前保持锁定的分钟数。

当因为密码策略用户被锁定后,一般可以等待一定时间后让账户自动解锁,也可以求助管理员来为用户解除锁定。

管理员在“设置”应用的“公司设置→用户”界面,进入要锁定或解锁的用户详情页面,点击右上角“锁定”或“解除锁定”按钮,即可变更相关用户的锁定状态。

*如果是唯一的管理员账户被锁定后。则需要修改数据库users表中该管理员记录的lockout属性值为false才可以解除锁定。*

登录策略

除了密码策略外,我们还可以在华炎魔方中为简档配置以下登录策略来进一步提高相关简档下用户账户的安全性:

  • 登录到期时间(天):可以在这里配置多少天后账户自动退出登录,默认为90天。
  • 手机App登录到期时间(天):可以在这里配置多少天后手机APP客户端上的账户自动退出登录,默认为90天。
  • 单例登录:勾选的话,一个账户只能在一处登录,每次登录某个客户端后,会自动注销以当前用户身份登录的其他客户端。
  • 手机App单例登录:勾选的话,一个账户只能在一个手机APP客户端上登录,每次登录某个手机客户端后,会自动注销以当前用户身份登录的其他客户端。
  • 多重身份验证(需要开启短信服务):勾选的话,会使用在密码登陆的基础上增加短信验证的两种组合的鉴别技术进行系统登陆。

权限集

权限集,与简档相似,是另一种权限集合,也是授予某一类的用户对各种对象和功能的访问权限的集合。但是,每个用户可以属于多个权限集,这是与简档的主要区别。

一般来说,简档是为大类用户配置基本权限,而权限集则可为小类用户扩展更多的功能访问权限。 华炎魔方里的每个用户,都会属于1个简档,也可能同时再属于1个或多个权限集。对系统管理员而言,再用户创建之后,用户属于哪个简档一般无需调整,更多的是对用户属于哪些权限集作适时的调整。

假设您在贵组织中拥有“库存”自定义对象。许多用户需要此对象的“读取”访问权限,而少数用户则需要“编辑”访问权限。您可以创建授予“读取”访问权限的权限集,并将其分配到适用用户。然后,您可以创建另一个权限集,可授予“库存”对象的“编辑”访问权限,并将其分配到人数较少的用户组。

标准权限集

华炎魔方内置了以下几个标准权限集,如果需要,可以另外添加自定义权限集来扩展它们。

API 名称名称备注
organization_admin分部管理员用于管理分部下组织及用户信息。
workflow_admin流程管理员用于管理审批王应用下的审批流程。
master_admin主数据管理员用于管理系统基础主数据。
  • 分部管理员: 是默认的组织管理员权限集,该权限集内的用户,对其主分部下的部门、用户具有管理权限,相当于是分部的系统管理员。
  • 流程管理员:是默认的流程管理员权限集,该权限集内用户,对其所属分部下的审批流程的相关对象,具有管理权限,相当于是分部的流程管理员。
  • 主数据管理员:这是一个名为“主数据“的内置软件包中的内置权限集,用于管理系统基础主数据,比如货币、税率、计量单位等。

自定义权限集

可以在“设置”应用的“公司设置→权限集”界面,点击权限集名称进入某个权限集详细页,然后点击页面右上角的“自定义”按钮来自定义某个标准权限集的属性配置。

除了可以按实际需求通过自定义按钮来修改某个标准权限集的属性配置外,还可以新建一个自定义权限集来扩展已有权限集,比如可以新建一个合同管理员,以分配合同管理的相关功能给该权限集下的用户。

配置权限集成员

与简档不一样,简档的成员是通过给相关人员设置其简档属性来配置成员的,要配置权限集的成员,可在“设置”应用的“公司设置→权限集”界面,当新建或修改权限集的时候直接设置其“成员”属性即可。

需要注意的是,一个用户可以属于多个权限集,当用户同时属于多个权限集时,最终用户的权限为其在各权限集中的权限叠加。

权限叠加规则

虽然每个用户只能属于一个简档,但是可能同时属于多个权限集,当一个用户属于多个权限集时,实际拥有的权限为各权限集权限的叠加。

权限叠加的意思是取各个权限集的最大权限,然后把它们叠加到一起,比如用户A属于1和3两个权限集,虽然他在权限集1中没有创建权限,但是在权限集3中有,取两个权限集中最大权限叠加后最终用户A是有创建权限的,相关规则请参考下图:

对象权限

华炎魔方权限引擎是基于权限集来计算用户对每一个对象的相关权限的,所以我们首先需要配置的就是对象权限以便实现对象级的权限控制。

在简档和权限集里,都可以设置对象权限,还可以直接在对象设置界面直接配置该对象的对象权限。 我们推荐在”设置“应用中进入”公司设置→简档/权限集“界面,找到并进入要设置对象权限的简档或权限集记录详细界面,在”对象权限“子表中新建或编辑某条对象权限记录来配置对象权限。

以下权限功能描述将基于我们假设已经创建了一个名为”合同管理员“的权限集,并且需要为该权限集下的用户配置”合同“对象权限。

增删改查

在对象权限记录的新建或编辑界面可以设置指定简档或权限集下的用户对该对象的基本操作权限。

  • 允许查看 勾选后,该权限集下的用户可以但仅限于查看所有者为自己合同记录。
  • 允许创建 勾选后,该权限集下的用户可以创建合同记录。
  • 允许编辑 勾选后,该权限集下的用户可以但仅限于编辑所有者为自己合同记录。
  • 允许删除 勾选后,该权限集下的用户可以但仅限于删除所有者为自己合同记录。
  • 查看所有记录 勾选后,该权限集下的用户可以查看所有的合同记录。
  • 修改所有记录 勾选后,该权限集下的用户可以编辑和删除所有的合同记录。

用户创建记录时,记录所有者字段值默认为当前登录用户,即默认情况下记录创建人就是记录所有者,也就是说默认情况下,除非用户有查看或修改所有记录权限,否则他最多只能查看或编辑自己创建的记录。

分部权限

在对象权限记录的新建或编辑界面可以设置指定简档或权限集下的用户对该对象的分部级操作权限。

  • 查看本分部 勾选后,该权限集下的用户除了可以查看所有者为自己合同记录外,还可以额外查看自己所属分部下的合同记录。
  • 修改本分部 勾选后,该权限集下的用户除了可以修改所有者为自己合同记录外,还可以额外修改自己所属分部下的合同记录。
  • 查看指定分部 选择希望额外授予查看权限的分部,可选择多个分部,表示该权限集下的用户除了可以查看所有者为自己合同记录外,还可以额外查看这里授权的分部下的合同记录。
  • 修改指定分部 选择希望额外授予修改权限的分部,可选择多个分部,表示该权限集下的用户除了可以修改所有者为自己合同记录外,还可以额外修改这里授权的分部下的合同记录。

假设在某个魔方项目中有以下几个分部,我们按南部和北部的地理位置来划分业务区域:

  • 总部:上海总公司
  • 南部:南京分公司、杭州分公司,苏州公公司
  • 北部:北京分公司、天津分公司、武汉分公司

在该项目中需要通过权限配置来实现以下业务需求:

  • 业务管理员可以查看和修改本公司所有合同数据;
  • 在总公司设立业务总监,可以额外查看全公司所有合同数据但不可以修改它们;
  • 在总公司设立南部分公司业务总监,分管南部所有分公司的合同数据,额外授予其查看和修改其分管分公司内的合同数据;
  • 在总公司设立北部分公司业务总监,分管北部所有分公司的合同数据,额外授予其查看和修改其分管分公司内的合同数据;

按以上业务需求,我们首先需要在“设置”应用中进入“公司设置→权限集”页面新建以下权限集:

  • 业务管理员:包括总公司和分公司在内的所有业务管理员都需要加到该权限集成员中。
  • 业务总监:总公司业务总监需要加到该权限集中。
  • 南部业务总监:分管南部分公司业务的业务总监需要加到该权限集中。
  • 北部业务总监:分管北部分公司业务的业务总监需要加到该权限集中。

接下来我们分别在上面新建的几个权限集的详细页面配置合同对象的对象权限:

  • 业务管理员:勾选“允许查看”、“允许创建”、“允许编辑”、“允许删除”、“查看本分部”、“修改本分部”选项。
  • 业务总监:跟上面业务管理员一样配置,但是额外勾选“查看所有记录”选项。
  • 南部业务总监:跟上面业务管理员一样配置,但是额外配置“查看指定分部”和“修改指定分部”选项,选择其分管的分公司。
  • 北部业务总监:跟上面业务管理员一样配置,但是额外配置“查看指定分部”和“修改指定分部”选项,选择其分管的分公司。

按以上配置方法配置完各个权限集的对象权限后,我们就完整实现了上面提到的分部级权限相关需求。

只有企业版才支持分部级权限相关功能。

列表视图权限

在对象权限记录的新建或编辑界面可以设置指定简档或权限集下的用户禁止查看该对象下的某些列表视图。

  • 禁用列表视图 选择希望禁用的列表视图,表示该权限集下的用户在合同对象列表上将看不到被禁用的列表视图选项。

附件权限

我们可以在对象设置界面勾选”允许上传附件“开关来放开对象的上传附件功能,该功能放开后,用户就可以在合同记录详细界面的附件子表上上传附件了。

在较早版本的华炎魔方中,放开合同对象的附件功能后,用户是否可以在合同记录详细界面上传、修改及删除附件取决于用户是否有附件所属主表记录,也就是当前合同记录的修改权限。

现在我们增强了这部分权限功能,可以在对象权限记录的新建或编辑界面配置更多与附件功能相关的权限。

  • 允许查看附件 勾选后,该权限集下的用户只可以在合同记录详细界面查看自己上传的附件。
  • 允许创建附件 勾选后,该权限集下的用户可以在合同记录详细界面上传附件。
  • 允许修改附件 勾选后,该权限集下的用户只可以在合同记录详细界面修改自己上传的附件信息。
  • 允许删除附件 勾选后,该权限集下的用户只可以在合同记录详细界面删除自己上传的附件。
  • 查看所有附件 勾选后,该权限集下的用户只可以在合同记录详细界面查看所有附件。
  • 修改所有附件 勾选后,该权限集下的用户只可以在合同记录详细界面修改所有附件。

字段权限

之前我们提到华炎魔方权限引擎是基于权限集来计算用户对每一个对象的相关权限的,华炎魔方权限引擎还进一步实现了不同权限集下的用户对每一个对象下的不同字段的权限计算,现在我们来看看如何为合同对象配置字段级的权限控制。

假设我们需要禁止公司的业务人员查看合同记录中与财务相关的字段,我们可以先新建一个名为”业务员“的简档,然后为该简档配置合同对象下的字段级权限。

要配置字段权限,请在”设置“应用中进入”公司设置→简档/权限集“界面,找到并进入要设置对象权限的简档或权限集记录详细界面,在”对象权限“子表中点击要设置字段权限的记录左侧序号链接来进入对象权限记录详细界面。

在对象权限记录详细界面我们可以看到底部有一个名为”字段权限“的子表,请按以下步骤来配置字段权限:

  • 找到要设置权限的字段,并双击“允许查看”或“允许编辑”单元格,然后勾选单元格中的勾选框来编辑其相关权限。
  • 点击表格空白处可以看到页面底部弹出了保存按钮,点击保存来保存字段权限配置。
  • 如果需要重置该对象的字段配置,可以点击右上角的“重置字段权限”按钮。

我们只要把与财务相关的字段的”允许编辑“和”允许查看“勾选框去除即可实现业务人员查看合同记录时隐藏财务相关的字段。

在华炎魔方中查询数据时,这里配置的字段权限也会被华炎魔方权限引擎识别并叠加到最终查询条件中,整个查询计算过程请参考该文档顶部提到的 权限计算 - 查询 示意图

应用权限

华炎魔方支持基于权限集来授权应用,它可以用来限制某个权限集/简档下的用户只能看到部分应用。

请在“设置”应用的“公司设置→权限集/简档”界面编辑要限制查看应用的权限集或简档,然后在”授权应用“字段中选择允许其查看的应用,这里未选择的应用其他应用即是被限制查看的应用了,如果设置授权应用为空则表示授权全部应用。

需要注意的是,应用权限也是适用于之前提到的权限叠加规则的,即如果某个用户属于多个权限集的话,他最终能看到的应用是每个权限集能看到的应用叠加在一起的。

因为权限叠加规则的存在,而”用户“简档代表的是整个系统中所有账户,所以如果要限制某个权限集/简档下的用户只能看到部分应用的话,可能需要先限制”用户“这个简档,只授权其最少的应用权限。

假设系统中有合同、财务、办公、审批等应用,对应的有以下两个权限集,同时合同管理员下的用户被分配到”用户“简档中:

  • 合同管理员:类型为权限集;
  • 账务管理员:类型为简档;

如果我们需要做到让合同管理员只能看到合同应用而看不到另外两个应用,同时财务管理员能看到所有应用,我们可以按以下步骤来配置相关应用权限:

  • 找到并编辑合同管理员权限集,在其”授权应用“字段中选择”合同“应用并保存。
  • 找到并编辑财务管理员简档,清空在其”授权应用“字段值并保存。
  • 找到名为”用户“的标准简档,进入该简档详细界面,点击右上角的”自定义“按钮后编辑该简档,在其”授权应用“字段中选择”办公“应用并保存,如果还有其他应用这里也可以多选择些其他应用,重点是不可以选择”合同“和”财务“应用。

上面第三个步骤比较重要,不可以省略,因为合同管理员下的用户属于”用户“这个简档,如果不配置第三步的话,叠加权限后合同管理员是可以看到所有应用的。

如果想省略第三步,我们可以把合同管理员这个权限集的类型改为简档,然后把所有相关人员的简档也配置为”合同管理员“而不是”用户“即可。

共享/限制规则

通过前面的介绍我们了解到华炎魔方权限引擎基于权限集可以实现对象级、分部级及字段级的权限控制,但是还缺少了记录级的权限控制的描述。

与之前提到的权限控制功能不同,华炎魔方并不是基于权限集来实现记录级权限控制的,而是通过在对象上配置共享规则和限制规则来实现查看权限在特定条件下的共享和的收缩。

要配置共享规则或限制规则,请在”设置“应用中进入”对象设置→对象“界面,点击要设置相关规则的对象名称进入对象设置界面,然后在底部可以看到”限制规则“和”共享规则“子表,在这里新建相关规则即可:

  • API名称 唯一标识名称,需要符合API命名规范。
  • 启用 勾选表示立刻启用规则。
  • 指定条目条件 用于配置该规则生效的条件,需输入类似 {{$user.profile !='user'}} 这种公式表达式,当条件成立时该规则才生效。
  • 记录过滤器 输入要共享或限制查看的数据的过滤条件,这里也支持“指定条目条件”中一样的公式表达式语法。

指定条目条件

给共享/限制规则设置的”指定条目条件“是一个公式表达式,华炎魔方权限引擎会执行该表达式并根据执行结果来判断是否要把规则中配置的”记录过滤器“时配置的过滤条件叠加到最终查询结果中。

公式格式

{{ javascript语法的表达式 }}

必须以 ‘{{‘ 开头,以 ’}}’ 结尾,其包裹的是一个标准的javascript表达式。

支持变量

公式表达式中支持以下变量,可以直接在表达式中引用它们:

  • $user : Dictionary<any>  userSession,当前登录用户信息,详细请参考后续附录。
  • global: Dictionary<any>  全局变量,目前只支持now变量。公式表达式global.now会输出当前时间值。
请注意该表达式是在服务端运行的,所以无法在表达式中引用前端浏览器中的全局变量或函数。

记录过滤器

给共享/限制规则设置的”记录过滤器“是要共享或限制查看的数据的过滤条件,其语法规则请参考该文档底部 查询过滤条件详解 小节。

这里输入的内容也是支持公式表达式语法的,其表达式语法与上面提到的“指定条目条件”是一样的。

只要在华炎魔方中查询数据,华炎魔方权限引擎就会把满足”指定条目条件“的共享/限制规则中的”记录过滤器“里配置的过滤条件叠加到最终查询条件中,整个查询计算过程请参考该文档顶部提到的 权限计算 - 查询 示意图

  • 共享规则:如果配置的是共享规则,过滤条件会以OR 的方式叠加到最终的查询过滤条件中。
  • 限制规则:如果配置的是限制规则,过滤条件会以AND 的方式叠加到最终的查询过滤条件中。

示例1

假设在某个魔方项目中有三个分部,分别表示上海总公司、南京分公司和杭州分公司,在该项目中需要通过权限配置来实现公司内部的业务管理人员可以查看本公司所有外部客户创建的合同数据。

这里说的外部客户指的是系统中简档为”customer“的用户,在不配置共享规则的情况下,我们可以按需求给”业务管理员 salesman“这个权限集下的用户配置”合同“对象权限,勾选”查看本分部“来允许用户查看本分部的所有合同数据。

细想我们会发现需求中提的要求是业务管理员只能看到其本人创建以及外部客户创建的合同数据,并不允许其查看其他内部人员创建的合同数据。

要实现该需求,我们可以通过给合同对象配置限制规则来缩小业务管理员的查看数据权限,也可以配置共享规则来放大业务管理员的查看数据权限,下面我们详细讲解下这两种方式的配置方法:

缩小查看权限

要通过缩小业务管理员的查看合同数据权限这种方式来实现需求的话,我们在给”业务管理员“这个权限集下配置的”合同“对象权限中,需要勾选”查看本分部“属性来允许用户查看本分部的所有合同数据,这样的话业务管理员比需求要求的权限就偏大了,我们可以给合同对象配置一个限制规则来缩小其权限:

  • 指定条目条件 {{$user.roles.indexOf("salesman") > -1}}
  • 记录过滤器 {{[["profile__c", "=", "customer"], "or", ["owner", "=", $user.userId]]}}
  • 描述 限制只能查看外部客户创建的合同数据或自己创建的数据,这里不需要配置["company_id", "=", "$user.company_id"]是因为对象权限中勾选了”查看本分部“属性会自动带上。

放大查看权限

要通过放大业务管理员的查看合同数据权限这种方式来实现需求的话,我们在给”业务管理员“这个权限集下配置的”合同“对象权限中,不能勾选”查看本分部“属性以确保业务管理员只有查看自己创建的合同数据这种最基本的数据查看权限,这样的话业务管理员比需求要求的权限就偏小了,我们可以给合同对象配置一个共享规则来放大其权限:

  • 指定条目条件 {{$user.roles.indexOf("salesman") > -1}}
  • 记录过滤器 {{[["company_id", "=", $user.company_id],["profile__c", "=", "customer"]]}}
  • 描述 共享本公司所有外部客户数据。

我们看到上面无论是配置限制规则还是共享规则的方式,在过滤条件中都用到了一个名为profile__c的字段,它是用于标识合同记录创建人的简档的自定义字段,当其简档值为customer时就表示该合同记录是外部客户创建的。

我们需要为合同对象创建一个名为简档的相关表字段来保存合同记录创建人的简档值:

  • 显示名称:简档
  • 字段名:profile__c
  • 字段类型:相关表
  • 默认值:{{global.user.profile}}

这里默认值是一个表单公式,详情请参考文档 表单公式

示例2

我们可以通过给部门对象和人员对象配置共享/限制规则,来改变这两个对象默认的权限规则。

部门

默认情况下普通用户是可以查看所有部门信息的,我们可以给部门对象配置限制规则来实现部门对象的分部级数据查看权限功能。

  • 指定条目条件 {{$user.roles.indexOf('user') > -1}}
  • 记录过滤器 {{[["_id", "=", $user.companies.map(function(n){return n.organization;})], "or", ["parents", "=",$user.companies.map(function(n){return n.organization;})]]}}
  • 描述 限制user简档下的用户只能查看自己所属分部及其子分部下的部门信息。

PS: 这里如果通过给部门对象配置自定义对象权限,去掉“查看所有记录”勾选框,勾上“查看本分部”,而不是配置限制规则的话也能实现部门对象的分部级数据查看权限功能,但是这种配置实现的是“让user简档下的用户只能查看自己所属分部的部门信息,且不能查看其子分部下的部门信息。”

人员

默认情况下普通用户只能看到自己所属分部下的人员,但是不能查看自己所属分部的子分部下的人员,我们可以给人员对象配置共享规则来让普通用户可以额外查看自己所属分部的子分部下的人员信息。

  • 指定条目条件 {{$user.roles.indexOf('user') > -1}}
  • 记录过滤器 {{[["organizations_parents", "=", $user.companies.map(function(n){return n.organization;})]]}}
  • 描述 给user简档下的用户共享查看自己所属分部及其子分部下的人员信息。
只有企业版才能配置共享/限制规则来实现相关高级权限功能。

触发器&权限

虽然在华炎魔方中使用上面介绍过的各种权限配置方法已经能轻松实现精确到字段和记录级的权限控制,但在千奇百怪的实际项目场景中还是会有些权限需求未能企及,所以华炎魔方权限引擎还结合了 对象触发器,允许通过编写代码的方式来解决各种个性化的更高级别的权限问题。

原理

华炎魔方中的对象触发器,按触发的先后时间次序有两种,一种是在操作执行前触发,一种是在操作执行后触发,并且前者是以before为前缀来命名,后者是以after为前缀来命名,比如beforeUpdateafterUpdate分别会在执行数据修改前和数据修改成功后触发。

要通过触发器来实现权限控制,我们只要在相关对象上编写操作执行前的触发器,即编写以before为前缀命名的增、删、改、查触发器,在触发器函数中判断到当前登录用户没有权限时通过有意throw Error或返回false值的方式来中断执行相关操作就可以达到权限控制的目的了。

数据操作权限控制

我们可以在beforeInsert/beforeUpdate/beforeDelete触发器中根据当前用户的身份来决定是否拒绝执行相关操作,当需要拒绝时只要在触发器函数中有意throw Error或返回false值就可以中断执行相关操作。

在操作执行前的触发器函数中如果需要返回错误信息可以通过`throw Error`来中断执行数据操作,反之可以通过`return false`来静默中断执行数据操作。

在触发器函数中可以通过表达式this.doc变量来获取要修改哪些字段值,我们可以移除该变量中的字段来拒绝保存当前用户没有权限保存的字段值,以下示例代码只允许管理员变更合同记录的owner字段值。

module.exports = {
    listenTo: 'contracts',
    beforeInsert: async function () {
        const userId = this.userId;
        const spaceId = this.spaceId;
        if (userId && spaceId) {
            const userSession = await auth.getSessionByUserId(userId, spaceId);
            var isAdmin = userSession.is_space_admin;
            if(!isAdmin && this.doc.owner){
                throw new Error("只有系统管理员才能变更合同所有者!");
                //return false // 把throw error换成这行,将会新建失败,但是不提示错误信息。
                //delete this.doc.owner //把throw error换成这行,将会新建成功,但是新建后的记录owner值为空。
            }
        }
    },
    beforeUpdate: async function () {
        const userId = this.userId;
        const spaceId = this.spaceId;
        if (userId && spaceId) {
            const userSession = await auth.getSessionByUserId(userId, spaceId);
            var isAdmin = userSession.is_space_admin;
            if(!isAdmin && this.doc.owner){
                throw new Error("只有系统管理员才能变更合同所有者!");
                //return false // 把throw error换成这行,将会修改失败,但是不提示错误信息。
                //delete this.doc.owner //把throw error换成这行,将会修改成功,但是记录owner值不会被修改。
            }
        }
    }
}

数据查询权限控制

我们可以在beforeFind触发器中根据当前用户的身份来决定是否额外叠加特定的过滤条件来限制数据查看范围。

beforeFind触发器函数中通过表达式this.query.filters变量可以获取原始的查询条件,然后可以变更该变量值来给原始查询条件额外叠加其他的过滤条件来收缩或放大用户的数据查看权限。

并不存在`beforeFindOne`触发器,执行`findOne`时与执行`find`时触发的是都`beforeFind`触发器。

假设有一个文档集对象,要实现以下权限规则:

  • 工作区管理员始终有所有文档集查看权限。
  • 授权查看的组织(非分部)和用户可以查看相关文档集。
  • 所有用户至少有查看owner等于自己的文档集的权限。

上述需求中需要把权限落实到具体的用户,所以无法通过分部级权限来实现该需求,我们可以给该对象添加两个额外的字段来保存相关权限配置:

  • 授权组织:被授权的组织可以额外拥有查看该文档集的权限。
  • 授权用户:被授权的用户可以额外拥有查看该文档集的权限。

然后编写以下代码为文档集document_collections对象添加beforeFind触发器逻辑实现相关权限控制需求。

const Filters = require('@steedos/filters');
module.exports = {
    listenTo: 'document_collections',
    beforeFind: async function(){
        const query = this.query;
        const userId = this.userId;
        const spaceId = this.spaceId;
        if (userId && spaceId) {
            //获取原始的查询条件
            let filters = query.filters;
            //自定义生成用户权限查询条件
            let permissionsFilters;
            const userSession = await auth.getSessionByUserId(userId, spaceId);
            var isAdmin = userSession.is_space_admin;
            if (!isAdmin) {
                var organizations = userSession.organizations.map((org) => { return org._id });
                permissionsFilters = [
                    ["owner", "=", userId],
                    "or",
                    ["allow_read_organizations", "=", organizations],
                    "or",
                    ["allow_read_users", "=", userId]
                ];
            }
            if(permissionsFilters){
                //修改查询条件; formatFiltersToODataQuery函数将数组形式的filters转换为字符串filters
                query.filters = `(${filters}) and (${Filters.formatFiltersToODataQuery(permissionsFilters)})`;
            }
        }
    }
}

用户身份判断

在触发器中编写权限控制相关代码时,我们经常需要会判断当前登录用户是否有相关权限,而要判断权限首先需要获取的是当前登录用户的身份信息,在华炎魔方中通过可以编写以下代码来获取用户身份。

系统管理员

可以通过以下代码来判断当前用户是否是系统管理员。

const userSession = await auth.getSessionByUserId(userId, spaceId);
var isAdmin = userSession.is_space_admin;
if (!isAdmin) {
    //当前登录用户不是管理员
}

所属组织

可以通过以下代码来获取当前用户所属组织,一般用于在过滤条件中按组织来过滤数据。

const userSession = await auth.getSessionByUserId(userId, spaceId);
var organizations = userSession.organizations.map((org) => { return org._id });

userSession

还可以通过以下代码来获取当前登录用户的userSession来获取更多用户信息。

const auth = require("@steedos/auth");
const userSession = await auth.getSessionByUserId(userId, spaceId);

userSession中所包含的用户信息详情请查阅本文档底部的 userSession详解 小节。

集成API

华炎魔方自动为业务对象生成的 GraphQL API,ODATA API都自带身份验证并与华炎魔方权限引擎集成,实现数据权限控制。

另外在微服务中对数据执行增删改查等操作,或是在触发器中调用 ObjectQL 数据查询或操作函数,都会经过华炎魔方权限引擎校验,只有校验通过的操作才会被执行。

查询过滤条件详解

华炎魔方使用数组格式定义过滤条件。

基本运算符

  • ”=”: 等于
  • ”!=”: 不等于
  • ”>”: 大于
  • ”>=”: 大于等于
  • ”<”: 小于
  • ”<=”: 小于等于
  • “startswith”: 以…开始
  • “contains”: 包含…
  • “notcontains”: 不包含…
  • “between”: 范围

数组类型字段

运算符为”=“时,条件自动按”or”裂变连接成多个筛选条件,类似实现了”in”操作功能,所以下两种写法结果相同:

[["status", "in", ["closed","open"]]]

[ [ "status", "=", "closed" ], "or", [ "status", "=", "open" ] ]

运算符为”!=“时,条件自动按”and”裂变连接成多个筛选条件,所以下两种写法结果相同:

[["status", "not in", ["closed","open"]]]

[ [ "status", "!=", "closed" ], "and", [ "status", "!=", "open" ] ]

运算符为”between”时,条件自动转换成”>=“及”<=“运算符对应的筛选条件,以下各组效果相同:

[["age", "between", [20,30]]] 等效于 [ [ "age", ">=", 20 ], "and", [ "age", "<=", 30 ] ]

[["age", "between", [null,30]]] 等效于 [ [ "age", "<=", 30 ] ]

[["age", "between", [20,null]]] 等效于 [ [ "age", ">=", 20 ] ]

between只支持数值及日期时间类型,且过滤值必须是两个元素的数组格式

其他情况一律自动按”or”裂变连接成多个筛选条件

[["tag", "contains", ["start","end"]]] 等效于 [ [ "tag", "contains", "start" ], "or", [ "tag", "contains", "end" ] ]

“与(and)”、“或(or)”操作

多个过滤器可以通过“与(and)”、“或(or)”操作进行组合,例如:

[ [ "value", ">", 3 ], "and", [ "value", "<", 7 ] ]

[ [ "value", ">", 7 ], "or", [ "value", "<", 3 ] ]

如果不指定“与(and)”、“或(or)”操作,系统默认按照“与(and)”操作执行过滤。所以下两种写法结果相同:

[ [ "value", ">", 3 ], "and", [ "value", "<", 7 ] ]

[ [ "value", ">", 3 ], [ "value", "<", 7 ] ]

userSession详解

userSession中包含了当前登录用户信息,下面列出了其主要变量清单:

属性名值类型属性含义
authTokenstring登录信息
companiesarray所属分部
companyobject主分部
company_idstring主分部id
company_idsarray所属分部id
emailstring邮箱
is_space_adminboolean是否是工作区管理员
languagestring语言
localestring本地语言
mobilestring手机号
namestring用户名
organizationobject主部门
organizationsarray所属部门
password_expiredboolean登录密码过期
permission_sharesarray共享规则
profilestring简档
rolesarray用户角色
spaceobject当前工作区
spaceIdstring当前工作区id
spacesarray用户所属工作区集合
userIdstring用户id
utcOffsetnumber国际化UTC时间偏差;北京时间时该值为 8。