Public release from ruodoo-project: 19.0 - 2026-05-31 21:19:12 UTC
This commit is contained in:
20
ir_rule_protected/README.rst
Normal file
20
ir_rule_protected/README.rst
Normal 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>`_
|
||||
1
ir_rule_protected/__init__.py
Normal file
1
ir_rule_protected/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import models
|
||||
14
ir_rule_protected/__manifest__.py
Normal file
14
ir_rule_protected/__manifest__.py
Normal 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,
|
||||
}
|
||||
4
ir_rule_protected/doc/changelog.rst
Normal file
4
ir_rule_protected/doc/changelog.rst
Normal file
@ -0,0 +1,4 @@
|
||||
`1.0.0`
|
||||
-------
|
||||
|
||||
- **Init version**
|
||||
19
ir_rule_protected/doc/index.rst
Normal file
19
ir_rule_protected/doc/index.rst
Normal 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.
|
||||
53
ir_rule_protected/i18n/de.po
Normal file
53
ir_rule_protected/i18n/de.po
Normal 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 ""
|
||||
56
ir_rule_protected/i18n/es_CR.po
Normal file
56
ir_rule_protected/i18n/es_CR.po
Normal 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"
|
||||
46
ir_rule_protected/i18n/ir_rule_protected.pot
Normal file
46
ir_rule_protected/i18n/ir_rule_protected.pot
Normal 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 ""
|
||||
BIN
ir_rule_protected/images/banner.jpg
Normal file
BIN
ir_rule_protected/images/banner.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
39
ir_rule_protected/models.py
Normal file
39
ir_rule_protected/models.py
Normal 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()
|
||||
BIN
ir_rule_protected/static/description/icon.png
Normal file
BIN
ir_rule_protected/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
1
ir_rule_protected/tests/__init__.py
Normal file
1
ir_rule_protected/tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import test_ir_rule
|
||||
81
ir_rule_protected/tests/test_ir_rule.py
Normal file
81
ir_rule_protected/tests/test_ir_rule.py
Normal 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")
|
||||
11
ir_rule_protected/views.xml
Normal file
11
ir_rule_protected/views.xml
Normal 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>
|
||||
Reference in New Issue
Block a user