限制数据访问¶
重要
This tutorial is an extension of the Server framework 101 tutorial. Make sure you have
completed it and use the estate
module you have built as a base for the exercises in this
tutorial.
到目前为止,我们主要关注实现有用的功能。然而,在大多数业务场景中, 安全性 很快成为一个问题:目前,
任何员工(即
group_user
)都可以创建、读取、更新或删除属性、属性类型或属性标签。If
estate_account
is installed then only agents allowed to interact with invoicing can confirm sales as that’s necessary to create an invoice.
然而:
我们不希望第三方能够直接访问属性。
并非我们所有的员工都是房地产经纪人(例如行政人员、物业经理等),我们不希望非经纪人看到可用的房产。
房地产经纪人不需要或无法决定哪些物业类型或标签是 可用的。
房地产经纪人可以拥有 独家 房产,我们不希望一个经纪人能够管理另一个经纪人的独家房产。
所有房地产经纪人都应该能够确认他们可以管理的房产的销售情况,但我们不希望他们能够验证或标记系统中的任何发票为已付款。
注解
对于小型企业,这些限制可能是可以接受的。
因为用户可以更容易地禁用不必要的安全规则,而不是从头开始创建它们,所以最好保守一些并限制访问:如果必要或方便,用户可以放宽访问权限。
用户组¶
目标
在本节的结尾处,
我们可以将员工设为 房地产经纪人 或 房地产经理。
The
admin
user is a real-estate manager.我们有一位新的 房地产经纪人 员工,没有访问发票或管理的权限。
如果每次需要更改就将安全规则附加到员工上,那么将不切实际,因此 组 将安全规则和用户链接起来。它们对应于可以分配给员工的角色。
对于大多数Odoo应用程序 1,一个很好的基线是拥有 用户 和 管理员 (或管理者)角色:管理员可以更改应用程序的配置并监督其全部使用,而用户可以使用应用程序 2。
这个基准线对我们来说似乎足够了:
房地产经理可以配置系统(管理可用类型和标签),并监督管道中的每个属性。
房地产经纪人可以管理他们负责的房产,或者是没有被任何经纪人特别管理的房产。
根据Odoo的数据驱动性质,一个组只是 res.groups
模型的一条记录。它们通常是模块的 主数据 的一部分,在模块的数据文件中定义。
一个简单的例子 可以在这里找到。
Exercise
在适当的文件夹中创建
security.xml
文件,并将其添加到__manifest__.py
文件中。如果还没有,请在您的
__manifest__.py
中添加一个'category'
字段,值为Real Estate/Brokerage
。添加一条记录,创建一个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
。
注解
那个 category 是从哪里来的?这是一个 module category。在这里,我们使用了 base.module_category_real_estate_brokerage
这个类别id,它是根据模块的 __manifest__.py
中设置的 category
自动由Odoo生成的。你还可以在这里找到 default module categories,这是Odoo提供的列表。
小技巧
由于我们修改了数据文件,请记得重新启动Odoo并使用 -u estate
更新模块。
如果你去 admin
用户(”Mitchell Admin”),你应该会看到一个新的部分:
将管理员用户设置为 房地产经理。
Exercise
通过Web界面,创建一个只有“房地产经纪人”访问权限的新用户。该用户不应具有任何发票或管理访问权限。
使用一个私密的标签页或窗口以新用户身份登录(记得设置密码),作为房地产经纪人,您应该只能看到房地产应用程序,可能还有讨论(聊天)应用程序:
访问权限¶
另请参阅
相关主题的文档可以在 访问权限 找到。
目标
在本节的结尾处,
不是房地产经纪人的员工将看不到房地产应用程序。
房地产经纪人将无法更新物业类型或标签。
Access rights were first introduced in Chapter 4: Security - A Brief Introduction.
Access rights are a way to give users access to models via groups: associate an access right to a group, then all users with that group will have the access.
例如,我们不希望房地产经纪人能够修改可用的房产类型,因此我们不会将该访问权限链接到“用户”组。
访问权限只能提供访问权限,不能删除它:当检查访问权限时,系统会查看与用户(通过任何组)关联的 任何 访问权限是否授予该访问权限。
组 |
创建 |
读取 |
更新 |
删除 |
A |
X |
X |
||
B |
X |
|||
C |
X |
拥有 A 和 C 组的用户可以做任何事情,但不能删除对象,而拥有 B 和 C 组的用户可以读取和更新它,但不能创建或删除它。
注解
访问权限的组可以省略,这意味着ACL适用于 每个用户 ,这是一个有用但有风险的后备方案,因为根据安装的应用程序,它甚至可以授予非用户对模型的访问权限。
如果用户没有任何访问权限,则不会被授予访问权限(默认拒绝)。
如果菜单项指向一个用户无法访问且没有子菜单可见的模型,则该菜单将不会显示。
Exercise
更新访问权限文件为:
将所有对象的完全访问权限授予您的房地产经理组。
给予经纪人(房地产用户)只读取类型和标签的访问权限。
不要给任何人删除属性的权限。
检查您的代理用户是否无法更改类型或标签,或删除属性,但是他们可以创建或更新属性。
警告
记得给你的 ir.model.access
记录分配不同的 xids,否则它们会互相覆盖。
由于“演示”用户没有成为房地产经纪人或经理,因此他们甚至不应该能够看到房地产应用程序。请使用私人选项卡或窗口进行检查(“演示”用户的密码为“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>
The 搜索域 is how access is managed: if the record passes then access is granted, otherwise access is rejected.
小技巧
因为规则往往相当复杂且不是批量创建的,所以它们通常是使用 XML 而不是用于访问权限的 CSV 创建的。
上述规则:
仅适用于“创建”、“更新”(写入)和“删除”(取消链接)操作:我们希望每个员工都能够查看其他用户的记录,但只有作者/受托人才能更新记录。
是 非全局 所以我们可以为例如经理提供额外的规则。
允许操作,如果当前用户(
user.id
)已设置(例如,已创建或已分配)到记录上,或者记录没有关联的用户。
注解
如果没有为模型和操作定义或应用规则,则允许该操作(default-allow),如果访问权限设置不正确(太宽松),可能会产生奇怪的效果。
Exercise
定义一个规则,限制代理商只能查看或修改没有销售员或者他们是销售员的属性。
您可能想创建第二个房地产经纪人用户,或者为一些物业指定销售经理或其他用户。
确认您的房地产经理仍然可以看到所有的房产。如果不能,为什么?请记住:
The
estate_group_manager
group needs to implyestate_group_user
.
安全性覆盖¶
绕过安全措施¶
目标
在本节结束时,代理人应该能够确认物业销售,而无需访问发票。
如果您作为房地产经纪人尝试将某个属性标记为“已售出”,则应该会收到访问错误的提示:
这是因为在过程中, estate_account
尝试创建发票,但创建发票需要拥有所有发票管理的权限。
我们希望代理商能够确认销售,而不需要他们拥有完全的发票访问权限,这意味着我们需要 绕过 Odoo的正常安全检查,以便 创建 发票,尽管当前用户没有权利这样做。
在Odoo中,有两种主要的绕过现有安全检查的方式,要么是故意的,要么是作为副作用:
The
sudo()
method will create a new recordset in “sudo mode”, this ignores all access rights and record rules (although hard-coded group and user checks may still apply).执行原始的 SQL 查询将绕过访问权限和记录规则,因为绕过 ORM 本身会产生副作用。
Exercise
更新 estate_account
在创建发票时绕过访问权限和规则。
危险
通常应避免使用这些功能,并在检查当前用户和操作是否能够绕过正常访问权限验证后,小心使用。
在这种模式下执行的操作应该尽可能少地依赖用户输入,并且应该尽可能地验证它们。
以编程方式检查安全性¶
目标
在本节结束时,发票的创建应该对安全问题具有弹性,无论对 estate
的更改如何。
In Odoo, access rights and record rules are only checked when performing data access via the ORM e.g. creating, reading, searching, writing, or unlinking a record via ORM methods. Other methods do not necessarily check against any sort of access rights.
在前一节中,我们在 action_sold
中创建发票时绕过了记录规则。任何用户都可以达到此绕过,而无需检查任何访问权限:
在创建发票之前,在
estate_account
的action_sold
中添加一个打印语句(因为创建发票会访问财产,从而触发 ACL 检查)例如:print(" reached ".center(100, '='))
您应该在Odoo日志中看到 reached
,然后是一个访问错误。
危险
仅仅因为你已经在 Python 代码中,并不意味着任何访问权限或规则已经或将被检查。
Currently the accesses are implicitly checked by accessing data on self
as
well as calling super()
(which does the same and updates self
),
triggering access errors and cancelling the transaction “uncreating” our
invoice.
However if this changes in the future, or we add side-effects to the method
(e.g. reporting the sale to a government agency), or bugs are introduced in
estate
, … it would be possible for non-agents to trigger operations they
should not have access to.
因此,在执行非CRUD操作,或者合法地绕过ORM或安全性,或者触发其他副作用时,执行 显式的安全检查 非常重要。
可以通过以下方式执行显式安全检查:
检查当前用户是谁(
self.env.user
)并将其与特定模型或记录进行匹配。检查当前用户是否具有特定的固定组来允许或拒绝操作(
self.env.user.has_group
)。Calling
check_access(operations)
on a recordset, this verifies that the current user is allowed to perform the operation on every record of the set. As a special case, when the recordset is empty, it verifies that the current user has some access rights to perform the operation on the model in general.
Exercise
Before creating the invoice, use check_access
to ensure that the current
user can update the property the invoice is for.
重新运行绕过脚本,检查错误是否在打印之前发生。
多公司安全性¶
目标
在本节结束时,代理商只能访问其所属机构(或机构)的属性。
由于某种原因,我们可能需要将我们的房地产业务管理为多个公司,例如我们可能拥有大部分自主机构、特许经营设置或多个品牌(可能是从收购其他房地产企业而来),这些公司在法律或财务上保持分离。
Odoo 可以用于在同一系统中管理多个公司,但实际处理由各个模块决定:Odoo 本身提供了管理公司相关字段和 多公司规则 的工具,这是我们要关注的。
我们希望不同的机构之间相互“隔离”,每个机构拥有自己的属性和用户(无论是代理商还是经理),只能看到与其机构相关联的属性。
与以前一样,由于这是基于非平凡记录的,用户放宽规则比收紧规则更容易,因此默认采用相对较强的安全模型是有意义的。
多公司规则只是基于 company_ids
或 company_id
字段的记录规则:
company_ids
is all the companies to which the current user has accesscompany_id
is the currently active company (the one the user is currently working in / for).
Multi-company rules will usually use the former i.e. check if the record is associated with one of the companies the user has access to:
<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模型可以直接与组(或公司、用户)关联。在使用之前,有必要弄清楚这种关联是 安全 还是 可见性 功能:
Visibility features mean a user can still access the model or record otherwise, either through another part of the interface or by performing operations remotely using RPC, things might just not be visible in the web interface in some contexts.
*安全性*功能意味着用户无法访问记录、字段或操作。
以下是一些例子:
在Python中, 模型字段 上的组是一种安全功能,组外的用户将无法检索该字段,甚至不知道它的存在。
示例:在服务器操作中,
只有系统用户才能查看或更新Python代码 <https://github.com/odoo/odoo/blob/7058e338a980268df1c502b8b2860bdd8be9f727/odoo/addons/base/models/ir_actions.py#L414-L417>
_。在 XML 中, 视图元素 上的组是一个可见性特性,组外的用户将无法看到表单中的元素或其内容,但他们仍然可以与对象(包括该字段)进行交互。
Example: 只有经理才有立即筛选器来查看他们团队的请假。
菜单和操作上的分组是可见性功能,菜单或操作不会显示在界面上,但这并不妨碍直接与底层对象进行交互。
Example: 只有系统管理员可以看到电子学习设置菜单.
Exercise
房地产经纪人无法添加物业类型或标签,但可以在创建物业时从物业表单视图中查看它们的选项。
设置菜单只会给他们的界面增加噪音,将其仅对经理可见。
尽管不再有访问“属性类型”和“属性标签”菜单的权限,但代理商仍然可以访问底层对象,因为他们仍然可以选择标签或类型来设置其属性。