限制数据访问¶
重要
本教程是 服务器框架入门 教程的扩展。确保您已完成该教程,并使用您构建的 estate
模块作为本教程练习的基础。
到目前为止,我们主要关注实现有用的功能。然而,在大多数业务场景中, 安全性 很快成为一个问题:目前,
任何员工(即
group_user
所代表的角色)都可以创建、读取、更新或删除房产、房产类型或房产标签。如果安装了
estate_account
,那么只有被允许处理发票的代理才能确认销售,因为这是 创建发票 所必需的。
然而:
我们不希望第三方能够直接访问房产信息。
并非所有员工都是房地产代理(例如行政人员、物业经理等),我们不希望非代理人员看到可用的房产信息。
房地产代理不需要也不应该决定哪些房产类型或标签是 可用的 。
房地产代理可以拥有 专属 房产,我们不希望一个代理能够管理另一个代理的专属房产。
所有房地产代理都应该能够确认他们可以管理的房产的销售,但不希望他们能够验证或将系统中的任何发票标记为已支付。
注解
对于小型企业来说,我们实际上可能对其中的一些或大部分情况是可以接受的。
因为用户禁用不必要的安全规则比从零开始创建它们更容易,所以最好谨慎行事并限制访问:如果必要或方便,用户可以放宽这些限制。
组¶
目标
在本节结束时,
我们可以将员工设为 房地产代理 或 房地产经理 。
admin
用户是一名房地产经理。我们有一名新的 房地产代理 员工,他无法访问发票或管理功能。
每次需要更改时都将单独的安全规则附加到员工身上并不实际,因此 组 将安全规则与用户关联起来。它们对应于可以分配给员工的角色。
对于大多数 Odoo 应用程序 1,一个良好的基线是拥有 用户 和 经理 (或管理员)角色:经理可以更改应用程序的配置并监督其全部使用,而用户则可以正常使用应用程序 2。
这个基线对我们来说似乎已经足够:
房地产经理可以配置系统(管理可用的类型和标签),并监督管道中的每处房产。
房地产代理可以管理他们负责的房产,或者那些未明确由任何代理负责的房产。
根据 Odoo 的数据驱动特性,一个组不过是 res.groups
模型的一条记录。它们通常是一个模块的 主数据 的一部分,并在一个模块的数据文件中定义。
一个简单的示例 可以在这里找到: 。
Exercise
在适当的文件夹中创建
security.xml
文件并将其添加到__manifest__.py
文件中。如果尚未添加,请在
__manifest__.py
中添加一个值为Real Estate/Brokerage
的'category'
字段。添加一条记录,创建一个 ID 为
estate_group_user
、名称为“Agent”、类别为base.module_category_real_estate_brokerage
的组。在其下,添加一条记录,创建一个 ID 为
estate_group_manager
、名称为“Manager”、类别为base.module_category_real_estate_brokerage
的组。estate_group_manager
组需要隐含estate_group_user
。
注解
类别 从何而来?它是一个 模块类别 。这里我们使用了类别 ID base.module_category_real_estate_brokerage
,它是基于模块的 __manifest__.py
中设置的 category
自动生成的。您还可以在此处找到 Odoo 提供的 默认模块类别列表 。
小技巧
由于我们修改了数据文件,请记住重启 Odoo 并使用 -u estate
更新模块。
如果您前往 admin
用户(“Mitchell Admin”),您应该会看到一个新部分:

将管理员用户设置为 房地产经理 。
Exercise
通过 Web 界面,创建一个仅具有“房地产代理”权限的新用户。该用户不应拥有任何发票或管理权限。
使用隐私标签页或窗口以新用户身份登录(记得设置密码)。作为房地产代理,您应该只能看到房地产应用,可能还能看到讨论(聊天)应用:

访问权限¶
参见
与此主题相关的文档可以在 访问权限 中找到。
目标
在本节结束时,
至少不是房地产代理的员工将无法看到房地产应用程序。
房地产代理将无法更新房产类型或标签。
访问权限在 第 4 章:安全性(Security)- 简介 中首次引入。
访问权限是一种通过组为用户授予模型访问权限的方式:将访问权限与组关联后,拥有该组的所有用户都将获得访问权限。
例如,我们不希望房地产代理能够修改可用的房产类型,因此不会将此访问权限链接到“用户”组。
访问权限只能授予权限,而不能移除权限:当检查访问权限时,系统会查看是否 有任何 与用户(通过任何组)关联的访问权限授予了该权限。
分组 |
创建 |
读取 |
更新 |
删除 |
A |
X |
X |
||
B |
X |
|||
C |
X |
拥有组 A 和 C 的用户将能够执行除删除对象之外的所有操作,而拥有组 B 和 C 的用户将能够读取和更新对象,但无法创建或删除它。
注解
可以省略访问权限的组,这意味着 ACL 将适用于 所有用户 ,这是一种有用但有风险的回退方式,因为根据安装的应用程序,它甚至可能授予非用户对该模型的访问权限。
如果没有适用的访问权限,则用户将被拒绝访问(默认拒绝)。
如果菜单项指向用户无法访问的模型,并且没有用户可见的子菜单,则该菜单将不会显示。
Exercise
更新访问权限文件为:
为您的房地产经理组授予对所有对象的完全访问权限。
仅授予代理(房地产用户)对类型和标签的读取权限。
不授予任何人删除房产的权限。
检查您的代理用户是否无法更改类型或标签,也无法删除房产,但可以创建或更新房产。
警告
请记住为您的 ir.model.access
记录分配不同的 xid,否则它们会相互覆盖。
由于“demo”用户未被设置为房地产代理或经理,他们甚至不应该能够看到房地产应用程序。请使用隐私标签页或窗口进行检查(“demo”用户的密码为“demo”)。
记录规则¶
参见
与此主题相关的文档可以在 记录规则 中找到。
目标
在本节结束时,代理将无法看到同事专属的房产;但经理仍然可以看到所有内容。
访问权限可以授予对整个模型的访问权限,但通常我们需要更具体:虽然代理可以与房产进行交互,但我们可能不希望他们更新甚至查看由其同事管理的房产。
记录 规则 提供了这种精确性:它们可以授予或拒绝对单个记录的访问权限:
<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
。
安全覆盖¶
绕过安全检查¶
目标
在本节结束时,代理应能够确认房产销售而无需拥有发票访问权限。
如果您尝试以房地产代理身份将房产标记为“已售”,您应该会收到一个访问错误:

这是因为在流程中 estate_account
尝试创建发票,但创建发票需要拥有对所有发票管理的权限。
我们希望代理能够在没有完整发票访问权限的情况下确认销售,这意味着我们需要 绕过 Odoo 的常规安全检查,以便在当前用户没有相应权限的情况下仍然能够创建发票。
在 Odoo 中绕过现有安全检查主要有两种方式,无论是有意为之还是作为副作用:
sudo()
方法将在“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_ids
或 company_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 远程执行操作 来访问模型或记录,只是在某些情况下它们可能不会显示在 Web 界面中。
安全性 功能意味着用户无法访问记录、字段或操作。
以下是一些示例:
模型字段 上的组(在 Python 中)是一种安全功能,不在组中的用户将无法检索该字段,甚至不知道它的存在。
示例:在服务器操作中,只有系统用户可以看到或更新 Python 代码。
视图元素 上的组(在 XML 中)是一种可见性功能,不在组中的用户将无法在表单中看到该元素或其内容,但仍然可以与对象(包括该字段)进行交互。
示例: 只有经理拥有立即查看团队休假的过滤器。
菜单和操作上的组是可见性功能,菜单或操作不会显示在界面中,但这并不妨碍直接与底层对象交互。
Exercise
房地产代理无法添加房产类型或标签,但在创建时可以从房产表单视图中看到它们的选项。
设置菜单只会给他们的界面增加噪音,请将其仅对经理可见。
尽管代理不再有权访问房产类型和房产标签菜单,但他们仍然可以访问底层对象,因为他们仍然可以选择标签或类型来设置他们的房产。