Odoo用户、组与权限

本文带你了解如何使用odoo中的权限规则防止用户看见不该看的东西,为了解权限,首先了解用户,odoo中的用户分为三类:
-
内部用户:企业内部的用户,拥有对系统内部的访问权限,具体根据不同的角色权限不同而不同。
-
门户用户:非企业内部用户,通常为业务合作伙伴用户,拥有有限的资源访问权限。
-
公共用户:面向公众的权限,可以理解为游客权限。
管理员登录系统,激活开发者模式,即可在设置-用户详情对用户类型进行编辑:
以上三类用户的信息都存在res_user与res_partner表中,那么在odoo中如何区分用户类型以及如何做权限控制呢?
这里我们需要了解用户组(group),用户组为多个用户的集合,一个用户可以属于多个用户组,一个用户组也可以有多个用户,权限与用户组关联,通过对用户组的控制可以完成对于菜单,页面,表,数据,字段,按钮等权限的控制。以上三种用户类型即属于三个用户组:
-
内部用户:base.group_user
-
门户用户:base.group_portal
-
公共用户:base.group_public
同样,我们在下文中自己定义的用户组,在用户详情页面也可以进行查看、编辑,并为用户分配不同的用户组:
此外,多个用户组(group)可以属于一个用户组分类(category),这方便我们对用户组进行操作管理
-
category与group:权限分为category(组分类)与group(用户组),每一个group都属于一个category,一个category可以有多个group,属于一对多的关系。
-
页面中管理用户组:用户组管理页面:设置-用户与公司-群组,在debug模式下可以管理员在管理页面中管理全部的用户组,通过用户组可以限制用户对于菜单,视图,某个模型增删改查,以及配置权限规则对于某些模型中的数据进行过滤。
-
对于视图中具体字段的管理需要在代码层面进行实现,例:某个字段对于某些用户组可编辑,对于另外的用户组是只读。
-
组与组之间的关系:group之间可以互相继承,该group中的用户也将自动加入继承的group,如果一个用户属于多个group,用户的权限则会取group的并集,因此设计用户组权限时一定要考虑好组与组之间权限是否会发生冲突。
1. 创建权限
1.1 通过配置security下的xml文件创建权限:
xml中的权限配置
<odoo>
<data>
<record id="base_material_page_category" model="ir.module.category">
<field name="name">物料管理-页面field>
record>
<record id="base_material_search_group" model="res.groups">
<field name="category_id" ref="base_material_page_category"/>
<field name="name">物料管理-查询field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
record>
data>
odoo>
eval语法:
-
(0, 0, {values}) 根据values的值新建一条记录
-
(2, ID, {values}) 更新id=ID的记录,(写入values的值)
-
(2, ID) 删除id=ID这条记录,(调用unlink方法,删除数据及整个主从数据链接关系)
-
(3, ID) 切断主从数据的链接关系但是不删除这个记录
-
(4, ID) 为id=ID的数据添加主从链接关系
-
(5) 删除所有的从数据的链接关系,也就是向所有的从数据调用(3, ID)
-
(6, 0, [IDs]}) 用IDs中的记录替换原来的记录(相当于先执行(5)在循环执行(4, ID))
修改xml后在升级应用,可以在设置中某个用户下看见此页面效果:
权限下拉框效果:
在页面中实现权限下拉框需逐级继承上级group,最后一级group拥有以上所以group权限
1.2 代码中新建权限:
-
group数据存在数据库中res_group表中,category存在数据库中ir_module_category表中,代码中新建group与category直接往ir_module_category表与res_group表中插入数据即可
-
需要在代码和xml中以eval引用group时需要权限的id(如上图base.group_user),此id保存在ir_model_data表中
-
创建group与category时向该表插入一条数据,即可通过id引用group或者category。
新建category:
#操作ir.module.category需要管理员权限,如果提示权限不足,可以使用sudo()切换至超级用户
category = self.env['ir.module.category'].create({
'create_uid': self.env.user['id'],
'name': field_name,
'visible': 't',
})
新建group:
group = self.env['res.groups'].sudo().create({
'name': 'name',#group名称
'category_id': category.id,#对应的category的id,可以使用ref取得,例:self.env.ref("xc_material.extend_material_tree_view").id
'create_uid': self.env.user['id'],
})
需要在代码中直接以权限组名字使用新增的category以及group则需要ir.model.data中新增一条数据:
request.env['ir.model.data'].create({
'noupdate': 'f',
'name': '',#这里的name即为xml中标签的id属性
'module': 'xc_material',#自定义
'model': 'ir.module.category',
'res_id': category.id,#对应group的id或者category的id
})
2. 表级别权限(也就是对象的访问权限)
表级别权限是定义在security/ir.model.access.csv文件中的。
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_base_material,access_base_material,model_base_material,base.group_user,1,1,1,1 access_extend_material,access_extend_material,model_extend_material,base.group_user,1,1,1,1
-
id:可以随便取,但是在一个模块中是唯一的,一般取名为 access_模型名_特定用户组名(用下划线连起来)
-
name:可以随便取,一般命名沿用模型名用“.”连接加 用户组名
-
model:id:要做权限控制的model,格式写法是 模块名.model_模块名(中间的‘.’换成‘_’),如果model在此模块中,可以省略模块名,如product.model_product_product
-
group:id:组的id,不是本模块的组,要在前面加上模块名,如:account.group_account_user
-
perm_read,perm_write,perm_create,perm_unlink:这些就是具体的权限:读写增删,1 有权限,0 无权限
3. 记录级别权限(记录规则)
记录规则的权限存放在"ir.rule"中,我们通过管理ir_rule表中的记录,即可以控制记录的权限
xml新建权限规则
<record id="quotation_group_pre_sale_rule" model="ir.rule">
<field name="name">售前权限field>
<field name="model_id" ref="model_quotation"/>
<field name="domain_force">[('create_uid', '=', user.id)]field>
<field name="groups" eval="[(4, ref('quotation_group_pre_sale'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
record>
同上,如果在代码中定义权限规则,直接向ir.rule表中以及ir.model.data中插入一条数据即可
4. 菜单权限
4.1第一种(所有的记录,都可以用这种方法添加及修改,如果id存在则修改这条记录,如果不存在id就是添加该记录)
<record id="menu_finance" model="ir.ui.menu">
<field name="name">Invoicingfield>
<field name="web_icon">account,static/description/icon.pngfield>
<field name="sequence">40field>
<field name="groups_id" eval="[(6, 0, [ref('account.group_account_user'), ref('account.group_account_manager'), ref('account.group_account_invoice')])]"/>
record>
4.2第二种(简洁写法)
"Invoicing" id="menu_finance" groups="group_account_user,group_account_manager,group_account_invoice" web_icon="account,static/description/icon.png" sequence="40"/>
上面的2个xml表示menu_finance这个菜单只能被group_account_user,group_account_manager,group_account_invoice 三个用户组访问
如果有上级菜单,加parent属性,如果上级菜单不在本模块中,需要加模块名如:account.menu_finance:
id="menu_finance_reports" name="Reports" parent="menu_finance" sequence="5" groups="group_account_invoice"/>
5. 字段权限
5.1 field标签有一个groups属性,可以是一个或多个权限角色,多个权限角色使用逗号分隔
对拥有groups权限组的人可见。值是定义的权限组的id,例如:
"component_type" groups="base.group_user,base.group_allow_export"/>
5.2 在页面从数据库加载视图时,会调用load_view接口,会调用fields_view_get方法,可以重写此方法以控制xml显示的效果
重写该方法以达到通过不同权限显示不同的效果(需要import lxml):
def fields_view_get(self, view_id=None, view_type='tree', toolbar=False,submenu=False):
res = super(ExtendMaterial, self).fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar,
submenu=submenu)
doc = etree.XML(res['arch'])
if res['name'] == "base.material.manage" or res['name'] == "base.material":
all_fields = doc.xpath("//field")
for field in all_fields:
name = field.get('name', False)
if self.env.user.has_group("xc_material." + name + "_invisible_group"):
# 只读权限
if self.env.user.has_group("xc_material." + name + "_read_group"):
# 没有编辑权限
if not self.env.user.has_group("xc_material." + name + "_edit_group"):
field.set('modifiers', '{"readonly":true}')
else:
# field.set('modifiers', "{'invisible':true}")
doc.remove(field)
res['fields'].pop(name)
else:
doc.remove(field)
res['fields'].pop(name)
res['arch'] = etree.tostring(doc)
return res
6. 按钮权限
自定义按钮权限,需要在form视图上增加自定义的按钮,另外需要在模型文件中添加一个name属性值相同的方法。例如:
说明:
标签有一个groups属性,可以是一个或多个权限角色,多个权限角色使用逗号分隔,对拥有groups权限组的人可见
如果是odoo框架自带的按钮,那么直接在视图里,继承过来改写一下即可。这里是form视图,隐藏了创建和编辑的按钮。例如:
或者直接在视图标签上添加也可以达到同样效果

