From eeb2b75b4c8eb797be9cbcd4e805097350ea6b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Thu, 10 Sep 2026 16:07:26 +0200 Subject: [PATCH] Forbid to set role to admin user The links between admin user and groups are managed by XML record. But the sync task may delete such link if a role is assigned to admin because the task is run as root which skip the XML check. Closes #15086 (grafted from 0512f49869883b95b74387a292753e9e43375f35) --HG-- branch : 8.0 --- modules/user_role/res.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/modules/user_role/res.py b/modules/user_role/res.py index 3ac6b39f88..5de1576ff7 100644 --- a/modules/user_role/res.py +++ b/modules/user_role/res.py @@ -4,7 +4,7 @@ import datetime as dt from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool, PoolMeta -from trytond.pyson import Bool, Eval, If +from trytond.pyson import Bool, Eval, Id, If class Role(ModelSQL, ModelView): @@ -49,7 +49,11 @@ class RoleGroup(ModelSQL): class User(metaclass=PoolMeta): __name__ = 'res.user' - roles = fields.One2Many('res.user.role', 'user', "Roles") + roles = fields.One2Many( + 'res.user.role', 'user', "Roles", + states={ + 'invisible': Eval('id', -1) == Id('res', 'user_admin'), + }) @classmethod def __setup__(cls): @@ -69,12 +73,19 @@ class User(metaclass=PoolMeta): @classmethod def sync_roles(cls, users=None, date=None, clear=False): + pool = Pool() + ModelData = pool.get('ir.model.data') + + admin_id = ModelData.get_id('res', 'user_admin') + if date is None: date = dt.datetime.now() if users is None: users = cls.search([]) to_write = [] for user in users: + if user.id == admin_id: + continue if not user.roles and not clear: continue new = {g.id for r in user.roles for g in r.role.groups @@ -93,7 +104,12 @@ class User(metaclass=PoolMeta): class UserRole(ModelSQL, ModelView): __name__ = 'res.user.role' user = fields.Many2One( - 'res.user', "User", ondelete='CASCADE', required=True) + 'res.user', "User", ondelete='CASCADE', required=True, + domain=[ + If(Eval('user', -1) == Id('res', 'user_admin'), + ('id', '=', -1), + ()), + ]) role = fields.Many2One( 'res.role', "Role", ondelete='CASCADE', required=True) from_date = fields.DateTime(