Skip to content

Commit

Permalink
⬆️ access_restricted: port to 17 + doc clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
yelizariev committed Dec 15, 2023
1 parent 0d4cac9 commit 4870685
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 37 deletions.
16 changes: 12 additions & 4 deletions access_restricted/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@ Restricted administration rights
================================

The module makes impossible for administrator to set (and see) more access rights (groups) than he already has.
The only partial exception of this rule is made if you are already a member of the 'Allow add implied groups from settings' security group.
The only partial exception of this rule is made if you are already a member of the *Allow add implied groups from settings* security group.
Then you are allowed to escalate your privileges but just from ``Settings`` menus (by means of ``group_XXX`` boolean fields of ``res.config.settings`` models views).

This doesn't affect superuser, of course.

Typical usage of the module.
----------------------------
Typical usage of the module
===========================

The superuser creates an administrator user without access group "Show Apps Menu" (see **access_apps** module). Then the administrator has access to settings, but not able to install new apps (without this module he can add himself to "Show Apps Menu" and get access to apps).

Tested on `Odoo 14.0 <https://github.com/odoo/odoo/commit/c16d4b5e7b9181c2c792f595a117de10510d45be>`_
Roadmap
=======

* Settings menu shows group fields are not updated without *Allow add implied groups from settings* (ok), but it shows the fields as not editable (not ok)

Further information
===================

Tested on `Odoo 16.0 <https://github.com/odoo/odoo/commit/40b19d89846303016098840f4958fe7cc105067c>`_
15 changes: 9 additions & 6 deletions access_restricted/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ Installation
Configuration
=============

* By default all users except a superuser restricted to escalate the privileges
* There is only one configuration option this module provides. Under superuser open menu ``[[ Settings ]] >> Users & Companies >> Users``
* In ``Access Rights`` tab you can select 'Allow add implied groups from settings' -
to allow some users to configure modules by means of ``group_XXX`` fields from ``Settings`` menus

* `Log in as SUPERUSER <https://odoo-development.readthedocs.io/en/latest/odoo/usage/login-as-superuser.html>`__
* Navigate to menu ``[[ Settings ]] >> Users & Companies >> Users``
* In ``Access Rights`` tab you can select *Allow add implied groups from settings*:
it allows a user to configure groups via ``group_XXX`` fields from ``Settings`` menu.

Usage
=====

By default all users except a superuser restricted to escalate the privileges.

Let's take ``Sales (sale_management)`` module as an example.

Without this module installed:
Expand All @@ -30,5 +33,5 @@ With this module installed:

* The user from previous example cannot increase his privileges. There is no ``Sales: Manager`` option for him, and also no ``Customer Addresses``
option in module configuration
* The only exception is done for users who are in special group 'Allow add implied groups from settings' - if your user is included in this group by the superuser then you may select
``Units of Measure`` from ``Sale`` module ``Configuration >> Settings`` menu
* The only exception is done for users who are in special group *Allow add implied groups from settings*: if your user is included in this group by the superuser then you may select
``Customer Addresses`` from ``Sale`` module ``Configuration >> Settings`` menu
4 changes: 3 additions & 1 deletion access_restricted/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ class ResConfigSettings(models.TransientModel):
def _get_classified_fields(self, fnames=None):
uid = self.env.uid
classified = super(ResConfigSettings, self)._get_classified_fields(fnames)
if uid == SUPERUSER_ID:
config = self.env.context.get("config")
is_execute_stage = config and isinstance(config, models.Model)
if uid == SUPERUSER_ID or is_execute_stage:
return classified

group = []
Expand Down
6 changes: 4 additions & 2 deletions access_restricted/models/test_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class TestConfigSettings(models.TransientModel):
_description = "Test config settings"
_inherit = ["res.config.settings"]

group_private_addresses = fields.Boolean(
group="base.group_system", implied_group="base.group_private_addresses"
group_test_access_restricted = fields.Boolean(
group="base.group_system",
# random group for test purposes
implied_group="base.group_multi_currency"
)
44 changes: 24 additions & 20 deletions access_restricted/tests/test_allow_implied.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
from odoo.exceptions import AccessError
from odoo.tests.common import TransactionCase, tagged
from odoo.tests.common import TransactionCase


GROUP = "base.group_multi_currency"


@tagged("at_install", "post_install")
class TestAllowImplied(TransactionCase):
def _get_classified_groups(self, config):
groups = config._get_classified_fields()["group"]
return [g[0] for g in groups]

def setUp(self):
super().setUp()
self.group = self.env.ref(GROUP)

def test_base(self):
demo_user = self.env.ref("base.user_demo")

group_system = self.env.ref("base.group_system")
group_private_addresses = self.env.ref("base.group_private_addresses")

demo_user.write({"groups_id": [(3, group_private_addresses.id)]})
group_private_addresses.write({"users": [(3, demo_user.id)]})
demo_user.write({"groups_id": [(3, self.group.id)]})
self.group.write({"users": [(3, demo_user.id)]})
self.assertFalse(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)

demo_user.write({"groups_id": [(4, group_system.id)]})

test_config_settings = (
self.env["res.config.settings"]
.with_user(demo_user.id)
.create({"group_private_addresses": True})
.create({"group_test_access_restricted": True})
)
self.assertFalse(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)

# check that the field is readonly
self.assertTrue(
test_config_settings.fields_get()["group_private_addresses"]["readonly"]
test_config_settings.fields_get()["group_test_access_restricted"]["readonly"]
)
# check that test group hasn't got appended to classified
self.assertNotIn(
"group_private_addresses", self._get_classified_groups(test_config_settings)
"self.group", self._get_classified_groups(test_config_settings)
)

group_allow = self.env.ref(
Expand All @@ -51,48 +56,47 @@ def test_base(self):

# check that now the field is not readonly
self.assertFalse(
test_config_settings.fields_get()["group_private_addresses"]["readonly"]
test_config_settings.fields_get()["group_test_access_restricted"]["readonly"]
)
# check that now the group is in classified
self.assertIn(
"group_private_addresses", self._get_classified_groups(test_config_settings)
"group_test_access_restricted", self._get_classified_groups(test_config_settings)
)

self.assertFalse(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)
test_config_settings.with_user(demo_user.id).execute()
self.assertTrue(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)

def test_assert_raises(self):

demo_user = self.env.ref("base.user_demo")
group_system = self.env.ref("base.group_system")
group_private_addresses = self.env.ref("base.group_private_addresses")

demo_user.write({"groups_id": [(3, group_private_addresses.id)]})
group_private_addresses.write({"users": [(3, demo_user.id)]})
demo_user.write({"groups_id": [(3, self.group.id)]})
self.group.write({"users": [(3, demo_user.id)]})
self.assertFalse(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)

demo_user.write({"groups_id": [(4, group_system.id)]})
self.assertFalse(
self.env["res.users"]
.with_user(demo_user.id)
.has_group("base.group_private_addresses")
.has_group(GROUP)
)

# check that there is no access to put test group into implied_ids anyways
with self.assertRaises(AccessError):
group_system.with_user(demo_user.id).write(
{"implied_ids": [(4, group_private_addresses.id)]}
{"implied_ids": [(4, self.group.id)]}
)
5 changes: 2 additions & 3 deletions access_restricted/tests/test_fields_get.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from odoo.tests.common import TransactionCase, tagged
from odoo.tests.common import TransactionCase

from odoo.addons.base.models.res_users import name_selection_groups


@tagged("at_install", "post_install")
class TestFieldsGet(TransactionCase):
def test_base(self):
demo_user = self.env.ref("base.user_demo")
Expand All @@ -19,7 +18,7 @@ def test_base(self):
self.env["res.users"]
.with_user(demo_user)
.with_context({"uid": demo_user.id})
.load_views([[view_users_form.id, "form"]])
.get_views([[view_users_form.id, "form"]])
)

sel_groups = name_selection_groups([group_erp_manager.id])
Expand Down
2 changes: 1 addition & 1 deletion access_restricted/tests/test_fields_view_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def add_access(self, user, group_xmlid):
def _view_form(self, user, view_xmlid):
view_id = self.env.ref(view_xmlid).id
# context = {'lang': "en_US", 'tz': "Europe/Brussels", 'uid': user.id}
self.env["res.users"].with_user(user.id).fields_view_get(view_id=view_id)
self.env["res.users"].with_user(user.id).get_view(view_id=view_id)

def view_preference_form(self, user):
self._view_form(user, "base.view_users_form_simple_modif")
Expand Down

0 comments on commit 4870685

Please sign in to comment.