details:   https://code.tryton.org/tryton/commit/cb7e3e7c8403
branch:    7.0
user:      Cédric Krier <[email protected]>
date:      Thu Sep 10 16:07:26 2026 +0200
description:
        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)
diffstat:

 modules/user_role/res.py |  22 +++++++++++++++++++---
 1 files changed, 19 insertions(+), 3 deletions(-)

diffs (59 lines):

diff -r cec1eb7aa67f -r cb7e3e7c8403 modules/user_role/res.py
--- a/modules/user_role/res.py  Thu Sep 03 17:23:33 2026 +0200
+++ b/modules/user_role/res.py  Thu Sep 10 16:07:26 2026 +0200
@@ -4,7 +4,7 @@
 
 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):
@@ -37,7 +37,11 @@
 
 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):
@@ -63,12 +67,19 @@
 
     @classmethod
     def sync_roles(cls, users=None, date=None):
+        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:
                 continue
             new = {g.id for r in user.roles for g in r.role.groups
@@ -88,7 +99,12 @@
     "User Role"
     __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", required=True)
     from_date = fields.DateTime(
         "From Date",

Reply via email to