Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC

This commit is contained in:
CI Publish Bot
2026-05-31 21:19:21 +00:00
commit aa4214c195
1213 changed files with 183945 additions and 0 deletions

View File

@ -0,0 +1,20 @@
.. image:: https://itpp.dev/images/infinity-readme.png
:alt: Tested and maintained by IT Projects Labs
:target: https://itpp.dev
Protect ir.rule records
=======================
The module allows protect ir.rule from modifying and deleting. Once a rule is marked as protected only superuser is able to control this rule.
Also, the module protect itself from uninstalling by non-superuser.
Roadmap
=======
* The module should allow specifying which admins can switch to superuser mode (set True to all existing admins and False for any new users)
Further information
===================
Tested on `Odoo 17.0 <https://github.com/odoo/odoo/commit/40b19d89846303016098840f4958fe7cc105067c>`_

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,14 @@
{
"name": "Protect ir.rule records",
"summary": "Non-superuser admins cannot change protected ir.rule or uninstall this module",
"version": "19.0.1.0.0",
"author": "IT-Projects LLC, Ivan Yelizariev",
"category": "Extra Tools",
"images": ["images/banner.jpg"],
"support": "apps@itpp.dev",
"website": "https://www.odoo.com/apps/modules/14.0/ir_rule_protected/",
"license": "Other OSI approved licence", # MIT
"depends": [],
"data": ["views.xml"],
"installable": True,
}

View File

@ -0,0 +1,4 @@
`1.0.0`
-------
- **Init version**

View File

@ -0,0 +1,19 @@
=========================
Protect ir.rule records
=========================
Installation
============
* `Install <https://odoo-development.readthedocs.io/en/latest/odoo/usage/install-module.html>`__ this module in a usual way
Usage
=====
* `Log in as SUPERUSER <https://odoo-development.readthedocs.io/en/latest/odoo/usage/login-as-superuser.html>`__
* `Activate Developer Mode <https://odoo-development.readthedocs.io/en/latest/odoo/usage/debug-mode.html>`__
* Open menu ``[[ Settings ]]>> Technical >> Security >> Record Rules``
* Select or create some record
* Click ``[Edit]``
* Set "Protected" checkbox and click ``[Save]`` button.
* RESULT: the record is protected from modifying and deleting by non-superuser.

View File

@ -0,0 +1,53 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ir_rule_protected
#
# Translators:
# Dawid Runowski <dawrun@outlook.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-26 19:17+0000\n"
"PO-Revision-Date: 2017-12-05 05:26+0000\n"
"Last-Translator: Dawid Runowski <dawrun@outlook.com>, 2019\n"
"Language-Team: German (https://www.transifex.com/it-projects-llc/teams/76080/"
"de/)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: ir_rule_protected
#: model:ir.model.fields,help:ir_rule_protected.field_ir_rule_protected
msgid "Make rule editable only for superuser"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_module_module
msgid "Module"
msgstr ""
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:39
#, python-format
msgid "Only admin can uninstall the module"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model.fields,field_description:ir_rule_protected.field_ir_rule_protected
msgid "Protected"
msgstr "Geschützt"
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:19
#, python-format
msgid "The Rule is protected. You don't have access for this operation"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_rule
msgid "ir.rule"
msgstr ""

View File

@ -0,0 +1,56 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ir_rule_protected
#
# Translators:
# Randall <randall_castro@me.com>, 2018
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-21 00:17+0000\n"
"PO-Revision-Date: 2017-11-28 13:02+0000\n"
"Last-Translator: Randall <randall_castro@me.com>, 2018\n"
"Language-Team: Spanish (Costa Rica) (https://www.transifex.com/it-projects-"
"llc/teams/76080/es_CR/)\n"
"Language: es_CR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: ir_rule_protected
#: model:ir.model.fields,help:ir_rule_protected.field_ir_rule__protected
msgid "Make rule editable only for superuser"
msgstr "Hacer que la regla sea editable sólo para Superusuario"
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_module_module
msgid "Module"
msgstr "Módulo"
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:38
#, python-format
msgid "Only admin can uninstall the module"
msgstr "Sólo admin puede desinstalar el módulo"
#. module: ir_rule_protected
#: model:ir.model.fields,field_description:ir_rule_protected.field_ir_rule__protected
msgid "Protected"
msgstr "Protegido"
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_rule
msgid "Record Rule"
msgstr ""
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:18
#, python-format
msgid "The Rule is protected. You don't have access for this operation"
msgstr "La regla está protegida. No tiene acceso para esta operación"
#~ msgid "ir.rule"
#~ msgstr "ir.rule"

View File

@ -0,0 +1,46 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ir_rule_protected
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: ir_rule_protected
#: model:ir.model.fields,help:ir_rule_protected.field_ir_rule__protected
msgid "Make rule editable only for superuser"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_module_module
msgid "Module"
msgstr ""
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:0
#, python-format
msgid "Only admin can uninstall the module"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model.fields,field_description:ir_rule_protected.field_ir_rule__protected
msgid "Protected"
msgstr ""
#. module: ir_rule_protected
#: model:ir.model,name:ir_rule_protected.model_ir_rule
msgid "Record Rule"
msgstr ""
#. module: ir_rule_protected
#: code:addons/ir_rule_protected/models.py:0
#, python-format
msgid "The Rule is protected. You don't have access for this operation"
msgstr ""

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

View File

@ -0,0 +1,39 @@
from odoo import SUPERUSER_ID, exceptions, fields, models
from odoo.tools.translate import _
MODULE_NAME = "ir_rule_protected"
class IRRule(models.Model):
_inherit = "ir.rule"
protected = fields.Boolean(
"Protected", help="Make rule editable only for superuser"
)
def check_restricted(self):
if self.env.user.id == SUPERUSER_ID:
return
for r in self:
if r.protected:
raise exceptions.UserError(
_("The Rule is protected. You don't have access for this operation")
)
def write(self, vals):
self.check_restricted()
return super(IRRule, self).write(vals)
def unlink(self):
self.check_restricted()
return super(IRRule, self).unlink()
class Module(models.Model):
_inherit = "ir.module.module"
def button_uninstall(self):
for r in self:
if r.name == MODULE_NAME and self.env.uid != SUPERUSER_ID:
raise exceptions.UserError(_("Only admin can uninstall the module"))
return super(Module, self).button_uninstall()

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -0,0 +1 @@
from . import test_ir_rule

View File

@ -0,0 +1,81 @@
# Copyright 2024 DOB
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import SUPERUSER_ID
from odoo.exceptions import UserError
from odoo.tests import new_test_user
from odoo.tests.common import TransactionCase
class TestIrRuleProtected(TransactionCase):
"""Tests for ir_rule_protected: non-superuser cannot modify protected ir.rule.
Validates: Requirement 6.2
"""
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(
context=dict(cls.env.context, tracking_disable=True, no_reset_password=True)
)
# Create a regular admin user (not superuser)
cls.regular_user = new_test_user(
cls.env,
name="Regular Admin",
login="test_regular_admin_ir_rule",
groups="base.group_user,base.group_system",
)
# Create a protected ir.rule
cls.protected_rule = cls.env["ir.rule"].with_user(SUPERUSER_ID).create({
"name": "Test Protected Rule",
"model_id": cls.env.ref("base.model_res_partner").id,
"protected": True,
})
def test_non_superuser_cannot_write_protected_rule(self):
"""WHEN a user without is_superuser tries to modify a protected ir.rule,
ir_rule_protected SHALL deny the modification with UserError.
Validates: Requirement 6.2
"""
with self.assertRaises(UserError):
self.protected_rule.with_user(self.regular_user).write(
{"name": "Attempted Rename"}
)
def test_non_superuser_cannot_unlink_protected_rule(self):
"""WHEN a user without is_superuser tries to delete a protected ir.rule,
ir_rule_protected SHALL deny the deletion with UserError.
Validates: Requirement 6.2
"""
with self.assertRaises(UserError):
self.protected_rule.with_user(self.regular_user).unlink()
def test_superuser_can_write_protected_rule(self):
"""WHEN the superuser modifies a protected ir.rule,
ir_rule_protected SHALL allow the modification.
Validates: Requirement 6.2 (positive case)
"""
original_name = self.protected_rule.name
self.protected_rule.with_user(SUPERUSER_ID).write({"name": "Superuser Rename"})
self.assertEqual(self.protected_rule.name, "Superuser Rename")
# Restore original name
self.protected_rule.with_user(SUPERUSER_ID).write({"name": original_name})
def test_non_superuser_can_write_unprotected_rule(self):
"""WHEN a user without is_superuser modifies an unprotected ir.rule,
ir_rule_protected SHALL allow the modification.
Validates: Requirement 6.2 (negative case — unprotected rule)
"""
unprotected_rule = self.env["ir.rule"].with_user(SUPERUSER_ID).create({
"name": "Unprotected Rule",
"model_id": self.env.ref("base.model_res_partner").id,
"protected": False,
})
# Should not raise
unprotected_rule.with_user(self.regular_user).write({"name": "Renamed OK"})
self.assertEqual(unprotected_rule.name, "Renamed OK")

View File

@ -0,0 +1,11 @@
<odoo>
<record id="view_view_form" model="ir.ui.view">
<field name="model">ir.rule</field>
<field name="inherit_id" ref="base.view_rule_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='model_id']" position="after">
<field name="protected" />
</xpath>
</field>
</record>
</odoo>