Files
odoo-addons/addons/cetmix_tower_server/tests/test_jet_dependency_access.py

421 lines
16 KiB
Python

# Copyright (C) 2025 Cetmix OÜ
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.exceptions import AccessError
from .common_jets import TestTowerJetsCommon
class TestTowerJetDependencyAccess(TestTowerJetsCommon):
"""
Test access rules for Jet Dependency model
"""
# ======================
# Manager Read Access Tests
# ======================
def test_manager_read_access_both_user_ids(self):
"""Test Manager: Read when in user_ids of both jets"""
_, _, dependency = self._create_jet_dependency(
"Jet 1",
"jet_1",
"Jet 2",
"jet_2",
jet_user_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
1,
"Manager should read when in user_ids of both jets",
)
self.assertIn(
dependency,
records,
"Manager should get exactly the dependency record we searched for",
)
def test_manager_read_access_both_manager_ids(self):
"""Test Manager: Read when in manager_ids of both jets"""
_, _, dependency = self._create_jet_dependency(
"Jet Manager 1",
"jet_manager_1",
"Jet Manager 2",
"jet_manager_2",
jet_manager_ids=[(4, self.manager.id)],
depends_on_manager_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
1,
"Manager should read when in manager_ids of both jets",
)
self.assertIn(
dependency,
records,
"Manager should get exactly the dependency record we searched for",
)
def test_manager_read_access_jet_user_depends_manager(self):
"""Test Manager: Read when in user_ids of jet and manager_ids of depends"""
_, _, dependency = self._create_jet_dependency(
"Jet User",
"jet_user",
"Depends Manager",
"depends_manager",
jet_user_ids=[(4, self.manager.id)],
depends_on_manager_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
1,
"Manager should read when in user_ids of jet and manager_ids of depends",
)
self.assertIn(
dependency,
records,
"Manager should get exactly the dependency record we searched for",
)
def test_manager_read_access_jet_manager_depends_user(self):
"""Test Manager: Read when in manager_ids of jet and user_ids of depends"""
_, _, dependency = self._create_jet_dependency(
"Jet Manager",
"jet_manager",
"Depends User",
"depends_user",
jet_manager_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
1,
"Manager should read when in manager_ids of jet and user_ids of depends",
)
self.assertIn(
dependency,
records,
"Manager should get exactly the dependency record we searched for",
)
def test_manager_read_no_access_jet_only(self):
"""Test Manager: No read when in jet but NOT in depends on jet"""
_, _, dependency = self._create_jet_dependency(
"Jet Has Access",
"jet_has_access",
"Depends No Access",
"depends_no_access",
jet_user_ids=[(4, self.manager.id)],
depends_on_user_ids=[(5, 0, 0)],
depends_on_manager_ids=[(5, 0, 0)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
0,
"Manager should not read when not in depends_on"
" jet user_ids or manager_ids",
)
def test_manager_read_no_access_depends_only(self):
"""Test Manager: No read when in depends on jet but NOT in jet"""
_, _, dependency = self._create_jet_dependency(
"Jet No Access",
"jet_no_access",
"Depends Has Access",
"depends_has_access",
jet_user_ids=[(5, 0, 0)],
jet_manager_ids=[(5, 0, 0)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
records = self.JetDependency.with_user(self.manager).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
0,
"Manager should not read when not in jet user_ids or manager_ids",
)
# ======================
# Manager CRUD Access Tests
# ======================
def test_manager_write_access(self):
"""
Test Manager:
Write access when in manager_ids of jet AND user_ids
or manager_ids of depends.
"""
# Test with depends_on user_ids (same conditions as create test,
# but tests write access on existing record)
_, _, dependency1 = self._create_jet_dependency(
"Write Jet Manager",
"write_jet_manager",
"Depends User",
"depends_user",
jet_manager_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
# Verify manager can access the dependency (write permissions allow read access)
try:
dependency1.invalidate_recordset()
dependency1.with_user(self.manager).read(["jet_id", "jet_depends_on_id"])
# Perform an actual write: switch to an alternative valid depends_on jet
depends_on_jet_alt = self._create_jet(
"Depends User Alt",
"depends_user_alt",
template=self.jet_template_tower_core,
user_ids=[(4, self.manager.id)],
server_user_ids=[(4, self.manager.id)],
)
dependency1.with_user(self.manager).write(
{"jet_depends_on_id": depends_on_jet_alt.id}
)
except AccessError:
self.fail(
"Manager should be able to write when in jet manager_ids "
"AND depends_on user_ids"
)
# Test with depends_on manager_ids - use different templates
# to avoid duplicate template dependency
_, _, dependency2 = self._create_jet_dependency(
"Write Jet Manager 2",
"write_jet_manager_2",
"Depends Manager",
"depends_manager",
jet_manager_ids=[(4, self.manager.id)],
depends_on_manager_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
jet_template=self.jet_template_nginx,
# Use different template to avoid duplicate
depends_on_template=self.jet_template_docker,
)
try:
dependency2.invalidate_recordset()
dependency2.with_user(self.manager).read(["jet_id", "jet_depends_on_id"])
# Perform an actual write: switch to an alternative valid depends_on jet
depends_on_jet_alt2 = self._create_jet(
"Depends Manager Alt",
"depends_manager_alt",
template=self.jet_template_docker,
manager_ids=[(4, self.manager.id)],
server_user_ids=[(4, self.manager.id)],
)
dependency2.with_user(self.manager).write(
{"jet_depends_on_id": depends_on_jet_alt2.id}
)
except AccessError:
self.fail(
"Manager should be able to write when in jet manager_ids"
" AND depends_on manager_ids"
)
def test_manager_create_access(self):
"""
Test Manager: Create when in manager_ids of jet AND user_ids
or manager_ids of depends.
"""
# Try to create dependency as manager
# (helper ensures proper template dependency)
try:
_, _, dependency = self._create_jet_dependency(
"Create Jet",
"create_jet",
"Create Depends",
"create_depends",
jet_manager_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
with_user=self.manager,
jet_template=self.jet_template_test,
depends_on_template=self.jet_template_tower_core,
)
records = self.JetDependency.search([("id", "=", dependency.id)])
self.assertIn(
dependency,
records,
"Manager should be able to create dependency",
)
except AccessError:
self.fail("Manager should be able to create when in jet manager_ids")
def test_manager_create_forbidden_not_in_jet_manager_ids(self):
"""Test Manager: Cannot create when not in jet manager_ids"""
# Should not be able to create (manager not in jet manager_ids)
self.assertRaises(
AccessError,
lambda: self._create_jet_dependency(
"No Create Jet",
"no_create_jet",
"No Create Depends",
"no_create_depends",
jet_user_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
with_user=self.manager,
jet_template=self.jet_template_test,
depends_on_template=self.jet_template_tower_core,
),
)
def test_manager_create_forbidden_not_in_depends(self):
"""
Test Manager: Cannot create when in jet manager_ids but NOT in depends.
"""
# Should not be able to create (manager has no access to depends)
self.assertRaises(
AccessError,
lambda: self._create_jet_dependency(
"Create Jet",
"create_jet",
"No Depends Access",
"no_depends_access",
jet_manager_ids=[(4, self.manager.id)],
depends_on_user_ids=[(5, 0, 0)],
depends_on_manager_ids=[(5, 0, 0)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
with_user=self.manager,
jet_template=self.jet_template_test,
depends_on_template=self.jet_template_tower_core,
),
)
def test_manager_unlink_access(self):
"""
Test Manager: Delete when in manager_ids of jet AND user_ids
or manager_ids of depends.
"""
_, _, dependency = self._create_jet_dependency(
"Delete Jet",
"delete_jet",
"Delete Depends",
"delete_depends",
jet_manager_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
with_user=self.manager,
)
# Refresh dependency in manager context to ensure access
dependency.invalidate_recordset()
dependency = dependency.with_user(self.manager)
try:
dependency.unlink()
records = self.JetDependency.search([("id", "=", dependency.id)])
self.assertEqual(
len(records),
0,
"Manager should be able to delete dependency",
)
except AccessError:
self.fail("Manager should be able to delete dependency")
def test_manager_unlink_forbidden_not_in_jet_manager_ids(self):
"""Test Manager: Cannot delete when not in jet manager_ids"""
_, _, dependency = self._create_jet_dependency(
"No Delete Jet",
"no_delete_jet",
"No Delete Depends",
"no_delete_depends",
jet_user_ids=[(4, self.manager.id)],
depends_on_user_ids=[(4, self.manager.id)],
jet_server_user_ids=[(4, self.manager.id)],
depends_on_server_user_ids=[(4, self.manager.id)],
)
self.assertRaises(AccessError, dependency.with_user(self.manager).unlink)
# ======================
# Root Access Tests
# ======================
def test_root_full_access(self):
"""Test Root: Full CRUD access regardless of access restrictions"""
# Root can create dependency via helper regardless of access
_, _, dependency = self._create_jet_dependency(
"Root Jet",
"root_jet",
"Root Depends",
"root_depends",
jet_user_ids=[(5, 0, 0)],
jet_manager_ids=[(5, 0, 0)],
depends_on_user_ids=[(5, 0, 0)],
depends_on_manager_ids=[(5, 0, 0)],
with_user=self.root,
jet_template=self.jet_template_test,
depends_on_template=self.jet_template_tower_core,
)
# Root can read
records = self.JetDependency.with_user(self.root).search(
[("id", "=", dependency.id)]
)
self.assertIn(dependency, records, "Root should be able to read")
# Root can write: switch depends_on to another valid jet
depends_on_jet_alt = self._create_jet(
"Root Depends Alt",
"root_depends_alt",
template=self.jet_template_tower_core,
)
dependency.invalidate_recordset()
dependency.with_user(self.root).write(
{"jet_depends_on_id": depends_on_jet_alt.id}
)
# Root can delete
dependency.with_user(self.root).unlink()
records = self.JetDependency.with_user(self.root).search(
[("id", "=", dependency.id)]
)
self.assertEqual(
len(records),
0,
"Root should be able to delete dependency",
)