[ENH] Add option 'Readonly User' to all models
parent
b70360140d
commit
caf810e0ee
|
@ -11,25 +11,56 @@ class IrModelAccess(models.Model):
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
@tools.ormcache_context(
|
@tools.ormcache_context(
|
||||||
"self._uid", "model", "mode", "raise_exception", keys=("lang",)
|
"self.env.uid",
|
||||||
|
"self.env.su",
|
||||||
|
"model",
|
||||||
|
"mode",
|
||||||
|
"raise_exception",
|
||||||
|
keys=("lang",),
|
||||||
)
|
)
|
||||||
def check(self, model, mode="read", raise_exception=True):
|
def check(self, model, mode="read", raise_exception=True):
|
||||||
if self.env.su:
|
if self.env.su:
|
||||||
return True
|
return True
|
||||||
res = super().check(model, mode, raise_exception)
|
res = super().check(model, mode, raise_exception)
|
||||||
self._cr.execute(
|
if mode != "read" and raise_exception:
|
||||||
"SELECT restrict_update FROM ir_model WHERE model = %s", (model,)
|
if self._test_readonly(model) or self._test_restrict_update(model):
|
||||||
)
|
|
||||||
query_res = self._cr.dictfetchone()
|
|
||||||
if (
|
|
||||||
query_res["restrict_update"]
|
|
||||||
and mode != "read"
|
|
||||||
and not self.env.user.unrestrict_model_update
|
|
||||||
):
|
|
||||||
if raise_exception:
|
|
||||||
raise AccessError(
|
raise AccessError(
|
||||||
_("You are only allowed to read this record. (%s - %s)")
|
_("You are only allowed to read this record. (%s - %s)")
|
||||||
% (model, mode)
|
% (model, mode)
|
||||||
)
|
)
|
||||||
return False
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _test_readonly(self, model):
|
||||||
|
exclude_models = self._readonly_exclude_models()
|
||||||
|
if model not in exclude_models and self.env.user.is_readonly_user:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _test_restrict_update(self, model):
|
||||||
|
self.env.cr.execute(
|
||||||
|
"SELECT restrict_update FROM ir_model WHERE model = %s", (model,)
|
||||||
|
)
|
||||||
|
query_res = self.env.cr.dictfetchone()
|
||||||
|
if query_res["restrict_update"] and not self.env.user.unrestrict_model_update:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _readonly_exclude_models(self):
|
||||||
|
""" Models updtate/create by system, and should be excluded from checking """
|
||||||
|
return (
|
||||||
|
self.sudo()
|
||||||
|
.search(
|
||||||
|
[
|
||||||
|
("group_id", "=", False),
|
||||||
|
"|",
|
||||||
|
("perm_write", "=", True),
|
||||||
|
"|",
|
||||||
|
("perm_create", "=", True),
|
||||||
|
("perm_unlink", "=", True),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
.mapped("model_id.model")
|
||||||
|
)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
# Copyright 2021 Quartile Limited
|
# Copyright 2021 Quartile Limited
|
||||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
from odoo import fields, models
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class ResUsers(models.Model):
|
class ResUsers(models.Model):
|
||||||
|
@ -11,7 +12,21 @@ class ResUsers(models.Model):
|
||||||
"Unrestrict Model Update",
|
"Unrestrict Model Update",
|
||||||
help="Set to true and the user can update restricted model.",
|
help="Set to true and the user can update restricted model.",
|
||||||
)
|
)
|
||||||
|
is_readonly_user = fields.Boolean(
|
||||||
|
"Ready User",
|
||||||
|
help="Set to true and the user are readonly user on all models",
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.constrains("is_readonly_user")
|
||||||
|
def _check_is_readonly_user(self):
|
||||||
|
for user in self:
|
||||||
|
if self.env.ref("base.group_system") in user.groups_id:
|
||||||
|
raise UserError(_("You cannot set admin user as a readonly user."))
|
||||||
|
|
||||||
def toggle_unrestrict_model_update(self):
|
def toggle_unrestrict_model_update(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.unrestrict_model_update = not record.unrestrict_model_update
|
record.unrestrict_model_update = not record.unrestrict_model_update
|
||||||
|
|
||||||
|
def toggle_is_readonly_user(self):
|
||||||
|
for record in self:
|
||||||
|
record.is_readonly_user = not record.is_readonly_user
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
Enable the "Update Restrict Model" of specific model to restrict update from unpermitted users.
|
Enable the "Update Restrict Model" of specific model to restrict update from unpermitted users.
|
||||||
To set an user as a permitted user to update restricted model(s), click on "Grant Update Permit" in user form.
|
To set a user as a permitted user to update restricted model(s), click on "Grant Update Permit" in user form.
|
||||||
|
Optionally, to set a user as readonly user to all models, click on "Readonly User" in user form.
|
||||||
|
|
|
@ -33,24 +33,32 @@ class TestBaseModelRestrictUpdate(SavepointCase):
|
||||||
|
|
||||||
def test_01_create_partner(self):
|
def test_01_create_partner(self):
|
||||||
with self.assertRaises(AccessError):
|
with self.assertRaises(AccessError):
|
||||||
self.env["res.partner"].sudo(self.restrict_test_user.id).create(
|
self.env["res.partner"].with_user(self.restrict_test_user.id).create(
|
||||||
{"name": "Test Partner"}
|
{"name": "Test Partner"}
|
||||||
)
|
)
|
||||||
self.env["res.partner"].sudo(self.permit_test_user.id).create(
|
self.env["res.partner"].with_user(self.permit_test_user.id).create(
|
||||||
{"name": "Test Partner"}
|
{"name": "Test Partner"}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_02_update_partner(self):
|
def test_02_update_partner(self):
|
||||||
with self.assertRaises(AccessError):
|
with self.assertRaises(AccessError):
|
||||||
self.test_partner.sudo(self.restrict_test_user.id).update(
|
self.test_partner.with_user(self.restrict_test_user.id).update(
|
||||||
{"name": "Test Partner 2"}
|
{"name": "Test Partner 2"}
|
||||||
)
|
)
|
||||||
self.test_partner.sudo(self.permit_test_user.id).update(
|
self.test_partner.with_user(self.permit_test_user.id).update(
|
||||||
{"name": "Test Partner 2"}
|
{"name": "Test Partner 2"}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_03_unlink_partner(self):
|
def test_03_unlink_partner(self):
|
||||||
test_partner = self.test_partner.sudo().copy()
|
test_partner = self.test_partner.sudo().copy()
|
||||||
with self.assertRaises(AccessError):
|
with self.assertRaises(AccessError):
|
||||||
test_partner.sudo(self.restrict_test_user.id).unlink()
|
test_partner.with_user(self.restrict_test_user.id).unlink()
|
||||||
test_partner.sudo(self.permit_test_user.id).unlink()
|
test_partner.with_user(self.permit_test_user.id).unlink()
|
||||||
|
|
||||||
|
def test_04_readonly_user_update_partner(self):
|
||||||
|
self.permit_test_user.toggle_is_readonly_user()
|
||||||
|
self.assertTrue(self.permit_test_user.is_readonly_user)
|
||||||
|
with self.assertRaises(AccessError):
|
||||||
|
self.test_partner.with_user(self.permit_test_user.id).update(
|
||||||
|
{"name": "Test Partner 2"}
|
||||||
|
)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
type="object"
|
type="object"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
icon="fa-pencil"
|
icon="fa-pencil"
|
||||||
|
attrs="{'invisible': [('is_readonly_user', '=', True)]}"
|
||||||
>
|
>
|
||||||
<field
|
<field
|
||||||
name="unrestrict_model_update"
|
name="unrestrict_model_update"
|
||||||
|
@ -19,6 +20,18 @@
|
||||||
options='{"terminology": {"string_true": "Update Permit", "string_false": "Unpermitted"}}'
|
options='{"terminology": {"string_true": "Update Permit", "string_false": "Unpermitted"}}'
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
name="toggle_is_readonly_user"
|
||||||
|
type="object"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-ban"
|
||||||
|
>
|
||||||
|
<field
|
||||||
|
name="is_readonly_user"
|
||||||
|
widget="boolean_button"
|
||||||
|
options='{"terminology": {"string_false": "Editable", "string_true": "Readonly"}}'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
Loading…
Reference in New Issue