Description of the issue:
=========================
Suppose we have 3 articles with the following structure:
root
L child
L grand child
When granting the "write" permission to a user in "root", this user
will inherit this permission in "child" and in "grand child".
When we then restrict this user with the "no access" permission in
child, this permission is inherited by grand-child (correct).
Then, when a member is invited in the root article, the right is not
herited by "child" nor "grand child" (correct). However, this
permission is shown as being inherited from "root" in the permission
panel in "grand child" (incorrect: "grand child" does not have this
permission, and is in fact inherited from "child" as expected).
Solution:
=========
Add the boolean "article_is_desynchronized" check in the inner
join condition of the recursive SQL request used to fetch the
permissions of an article and its parents.
This boolean check allows to prune the recursion when an article
is desynchronized, so that the permissions of the parents of this
desynchronized article are not fetched, since they are not
inherited in the desynchronized articles nor in its children.
Some tests have been added to ensure the correct behaviour of the
share panel. Especially, the scenario described in the description of
the issue is tested.
Task-2866950
closes odoo/odoo#94834
X-original-commit: 252fce15f20a13ab3e71f7aafc247b5ac88a354a
Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
495 lines
27 KiB
Python
495 lines
27 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import exceptions
|
|
from odoo.addons.knowledge.tests.common import KnowledgeArticlePermissionsCase
|
|
from odoo.tests.common import tagged, users
|
|
from odoo.tools import mute_logger
|
|
|
|
|
|
@tagged('knowledge_acl')
|
|
class TestKnowledgeArticlePermissions(KnowledgeArticlePermissionsCase):
|
|
|
|
@users('employee')
|
|
def test_article_main_parent(self):
|
|
""" Test root article computation """
|
|
article_roots = self.article_roots.with_env(self.env)
|
|
|
|
articles_write = (self.article_write_contents + self.article_write_contents_children).with_env(self.env)
|
|
self.assertEqual(articles_write.root_article_id, article_roots[0])
|
|
|
|
articles_write = self.article_read_contents.with_env(self.env)
|
|
self.assertEqual(articles_write.root_article_id, article_roots[1])
|
|
|
|
# desynchronized still have a root (do as sudo)
|
|
self.assertEqual(self.article_write_desync.root_article_id, article_roots[0])
|
|
self.assertEqual(self.article_read_desync.root_article_id, article_roots[1])
|
|
|
|
def test_article_permissions_desync(self):
|
|
""" Test computed fields based on permissions (independently from ACLs
|
|
aka not user_permission, ...). Main use cases: desynchronized articles
|
|
or articles without parents. """
|
|
for (exp_inherited_permission,
|
|
exp_inherited_permission_parent_id,
|
|
exp_internal_permission
|
|
), article in zip(
|
|
[('read', self.env['knowledge.article'], 'read'),
|
|
('read', self.article_write_desync[0], False),
|
|
('none', self.env['knowledge.article'], 'none'),
|
|
('none', self.article_read_desync[0], False),
|
|
('write', self.env['knowledge.article'], 'write'),
|
|
('read', self.env['knowledge.article'], 'read'),
|
|
],
|
|
self.article_write_desync + self.article_read_desync + self.article_roots
|
|
):
|
|
self.assertEqual(article.inherited_permission, exp_inherited_permission,
|
|
f'Permission: wrong inherit computation for {article.name}: {article.inherited_permission} instead of {exp_inherited_permission}')
|
|
self.assertEqual(article.inherited_permission_parent_id, exp_inherited_permission_parent_id,
|
|
f'Permission: wrong inherit computation for {article.name}: {article.inherited_permission_parent_id.name} instead of {exp_inherited_permission_parent_id.name}')
|
|
self.assertEqual(article.internal_permission, exp_internal_permission,
|
|
f'Permission: wrong inherit computation for {article.name}: {article.internal_permission} instead of {exp_internal_permission}')
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule')
|
|
def test_article_permissions_inheritance_desync(self):
|
|
""" Test desynchronize (and therefore member propagation that should be
|
|
stopped). """
|
|
article_desync = self.article_write_desync[0]
|
|
self.assertMembers(article_desync, 'read', {self.partner_employee_manager: 'write'})
|
|
|
|
# as employee w write perms
|
|
article_desync = article_desync.with_user(self.user_employee_manager)
|
|
self.assertTrue(article_desync.user_has_write_access)
|
|
self.assertTrue(article_desync.user_has_access)
|
|
|
|
# as employee
|
|
article_desync = article_desync.with_user(self.user_employee)
|
|
self.assertFalse(article_desync.user_has_write_access)
|
|
self.assertTrue(article_desync.user_has_access)
|
|
|
|
# as portal
|
|
article_desync = article_desync.with_user(self.user_portal)
|
|
self.assertFalse(article_desync.user_has_write_access)
|
|
self.assertFalse(article_desync.user_has_access, 'Permissions: member rights should not be fetch on parents')
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule')
|
|
@users('employee')
|
|
def test_article_permissions_inheritance_employee(self):
|
|
article_roots = self.article_roots.with_env(self.env)
|
|
|
|
# roots: based on internal permissions
|
|
self.assertEqual(article_roots.mapped('user_has_write_access'), [True, False, False, True])
|
|
self.assertEqual(article_roots.mapped('user_has_access'), [True, True, True, True])
|
|
self.assertEqual(article_roots.mapped('user_permission'), ['write', 'read', 'read', 'write'])
|
|
|
|
# write permission from ancestors
|
|
article_write_ancestor = self.article_write_contents[2].with_env(self.env)
|
|
self.assertEqual(article_write_ancestor.inherited_permission, 'write')
|
|
self.assertEqual(article_write_ancestor.inherited_permission_parent_id, self.article_roots[0])
|
|
self.assertFalse(article_write_ancestor.internal_permission)
|
|
self.assertEqual(article_write_ancestor.user_permission, 'write')
|
|
|
|
# write permission from ancestors overridden by internal permission
|
|
article_read_forced = self.article_write_contents[1].with_env(self.env)
|
|
self.assertEqual(article_read_forced.inherited_permission, 'read')
|
|
self.assertFalse(article_read_forced.inherited_permission_parent_id)
|
|
self.assertEqual(article_read_forced.internal_permission, 'read')
|
|
self.assertEqual(article_read_forced.user_permission, 'read')
|
|
|
|
# write permission from ancestors overridden by member permission
|
|
article_read_member = self.article_write_contents[0].with_env(self.env)
|
|
self.assertEqual(article_read_member.inherited_permission, 'write')
|
|
self.assertEqual(article_read_member.inherited_permission_parent_id, self.article_roots[0])
|
|
self.assertFalse(article_read_member.internal_permission)
|
|
self.assertEqual(article_read_member.user_permission, 'read')
|
|
|
|
# forced lower than base article perm (see 'Community Paranoïa')
|
|
article_lower = self.article_read_contents[1].with_env(self.env)
|
|
self.assertEqual(article_lower.inherited_permission, 'write')
|
|
self.assertFalse(article_lower.inherited_permission_parent_id)
|
|
self.assertEqual(article_lower.internal_permission, 'write')
|
|
self.assertEqual(article_lower.user_permission, 'read')
|
|
|
|
# read permission from ancestors
|
|
article_read_ancestor = self.article_read_contents[2].with_env(self.env)
|
|
self.assertEqual(article_read_ancestor.inherited_permission, 'read')
|
|
self.assertEqual(article_read_ancestor.inherited_permission_parent_id, self.article_roots[1])
|
|
self.assertFalse(article_read_ancestor.internal_permission)
|
|
self.assertEqual(article_read_ancestor.user_permission, 'read')
|
|
|
|
# permission denied
|
|
article_none = self.article_read_contents[3].with_env(self.env)
|
|
with self.assertRaises(exceptions.AccessError):
|
|
article_none.name
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule')
|
|
@users('portal_test')
|
|
def test_article_permissions_inheritance_portal(self):
|
|
article_roots = self.article_roots.with_env(self.env)
|
|
|
|
with self.assertRaises(exceptions.AccessError):
|
|
article_roots.mapped('internal_permission')
|
|
|
|
article_members = self.article_read_contents[0:2].with_env(self.env)
|
|
self.assertEqual(article_members.mapped('inherited_permission'), ['write', 'write']) # TDE: TOCHECK
|
|
self.assertEqual(article_members.mapped('internal_permission'), ['write', 'write']) # TDE: TOCHECK
|
|
self.assertEqual(article_members.mapped('user_has_write_access'), [False, False], 'Portal: can never write')
|
|
self.assertEqual(article_members.mapped('user_has_access'), [True, True], 'Portal: access through membership')
|
|
self.assertEqual(article_members.mapped('user_permission'), ['read', 'read'])
|
|
|
|
@users('employee')
|
|
def test_article_permissions_employee_new_mode(self):
|
|
""" Test transient / cache mode: computed fields without IDs, ... """
|
|
article = self.env['knowledge.article'].new({'name': 'Transient'})
|
|
self.assertFalse(article.inherited_permission)
|
|
self.assertFalse(article.internal_permission)
|
|
self.assertTrue(article.user_has_write_access)
|
|
self.assertTrue(article.user_has_access)
|
|
self.assertEqual(article.user_permission, 'write')
|
|
|
|
|
|
@tagged('knowledge_internals', 'knowledge_management')
|
|
class KnowledgeArticlePermissionsInitialValues(KnowledgeArticlePermissionsCase):
|
|
""" Test initial values or our test data once so that other tests do not have
|
|
to do it. """
|
|
|
|
def test_initial_values(self):
|
|
article_roots = self.article_roots.with_env(self.env)
|
|
article_headers = self.article_headers.with_env(self.env)
|
|
|
|
# roots: defaults on write, inherited = internal
|
|
self.assertEqual(article_roots.mapped('inherited_permission'), ['write', 'read', 'none', 'none'])
|
|
self.assertFalse(article_roots.inherited_permission_parent_id)
|
|
self.assertEqual(article_roots.mapped('internal_permission'), ['write', 'read', 'none', 'none'])
|
|
|
|
# childs: allow void permission, inherited = go up to first defined permission
|
|
self.assertEqual(article_headers.mapped('inherited_permission'), ['write', 'read', 'read'])
|
|
self.assertEqual(
|
|
[p.inherited_permission_parent_id for p in article_headers],
|
|
[article_roots[0], article_roots[1], article_roots[1]]
|
|
)
|
|
self.assertEqual(article_headers.mapped('internal_permission'), [False, False, False])
|
|
|
|
@users('employee')
|
|
def test_initial_values_as_employee(self):
|
|
""" Ensure all tests have the same basis (user specific computed as
|
|
employee for acl-dependent tests) """
|
|
article_write_inherit = self.article_write_contents[2].with_env(self.env)
|
|
|
|
# initial values: write through inheritance
|
|
self.assertMembers(article_write_inherit, False, {self.partner_portal: 'read'})
|
|
self.assertFalse(article_write_inherit.internal_permission)
|
|
self.assertFalse(article_write_inherit.is_desynchronized)
|
|
self.assertTrue(article_write_inherit.user_has_write_access)
|
|
self.assertTrue(article_write_inherit.user_has_access)
|
|
|
|
article_write_inherit_as2 = article_write_inherit.with_user(self.user_employee2)
|
|
self.assertTrue(article_write_inherit_as2.user_has_write_access)
|
|
self.assertTrue(article_write_inherit_as2.user_has_access)
|
|
|
|
|
|
@tagged('knowledge_acl')
|
|
class TestKnowledgeArticlePermissionsTools(KnowledgeArticlePermissionsCase):
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule')
|
|
@users('employee')
|
|
def test_downgrade_internal_permission_none(self):
|
|
writable_as1 = self.article_write_contents[2].with_env(self.env)
|
|
writable_as2 = writable_as1.with_user(self.user_employee2)
|
|
self.assertEqual(writable_as2.user_has_access, True)
|
|
writable_root = self.article_roots[0].with_env(self.env)
|
|
writable_children = self.article_write_contents_children.with_env(self.env)
|
|
for child in writable_children:
|
|
self.assertEqual(child.inherited_permission_parent_id, writable_root)
|
|
|
|
# downgrade write global perm to read
|
|
writable_as1._set_internal_permission('none')
|
|
writable_as1.flush_model() # ACLs are done using SQL
|
|
self.assertMembers(
|
|
writable_as1, 'none',
|
|
{self.partner_portal: 'read', # untouched by downgrade
|
|
self.env.user.partner_id: 'write'},
|
|
'Permission: lowering permission adds current user in members to have write access'
|
|
)
|
|
self.assertTrue(writable_as1.is_desynchronized)
|
|
self.assertTrue(writable_as1.user_has_write_access)
|
|
self.assertTrue(writable_as1.user_has_access)
|
|
|
|
# check internal permission has been lowered
|
|
with self.assertRaises(exceptions.AccessError):
|
|
writable_as2.body # trigger ACLs
|
|
|
|
# check children inherits downgraded permissions from article
|
|
for child in writable_children:
|
|
self.assertEqual(child.inherited_permission, 'none', 'Permission: lowering permission should lower the permission of the children')
|
|
self.assertEqual(child.inherited_permission_parent_id, writable_as1, 'Permission: lowering permission should make the children inherit the permission from this article')
|
|
|
|
@users('employee')
|
|
def test_downgrade_internal_permission_read(self):
|
|
writable_as1 = self.article_write_contents[2].with_env(self.env)
|
|
writable_as2 = writable_as1.with_user(self.user_employee2)
|
|
self.assertEqual(writable_as2.user_has_access, True)
|
|
writable_root = self.article_roots[0].with_env(self.env)
|
|
writable_children = self.article_write_contents_children.with_env(self.env)
|
|
for child in writable_children:
|
|
self.assertEqual(child.inherited_permission_parent_id, writable_root)
|
|
|
|
# downgrade write global perm to read
|
|
writable_as1._set_internal_permission('read')
|
|
writable_as1.flush_model() # ACLs are done using SQL
|
|
self.assertMembers(
|
|
writable_as1, 'read',
|
|
{self.partner_portal: 'read', self.env.user.partner_id: 'write'},
|
|
'Permission: lowering permission adds current user in members to have write access'
|
|
)
|
|
self.assertTrue(writable_as1.is_desynchronized)
|
|
self.assertTrue(writable_as1.user_has_write_access)
|
|
self.assertTrue(writable_as1.user_has_access)
|
|
self.assertFalse(writable_as2.user_has_write_access)
|
|
self.assertTrue(writable_as2.user_has_access)
|
|
|
|
# check children inherits downgraded permissions from article
|
|
for child in writable_children:
|
|
self.assertEqual(child.inherited_permission, 'read', 'Permission: lowering permission should lower the permission of the children')
|
|
self.assertEqual(child.inherited_permission_parent_id, writable_as1, 'Permission: lowering permission should make the children inherit the permission from this article')
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule', 'odoo.models.unlink')
|
|
@users('employee')
|
|
def test_remove_member_inherited_rights(self):
|
|
""" Remove a member from a child inheriting rights: will desync """
|
|
writable = self.article_write_contents[2].with_env(self.env)
|
|
self.assertTrue(writable.user_has_access)
|
|
self.assertTrue(writable.user_has_write_access)
|
|
self.assertMembers(writable, False,
|
|
{self.partner_portal: 'read'})
|
|
|
|
# set partner employee manager as writable member of its root
|
|
writable_root = writable.root_article_id
|
|
writable_root._add_members(self.partner_employee_manager, 'write')
|
|
self.assertMembers(writable_root, 'write',
|
|
{self.partner_employee_manager: 'write'})
|
|
|
|
# remove partner employee manager that has rights based on inheritance
|
|
writable_children = self.article_write_contents_children.with_env(self.env)
|
|
for child in writable_children:
|
|
self.assertIn(
|
|
self.partner_employee_manager.id,
|
|
child._get_article_member_permissions()[child.id],
|
|
'Share Panel: if an article inherits a permission, its children should inherit that permission too')
|
|
manager_member = writable_root.article_member_ids.filtered(lambda m: m.partner_id == self.partner_employee_manager)
|
|
writable._remove_member(manager_member)
|
|
self.assertTrue(writable.is_desynchronized,
|
|
'Permission: when removing a member having inherited rights it has be be desynchronized')
|
|
self.assertMembers(writable, 'write',
|
|
{self.partner_portal: 'read'})
|
|
for child in writable_children:
|
|
self.assertNotIn(
|
|
self.partner_employee_manager.id,
|
|
child._get_article_member_permissions()[child.id],
|
|
'Share Panel: when removing a member having inherited rights, the member should be removed from the children that inherited that right')
|
|
|
|
# resync
|
|
writable.restore_article_access()
|
|
self.assertFalse(writable.is_desynchronized)
|
|
self.assertMembers(writable, False,
|
|
{self.partner_portal: 'read'})
|
|
|
|
# remove portal partner that has rights based on membership
|
|
portal_member = writable.article_member_ids.filtered(lambda m: m.partner_id == self.partner_portal)
|
|
writable._remove_member(portal_member)
|
|
self.assertFalse(writable.is_desynchronized)
|
|
self.assertMembers(writable, False, {})
|
|
|
|
@mute_logger('odoo.models.unlink')
|
|
@users('employee')
|
|
def test_set_member_permission(self):
|
|
""" Test setting member-specific permission """
|
|
writable = self.article_write_contents[2].with_env(self.env)
|
|
self.assertTrue(writable.user_has_access)
|
|
self.assertTrue(writable.user_has_write_access)
|
|
|
|
# set partner employee manager as readable member of its root
|
|
writable_root = writable.root_article_id
|
|
writable_root._add_members(self.partner_employee_manager, 'read')
|
|
self.assertMembers(writable_root, 'write',
|
|
{self.partner_employee_manager: 'read'})
|
|
|
|
# update a member permission directly
|
|
portal_member = writable.article_member_ids.filtered(lambda m: m.partner_id == self.partner_portal)
|
|
writable._set_member_permission(portal_member, 'none')
|
|
self.assertMembers(writable, False,
|
|
{self.partner_portal: 'none'})
|
|
|
|
# upgrade a permission based on inheritance
|
|
manager_member_root = writable_root.article_member_ids.filtered(lambda m: m.partner_id == self.partner_employee_manager)
|
|
writable._set_member_permission(manager_member_root, 'write', is_based_on=True)
|
|
self.assertFalse(writable.is_desynchronized)
|
|
self.assertMembers(writable, False,
|
|
{self.partner_portal: 'none',
|
|
self.partner_employee_manager: 'write'})
|
|
|
|
# now test downgrading
|
|
manager_member = writable.article_member_ids.filtered(lambda m: m.partner_id == self.partner_employee_manager)
|
|
writable_root._set_member_permission(manager_member_root, 'write')
|
|
writable._remove_member(manager_member)
|
|
self.assertMembers(writable_root, 'write',
|
|
{self.partner_employee_manager: 'write'})
|
|
self.assertMembers(writable, False,
|
|
{self.partner_portal: 'none'})
|
|
|
|
# downgrade a permission, should desynchronize from parent
|
|
writable_children = self.article_write_contents_children.with_env(self.env)
|
|
for child in writable_children:
|
|
self.assertEqual(
|
|
child._get_article_member_permissions()[child.id][self.partner_employee_manager.id]['permission'],
|
|
'write',
|
|
'Share Panel: if an article inherits a permission, its children should inherit that permission too')
|
|
writable_root._set_member_permission(manager_member_root, 'write')
|
|
writable._set_member_permission(manager_member_root, 'read', is_based_on=True)
|
|
self.assertTrue(writable.is_desynchronized,
|
|
'Permission: when removing a member having inherited rights it has be be desynchronized')
|
|
self.assertMembers(writable, 'write',
|
|
{self.partner_portal: 'none',
|
|
self.partner_employee_manager: 'read'})
|
|
for child in writable_children:
|
|
self.assertEqual(
|
|
child._get_article_member_permissions()[child.id][self.partner_employee_manager.id]['permission'],
|
|
'read',
|
|
'Share Panel: when downgrading a member having inherited rights, the member should be downgraded from the children that inherited that right')
|
|
|
|
# adding a member to parent, should not be inherited by children
|
|
writable_root._add_members(self.partner_employee2, 'read')
|
|
self.assertNotIn(
|
|
self.partner_employee2.id,
|
|
writable._get_article_member_permissions()[writable.id],
|
|
'Share Panel: when adding a member on a parent of a desynced article, the member should not be added on the desynced article')
|
|
for child in writable_children:
|
|
self.assertNotIn(
|
|
self.partner_employee2.id,
|
|
child._get_article_member_permissions()[child.id],
|
|
'Share Panel: when adding a member on a parent of a desynced article, the member should not be added on the children of the desynced article')
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule')
|
|
@users('employee')
|
|
def test_update_internal_permission_escalation(self):
|
|
""" Check no privilege escalation is possible """
|
|
# direct try at setting higher internal permission
|
|
readonly = self.article_read_contents[1].with_env(self.env)
|
|
self.assertTrue(readonly.user_has_access)
|
|
self.assertFalse(readonly.user_has_write_access)
|
|
writable = self.article_write_contents[2].with_env(self.env)
|
|
self.assertTrue(writable.user_has_access)
|
|
self.assertTrue(writable.user_has_write_access)
|
|
|
|
with self.assertRaises(exceptions.AccessError,
|
|
msg='Permission: that is plain stupid trying to do this'):
|
|
readonly.write({'internal_permission': 'write'})
|
|
with self.assertRaises(exceptions.AccessError,
|
|
msg='Permission: do not allow privilege escalation'):
|
|
readonly._set_internal_permission('write')
|
|
|
|
portal_member = writable.article_member_ids.filtered(lambda m: m.partner_id == self.partner_portal)
|
|
with self.assertRaises(exceptions.ValidationError,
|
|
msg='Permission: share partner cannot gain write access'):
|
|
writable._set_member_permission(portal_member, 'write')
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_rule', 'odoo.models.unlink')
|
|
@users('employee')
|
|
def test_update_permissions_rights(self):
|
|
""" Check no privilege escalation is possible """
|
|
# direct try at setting higher internal permission
|
|
readonly = self.article_read_contents[1].with_env(self.env)
|
|
self.assertTrue(readonly.user_has_access)
|
|
self.assertFalse(readonly.user_has_write_access)
|
|
with self.assertRaises(exceptions.AccessError,
|
|
msg='Permission: that is plain stupid trying to do this'):
|
|
readonly.write({'internal_permission': 'write'})
|
|
with self.assertRaises(exceptions.AccessError,
|
|
msg='Permission: do not allow privilege escalation'):
|
|
readonly._set_internal_permission('write')
|
|
|
|
other_member = readonly.article_member_ids.filtered(lambda m: m.partner_id == self.partner_portal)
|
|
with self.assertRaises(exceptions.AccessError,
|
|
msg='Permission: do not allow to remove members when having only read access'):
|
|
readonly._remove_member(other_member)
|
|
self.assertMembers(readonly, 'write',
|
|
{self.env.user.partner_id: 'read',
|
|
self.partner_portal: 'read'})
|
|
|
|
# cannot gain privilege: setting none if I remove myself to ensure no gain is performed
|
|
my_member = readonly.article_member_ids.filtered(lambda m: m.partner_id == self.env.user.partner_id)
|
|
readonly._remove_member(my_member)
|
|
self.assertMembers(readonly, 'write',
|
|
{self.env.user.partner_id: 'none',
|
|
self.partner_portal: 'read'})
|
|
|
|
|
|
@tagged('knowledge_acl')
|
|
class TestKnowledgeArticleSearch(KnowledgeArticlePermissionsCase):
|
|
|
|
@users('admin')
|
|
def test_article_search_admin(self):
|
|
""" Test admin: can read / write everything but user_has_access and
|
|
user_has_write_access should still be based on real permissions. """
|
|
self.assertTrue(self.env.user.has_group('base.group_system'))
|
|
articles = self.env['knowledge.article'].search([])
|
|
expected = self.articles_all
|
|
self.assertEqual(articles, expected,
|
|
'Search on user_has_write_access: aka write access (additional: %s, missing: %s)' %
|
|
((articles - expected).mapped('name'), (expected - articles).mapped('name'))
|
|
)
|
|
|
|
articles = self.env['knowledge.article'].search([('user_has_write_access', '=', True)])
|
|
expected = self.article_roots[0] + self.article_headers[0] + \
|
|
self.article_write_contents[0] + self.article_write_contents[2] + \
|
|
self.article_write_contents_children + \
|
|
self.article_read_contents[0:2]
|
|
self.assertEqual(articles, expected,
|
|
'Search on user_has_write_access: aka write access (additional: %s, missing: %s)' %
|
|
((articles - expected).mapped('name'), (expected - articles).mapped('name'))
|
|
)
|
|
|
|
@users('employee')
|
|
def test_article_search_employee(self):
|
|
""" Test regular searches using permission-based ACLs """
|
|
# explicitly remove an article, check it is not included (nor its child)
|
|
self.article_write_desync[0].write({
|
|
'article_member_ids': [
|
|
(0, 0, {'partner_id': self.user_employee.partner_id.id,
|
|
'permission': 'none'})]
|
|
})
|
|
articles = self.env['knowledge.article'].search([])
|
|
# not reachable: 'none', desynchronized 'none' (and their children)
|
|
expected = self.articles_all - self.article_read_contents[3] - self.article_write_desync
|
|
self.assertEqual(articles, expected,
|
|
'Search on main article: aka everything except "none"-based articles (additional: %s, missing: %s)' %
|
|
((articles - expected).mapped('name'), (expected - articles).mapped('name'))
|
|
)
|
|
|
|
# add its child as readable through membership and perform a new search
|
|
self.article_write_desync[1].write({
|
|
'article_member_ids': [
|
|
(0, 0, {'partner_id': self.user_employee.partner_id.id,
|
|
'permission': 'read'})]
|
|
})
|
|
|
|
articles = self.env['knowledge.article'].search([('root_article_id', '=', self.article_roots[0].id)])
|
|
expected = self.article_roots[0] + self.article_headers[0] + \
|
|
self.article_write_contents + self.article_write_contents_children + self.article_write_desync[1]
|
|
self.assertEqual(articles, expected,
|
|
'Search on main article: aka read access on read root + its children (additional: %s, missing: %s)' %
|
|
((articles - expected).mapped('name'), (expected - articles).mapped('name'))
|
|
)
|
|
|
|
@users('employee')
|
|
def test_article_search_employee_method_based(self):
|
|
""" Test search methods """
|
|
articles = self.env['knowledge.article'].search([('user_has_write_access', '=', True)])
|
|
expected = self.article_roots[0] + self.article_roots[3] + \
|
|
self.article_headers[0] + \
|
|
self.article_write_contents[2] + self.article_write_contents_children + \
|
|
self.article_read_contents[0] + self.article_read_desync
|
|
self.assertEqual(articles, expected,
|
|
'Search on user_has_write_access: aka write access (additional: %s, missing: %s)' %
|
|
((articles - expected).mapped('name'), (expected - articles).mapped('name'))
|
|
)
|