限制对数据的访问

重要

本教程是 服务器框架 101 教程的延伸。请确保您已完成该教程,并使用您之前构建的 estate 模块作为本教程中练习的基础。

到目前为止,我们主要关注了实现有用的功能。然而在大多数业务场景中,*安全*很快会成为一个关注点:目前,

  • 任何员工(即 group_user 所代表的)都可以创建、读取、更新或删除房产、房产类型或房产标签。

  • 如果安装了 estate_account 模块,则只有代理人员可以与开票功能进行交互,以便确认销售,因为这是 创建发票 所必需的。

然而:

  • 我们不希望第三方能够直接访问属性。

  • 并非所有员工都是房地产经纪人(例如行政人员、物业经理等),我们不希望非经纪人员查看可用的房产。

  • 房地产经纪人不需要也不参与决定哪些房产类型或标签是**可用的**。

  • 房地产经纪人可以拥有*独家*房产,我们不希望一个经纪人能够管理另一个经纪人的独家房产。

  • 所有房地产经纪人应该能够确认他们可以管理的房产销售,但我们不希望他们能够验证或在系统中将发票标记为已付款。

注解

我们可能对于小型企业来说,这些内容中的某些或大部分都是可以接受的。

因为对于用户来说,禁用不必要的安全规则比从零开始创建它们更容易,因此最好采取谨慎的态度,限制访问权限:如果有必要或方便,用户可以放松这些访问限制。

另请参见

与该主题相关的文档可以在 安全参考 中找到。

编码规范 文档记录了主数据项的格式和库位。

目标

本节结尾处,

  • 我们可以将员工设置为 房地产经纪人房地产经理

  • “admin” 用户是房地产管理员。

  • 我们有一位新的 房地产经纪人 员工,没有发票或管理的访问权限。

这在需要更改时,每次为员工单独添加安全规则是不现实的,因此使用 来将安全规则与用户关联起来。它们对应于可以分配给员工的角色。

对于大多数 Odoo 应用 1 来说,一个良好的基础是设置 用户*经理*(或管理员)角色:经理可以更改应用的配置并监督其全部使用情况,而用户则可以正常使用该应用 2

这个基准对我们来说似乎已经足够:

  • 房地产经理可以配置系统(管理可用类型和标签),并监督管道中的每处房产。

  • 房地产经纪人可以管理他们负责的房产,或者未明确由任何经纪人负责的房产。

按照 Odoo 的数据驱动特性,用户组只不过是 res.groups 模型的一条记录。它们通常属于模块的 主数据,定义在模块的一个数据文件中。

如简单示例可在此处找到 <https://github.com/odoo/odoo/blob/532c083cbbe0ee6e7a940e2bdc9c677bd56b62fa/addons/hr/security/hr_security.xml#L9-L14>。

Exercise

  1. 在适当的文件夹中创建 security.xml 文件,并将其添加到 __manifest__.py 文件中。

  2. 如果没有的话,请将一个 'category' 字段添加到您的 __manifest__.py 中,值设为 Real Estate/Brokerage

  3. 添加一条记录,创建一个 ID 为 estate_group_user、名称为“代理人”且类别为 base.module_category_real_estate_brokerage 的用户组。

  4. 在下方,添加一条记录,创建一个 ID 为 estate_group_manager、名称为“管理员”、类别为 base.module_category_real_estate_brokerage 的用户组。该 estate_group_manager 用户组需要包含 estate_group_user

注解

那里这个 类别 是从哪里来的?它是一个 模块类别。在这里我们使用了类别 ID base.module_category_real_estate_brokerage,该 ID 是由 Odoo 根据模块的 __manifest__.py 中设置的 category 自动生成的。您也可以在这里找到 Odoo 提供的 默认模块类别列表

小技巧

由于我们修改了数据文件,请记得重启 Odoo 并使用 -u estate 更新模块。

如果你进入 设置 ‣ 管理用户 并打开 admin 用户(”Mitchell Admin”),你应该会看到一个新部分:

../../_images/groups.png

将管理员用户设置为 房地产经理

Exercise

通过网页界面,创建一个仅具有“房地产经纪人”访问权限的新用户。该用户不应具有任何开票或管理访问权限。

使用私有标签或窗口以新用户身份登录(记得设置密码),作为房地产经纪人,您只能看到房地产应用,以及可能的讨论(聊天)应用:

../../_images/agent.png

访问权限

另请参见

与该主题相关的文档可以在 访问权限 中找到。

目标

本节结尾处,

  • 不是房地产代理的员工将看不到房地产应用。

  • 房地产经纪人将无法更新房产类型或标签。

访问权限最初在 Chapter 4: Security - A Brief Introduction 中引入。

访问权限是一种通过用户组来赋予用户对模型的访问方式:将访问权限关联到一个用户组,然后该用户组中的所有用户都将拥有该访问权限。

例如,我们不希望房地产经纪人能够修改可用的房产类型,因此我们不会将该访问权限关联到“用户”组。

访问权限只能授予访问,不能撤销:当检查访问权限时,系统会查看用户(通过任何用户组关联的)任何访问权限是否授予该访问。

用户组

创建

读取

更新

删除

一个

X

X

B

X

C

X

具有组 A 和 C 的用户可以执行任何操作,但不能删除对象;而具有组 B 和 C 的用户可以读取和更新对象,但不能创建或删除它。

注解

  • 该访问权限的用户组可以省略,这意味着 ACL 将适用于 所有用户,这是一种有用但具有风险的默认选项,因为根据安装的应用程序不同,它可能会让非用户也能访问该模型。

  • 如果没有任何访问权限适用于用户,他们将不被授予访问权限(默认拒绝)。

  • 如果一个菜单项指向的模型用户没有访问权限,并且也没有用户可见的子菜单,该菜单将不会被显示。

Exercise

更新访问权限文件为:

  • 将所有对象的完整访问权限授予您的房地产经理用户组。

  • 让代理(房地产用户)仅对类型和标签拥有只读访问权限。

  • 不要给予任何人删除属性的权限。

  • 检查您的代理用户是否无法修改类型或标签,也无法删除属性,但可以正常创建或更新属性。

警告

请记住,为您的 ir.model.access 记录分配不同的 xid,否则它们会互相覆盖。

由于 “demo” 用户未被设置为房地产经纪人或经理,他们甚至不应该能够看到房地产应用。请使用私有标签或窗口进行检查(”demo” 用户的密码是 “demo”)。

记录规则

另请参见

与该主题相关的文档可以在 参考/security/rules 中找到。

目标

在本节结束时,代理将无法看到仅属于其同事的属性;但经理仍然可以查看所有内容。

访问权限可以授予对整个模型的访问权限,但通常我们需要更具体一些:虽然代理可以与一般属性进行交互,但我们可能不希望他们更新甚至查看由其同事管理的属性。

记录 规则 提供了这种精确性:它们可以授予或拒绝对单个记录的访问权限:

<record id="rule_id" model="ir.rule">
    <field name="name">A description of the rule's role</field>
    <field name="model_id" ref="model_to_manage"/>
    <field name="perm_read" eval="False"/>
    <field name="groups" eval="[Command.link(ref('base.group_user'))]"/>
    <field name="domain_force">[
        '|', ('user_id', '=', user.id),
             ('user_id', '=', False)
    ]</field>
</record>

搜索条件 是访问管理的方式:如果记录通过,则授予访问权限,否则访问将被拒绝。

小技巧

因为规则通常较为复杂,且不是批量创建的,所以它们通常以 XML 格式创建,而不是用于访问权限的 CSV 格式。

上面的规则:

  • 仅适用于 “创建”、”更新”(写入)和 “删除”(解除关联)操作:在此场景中,我们希望每个员工都能查看其他用户记录,但只有作者/负责人可以更新记录。

  • 是否为 **非全局**(非全局),因此我们可以为例如经理提供额外的规则。

  • 允许执行操作,如果当前用户(user.id)已设置(例如,已创建,或已被分配)在该记录上,或者该记录完全没有关联的用户。

注解

如果未为某个模型和操作定义规则或规则不适用,则该操作将被允许(默认允许),如果访问权限设置不正确(过于宽松),可能会产生意外效果。

Exercise

定义一条规则,该规则限制代理只能查看或修改没有销售员的房产,或者他们自己是销售员的房产。

您可能需要创建第二个房地产经纪人用户,或者创建一些由销售代表担任经理或其他用户的房产。

验证您的房地产经理是否仍然可以查看所有房产。如果不能,为什么呢?请记住:

estate_group_manager 用户组需要包含 estate_group_user 用户组。

安全覆盖

绕过安全措施

目标

在本节结束时,代理应能够确认房产销售,而无需发票访问权限。

如果你作为房地产经纪人尝试将一个房产标记为“已售”,你应该会收到一个访问错误:

../../_images/error.png

这是因为 estate_account 模块在过程中尝试创建一张发票,但创建发票需要拥有所有发票管理的权限。

我们希望代理能够在不拥有完整开票权限的情况下确认销售,这意味着我们需要*绕过* Odoo 的正常安全检查,以便*即使*当前用户没有相应权限,也能创建发票。

有两种主要方式可以绕过 Odoo 中现有的安全检查,无论是有意为之还是作为副作用:

  • sudo() 方法将以 “超级用户模式” 创建一个新的记录集,这将忽略所有访问权限和记录规则(尽管硬编码的用户组和用户检查可能仍然适用)。

  • 执行原始 SQL 查询会绕过访问权限和记录规则,这是由于绕过了 ORM 本身所导致的副作用。

Exercise

estate_account 更新为在创建发票时绕过访问权限和规则。

危险

这些功能通常应避免使用,只有在确认当前用户和操作有权绕过正常的访问权限验证后,才应谨慎使用。

在这些模式下执行的操作也应尽可能减少对用户输入的依赖,并尽可能全面地进行验证。

通过程序检查安全权限

目标

在本节结束时,无论对 estate 进行何种更改,发票的创建都应具备抵御安全问题的能力。

在 Odoo 中,访问权限和记录规则仅在通过 ORM 执行数据访问时进行检查,例如通过 ORM 方法创建、读取、搜索、写入或删除一条记录。其他方法**不一定**会检查任何类型的访问权限。

在上一节中,我们在创建发票时绕过了记录规则,即在 action_sold 方法中。这种绕过方式可以被任何用户使用,而无需检查任何访问权限:

  • estate_account 模块中的 action_sold 方法中添加一个打印语句,位于发票创建之前(因为创建发票会访问房产,从而触发 ACL 检查),例如::

    print(" reached ".center(100, '='))
    

你应该在 Odoo 日志中看到 reached,后面跟着一个访问错误。

危险

即使您已经在 Python 代码中,也不意味着会检查或应用任何访问权限或规则。

目前,访问权限是通过在 self 上访问数据以及调用 super()``(这会执行相同的操作并 *更新* ``self)来隐式检查的,这会触发访问错误并取消事务,“删除”我们的发票。

然而,如果将来发生更改,或者我们在该方法中添加了副作用(例如,将销售报告给政府部门),或者在 estate 模块中引入了错误,… 那么非代理用户可能会触发他们不应具有访问权限的操作。

因此,在执行非 CRUD 操作,或合法地绕过 ORM 或安全机制,或触发其他副作用时,进行**显式的权限检查**至关重要。

可以通过以下方式执行显式安全检查:

  • 检查当前用户(self.env.user)并将其与特定模型或记录进行匹配。

  • 检查当前用户是否具有特定的组(硬编码以允许或拒绝操作)(self.env.user.has_group)。

  • 调用记录集上的 check_access(operations),这会验证当前用户是否被允许对集合中的 每个 记录执行该操作。作为特殊情况,当记录集为空时,它会验证当前用户是否具有对该模型一般性操作的访问权限。

Exercise

在创建发票之前,使用 check_access 确保当前用户可以更新该发票所对应的房产。

重新运行绕过脚本,检查在打印之前是否出现错误。

多公司安全

另请参见

多公司指南 了解多公司功能的总体情况,特别是 多公司安全规则

关于规则的文档,再次参见 记录规则

目标

在本节结束时,代理人员应仅能访问其所属代理机构(或多个代理机构)的房产。

出于各种原因,我们可能需要将房地产业务作为多个公司来管理,例如我们可能拥有高度自主的代理机构、特许经营体系,或多个品牌(可能是通过收购其他房地产企业而获得的),这些品牌在法律上或财务上彼此独立。

Odoo 可以用于在同一个系统中管理多个公司,但实际处理方式由各个模块决定:Odoo 本身提供了管理公司相关字段和 多公司规则 的工具,这正是我们接下来要关注的内容。

我们希望不同的机构之间实现“隔离”,即属于某个机构的房产,只有该机构的用户(无论是代理人还是经理)才能查看与该机构相关的房产。

正如之前所述,由于这是基于非简单记录的,用户更容易放松规则而不是收紧规则,因此默认采用相对更强的安全模型是合理的。

多公司规则是基于 company_idscompany_id 字段的记录规则:

  • company_ids 是当前用户具有访问权限的所有公司

  • company_id 是当前激活的公司(用户当前正在使用/工作的公司)。

多公司规则通常会使用前者,即检查该记录是否与用户有权访问的 一家 公司相关:

<record model="ir.rule" id="hr_appraisal_plan_comp_rule">
    <field name="name">Appraisal Plan multi-company</field>
    <field name="model_id" ref="model_hr_appraisal_plan"/>
    <field name="domain_force">[
        '|', ('company_id', '=', False),
             ('company_id', 'in', company_ids)
    ]</field>
</record>

危险

多公司规则通常是 全局 的,否则可能会有很高的风险,导致额外的规则绕过多公司规则。

Exercise

  • estate.property 中添加一个 company_id 字段,该字段应为必填项(我们不希望有无代理的房产),并且默认值应设置为当前用户的当前公司。

  • 创建一个新公司,并在该公司中添加一名新的房产经纪人。

  • 经理应该是两家公司的成员。

  • 旧代理只能是旧公司的成员。

  • 在每家公司中创建一些属性(可以使用公司选择器作为管理者,或者使用代理)。取消默认销售员以避免触发*该*规则。

  • 所有代理都可以查看所有公司,这并不理想,添加一条限制此行为的记录规则。

警告

记得在更改模块的模型或数据时使用 --update 更新你的模块

可见性 ≠ 安全

目标

在本节结束时,房地产经纪人不应看到房地产应用的”设置”菜单,但仍应能够设置房产类型或标签。

特定的 Odoo 模型可以直接与组(或公司,或用户)相关联。在使用之前,需要弄清楚这种关联是*安全*功能还是*可见性*功能:

  • 可见性 功能意味着用户仍然可以访问模型或记录,无论通过界面的其他部分,还是通过 使用 RPC 远程执行操作,在某些上下文中,这些内容可能不会在网页界面上显示。

  • 安全 功能意味着用户无法访问记录、字段或操作。

以下是一些示例:

Exercise

房地产经纪人不能添加房产类型或标签,但在创建房产时可以从房产表单视图中查看其选项。

设置菜单只是对他们界面的一种干扰,仅对管理人员可见。

尽管代理不再能够访问“房产类型”和“房产标签”菜单,但他们仍然可以访问底层对象,因为他们仍然可以选择标签或类型来设置在自己的房产上。

1

一个 Odoo 应用程序是一组涵盖特定业务领域或行业的相关模块,通常由一个基础模块和多个基于该基础模块的扩展模块组成,以添加可选或特定功能,或者与其他业务领域进行关联。

2

对于大多数或所有员工都会使用的应用程序,可以省略“应用用户”角色,并直接将相关权限授予所有员工。例如,通常所有员工都可以提交费用或申请休假。