Files
enterprise-addons/documents/tests/test_documents_ir_actions_server.py
T

203 lines
8.7 KiB
Python

from odoo import Command
from odoo.addons.documents.tests.test_documents_common import TransactionCaseDocuments
from odoo.exceptions import UserError, AccessError
class TestIrActionsServer(TransactionCaseDocuments):
def test_documents_embedded_actions_security(self):
base_document = self.document_txt.with_user(self.internal_user).copy()
source_folder = self.folder_a
no_embeds_folder1 = self.folder_b
no_embeds_folder2 = self.folder_a_a
target_tag = self.tag_b
source_folder.access_internal = "edit"
no_embeds_folder1.access_internal = "edit"
no_embeds_folder2.access_internal = "view"
def get_add_tag_action(seq=5):
return self.env["ir.actions.server"].create({
"name": "Add Tag",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"state": "object_write",
"update_path": "tag_ids",
"update_m2m_operation": "add",
"resource_ref": f"documents.tag,{target_tag.id}",
"usage": "documents_embedded",
"sequence": seq,
})
def get_move_action(folder, seq=5):
return self.env["ir.actions.server"].create({
"name": f"Move to {folder.name}",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"state": "object_write",
"update_path": "folder_id",
"resource_ref": f"documents.document,{folder.id}",
"usage": "documents_embedded",
"sequence": seq,
})
def get_code_move_action(folder, seq=5):
return self.env["ir.actions.server"].create({
"name": f"Code Move to {folder.name}",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"state": "code",
"code": f"record.write({{'folder_id': {folder.id}}})",
"usage": "documents_embedded",
"sequence": seq,
})
def run_scenario(child_ids, start_folder=source_folder, embed_multi_on=None, expect_failure=False):
folders_to_embed = [start_folder] if embed_multi_on is None else embed_multi_on
doc = base_document.with_user(self.internal_user).copy()
doc.folder_id = start_folder
doc.tag_ids = [Command.clear()]
parent_action = self.env["ir.actions.server"].create({
"name": "Multi Wrapper",
"state": "multi",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"child_ids": child_ids,
"usage": "documents_embedded",
})
for folder in folders_to_embed:
folder._embed_action(parent_action.id)
if expect_failure:
with self.assertRaises(UserError) as err:
parent_action.with_user(self.internal_user).with_context(
active_model="documents.document", active_id=doc.id
).run()
self.assertEqual(err.exception.args[0], "This action was not made available on the containing folder.")
else:
parent_action.with_user(self.internal_user).with_context(
active_model="documents.document", active_id=doc.id
).run()
self.assertIn(target_tag, doc.tag_ids)
with self.subTest("Standalone Action - Fail if not pinned"):
doc = base_document.with_user(self.internal_user).copy()
doc.folder_id = no_embeds_folder1
with self.assertRaises(UserError) as err:
get_add_tag_action().with_user(self.internal_user).with_context(
active_model="documents.document", active_id=doc.id
).run()
self.assertEqual(err.exception.args[0], "This action was not made available on the containing folder.")
with self.subTest("Standalone Action - User cannot bypass security by sending the context key via RPC."):
doc = base_document.with_user(self.internal_user).copy()
doc.folder_id = no_embeds_folder1
with self.assertRaises(UserError) as err:
get_add_tag_action().with_user(self.internal_user).with_context(
active_model="documents.document",
active_id=doc.id,
__documents_embedded_checked=True,
).run()
self.assertEqual(err.exception.args[0], "This action was not made available on the containing folder.")
with self.subTest("Multi Action [Tag -> Move] - Fail if not pinned on start"):
run_scenario(
[
get_add_tag_action(seq=5).id,
get_move_action(no_embeds_folder1, seq=10).id,
],
embed_multi_on=[],
expect_failure=True,
)
with self.subTest("Multi Action [Tag -> Move] - Success"):
run_scenario([
get_add_tag_action(seq=5).id,
get_move_action(no_embeds_folder1, seq=10).id,
])
with self.subTest("Multi Action [Tag] - Success"):
run_scenario([
get_add_tag_action().id,
])
with self.subTest("Multi Action [Move -> Tag] - Success"):
run_scenario([
get_move_action(no_embeds_folder1).id,
get_add_tag_action(seq=10).id,
])
with self.subTest("Multi Action [Code Move -> Tag] - Success"):
# Prevent regressions for solutions that'd rely only on "state": "object_write"
run_scenario([
get_code_move_action(no_embeds_folder1).id,
get_add_tag_action(seq=10).id,
])
with self.subTest("Multi Action [Move -> Move -> Tag -> Move] - Success"):
run_scenario([
get_move_action(no_embeds_folder1, seq=1).id,
get_move_action(no_embeds_folder2).id,
get_add_tag_action(seq=10).id,
get_move_action(no_embeds_folder1, seq=15).id,
])
nested_tag_action = self.env["ir.actions.server"].create({
"name": "Nested Wrapper",
"state": "multi",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"child_ids": [get_add_tag_action().id],
})
with self.subTest("Deep Nested: Fail if Root is not pinned (even if move exists)"):
run_scenario(
[
get_move_action(no_embeds_folder1).id,
nested_tag_action.id,
],
embed_multi_on=[],
expect_failure=True,
)
with self.subTest("Deep Nested: Success if Root is pinned"):
run_scenario([
get_move_action(no_embeds_folder1).id,
nested_tag_action.id,
])
with self.subTest("We can move the documents even if we have no write access on the destination folder"):
doc = base_document.with_user(self.internal_user).copy()
with self.assertRaises(AccessError):
doc.folder_id = no_embeds_folder2
run_scenario([
get_move_action(no_embeds_folder2).id,
get_add_tag_action(seq=10).id,
])
with self.subTest("Running child action individually fails if Root is not pinned."):
doc = base_document.with_user(self.internal_user).copy()
move_action = get_move_action(no_embeds_folder1, seq=1)
tag_action = get_add_tag_action()
parent_action = self.env["ir.actions.server"].create({
"name": "Multi Wrapper",
"state": "multi",
"model_id": self.env["ir.model"]._get_id("documents.document"),
"child_ids": [move_action.id, tag_action.id],
})
# Set location of doc to destination of move to prevent regressions
# Ex: this test would fail if embed bypass would allow unpinned actions on destinations move actions
doc.folder_id = no_embeds_folder1
with self.assertRaises(UserError) as err:
tag_action.with_user(self.internal_user).with_context(
active_model="documents.document", active_id=doc.id
).run()
self.assertEqual(err.exception.args[0], "This action was not made available on the containing folder.")
no_embeds_folder1._embed_action(parent_action.id)
tag_action.with_user(self.internal_user).with_context(
active_model="documents.document", active_id=doc.id
).run()
self.assertIn(target_tag, doc.tag_ids)