odoo的权限控制是通过用户组来实现的,在用户组中配置控制权限,然后再添加用户到用户组中,从而实现对用户的访问和操作权限控制。一个用户可以属于多个用户组,用户最终的权限范围取决于所属用户组权限的并集。

在用户组中可以声明哪些数据的控制权限呢?我们打开开发者模式,通过“设置-用户&公司-用户组”导航到用户组,点击任一用户组,打开如下截图界面:

我们看到可以为一个用户组配置以下几方面的控制:菜单、视图、访问权限、记录规则。既然如此,那就赶快创建一个用户组,然后配置权限,然后添加用户,然后……不就可以达到我想要的权限控制目的了吗。在进行下一步操作之前,我们先介绍几个概念,还是用图片来说明吧:

1.用户组分类

我们创建用户组的时候,通常希望把用户组放在自定义的权限组分类中,以便管理维护;但是发现在odoo系统中没有创建用户组分类的地方,怎么办呢?原来odoo的用户组分类只能通过配置文件创建。

接下来我们就参考odoo自身模块,通过xml配置文件创建,大致有三个步骤:

1.在模块的security目录中创建xml配置文件:logistics_security.xml

2.在manifest.py清单文件中引入logistics_security.xml文件

3.编辑配置文件:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="category_custom_logistic" model="ir.module.category">
        <field name="name">物流管理</field><!--用户组分类名称-->
        <field name="sequence">1</field> <!--组分类显示顺序、优先级-->
    </record>
</odoo>

以上配置完成后,执行模块升级操作,我们就可以看到新创建的用户组分类了。

2.用户组

odoo将用户类型分成3种,在创建用户的时候可以看到;3种用户类型分别对应3种不同的内置用户组:

  • 内部用户:
    1
    base.group_user
  • 门户用户:
    1
    base.group_portal
  • 公开用户:
    1
    base.group_public

这3个是系统内置的用户组,可以通过odoo管理系统查看:

自定义用户组有两种创建方式,一种是直接在odoo管理系统中进行配置,通过“设置-用户&公司-用户组”菜单导航,即可进行操作;另一种依然是通过xml配置文件进行配置操作。通常我们希望在模块安装后就可以直接使用,故此我们略过第一种创建方式,这里只介绍第二种通过xml配置文件创建用户组的方式。

1.分别添加管理员组和普通用户组的配置,编辑security/logistics_security.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="category_custom_logistic" model="ir.module.category">
        <field name="name">物流管理</field>        <!--用户组分类名称-->
        <field name="sequence">1</field>        <!--组分类显示顺序、优先级-->
    </record>
    <record id="groups_logistic_manager" model="res.groups">
        <field name="name">管理员</field>
        <field name="category_id" ref="category_custom_logistic"/>
    </record>
    <record id="groups_logistic_user" model="res.groups">
        <field name="name">员工</field>
        <field name="category_id" ref="category_custom_logistic"/>
    </record>
</odoo>

2.执行模块升级操作,即可看到新添加的用户组数据:

3.用户

用户名和密码我们一般是通过odoo管理系统进行创建,因为我们不能提前确定好到底用什么用户名和密码,只有在实施的时候才能确定。管理系统如何创建用户,如何添加到用户组,操作简单,就不做介绍了。这里我们介绍下odoo系统中base.user_rootbase.user_admin这两个特殊用户的区别:

  • base.user_root:odoo中的超级用户,在系统中具有最高级别的权限。通常用于系统的初始化和特殊的管理任务,具有对系统中所有对象和功能的完全访问权限,而不受用户组的权限限制。
  • base.user_admin:odoo中的管理员用户,具有系统管理权限,例如安装/卸载模块、配置用户权限、管理数据库等。但这个用户受用户组的权限限制。

为什么说它们是用户而不是用户组呢,可以参考odoo基础模块中对他们的定义;以下截图的最顶部是代码文件的相对路径:

4.菜单

菜单权限可以通过odoo管理系统进行添加,也可以通过配置文件进行初始化,通常我们会选择后者。目前最常用的方式就是在菜单定义的地方添加用户组即可实现,比如我们为档案管理菜单添加上面定义的两个用户组:

执行模块升级操作,就可以发现这两个用户组下面有了档案管理的菜单权限。

与此同时,我们发现管理员用户没有了档案管理的菜单权限,如下图:

出现这种情况的原因是因为管理员账号本身受用户组权限的影响,我们把档案管理的菜单权限只分配给了以上定义的两个用户组,管理员账号不在当前的两个用户组中,所以就无法访问了。如果我们想要管理员拥有档案管理菜单的访问权限,我们可以在定义用户组的时候,初始管理员账号,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="category_custom_logistic" model="ir.module.category">
        <field name="name">物流管理</field>        <!--用户组分类名称-->
        <field name="sequence">1</field>        <!--组分类显示顺序、优先级-->
    </record>
    <record id="groups_logistic_manager" model="res.groups">
        <field name="name">管理员</field>
        <field name="category_id" ref="category_custom_logistic"/>
        <field name="users" eval="[(4, ref('base.user_admin'))]"/><!--为用户组添加管理员用户 -->  
    </record>
    <record id="groups_logistic_user" model="res.groups">
        <field name="name">员工</field>
        <field name="category_id" ref="category_custom_logistic"/>
        <field name="users" eval="[(4, ref('base.user_admin'))]"/><!--为用户组添加管理员用户 -->  
    </record>
</odoo>

执行模块升级操作,此时管理员已经有了档案管理菜单的访问权限:

两个用户组中也有了初始的管理员用户:

在这里我们对eval中的表达式做简要的说明:

  • (0, 0, values) 从提供的valueS字典创建新记录
  • (2, ID, values) 使用values字典中的值更新id值=ID的现有记录
  • (2, ID) 删除id=ID这条记录(调用unlink方法,删除数据及整个主从数据链接关系)
  • (3, ID) 删除主从数据的链接关系但是不删除这个记录
  • (4, ID) 为id=ID的数据添加主从链接关系
  • (5) 去除所有的链接关系,也就是循环所有的从数据且调用(3,ID)
  • (6, 0, [IDs]) 用IDs中的记录替换原来链接的记录(相当于先执行(5)再循环执行(4, ID))

在odoo自带的模块中,最常用的就是4,其它的偶尔会用到;大家在具体的使用场景中,更容易理解。

5.视图

视图权限的配置在odoo系统中很少用到。在odoo中,视图权限通常是通过模型权限继承获得的。如果已经为模型设置了权限,那么这些权限通常会自动应用到相关的视图上,无需额外的配置。

这里我们对QWeb视图的权限配置做简要说明,因为QWeb通常用于网页和报表,和模型之间没有依赖关系,所以就无法通过模型权限的继承来获取。我们来看odoo自带模块中的代码:

1
2
3
4
5
6
7
8
<!--代码路径:odoo\addons\web\views\lazy_assets.xml-->
<?xml version="1.0" encoding="utf-8"?>
<odoo>
  <!--表示为内部用户添加assets_backend_legacy_lazy视图的访问权限-->
  <template id="assets_backend_legacy_lazy" name="Lazy assets for legacy Views" groups="base.group_user">
    <t t-call-assets="web.assets_backend_legacy_lazy" />
  </template>
</odoo>

就是通过为视图指定所属的组来实现的,与菜单的配置方式相似。这里为内部用户添加了assets_backend_legacy_lazy视图的访问权限,可以在截图中看到效果。

6.访问权限

访问权限就是以上提到的模型权限,权限配置有读取访问、写入访问、创建访问、删除访问4个方面,如下截图:

可以在odoo管理系统中添加,也可以通过配置文件在模块安装的时候初始化,这里我们介绍通过配置文件添加的方式。在模块的security目录中创建ir.model.access.csv文件,配置内容主要是以下选项:

  • id 自定义外部标识,模块中保持唯一,一般命名为 access_模型名称_用户组名称
  • name 自定义ir.model.access的名称,一般命名沿用id取值即可
  • model_id:id 标准格式为 model_name,其中name 为模块中模型名称替换 . _ 后的值
  • group_id:id 权限组id
  • perm_read 读取访问,1表示有访问权限,0表示无权限
  • perm_write 写入访问,1表示有访问权限,0表示无权限
  • perm_create 创建访问,1表示有访问权限,0表示无权限
  • perm_unlink 删除访问,1表示有访问权限,0表示无权限

我们为先前创建的权限组定义不同的操作权限:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_waybill_manager,access.waybill.manager,model_waybill,groups_logistic_manager,1,1,1,1
access_waybill_user,access.waybill.user,model_waybill,groups_logistic_user,1,1,1,1
access_waybill_detail_manager,access.waybill.detail.manager,model_waybill_detail,groups_logistic_manager,1,1,1,1
access_waybill_detail_user,access.waybill.detail.user,model_waybill_detail,groups_logistic_user,1,1,1,1
access_goods_manager,access.goods.manager,model_goods,groups_logistic_manager,1,1,1,1
access_goods_user,access.goods.user,model_goods,groups_logistic_user,1,1,1,0
access_package_manager,access.package.manager,model_package,groups_logistic_manager,1,1,1,1
access_package_user,access.package.user,model_package,groups_logistic_user,1,1,1,0
access_shipper_manager,access.shipper.manager,model_shipper,groups_logistic_manager,1,1,1,1
access_shipper_user,access.shipper.user,model_shipper,groups_logistic_user,1,1,1,0
access_receiver_manager,access.receiver.manager,model_receiver,groups_logistic_manager,1,1,1,1
access_receiver_user,access.receiver.user,model_receiver,groups_logistic_user,1,1,1,0
access_city_manager,access.city.manager,model_city,groups_logistic_manager,1,1,1,1
access_city_user,access.city.user,model_city,groups_logistic_user,1,1,1,0
access_rate_manager,access.rate.manager,model_rate,groups_logistic_manager,1,1,1,1
access_rate_user,access.rate.user,model_rate,groups_logistic_user,1,0,0,0

csv表格模式显示如下截图所示:

执行模块升级操作,即可看到添加好的管理员员工这两个用户组的访问权限数据:

7.记录规则

记录规则是在访问权限的基础上,对数据访问的进一步限制;我们首先分析odoo自带模块中的配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
    <!--代码所在文件相对路径:odoo\addons\mail\security\mail_security.xml-->

    <record id="res_users_settings_volumes_rule_user" model="ir.rule">
        <field name="name">res.users.settings.volumes: access their own entries</field>
        <field name="model_id" ref="model_res_users_settings_volumes"/>
        <field name="groups" eval="[Command.link(ref('base.group_user'))]"/>
        <field name="domain_force">[('user_setting_id.user_id', '=', user.id)]</field>
        <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>

1.通过domain_force来限定模型数据的访问范围,domain_force是一个可以使用以下变量的python表达式:

  • time Python的 time 模块
  • user 标识当前用户
  • company_id 当前用户所选择的公司id
  • company_ids 当前用户可以访问的公司id列表
  • 官方文档参考地址:record-rules

2.通过groups来指定记录规则所属的用户组,如果记录规则未指定用户组,那么当前规则就是全局规则。全局规则会影响到所有的用户,不管用户属于哪个用户组。通过全局规则,可以在模型上设置默认的访问策略,例如限制用户只能访问属于自己公司的记录,配置如下所示:

1
2
3
4
5
<record id="global_rule" model="ir.rule">
    <field name="name">Global Rule</field>
    <field name="model_id" ref="your_module.your_model"/>
    <field name="domain_force">[('company_id', '=', user.company_id.id)]</field>
</record>

关于跨公司数据共享或是只能访问公司自身数据的策略,可以参考官方说明文档:security-rules

3.通过perm_read/perm_write/perm_create/perm_unlink来控制过滤之后的数据的访问权限:

最后我们一起看下odoo自身的记录规则在系统中的显示:

至于字段级别的控制,我们在此就不做介绍了,感兴趣的朋友移步odoo官方说明文档:field-access

作者 菜园君