创建安全组并分配用户
Odoo中的访问权限通过安全组成进行配置:给组指定权限,然后为组分配用户。每个功能区都有中枢应用所提供的基础安全组。
在插件继承已有应用时,它们应对相应的组添加权限,参见本章稍后的向模型添加访问权限一节。
在插件模块添中添加一个已有中枢应用所未涵盖的新功能区时,应添加相应的安全组。通常,我们至少应该有用户和管理员这两个角色。
以第四章 创建Odoo插件模块中所介绍的图书馆应用为例,它与Odoo的核心应用并没有很好的融合,下面我们来对其添加安全组。
准备工作
本节假定你已经准备好了Odoo实例,并且有第四章 创建Odoo插件模块中所讲解的my_module模块。
如何实现…
执行如下步骤来向模块添加新的访问安全组:
- 确保__manifest__.py插件模块声明中有定义有category键:
1'category': 'Library', - 在声明文件的data键中新增security/groups.xml文件:
1234'data': ['security/groups.xml','views/library_book.xml',], - 在security/library_security.xml中为数据记录新增XML文件,先使用一个空的结构:
1234<?xml version="1.0" encoding="utf-8"?><odoo><!-- Data records go here --></odoo> - 在XML的data元素中添加两个新组的record标签:
12345678910111213141516<record id="group_library_user" model="res.groups"><field name="name">User</field><field name="category_id"ref="base.module_category_library"/><field name="implied_ids" eval="[(4,ref('base.group_user'))]"/></record><record id="group_library_librarian" model="res.groups"><field name="name">Librarians</field><field name="category_id"ref="base.module_category_library"/><field name="implied_ids" eval="[(4,ref('group_library_user'))]"/><field name="users" eval="[(4, ref('base.user_admin'))]"/></record>
如果我们升级该插件模块,会加载这两条记录。要在用户界面中看到这些组,需要启用开发者模式。然后就可以通过Settings > Users > Groups菜单项进行查看了。
ℹ️在Odoo v12中,新增模型的默认权限与之前的模型稍有不同。在v12中,如果新增了一个模型,admin用户对该模型并没有访问权限。这表示为该模型所增加的菜单和视图对admin用户均不可见。要进行显示,需要对该模型添加权限组,在向模型添加访问权限一节中会进行讲解。此外,我们可以通过超级用户来访问新增的模型,更多相关知识请参见第四章 创建Odoo插件模块的以超级用户访问 Odoo一节。
运行原理…
插件模块以功能区或主应用进行组织,如财务和金融、销售或人力资源。它们在声明文件中以category键进行定义。
如果category名称不存在,会进行自动创建。为方便起见,会为新的分类名创建一个小写的base.module_category_<category_name_in_manifest>的XML ID,并将空格替换为下划线。 这可以用于通过应用分类来关联安全组。
本例中,我们使用了Library分类名,它生成了一个base.module_category_library的XML标识符。
按照惯例,包含权限相关元素的数据文件应放在security子目录内。
还需要在声明文件中注册权限文件。模块声明中data键所声明文件的顺序非常重要,因为无法在其它视图或ACL文件中引用还未定义的组。最好将权限数据文件放在ACL文件和其它用户界面数据文件列表的最上面。
本例中,我们使用<record>标签创建了组,它会在res.groups模型中创建一条记录。res.groups模型中最重要的字段如下:
- name:这是组的显示名称。
- category_id:这是对应用分类的引用,用于在用户表单中组织分组。
- implied_ids:它们是所要继承权限的其它组。
- users:这是属于该组的用户。在新的插件模块中,我们通常希望admin用户属于应用的管理员组。
第一个安全组使用了implied_ids来作为base.group_user组。它是Employee用户组,是所有后台用户所会共享的基本安全组。
第二个安全组在users字段上设置值来将其分配给管理员用户,其XML ID为base.user_admin。
属于一个安全组的用户会自动属于其所继承的组。如果为任意用户分配Librarians组,该用户会自动的包含在User组中,因为Librarian组s在implied_ids字段中有User组。
同时,安全组所授予的安全权限是累加的。用户具有他所属于的任意组(直接授权或通过继承授权)的权限。
有些安全组在用户表单中显示为选择框而非单个复选框。这种情况发生在所涉及的组在同一应用分类中并且通过implied_ids线性相关联。例如,Group A继承自Group B,Group B继承自Group C。如果组没有通过implied_ids与任何其它组相关联,我们看到的会是选择框,而不是复选框。
注意在前面字段中定义的关联还有反向关联,还在关联的模型中进行编辑,如安全组和用户。
设置category_id和implied_ids等引用字段的值通过使用关联记录的XML ID和一些特殊的语法完成。这种语法在第七章 模块数据中进行了详细讲解。
扩展知识…
另外值得注意的是特殊的名为Extra Rights的group_no_one安全组。在早期的Odoo版本中,它用于一些默认隐藏的功能,仅在激活了Technical Features 标记时才进行显示。从9.0,开始发生了一些改变,只要开启了开发者模式就会显示这些功能。
通过安全组授予的访问权限仅可以进行叠加。无法拒绝某个组所授予的权限。这表示手动创建的用于自定义权限的组应继承最邻近少于所需权限的组(若有),然后添加剩余所有需要的权限。
组中还可以有以下的额外字段:
- 菜单(menu_access字段):指定组所能访问的菜单项
- 视图(view_access字段):指定组所能访问的UI界面视图
- 访问权限(model_access字段):指定组所能访问的模型,在为模型添加访问权限一节中会进行详述
- 访问规则(rule_groups字段):指定一些应用于组的记录级访问规则,在限制模型中字段的访问一节中会进行详述
- 提示(comment记录):这是组的描述或注释文本
为模型添加访问权限
为插件模块新增模型非常的普通。例如在第四章 创建Odoo插件模块中,我们新增了一个Library Books模型。在开发过程中很容易漏掉对新模型安全权限的创建,这时会很验证看到所创建的菜单和视图,因为从Odoo 12起,admin用户默认对新模型没有访问权限。要看到新模型的视图和菜单,需要添加权限的访问控制列表(ACL)。
但是没有ACL的模型在加载时会触发警告日志消息,告知用户所漏掉的ACL定义。
1
|
WARNING The model library.book has no access rules, consider adding one example, access_library_book, access_library_book, model_library_book, base.group_user,1,0,0,0
|
可以通过超级用户访问新增的模型,它不受任何权限规则所限制。更多相关知识,请参见第四章 创建Odoo插件模块的以超级用户访问 Odoo一节。超级用户的功能仅有管理员(administrator)用户可用。因此,非管理员用户如需要使用新模型,需要为他们定义访问权限列表,这样Odoo就知道他们能以何种方式访问以及每个用户组所能执行的操作有哪些。
准备工作
我们将使用第四章 创建Odoo插件模块中所创建的模块,并为其添加所漏掉的ACL。
如何实现…
my_library中应该已经包含了创建library.book模型的models/library_book.py这一Python文件。现在我们会执行如下步骤添加一个描述该模型的权限访问控制的数据文件:
- 编辑__manifest__.py文件来声明一个新的数据文件:
12345data: [# ...Security Groups'security/ir.model.access.csv',# ...Other data files] - 在模块中新增一个包含如下内容的security/ir.model.access.csv文件:
123id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlinkacl_book,library.book_default,model_library_book,base_group_user,1,0,0,0acl_book_librarian,library.book_librarian,model_library_book,group_library_librarian,1,1,1,1
我应当行升级模块来把这些ACL记录添加到我们的Odoo数据库中。更重要的是,如果我们使用demo用户登录到演示数据库中,应该可以访问Library菜单项且不会出现任何权限错误。
运行原理…
权限控制访问列表存储在核心的ir.model.access模型中。我们只需要添加描述针对每个用户组访问权限的记录。
任何类型的数据文件都可以,但常用实践是使用CSV文件。该文件可放在插件模块目录中的任何位置,但惯例是将所有安全权限相关的文件放在security子目录中。
本节中的第一步向声明文件新增了一个数据文件。第二步添加了一个描述权限访问控制规则 的文件。CSV文件必须按照所要加载记录的模型来进行命名,因此所使用名称不止是一种惯例而是强制如此:参见第七章 模块数据获取更多信息。
如果模块还创建新的安全组,它的数据文件应在声明文件的ACL数据文件之前进行声明,因为在ACL中可能会使用到它们。在处理ACL文件时它们必须是已经创建了的。
CSV文件中的字段如下:
- id:这是针对这一规则的XML ID内部标识符。该模块中的任何唯一名称都可以,但按惯例使用access_<model>_<group>。
- name:这是访问规则的标题。常用实践是使用access.<model>.<group>这一名称。
- model_id:id:这是该模型的XML ID。Odoo使用model_<name>格式对这些模型自动分配这种类型的ID,使用带下划线而非点号的模型名称。如果模弄是在另一个模块中创建的,则需要完整的包含模块名称的有效完整XML ID。
- group_id:id:这是针对用户组的XML ID。如果留空,它应用于所有用户。base模块中提供了一些基本用户级,比如针对所有用户的base.group_user和针对管理员用户的base.group_system。其它应用可以添加它们自己的用户组。
- perm_read:前述组中成员可以读取模型记录。它接收两个值:0或1。使用0来限制模型上的读权限,1来提供读权限。
- perm_write:前述组中成员可以更新模型记录。它接收两个值:0或1。使用0来限制模型上的写权限,1来提供写权限。
- perm_create:前述组中成员可以在该模型中新增记录。它接收两个值:0或1。使用0来限制模型上的创建权限,1来提供创建的权限。
- perm_unlink:前述组中成员可以在该模型中删除记录。它接收两个值:0或1。使用0来限制模型上的删除权限,1来提供删除的权限。
我们所使用的CSV文件对Employees > Employee标准安全组添加了只读权限并向Administration > Settings 组添加了全部的写权限。
Employee用户组base.group_user尤为重要,因为Odoo标准应用所添加的用户组都会继承它。这表示如果我们需要让所有后台用户都能访问新模型的问,不管它们所配使用的具体应用是什么,都应将权限在Employee组中进行添加。
结果产生的ACL可通过访问Settings > Technical > Security >Access Controls List 来在图形界面中进行查看,如下图所示:
TODO
有些人觉得使用用户界面定义ACL然后用导出功能生成CSV文件会更为容易。
扩展知识…
将这个权限授予创建安全组并分配用户一节中所定义的User 和Librarian组会显而易见。如果你按照该节进行操作的话,跟着本节操作会是很好的练习,将组标识符调整为图书对应的组。
要重点注意不应直接对由插件模块提供的访问列表进行自定义,因为它们会在下一次模块升级时进行重载,破坏在用户界面中所做的任何自定义内容。
可采用两种方法来自定义 ACL。一种是创建继承模块中所提供组的新安全组并在其上添加新的权限,但这样我们只能添加权限,不能删除权限。更灵活的方法是对具体的ACL行取消Active标记的勾选来进行禁用。默认active字段不可见,因此我们需要编辑树状视图来添加<field name=”active” />列。我们还可以添加新的ACL行来添加或替换权限。在模块升级时,禁用的ACL不会重新激活,添加的ACL行也不会受到影响。
另外值得注意的是ACL仅应用于普通模型,无需对抽象模型或临时模型进行定义。如若定义,它会被忽略,在服务日志中会打出警告信息。
限制模型中字段的访问
在某些情况下,我们可能会需要更精准控制的访问控制,可能会需要限制对模型具体字段的访问。
可以使用groups属性来让字段仅能由指定的安全组所访问。下面会展示如何添加仅能供图书模型访问的字段。
如何实现…
添加仅针对特定安全组有访问权限的字段,可执行如下步骤:
- 编辑模型文件来添加字段:
12is_public =fields.Boolean(groups='my_library.group_library_librarian')private_notes =fields.Text(groups='my_library.group_library_librarian') - 在XML文件中编辑视图来添加该字段:
12<field name="is_public" /><field name="private_notes" />
这样就好了。现在对插件模块进行升级来应用模型中的修改。如果使用没有系统配置权限的用户登录的话,比如用 demo 用户登录带有演示数据的数据库,图书表单中就不会显示该字段。
运行原理…
带有groups属性的字段进行了特殊处理,它检查用户是否属于属性中所指定的任意安全组。如果用户不属于指定的某一组,Odoo会在用户界面中删除该字段并限制对该字段的ORM操作。
注意这并不仅仅是表面工作。该字段不仅在用户界面中会隐藏,同时在用户的其它ORM操作中也无法使用它,比如读和写。对于XML-RPC或JSON-RPC调用同样如此。
在业务逻辑或用户界面on-change事件(@api.onchange方法)中使用这些字段时需要小心,在用户没有该字段访问权限时会抛出错误。一种避免的方式是使用提权,比如sudo()模型方法或计算字段的compute_sudo字段属性。
groups的值是包含安全组的有效XML ID的逗号分隔列表的一个字符串。查找 一个指定组的XML ID的最简单的方式是激活开发者模式并通过Settings > Users > Groups导航到组的表单上,然后通过调试菜单访问View Metadata选项,如下图所示:
TODO
扩展知识…
有些情况下,我们需要根据具体条件来让字段可用或不可用,像字段中的值,比如stage_id或state。这通常使用states或attrs属性来在视图级别进行处理来根据特定条件动态显示或隐藏字段。参见第十章 后端视图获取更多详情。
注意这些技巧仅作用于用户界面级别,并不提供实际的访问安全。要实现访问安全,需要在业务逻辑层添加检查。要么添加使用@constrains装饰的模型方法,实现具体的验证,要么对验证逻辑继承create, write或unlink方法。在第六章 基本服务端部署中可以获取更多的相关知识。
使用记录规则限制记录的访问
对一个应用的常见需求是对指定模型上的每个用户限制所能使用的记录。
这通过使用记录规则来实现。记录规则是一个定义在模型上的域过滤器表达式,然后添加到所受影响的用户每个数据查询上。
作为示例,我们将在图书模型上添加记录规则来让Employee组中的用户仅能访问公共图书。
本节假定你已经准备好了第四章 创建Odoo插件模块中所讲解的my_library模块,并启动了实例。
如何实现…
记录规则使用XML数据文件进行添加。执行如下步骤来进行实现:
- 确保在声明文件的 data 键中添加了security/library_security.xml文件:
1234'data': ['security/library_security.xml',# ...], - 应当有一个security/library_security.xml 数据文件,在<odoo>版块中创建安全组:
12345678910111213141516171819<odoo noupdate="1"><record model="ir.rule" id="library_book_user_rule"><field name="name">Library: see only own books</field><field name="model_id" ref="model_library_book"/><field name="groups" eval="[(4,ref('my_library.group_library_user'))]"/><field name="domain_force">[('is_public', '=', True)]</field></record><record model="ir.rule" id="library_book_all_rule"><field name="name">Library: see all books</field><field name="model_id" ref="model_library_book"/><field name="groups"eval="[(4,ref('my_library.group_library_librarian'))]"/><field name="domain_force">[(1, '=', 1)]</field></record></odoo>
升级插件模块会在Odoo实例中加载记录规则。如果你使用的是演示数据,可以使用默认的 demo 用户来将图书用户权限授予 demo 用户进行测试。如果使用的不是演示数据,可以新建一个带有图书用户权限的用户。
运行原理…
记录规则仅仅是在 ir.rule 核心模型中所加载的数据记录。而添加规则的文件可以放在模块的任意位置,按照惯例会将其放在security子目录中。在单个XML文件中同时放置安全组和记录规则很常见。
与安全组不同,在标准模块中记录规则是通过noupdate=”1″ 属性在来odoo代码区中进行加载的。借助于它,这些记录不会在模块升级时重新加载,也就是说对它们做的手动自定义在后续升级中是安全并且会得以保留的。
为保持与官方模块的一致,我们也应将记录规则放在<odoo noupdate=”1″>内。
记录规则在用户界面中也可以通过Settings > Technical > Security > Record Rules菜单项进行查看,如下图所示:
TODO
以下是本例中所使用的最重要的记录规则字段:
- 名称(name):针对规则的描述性标题。
- 对象(model_id):对规则所应用模型的引用。
- 组(groups):规则所应用的安全组。如未指定安全组,规则应视作全局并使用不同的方式进行应用(继续阅读本节来学习有关组的更多知识)。
- 域(domain):域表达式用于过滤记录。该记录将仅用于对这些过滤的记录进行应用。
我们所创建的第一条记录规则针对Library User 安全组。它使用[(‘is_public’, ‘=’, True)] 来仅选择公开的图书。因此Library User安全组中的用户仅能看到对外开放的图书。
服务器端在记录规则中使用的域表达式使用ORM对象。因此,左手边的字段(元组第一个元素)可使用点号标记。[(‘country_id.code’, ‘=’, ‘IN’)]域表达式会仅显示国家为印度的记录。
因为记录规则多基于当前用户,你可以使用域中右手边的用户记录集(元组第三个元素)。因此,如果你想要为当前公司的用户显示记录,可以使用 [(‘conpany_id’, ‘=’, user.company_id.id)]域。同时,如果想要显示由当前用户创建的记录,可以使用[(‘user_id’, ‘=’, user.id)]域。
我们希望Librarian安全组能够看到所有图书,不论是公开的还是对内的。因其继承了Library User组,除非进行相关的操作,否则它将仅能看到公开的图书。
非全局记录规则使用逻辑运算符OR来进行连接,每条规则增加权限且永不删除该权限。要让Librarian能访问所有图书,可以将其添加到记录规则中来为所有图书添加权限,如下:[(‘is_public’, ‘in’, [True, False])]。
这里我们选择了不同的方式,没有使用无条件访问所有图书记录的特殊规则 [(1, ‘=’, 1)]。虽然看起来可能多余,但要记住Library用户规则可以通过另一种方式来不让Settings用户来访问一些图书。域很特别,因为域元组的第一个元素必须为字段名,这个用例是并非如此的两个用例之一。特殊域[(1, ‘=’, 0)]永不为真,但在记录规则的用例中并没有什么作用,因为使用这种类型的规则会限制对所有记录的访问。在访问控制列表中也可以做类似的事。
ℹ️在启用超级用户时会忽略记录规则。测试记录规则时,记得使用其它用户来进行。
扩展知识…
在记录规则未分配给任何安全组时,它被标记为全局并由不同的规则来进行处理。
全局记录规则比组级别的记录规则影响要更深广,它设置一些无覆盖的访问限制。技术层面,它们通过AND运算符来进行连接。在标准模块中,它们用于实现多公司安全访问,这样每个用户仅能看到自己公司的数据。
总的来说,常规非全局记录规则通OR运算符连接在一起,它们在一起添加,仅在其中的规则授予权限时才能访问对应的记录。全局记录规则通过常规记录规则使用AND运算符来添加访问限制。全局记录规则所添加的限制无法由常规记录规则覆盖。
使用安全组启用功能
安全组可以限制一些功能,这样仅有属于这些组的用户才能访问。安全组也能继承其它组,因此也继承它们的权限。
这两个功能合在一起,用于实现Odoo中的可用性功能:功能切换。安全组也可以用于对Odoo实例中的一些或所有用户启用或禁用功能。
本节展示如何对配置设置添加选项并展示启用额外功能的两种方法,使用安全组让它们可见或通过安装额外的模块来添加它们。
对于第一种情况,我们将会让图书发行日期成为一个可选的额外功能,对于第二种情况,作为示例我们将提供一种选项来安装Notes模块。
准备工作
本节使用第四章 创建Odoo插件模块中所讲解的my_library模块。我们需要使用到安全组,因此还需要按照本章中向模型添加访问权限一节进行操作。
本节中,一些标识符需要引用插件模块的技术名称。我们将假定其为my_library。如果你使用了不同的名称,请将my_library替换为你的插件模块中所使用的实际技术名称。
如何实现…
按照如下给定步骤来添加配置项:
- 要添加所需的依赖和新的XML数据文件,像下面这样编辑__manifest__.py文件并确保它依赖于base_setup:
1234567891011{'name': 'Cookbook code','category': 'Library','depends': ['base_setup'],'data': ['security/ir.model.access.csv','security/groups.xml','views/library_book.xml','views/res_config_settings.xml',],} - 要添加用于启用功能的新安全组,编辑security/library_book.xml文件并向其添加如下记录:
12345<record id="group_release_dates" model="res.groups"><field name="name">Library: release date feature</field><field name="category_id"ref="base.module_category_hidden" /></record> - 编辑 models/library_book.py文件中的字段定义,来让图书发行日期仅在启用了该选项时可见:
123456class LibraryBook(models.Model):# ...date_release = fields.Date('Release Date',groups='my_library.group_release_dates',) - 编辑models/__init__.py文件来添加针对配置设置模型的新Python文件:
12from . import library_bookfrom . import res_config_settings - 通过向其添加新选项来继承核心配置向导,添加models/res_config_settings.py文件及如下代码:
123456789from odoo import models, fieldsclass ConfigSettings(models.TransientModel):_inherit = 'res.config.settings'group_release_dates = fields.Boolean("Manage book release dates",group='base.group_user',implied_group='my_library.group_release_dates',)module_note = fields.Boolean("Install Notes app") - 要让选项在用户界面中可以使用,添加views/res_config_settings.xml,它继承表单视图:
12345678910111213141516171819202122232425262728293031323334353637383940<?xml version="1.0" encoding="utf-8"?><odoo><record id="view_general_config_library" model="ir.ui.view"><field name="name">Configuration: add Library options</field><field name="model">res.config.settings</field><field name="inherit_id"ref="base_setup.res_config_settings_view_form" /><field name="arch" type="xml"><div id="business_documents" position="before"><h2>Library</h2><div class="row mt16 o_settings_container"><!-- Release Dates option --><div class="col-12 col-lg-6 o_setting_box"><div class="o_setting_left_pane"><field name="group_release_dates" class="oe_inline"/></div><div class="o_setting_right_pane"><label for="group_release_dates"/><div class="text-muted">Enable relase date feature on books</div></div></div><!-- Release Dates option --><div class="col-12 col-lg-6 o_setting_box"><div class="o_setting_left_pane"><field name="module_note" class="oe_inline"/></div><div class="o_setting_right_pane"><label for="module_note"/><div class="text-muted">Install note module</div></div></div></div></div></field></record></odoo>
在升级了这些插件模块之后,在Settings > General > Settings 中两个新的配置选项中应该就可以使用了。参见如下截图:
TODO
运行原理…
核心base模块中有res.config.settings模型,它提供了选项激活选项背后的业务逻辑。base_setup插件模块使用 res.config.settings模型来提供一些基本配置选项来添加到新数据库中。它还在用户界面中打开了Settings > General Settings菜单。
base_setup模块将res.config.settings适配到中央管理仪表盘上,因此我们需要继承它来添加我们自己的配置设置。
如果我们决定为图书应用创建具体的设置表单,还可以使用一个不同的_name来继承res.config.settings模型,然后为这些设置提供菜单项和表单视图。我们在第九章 高级服务端开发技巧的添加自定义设置选项一节中已进行过相关学习。
我们使用了两种不同的方法来激活该功能:一种是通过启用安全组并让功能对用户可见,另一种是通过安装一个带有该功能的插件模块。处理这两种用例的逻辑由res.config.settings 模型提供。
第一步在依赖中添加了base_setup插件模块,因为它提供对我们想要使用的res.config.settings模型的继承。它还添加了我们所需的额外的XML数据文件来向General Settings表单添加新的选项。
第二步中,我们创建了一个新的安全组,Library: release date feature。功能的启用仅对该组可见,这样直到组启用时它都会隐藏。
在我们的示例中,我们希望仅在相应的配置项启用时图书发行日期才可用。要进行实现,我们对该字段使用groups属性,这样它仅对这一安全组可用。我们在模型层面进行了操作,这样它会自动应用于使用该字段的UI视图。
最后,我们继承了res.config.settings模型来添加新选项。每个选项都是一个布尔字段,根据所要实现的功能名称必须以group_或module_开头。
group_选项字段应有一个implied_group属性并且应为一个包含安全组的XML ID逗号分隔列表来在启用时进行激活。XML ID必须是一种完整的形式,带有模块名、点号和标识符名称,如module_name.identifier。
我们还可以添加group属性来指定启用该功能所针对的安全组。如果未定义组,它会对所有基于Employee组的组启用。因此相关组不应用于门户(portal)安全组,因为它们没有像其它常规安全组那样继承Employee基安全组,
激活背后的机制相当简单:对implied_group它在group属性中添加安全组,因此让相关的功能对对应的用户可见。
module_选项字段不需要任何的额外属性。字段名的剩余部分标识在启用该选项时安装的模块。在本例中,module_note会安装note模块。
ℹ️取消复选框的勾选会卸载该模块并且没有警告,这会导致数据丢失(会导致模模型或字段以及模块数据的删除)。为避免不小心取消勾选,secure_uninstall社区模块(来自https://github.com/OCA/server-tools))会在卸载插件模块时弹出要求用户输入密码。
本节的最后一步在Business documents组前添加了General Settings表单视图,其id=”business_documents”。我们使用该id来进行视图继承。它使用模块名作为id创建了自己的div,这是一种良好实践,因为这样继承my_library的其它模块可以轻易地向这个div添加配置项。
扩展知识…
配置设置也可以带有名为default_前缀的字段。其中有值的话,ORM会将其设置为全局默认值。设置字段应有一个default_model属于,用于标识受影响的模型,default_前缀后的字段名标识有默认值集合的模型字段。
此外,没琛有这三种前缀的字段可用于其它设置,但你需要使用get_default_为前缀的方法实现逻辑来获取它们的值,使用set_为前缀的方法来编辑它们的值。
如果想要更深入地了解配置设置,最好的文档是Odoo的源代码./odoo/addons/base/models/res_config.py,其中有大量的注释进行了详细的讲解。
以超级用户访问记录集
在前面小节中,我们学习安全技术,如访问规则、安全组和记录规则。使用这些技术,可以规避未授权的访问。但有时会有复杂的业务用例,其中会需要访问或修改用户没有访问权限的记录。例如,公共用户可能无法访问线索记录,但通过提交网站的表单,用户可以在后台中生成线索记录。
使用sudo(),可以用超级用户访问记录集。在第九章 高级服务端开发技巧的更改执行动作的用户一节中我们已学习过sudo()。这里我们将学习到即使有给定的ACL规则或对字段添加了安全组,仍然可以通过sudo()进行访问。
如何实现…
我们将使用与前面小节相同的my_library模块。其中已添加了ALC规则,授予了普通用户只读权限。我们还将通过安全组新增字段,这样仅图书管理员能访问它。然后,我们将通过普通用户来修改字段值。按照如下步骤来进行实现:
- 在library.book模型中新增字段:
123report_missing = fields.Text(string="Book is missing",groups='my_library.group_library_librarian') - 在表单视图中添加该字段:
1<field name="report_missing"/> - 在library.book模型中添加report_missing_book()方法:
1234567def report_missing_book(self):self.ensure_one()message = "Book is missing (Reported by: %s)" %self.env.user.nameself.sudo().write({'report_missing': message}) - 在表单视图中添加按钮来在用户界面中触发这个方法:
123<button name="report_missing_book"string="Report Missing Book"type="object"/>
重启服务并更新模块来应用这些修改。
运行原理…
在第1和第2步中,我们分别在模型和表单视图中添加了名为report_missing的新字段。注意我们的Python代码中对该字段添加了my_library.group_library_librarian组,宁样仅有Librarian组中的用户才能访问该字段。
接下来的步骤中,我们添加了report_missing_book() 方法,在方法体中我们更新了report_missing字段的值。注意我们在调用写方法之前使用了sudo()。
最后,我们在表单视图中添加了一个按钮,用于在用户界面中触发该方法。
要测试这一实现,需要通过非图书管理用户进行登录。如果你使用演示数据加载了数据库的话,可以通过demo用户来登录,然后点击图书表单视图中的Report Missing Book 按钮。点击该按钮时会调用report_missing_book() 方法,这会在report_missing字段写入消息,虽然该用户并没有相应的权限。可以通过管理员用户来查看该字段值,因为对demo用户该字段是隐藏的。
在点击Report Missing Book按钮时,我们会在report_missing_book()方法通过 self 参数得到一个当前图书的记录集。在图书记录集中写入值之前,我们使用了self.sudo()。这会以不同的环境返回同一记录集。这条记录具体通过超级用户获取的环境,它会跳过所有访问规则和记录规则。因此,非图书管理用户就可以对图书记录进行写入。在模型的日志字段write_uid中,其值也是超级用户。
扩展知识…
在使用sudo()时需要加倍小心,因为它跳过了所有的访问权限。在多公司环境中,使用不当可能会产生问题。如果你想以另一个用户访问记录的话,可以将该用户的ID传递给sudo,如self.sudo(uid).。这会返回带有该用户环境的记录集。通过这种方法,它不会跳过多访问规则和权限规则,但你可以执行授予该用户的所有动作。
根据组来隐藏元素和菜单
在前面小节中,我们学习了如何在Python字段定义中通过group参数来对一些用户隐藏字段。还有一种在用户界面中隐藏字段的方式:通过在视图定义中在XML标签上添加安全组。也可以对菜单使用安全组来对指定用户隐藏菜单。
准备工作
本节中,我们将复用前一节中的my_library插件模块。在前一节中,我们在<header>标签中添加了一个按钮。通过对其添加groups属性我们可以对一些用户隐藏整个头部。
为book.category模型添加该模型、视图和菜单。我们将对用户隐藏分类菜单。参见第五章 应用模型来学习如何添加模型视图和菜单。
如何实现…
按照如下步骤来根据安全组隐藏元素:
- 在<header>中添加groups属性来对其它用户隐藏它:
123...<header groups="my_library.group_library_user">... - 在<menuitem>图书分类上添加groups属性来仅对图书用户显示它:
12345<menuitem name="Book Categories"id="library_book_category_menu"parent="library_base_menu"action="library_book_category_action"groups="my_library.group_library_librarian"/>
重启服务并更新模块来应用修改。
运行原理…
第一步中,我们在<header>中添加了groups=”my_library.group_library_user”。这表示整个header部分仅对图书用户和图书管理员可见。不属于group_library_user的普通后台用户则无法看到header部分。
第2步中,我们对菜单项添加了groups=”my_library.group_library_librarian”。这样菜单仅对图书管理员可见。
几乎可以在任何地方使用groups属性,包括<field>, <notebook>, <group>, <menuitems>和视图结构中的任意标签。如果用户不属于该组的话则会对其隐藏这些元素。可以在网页和QWeb报表中使用相同的组属性,这将在第十三章 自动化、工作流和打印件和第十五章 CMS网站开发中进行讲解。
正如在本章以超级用户访问记录集一节中所看到的,我们可以在Python字段定义中使用groups参数来对一些用户隐藏字段。注意字段上的安全组和视图中的Python安全组有很大的区别。Python中的安全组提供了真正的安全,非授权用户无法通过ORM或RPC调用来访问字段。但是视图中的组仅为了提升易用性。XML文件中通过组隐藏的字段仍可以通过RPC或ORM进行访问。