changeset 670b2dd25ca9 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=670b2dd25ca9
description:
Add parent to group to inherit accesses
issue10032
review320661002
diffstat:
CHANGELOG | 1 +
trytond/ir/model.py | 4 +-
trytond/ir/rule.py | 3 +-
trytond/res/group.py | 36 +++++++++++++++++++++++++++-
trytond/res/ir.xml | 26 ++++++++++----------
trytond/res/user.py | 31 +++++++++++++++++++++---
trytond/res/view/group_form.xml | 3 ++
trytond/res/view/group_list.xml | 3 +-
trytond/tests/test_access.py | 51 +++++++++++++++++++++++++++++++++++++++++
9 files changed, 135 insertions(+), 23 deletions(-)
diffs (390 lines):
diff -r 74f7b0322ede -r 670b2dd25ca9 CHANGELOG
--- a/CHANGELOG Sun Mar 07 11:13:53 2021 +0100
+++ b/CHANGELOG Sun Mar 07 12:43:09 2021 +0100
@@ -1,3 +1,4 @@
+* Add parent to group to inherit accesses
* Add __access__ to Model
* Add route wrapper to allow null origin
* Add header grouping on report
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/ir/model.py
--- a/trytond/ir/model.py Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/ir/model.py Sun Mar 07 12:43:09 2021 +0100
@@ -523,7 +523,7 @@
user = Transaction().user
model_access = cls.__table__()
ir_model = Model.__table__()
- user_group = UserGroup.__table__()
+ user_group = UserGroup.user_group_all_table()
group = Group.__table__()
access = {}
@@ -729,7 +729,7 @@
field_access = cls.__table__()
ir_model = Model.__table__()
model_field = ModelField.__table__()
- user_group = UserGroup.__table__()
+ user_group = UserGroup.user_group_all_table()
group = Group.__table__()
accesses = {}
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/ir/rule.py
--- a/trytond/ir/rule.py Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/ir/rule.py Sun Mar 07 12:43:09 2021 +0100
@@ -143,6 +143,7 @@
user = EvalEnvironment(User(user_id), User)
return {
'user': user,
+ 'groups': User.get_groups()
}
@staticmethod
@@ -161,7 +162,7 @@
rule_table = cls.__table__()
rule_group = RuleGroup.__table__()
rule_group_group = RuleGroup_Group.__table__()
- user_group = User_Group.__table__()
+ user_group = User_Group.user_group_all_table()
model = Model.__table__()
transaction = Transaction()
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/res/group.py
--- a/trytond/res/group.py Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/res/group.py Sun Mar 07 12:43:09 2021 +0100
@@ -1,7 +1,12 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from itertools import chain
-from trytond.model import ModelView, ModelSQL, DeactivableMixin, fields, Unique
+
+from sql import With
+
+from trytond.model import ModelView, ModelSQL, DeactivableMixin, tree, fields
+from trytond.model import Unique
+from trytond.pool import Pool
from trytond.tools import grouped_slice
@@ -23,11 +28,14 @@
return res
-class Group(DeactivableMixin, ModelSQL, ModelView):
+class Group(DeactivableMixin, tree(), ModelSQL, ModelView):
"Group"
__name__ = "res.group"
name = fields.Char('Name', required=True, select=True, translate=True)
users = fields.Many2Many('res.user-res.group', 'group', 'user', 'Users')
+ parent = fields.Many2One(
+ 'res.group', "Parent",
+ help="The group to inherit accesses from.")
model_access = fields.One2Many('ir.model.access', 'group',
'Access Model')
field_access = fields.One2Many('ir.model.field.access', 'group',
@@ -51,6 +59,19 @@
cls._order.insert(0, ('name', 'ASC'))
@classmethod
+ def write(cls, *args):
+ super().write(*args)
+ pool = Pool()
+ # Restart the cache on the domain_get method
+ pool.get('ir.rule')._domain_get_cache.clear()
+ # Restart the cache for get_groups
+ pool.get('res.user')._get_groups_cache.clear()
+ # Restart the cache for model access and view
+ pool.get('ir.model.access')._get_access_cache.clear()
+ pool.get('ir.model.field.access')._get_access_cache.clear()
+ ModelView._fields_view_get_cache.clear()
+
+ @classmethod
def copy(cls, groups, default=None):
if default is None:
default = {}
@@ -67,3 +88,14 @@
default['name'] = name
new_groups.extend(super(Group, cls).copy([group], default=default))
return new_groups
+
+ @classmethod
+ def group_parent_all_cte(cls):
+ group = cls.__table__()
+ parents = With('id', 'parent', recursive=True)
+ parents.query = group.select(group.id, group.parent)
+ parents.query |= group.select(group.id, group.id)
+ parents.query |= (group
+ .join(parents, condition=group.parent == parents.id)
+ .select(group.id, parents.parent))
+ return parents
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/res/ir.xml
--- a/trytond/res/ir.xml Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/res/ir.xml Sun Mar 07 12:43:09 2021 +0100
@@ -445,7 +445,7 @@
</record>
<record model="ir.rule" id="rule_menu1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_menu"/>
</record>
@@ -460,7 +460,7 @@
</record>
<record model="ir.rule" id="rule_action1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action"/>
</record>
@@ -475,7 +475,7 @@
</record>
<record model="ir.rule" id="rule_action_keyword1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action_keyword"/>
</record>
@@ -491,7 +491,7 @@
</record>
<record model="ir.rule" id="rule_action_report1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action_report"/>
</record>
@@ -507,7 +507,7 @@
</record>
<record model="ir.rule" id="rule_action_act_window1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action_act_window"/>
</record>
@@ -523,7 +523,7 @@
</record>
<record model="ir.rule" id="rule_action_wizard1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action_wizard"/>
</record>
@@ -539,7 +539,7 @@
</record>
<record model="ir.rule" id="rule_action_url1">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_action_url"/>
</record>
@@ -778,7 +778,7 @@
</record>
<record model="ir.rule" id="rule_sequence">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence"/>
</record>
@@ -791,7 +791,7 @@
</record>
<record model="ir.rule" id="rule_sequence_strict">
<field name="domain"
- eval="[('groups', 'in', Eval('user', {}).get('groups', []))]"
+ eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_sequence_strict"/>
</record>
@@ -949,7 +949,7 @@
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.rule" id="rule_group_export_read1">
- <field name="domain" eval="[('groups', 'in', Eval('user.groups',
[]))]" pyson="1"/>
+ <field name="domain" eval="[('groups', 'in', Eval('groups', []))]"
pyson="1"/>
<field name="rule_group" ref="rule_group_export_read"/>
</record>
<record model="ir.rule" id="rule_group_export_read2">
@@ -968,7 +968,7 @@
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.rule" id="rule_group_export_write1">
- <field name="domain" eval="[('write_groups', 'in',
Eval('user.groups', []))]" pyson="1"/>
+ <field name="domain" eval="[('write_groups', 'in', Eval('groups',
[]))]" pyson="1"/>
<field name="rule_group" ref="rule_group_export_write"/>
</record>
<record model="ir.rule" id="rule_group_export_write2">
@@ -1006,7 +1006,7 @@
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.rule" id="rule_group_export_line_read1">
- <field name="domain" eval="[('export.groups', 'in',
Eval('user.groups', []))]" pyson="1"/>
+ <field name="domain" eval="[('export.groups', 'in', Eval('groups',
[]))]" pyson="1"/>
<field name="rule_group" ref="rule_group_export_line_read"/>
</record>
<record model="ir.rule" id="rule_group_export_line_read2">
@@ -1025,7 +1025,7 @@
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.rule" id="rule_group_export_line_write1">
- <field name="domain" eval="[('export.write_groups', 'in',
Eval('user.groups', []))]" pyson="1"/>
+ <field name="domain" eval="[('export.write_groups', 'in',
Eval('groups', []))]" pyson="1"/>
<field name="rule_group" ref="rule_group_export_line_write"/>
</record>
<record model="ir.rule" id="rule_group_export_line_write2">
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/res/user.py
--- a/trytond/res/user.py Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/res/user.py Sun Mar 07 12:43:09 2021 +0100
@@ -24,7 +24,7 @@
from operator import attrgetter
from ast import literal_eval
-from sql import Literal
+from sql import Literal, Null
from sql.functions import CurrentTimestamp
from sql.conditionals import Coalesce, Case
from sql.aggregate import Count
@@ -587,15 +587,22 @@
return res
@classmethod
- def get_groups(cls):
+ def get_groups(cls, name=None):
'''
- Return a list of group ids for the user
+ Return a list of all group ids for the user
'''
user = Transaction().user
groups = cls._get_groups_cache.get(user)
if groups is not None:
return groups
- groups = cls.read([Transaction().user], ['groups'])[0]['groups']
+ pool = Pool()
+ UserGroup = pool.get('res.user-res.group')
+ cursor = Transaction().connection.cursor()
+ user_group = UserGroup.user_group_all_table()
+ cursor.execute(*user_group.select(
+ user_group.group,
+ where=user_group.user == user))
+ groups = [g for g, in cursor]
cls._get_groups_cache.set(user, groups)
return groups
@@ -969,6 +976,22 @@
pool.get('ir.model.field.access')._get_access_cache.clear()
ModelView._fields_view_get_cache.clear()
+ @classmethod
+ def user_group_all_table(cls):
+ pool = Pool()
+ Group = pool.get('res.group')
+ user_group = cls.__table__()
+ group_parents = Group.group_parent_all_cte()
+
+ return (user_group
+ .join(group_parents,
+ condition=user_group.group == group_parents.id)
+ .select(
+ user_group.user.as_('user'),
+ group_parents.parent.as_('group'),
+ where=group_parents.parent != Null,
+ with_=group_parents))
+
class Warning_(ModelSQL, ModelView):
'User Warning'
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/res/view/group_form.xml
--- a/trytond/res/view/group_form.xml Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/res/view/group_form.xml Sun Mar 07 12:43:09 2021 +0100
@@ -6,6 +6,9 @@
<field name="name"/>
<label name="active"/>
<field name="active"/>
+
+ <label name="parent"/>
+ <field name="parent"/>
<notebook>
<page string="Members" col="2" id="members">
<field name="users" colspan="2"/>
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/res/view/group_list.xml
--- a/trytond/res/view/group_list.xml Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/res/view/group_list.xml Sun Mar 07 12:43:09 2021 +0100
@@ -2,5 +2,6 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree>
- <field name="name" expand="1"/>
+ <field name="name" expand="2"/>
+ <field name="parent" expand="1"/>
</tree>
diff -r 74f7b0322ede -r 670b2dd25ca9 trytond/tests/test_access.py
--- a/trytond/tests/test_access.py Sun Mar 07 11:13:53 2021 +0100
+++ b/trytond/tests/test_access.py Sun Mar 07 12:43:09 2021 +0100
@@ -255,6 +255,31 @@
self._assert(record)
+ @with_transaction(context=_context)
+ def test_access_inherited_from_parent(self):
+ "Test access inherited from parent"
+ pool = Pool()
+ Group = pool.get('res.group')
+ ModelAccess = pool.get('ir.model.access')
+ TestAccess = pool.get('test.access')
+ record, = TestAccess.create([{}])
+ group, = Group.create([{'name': 'Test'}])
+ Group.write([self.group], {
+ 'parent': group.id,
+ })
+ ModelAccess.create([{
+ 'model': self.model.id,
+ 'group': self.group.id,
+ self._perm: False,
+ }])
+ ModelAccess.create([{
+ 'model': self.model.id,
+ 'group': group.id,
+ self._perm: True,
+ }])
+
+ self._assert(record)
+
class ModelAccessReadTestCase(_ModelAccessTestCase):
_perm = 'perm_read'
@@ -665,6 +690,32 @@
self._assert2(record)
@with_transaction(context=_context)
+ def test_access_inherited_from_parent(self):
+ "Test no access with other group"
+ pool = Pool()
+ Group = pool.get('res.group')
+ FieldAccess = pool.get('ir.model.field.access')
+ TestAccess = pool.get('test.access')
+ record, = TestAccess.create([{}])
+ group, = Group.create([{'name': 'Test'}])
+ Group.write([self.group], {
+ 'parent': group.id,
+ })
+ FieldAccess.create([{
+ 'field': self.field1.id,
+ 'group': self.group.id,
+ self._perm: False,
+ }])
+ FieldAccess.create([{
+ 'field': self.field1.id,
+ 'group': group.id,
+ self._perm: True,
+ }])
+
+ self._assert1(record)
+ self._assert2(record)
+
+ @with_transaction(context=_context)
def test_two_access(self):
"Test two access"
pool = Pool()