Odoo权限机制 除手动通过自定义模式管理权限外,Odoo还提供了两种数据驱动的机制来管理或限制对数据的访问。
这两种机制都通过组来与具体用户相关联:一个用户可属于多个组,安全机制与组进行关联,进而应用对用户应用安全机制。
1 2 3 用户---拥有----> 1个或多个组 ----拥有---->多个权限 通过组,用户既可以拥有权限
Odoo的权限控制方式有以下: 一是访问权限(模型ir.model.access
)即表级权限 二是记录规则(模型ir.rule
)即行级权限 三是字段级权限,一个对象或表上的某些字段的访问权限。(通过group
实现) 四是视图界面级权限,不属于指定视图元素所包含组的用户看不到。(通过group
实现)
群组定义 组的模型为res.groups
,只需要在xml文件中定义就行。
如下示例,定义一个销售管理员组
1 2 3 4 5 6 7 <record id ="group_sale_manager" model ="res.groups" > <field name ="name" > Administrator</field > <field name ="comment" > the user will have an access to the sales configuration as well as statistic reports.</field > <field name ="category_id" ref ="base.module_category_sales_sales" /> <field name ="implied_ids" eval ="[(4, ref('group_sale_salesman_all_leads'))]" /> <field name ="users" eval ="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]" /> </record >
其中:name
:这是组的显示名称。category_id
:这是对应用分类的引用,用于在用户表单中组织分组。implied_ids
:它们是所要继承权限的其它组。users
:这是属于该组的用户。在新的插件模块中,我们通常希望admin用户属于应用的管理员组。
再做一些解释:category_id
的作用就是分类。比如销售应用的组,采购应用的组,技术设置类的组。模型是ir.module.category
,odoo翻译为应用。也是在xml中定义。
1 2 3 4 <record model ="ir.module.category" id ="module_category_sales" > <field name ="name" > Sales</field > <field name ="sequence" > 6</field > </record >
implied_ids
很多人翻译为继承的,可能有些不太好理解,实际上这是一个自关联字段。举个例子:封建社会中有 金钱/权力/女人/马车/豪宅/奴隶。 若是个奴隶,就只有苟活的权利,只能眼巴巴的望着这纸醉金迷的色会。若是个大官,金钱/权力/女人…都会主动靠近,自己拥有。 说白了,implied_ids
相当于附加的。有老婆就会有老婆饼,你只有老婆饼就只能很遗憾的想着小金莲。
1 2 3 4 5 6 组 implied_ids 组 A B、C、D B C、D 用户属于A组,就自动拥有B、C、D组的权限 用户属于B组,就自动拥有C、D组的权限
访问权限 模型权限访问管理:模型级的权限控制,该模型的所有记录,对于群组内用户(如无定义,则对所有用户)的读写改删权限控制。 access rule是通过security文件夹下的ir.model.access.csv文件来控制的: 这个文件第一行指明了需要控制的内容:
1 2 3 4 5 6 7 8 id,name,model_id,group_id,perm_read,perm_write,perm_create,perm_unlink 分别对应: id:记录的外部标识符 (也称为 XML ID)。在我们的模块中它应该是唯一。 name:描述标题。官方模块通常使用模型名称和组的圆点分隔的字符串。如: todo.task.user model_id :模型的外部标识符。todo.task对应该标识符是model_todo_task group_id:权限组,在第一步中通过groups.xml。最重要的一点是供定义它的模块名前缀。比如员工组,它的标识符为base.group_user。 perm_XX:字段标记授予 读, 写, 创建,删除 权限。
举例
1 2 3 4 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_crm_team,crm.team,model_crm_team,base.group_user,1,0,0,0 access_crm_team_user,crm.team.user,model_crm_team,sales_team.group_sale_salesman,1,0,0,0 access_crm_team_manager,crm.team.manager,model_crm_team,sales_team.group_sale_manager,1,1,1,1
记录规则 记录规则也是通过xml文件定义,定义方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="utf-8"?> <odoo > <record id ="模块名_对象_rule" model ="ir.rule" > //为某对象添加记录规则 <field name ="name" > 规则名</field > <field name ="model_id" ref ="model_对象" /> <field name ="domain_force" > [('字段名','操作符',值)]</field > //记录过滤表达式:符合该表达式的记录才能被访问 <field name ="groups" eval ="[(值,ref('访问组id'))]" /> //访问组id在第一步创建时指定,用于指定本规则作用于哪些组 <field name ="perm_read" eval ="0/1" /> <field name ="perm_write" eval ="0/1" /> <field name ="perm_create" eval ="0/1" /> <field name ="perm_unlink" eval ="0/1" /> </record > </odoo >
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //只看自己的 <record id ="sale_order_personal_rule" model ="ir.rule" > <field name ="name" > Personal Orders</field > <field ref ="model_sale_order" name ="model_id" /> <field name ="domain_force" > ['|',('user_id','=',user.id),('user_id','=',False)]</field > <field name ="groups" eval ="[(4, ref('sales_team.group_sale_salesman'))]" /> </record > //看所有的 <record id ="sale_order_see_all" model ="ir.rule" > <field name ="name" > All Orders</field > <field ref ="model_sale_order" name ="model_id" /> <field name ="domain_force" > [(1,'=',1)]</field > <field name ="groups" eval ="[(4, ref('sales_team.group_sale_salesman_all_leads'))]" /> </record >
在记录规则未分配给任何安全组时,它被标记为全局并由不同的规则来进行处理。 规则适用的记录行,用domain来定义,符合domain规则的可访问,不符合的则不能访问。这里的domain有两个上下文可以使用:user是当前用户,time是时间。 全局规则(规则中未指定组)和组规则(规则中指定了组)的使用方式截然不同: 全局规则是减法的,必须全部匹配才能访问记录 组规则是加法的,其中任何一个匹配(并且全部的全局规则都匹配)就能访问记录 这意味着第一个组规则限制记录的访问,但是其它的所有组规则都在扩展访问。而全局规则是每一个规则都在限制记录的访问(或者不起作用)。
字段级权限 在某些情况下,我们可能会需要更精准控制的访问控制,可能会需要限制对模型具体字段的访问。 在定义字段时候,可以使用groups属性来让字段仅能由指定的安全组所访问。
1 phone = fields.Char(string="Private Phone" , groups="hr.group_hr_user" )
带有groups属性的字段进行了特殊处理,它检查用户是否属于属性中所指定的任意安全组。如果用户不属于指定的某一组,Odoo会在用户界面中删除该字段并限制对该字段的ORM操作。 注意这并不仅仅是表面工作。该字段不仅在用户界面中会隐藏,同时在用户的其它ORM操作中也无法使用它,比如读和写。对于XML-RPC或JSON-RPC调用同样如此。 在业务逻辑或用户界面on-change事件(@api.onchange方法)中使用这些字段时需要小心,在用户没有该字段访问权限时会抛出错误。一种避免的方式是使用提权,比如sudo()模型方法或计算字段的compute_sudo字段属性。
视图界面级权限 就是给视图界面元素定义时,添加groups
属性,同时还有div
标签、button
标签同样适用
1 2 3 4 5 6 7 8 9 10 11 12 13 <menuitem id ="menu_human_resources_configuration" name ="Configuration" parent ="menu_hr_root" groups ="group_hr_manager" sequence ="100" /> <div class ="app_settings_block" data-string ="Employees" string ="Employees" data-key ="hr" groups ="hr.group_hr_manager" > ... </div > <button name ="set_recruit" string ="Start Recruitment" states ="open" type ="object" class ="oe_highlight" groups ="base.group_user" />
启用禁用功能 如Odoo中的启用多组织、多计量,就是利用安全组的策略实现的。
第一步,定义一个安全组
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="utf-8"?> <odoo > <data noupdate ="0" > <record id ="group_uom" model ="res.groups" > <field name ="name" > Manage Multiple Units of Measure</field > <field name ="category_id" ref ="base.module_category_hidden" /> </record > </data > </odoo >
第二步,继承res.config.settings
模型,增加字段,并在xml中添加字段用以展示
1 2 3 4 class ResConfigSettings (models.TransientModel) : _inherit = 'res.config.settings' group_uom = fields.Boolean("Units of Measure" , group='base.group_user' , implied_group='uom.group_uom' )
注意: 字段必须以group_
开头, implied_group
属性就是我们第一步创建的组。group
属性可以不写,默认为base.group_user
。
第三步, 给我们需要启用的功能添加组
1 <field name ="product_uom" groups ="uom.group_uom" />
第四步,在设置界面对字段group_uom
进行勾选保存,就可以看到product_uom
字段已经启用。
原理很简单,你只要记得我上面说的老婆饼和小金莲就懂了,源码位于\base\models\res_config.py
567行。 就是让implied_group
成为group
的附加的就可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 current_settings = self.default_get(list(self.fields_get())) with self.env.norecompute(): for name, groups, implied_group in sorted(classified['group' ], key=lambda k: self[k[0 ]]): groups = groups.sudo() implied_group = implied_group.sudo() if self[name] == current_settings[name]: continue if int(self[name]): groups.write({'implied_ids' : [(4 , implied_group.id)]}) else : groups.write({'implied_ids' : [(3 , implied_group.id)]}) implied_group.sudo().write({'users' : [(5 ,)]})
权限控制代码层面原理 原理就是在加载视图时会进行权限校验,受限制的字段将在fields_get()请求中被移除。 具体代码:models.py中check_access_rights
和check_access_rule
两个校验方法 此外:使用 user_has_groups
方法可以判断当前用户是否拥有组