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

1833 lines
96 KiB
Python

import datetime
from unittest.mock import patch
from odoo import Command, fields
from odoo.addons.documents.tests.test_documents_common import TransactionCaseDocuments
from odoo.addons.mail.tests.common import MockEmail
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tests import freeze_time, users
from odoo.tools import html2plaintext, mute_logger
from odoo.tests.common import RecordCapturer
class TestDocumentsAccess(TransactionCaseDocuments, MockEmail):
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.addons.base.models.ir_rule')
def test_access_type_internal(self):
"""Check that the 'internal' access_type_role works as expected."""
self.assertEqual(self.folder_a.access_internal, 'view')
self._assert_no_members(self.folder_a)
self.assertTrue(self.public_user._is_public())
self.assertTrue(self.portal_user._is_portal())
self._assert_raises_check_access_rule(self.folder_a.with_user(self.public_user))
self._assert_raises_check_access_rule(self.folder_a.with_user(self.portal_user))
self.folder_a.with_user(self.internal_user).check_access('read')
self._assert_raises_check_access_rule(self.folder_a.with_user(self.internal_user), 'write')
self.folder_a.access_internal = 'edit'
self._assert_raises_check_access_rule(self.folder_a.with_user(self.public_user))
self._assert_raises_check_access_rule(self.folder_a.with_user(self.portal_user))
self.folder_a.with_user(self.internal_user).check_access('write')
self.assertTrue(self.env['documents.document'].search([('id', '=', self.folder_a.id)]))
self.assertFalse(self.env['documents.document'].with_user(self.portal_user).search([('id', '=', self.folder_a.id)]))
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_from_documents_access(self):
"""Check recursive access to documents via documents.access records."""
self.assertEqual(self.folder_a.access_internal, 'view')
self.assertEqual(self.folder_a.access_via_link, 'none')
self.assertEqual(self.folder_a_a.access_internal, 'view')
self.assertEqual(self.folder_a_a.access_via_link, 'none')
self._assert_no_members(self.folder_a)
self.assertTrue(self.portal_user._is_portal())
folder_a_as_portal = self.folder_a.with_user(self.portal_user)
folder_a_a_as_portal = self.folder_a_a.with_user(self.portal_user)
folder_a_as_internal = self.folder_a.with_user(self.internal_user)
self._assert_raises_check_access_rule(folder_a_as_portal)
self._assert_raises_check_access_rule(folder_a_a_as_portal)
portal_access = self.env['documents.access'].create({
'document_id': self.folder_a.id,
'partner_id': self.portal_user.partner_id.id,
'role': 'view',
})
folder_a_as_internal.check_access('read')
self._assert_raises_check_access_rule(folder_a_as_internal, 'write')
folder_a_as_portal.check_access('read')
self._assert_raises_check_access_rule(folder_a_a_as_portal, 'read', 'No access given to A-A')
self._assert_raises_check_access_rule(folder_a_as_portal, 'write')
portal_access.role = 'edit'
folder_a_as_portal.check_access('write')
self.folder_a.access_internal = 'none'
self._assert_raises_check_access_rule(folder_a_as_internal)
folder_a_as_portal.check_access('write')
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_from_past_access(self):
"""Check recursive access to documents via documents.log records."""
self.folder_a.access_via_link = 'view'
self.folder_a.access_internal = 'none'
self.folder_a_a.access_via_link = 'view'
self.folder_a_a.access_internal = 'none'
self._assert_no_members(self.folder_a)
self.assertTrue(self.portal_user._is_portal())
folder_a_as_portal = self.folder_a.with_user(self.portal_user)
folder_a_a_as_portal = self.folder_a_a.with_user(self.portal_user)
folder_a_as_internal = self.folder_a.with_user(self.internal_user)
self.assertEqual(self.folder_a.access_ids.partner_id, self.doc_user.partner_id) # Owner's log
self._assert_raises_check_access_rule(folder_a_as_portal)
self.env['documents.access'].create({
'document_id': self.folder_a.id,
'partner_id': self.portal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
})
self._assert_raises_check_access_rule(folder_a_as_internal)
folder_a_as_portal.check_access('read')
folder_a_a_as_portal.check_access('read')
self.assertEqual(folder_a_as_portal.user_permission, 'view')
self._assert_raises_check_access_rule(folder_a_as_portal, 'write')
self.folder_a.access_via_link = 'edit'
folder_a_as_portal.check_access('write')
self._assert_raises_check_access_rule(folder_a_a_as_portal, 'write')
self.folder_a.access_via_link = 'none'
self._assert_raises_check_access_rule(folder_a_as_portal)
self._assert_raises_check_access_rule(folder_a_a_as_portal) # logged access it to parent, now mute.
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_from_search_panel_token(self):
"""Check that a shared folder (e.g. embedded view) is unlocked by its token."""
(self.folder_a + self.folder_a_a).write({'access_internal': 'none', 'access_via_link': 'none'})
self._assert_no_members(self.folder_a)
folder_a_as_internal = self.folder_a.with_user(self.internal_user)
self._assert_raises_check_access_rule(folder_a_as_internal, 'read')
def select_range(folder_id=None, access_token=None):
"""Return the {folder name: parent} of the search panel of the folder."""
context = {
**({'documents_unique_folder_id': folder_id} if folder_id else {}),
**({'documents_shared_access_token': access_token} if access_token else {}),
}
values = self.env['documents.document'].with_user(self.internal_user).with_context(
context).search_panel_select_range('user_folder_id')['values']
return {vals['display_name']: vals.get('user_folder_id') for vals in values}
self.assertFalse(select_range(self.folder_a.id, self.folder_a.access_token), msg="Not shared via link")
self.assertEqual(folder_a_as_internal.user_permission, "none")
(self.folder_a + self.folder_a_a).access_via_link = 'view'
self.assertFalse(select_range(self.folder_a.id), msg="No token")
self.assertEqual(folder_a_as_internal.user_permission, "none")
self.assertFalse(select_range(self.folder_a.id), msg="No token")
self.assertEqual(folder_a_as_internal.user_permission, "none")
all_range = select_range(None, self.folder_a.access_token)
self.assertNotIn("folder a", all_range, msg="id not provided with token, save resources")
self.assertNotIn("folder a - a", all_range)
self.assertEqual(folder_a_as_internal.user_permission, "none")
# With both, the folder and its content are unlocked
expected = {'folder A': False, 'folder A - A': self.folder_a.id}
with RecordCapturer(self.env["documents.access"]) as capture:
self.assertEqual(select_range(self.folder_a.id, self.folder_a.access_token), expected)
self.assertEqual(self.folder_a.with_user(self.internal_user).user_permission, "view")
logged_access = capture.records
self.assertEqual(logged_access.document_id, self.folder_a)
self.assertFalse(logged_access.role)
def test_access_rights_inherited_on_create(self):
(self.folder_a + self.folder_b).write({
'access_via_link': 'none',
'access_internal': 'none'
})
self._assert_no_members(self.folder_a + self.folder_b)
folder_a1 = self.env['documents.document'].create(
{'name': 'Folder A1', 'folder_id': self.folder_a.id, 'type': 'folder'}
)
# Owner of parent folder is given membership
self.assertEqual(folder_a1.access_ids.partner_id, self.folder_a.owner_id.partner_id)
self.assertEqual(folder_a1.access_internal, 'none')
self.assertEqual(folder_a1.access_via_link, 'none')
self.folder_a.access_internal = 'view'
self.assertEqual(len(self.folder_a.access_ids), 1) # owner's log
self.folder_a.action_update_access_rights(partners={self.portal_user.partner_id.id: ('view', False)})
self.assertEqual(len(self.folder_a.access_ids), 2) # owner's log + portal member
self.folder_b.access_via_link = 'edit'
self.folder_b.action_update_access_rights(partners={self.portal_user.partner_id.id: (False, False)})
folder_a2, folder_b1 = self.env['documents.document'].create([
{'name': 'Folder A2', 'folder_id': self.folder_a.id, 'type': 'folder'},
{'name': 'Folder B1', 'folder_id': self.folder_b.id, 'type': 'folder'},
])
self.assertEqual(len(folder_a2.access_ids), 2) # inherited portal member + folder_a's owner
self.assertEqual(folder_a2.access_ids.partner_id, (self.portal_user + self.doc_user).partner_id)
self.assertEqual(folder_a2.access_internal, 'view')
self.assertEqual(folder_a2.access_via_link, 'none')
self.assertEqual(folder_b1.access_ids.partner_id, self.doc_user.partner_id) # parent's owner (!= from child)
self.assertEqual(folder_b1.access_internal, 'none')
self.assertEqual(folder_b1.access_via_link, 'edit')
folder_a3, folder_a4 = self.env['documents.document'].create([
{'name': 'Folder A3', 'folder_id': self.folder_a.id, 'type': 'folder', 'access_ids': False},
{'name': 'Folder A4', 'folder_id': self.folder_a.id, 'type': 'folder',
'access_ids': [
Command.create({'partner_id': self.internal_user.partner_id.id, 'role': 'view'})
]},
])
self.assertFalse(folder_a3.access_ids)
self.assertEqual(len(folder_a4.access_ids), 3)
self.assertEqual(
folder_a4.access_ids.mapped(lambda a: (a.partner_id, a.role)),
[
(self.internal_user.partner_id, 'view'),
(self.portal_user.partner_id, 'view'),
(self.doc_user.partner_id, 'edit'),
]
)
self.folder_a.access_internal = 'edit'
self.folder_a.access_via_link = 'view'
# Create another log
self.env['documents.access'].create({
'document_id': self.folder_a.id,
'partner_id': self.internal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
})
self.assertEqual(len(self.folder_a.access_ids), 3)
folder_a5 = self.env['documents.document'] \
.with_context(default_access_internal='view', default_folder_id=self.folder_a.id) \
.create({'name': 'Folder A5', 'type': 'folder'})
self.assertEqual(len(folder_a5.access_ids), 2)
# Inherited from member + owner, not logged access
self.assertEqual(folder_a5.access_ids.partner_id, (self.portal_user + self.doc_user).partner_id)
self.assertEqual(folder_a5.access_internal, 'edit')
self.assertEqual(folder_a5.access_via_link, 'view')
folder_a6 = self.env['documents.document'] \
.with_context(default_access_internal='view', default_folder_id=self.folder_a.id) \
.create({'name': 'Folder A6', 'type': 'folder', 'access_ids': False, 'access_internal': 'none'})
self.assertFalse(folder_a6.access_ids)
self.assertEqual(folder_a6.access_internal, 'none')
self.assertEqual(folder_a6.access_via_link, 'view')
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_owner(self):
self.folder_a.write({
'access_via_link': 'none',
'access_internal': 'none'
})
self._assert_no_members(self.folder_a)
self._assert_raises_check_access_rule(self.folder_a.with_user(self.internal_user))
self.assertEqual(self.folder_a.owner_id, self.doc_user)
self.folder_a.with_user(self.doc_user).check_access('read')
self.folder_a.with_user(self.doc_user).check_access('write')
@mute_logger('odoo.addons.base.models.ir_rule')
def test_documents_access_cu(self):
secret = self.env['documents.document'].create({
'access_internal': 'none',
'access_via_link': 'none',
'name': 'secret',
})
public = self.env['documents.document'].create({
'access_internal': 'edit',
'access_via_link': 'none',
'name': 'secret',
})
secret_id = secret.id
self.env.invalidate_all()
with self.assertRaises(AccessError):
secret.with_user(self.doc_user).name
with self.assertRaises(AccessError):
self.env['documents.access'].with_user(self.doc_user).create({
'role': 'edit',
'document_id': secret_id,
'partner_id': self.doc_user.partner_id.id,
})
with self.assertRaises(AccessError):
self.env['documents.access'].with_user(self.doc_user).with_context(
default_document_id=secret_id).create({
'role': 'edit',
'partner_id': self.doc_user.partner_id.id,
})
access = self.env['documents.access'].with_user(self.doc_user).create({
'role': 'edit',
'document_id': public.id,
'partner_id': self.doc_user.partner_id.id,
})
with self.assertRaises(AccessError):
access.document_id = secret_id
with self.assertRaises(AccessError):
access.copy(default={'document_id': secret_id})
access_admin = self.env['documents.access'].create({
'role': 'edit',
'document_id': secret_id,
'partner_id': self.env.user.partner_id.id,
})
with self.assertRaises(AccessError):
access_admin.with_user(self.doc_user).unlink()
# check that the user can not upgrade a view access
access = self.env['documents.access'].create({
'role': 'view',
'document_id': secret.id,
'partner_id': self.doc_user.partner_id.id,
})
with self.assertRaises(AccessError):
access.with_user(self.doc_user).role = 'edit'
with self.assertRaises(AccessError):
access.with_user(self.doc_user).expiration_date = datetime.datetime.now()
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_users_drive_is_private(self):
# Make folder_a a folder in internal users' drive
self.folder_a.write({
'access_internal': 'none',
'access_via_link': 'none',
'folder_id': False,
'owner_id': self.internal_user,
})
self.folder_a.access_ids = False
self._assert_no_members(self.folder_a)
not_authorized = self.doc_user + self.document_manager + self.portal_user
for not_authorized_user in not_authorized:
with self.subTest(user=not_authorized_user.name):
folder_a_with_user = self.folder_a.with_user(not_authorized_user)
self.assertEqual(self.folder_a.with_user(self.doc_user).user_permission, 'none')
self.assertFalse(folder_a_with_user.search([('id', '=', self.folder_a.id)]))
self._assert_raises_check_access_rule(folder_a_with_user)
def test_authorized_users(authorized_users):
for authorized_user in authorized_users:
folder_a = self.folder_a.with_user(authorized_user)
with self.subTest(user=authorized_user.name, method='compute'):
self.assertEqual(folder_a.user_permission, 'edit')
with self.subTest(user=authorized_user.name, method='search'):
self.assertEqual(
folder_a.search([('id', '=', folder_a.id), ('user_permission', '=', 'edit')]),
folder_a
)
self.assertEqual(folder_a.search([('id', '=', self.folder_a.id)]), self.folder_a)
self.document_manager.group_ids |= self.env.ref('documents.group_documents_system')
test_authorized_users(self.internal_user + self.document_manager)
self.folder_a.action_update_access_rights(partners={self.portal_user.partner_id: ('edit', False)})
test_authorized_users(self.portal_user)
self.folder_a.action_update_access_rights(access_internal='edit')
test_authorized_users(self.doc_user + self.document_manager)
self.folder_a.action_update_access_rights(access_internal='view')
test_authorized_users(self.document_manager)
@mute_logger('odoo.addons.base.models.ir_rule')
def test_action_update_access_rights_partners(self):
"""Check that we can update partners access to a document."""
self._assert_no_members(self.folder_a)
portal_user_2 = self.portal_user.copy()
folder_a_as_portal = self.folder_a.with_user(self.portal_user)
folder_a_as_portal_2 = self.folder_a.with_user(portal_user_2)
self._assert_raises_check_access_rule(folder_a_as_portal)
self._assert_raises_check_access_rule(folder_a_as_portal_2)
# Make portal and portal 2 viewers, permanently or for one day, resp.
IN_ONE_DAY = fields.Datetime.now() + datetime.timedelta(days=1)
partners = {
self.portal_user.partner_id.id: ('view', False),
portal_user_2.partner_id.id: ('view', IN_ONE_DAY)
}
self.env.invalidate_all()
with self.assertQueryCount(13):
self.folder_a.action_update_access_rights(partners=partners)
folder_a_as_portal.check_access('read')
folder_a_as_portal_2.check_access('read')
folder_a_as_internal = self.folder_a.with_user(self.internal_user)
# Check that expiration was propagated too
portal_2_a_a_access = self.folder_a_a.access_ids.filtered(
lambda a: a.partner_id == portal_user_2.partner_id)
self.assertEqual(len(portal_2_a_a_access), 1)
self.assertEqual(portal_2_a_a_access.expiration_date, IN_ONE_DAY)
# Update expiration alone via parent
IN_12_H = IN_ONE_DAY - datetime.timedelta(hours=12)
self.env.invalidate_all()
with self.assertQueryCount(8):
self.folder_a.action_update_access_rights(partners={portal_user_2.partner_id: ('view', IN_12_H)})
self.assertEqual(portal_2_a_a_access.expiration_date, IN_12_H)
# Update role+expiration via parent
self.env.invalidate_all()
with self.assertQueryCount(8):
self.folder_a.action_update_access_rights(partners={portal_user_2.partner_id: ('edit', IN_ONE_DAY)})
self.assertEqual(portal_2_a_a_access.expiration_date, IN_ONE_DAY)
self.assertEqual(portal_2_a_a_access.role, 'edit')
# Update role alone via parent
self.env.invalidate_all()
with self.assertQueryCount(8):
self.folder_a.action_update_access_rights(partners={portal_user_2.partner_id: ('view', None)})
self.assertEqual(portal_2_a_a_access.expiration_date, IN_ONE_DAY)
self.assertEqual(portal_2_a_a_access.role, 'view')
# Make portal viewer of grandchild folder only
partners = {self.portal_user.partner_id.id: (False, None)}
self.env.invalidate_all()
with self.assertQueryCount(10):
self.folder_a.action_update_access_rights(partners=partners)
self.assertFalse(self.folder_a.access_ids.filtered(lambda a: a.partner_id == self.portal_user.partner_id))
self._assert_raises_check_access_rule(folder_a_as_portal, 'read')
folder_a_a_p = self.env['documents.document'].create({
'type': 'folder',
'name': "A cool name",
'folder_id': self.folder_a_a.id,
})
partners = {self.portal_user.partner_id.id: ('view', False)}
self.env.invalidate_all()
with self.assertQueryCount(7):
folder_a_a_p.action_update_access_rights(partners=partners)
# Make portal and internal editors of parent folder, this should propagate down
partners = {
partner_id: ('edit', False)
for partner_id in (self.portal_user | self.internal_user).partner_id.ids
}
self.env.invalidate_all()
with self.assertQueryCount(8):
self.folder_a.action_update_access_rights(partners=partners)
folder_a_as_portal.check_access('write')
folder_a_a_as_portal = self.folder_a_a.with_user(self.portal_user)
folder_a_a_as_portal.check_access('write')
folder_a_as_internal.check_access('write')
self._assert_raises_check_access_rule(folder_a_as_portal_2, 'write')
# Remove portal 2 access
portal_2_partner_id = portal_user_2.partner_id.id
self.env.invalidate_all()
with self.assertQueryCount(7):
self.folder_a.action_update_access_rights(partners={portal_2_partner_id: (False, False)})
self._assert_raises_check_access_rule(folder_a_as_portal_2)
# Add portal 2 access to 1st level child and remove from 2nd
self.env.invalidate_all()
with self.assertQueryCount(7):
self.folder_a_a.action_update_access_rights(partners={portal_2_partner_id: ('view', False)})
folder_a_a_p.action_update_access_rights(partners={portal_user_2.partner_id.id: (False, None)})
self.assertFalse(folder_a_a_p.access_ids.filtered(lambda a: a.partner_id == portal_user_2.partner_id))
# check that the accesses are propagated on the documents even if they are in the trash
folder_a_a_p.action_archive()
self.folder_a_a.action_update_access_rights(partners={portal_user_2.partner_id.id: ('edit', None)})
self.assertEqual(folder_a_a_p.access_ids.filtered(lambda a: a.partner_id == portal_user_2.partner_id).role, 'edit')
def test_action_update_access_rights_internal_propagation(self):
self.assertEqual(set((self.folder_b + self.document_gif).mapped('access_internal')), {'view'})
self.folder_b.action_update_access_rights(access_internal='none')
self.assertEqual(set((self.folder_b + self.document_gif).mapped('access_internal')), {'none'})
# check that the accesses are propagated on the documents even if they are in the trash
self.document_gif.action_archive()
self.folder_b.action_update_access_rights(access_internal='edit')
self.assertEqual(set((self.folder_b + self.document_gif).mapped('access_internal')), {'edit'})
self.document_gif.action_update_access_rights(access_internal='view')
self.assertEqual((self.folder_b + self.document_gif).mapped('access_internal'), ['edit', 'view'])
def test_action_update_access_rights_no_propagation(self):
doc_partner = self.doc_user.partner_id
self.folder_b.action_update_access_rights(
access_internal='view', access_via_link='view', is_access_via_link_hidden=True,
partners={self.doc_user.partner_id: ('view', False)})
self.assertEqual(self.folder_b.access_ids.filtered(lambda a: a.partner_id == doc_partner).mapped(
lambda a: (a.role, a.expiration_date)), [('view', False)])
self.assertEqual(self.document_gif.access_ids.filtered(lambda a: a.partner_id == doc_partner).mapped(
lambda a: (a.role, a.expiration_date)), [('view', False)])
self.assertEqual(self.folder_b.mapped(
lambda d: (d.access_internal, d.access_via_link, d.is_access_via_link_hidden)),
[('view', 'view', True)])
self.assertEqual(self.document_gif.mapped(lambda d: (d.access_internal, d.access_via_link)), [('view', 'view')])
self.assertFalse(self.document_gif.is_access_via_link_hidden, "`is_access_via_link_hidden` should not propagate")
IN_ONE_DAY = fields.Datetime.now() + datetime.timedelta(days=1)
self.document_txt.is_access_via_link_hidden = True
self.folder_b.action_update_access_rights(
access_internal='edit', access_via_link='edit', is_access_via_link_hidden=False,
partners={doc_partner: ('edit', IN_ONE_DAY)}, no_propagation=True)
self.assertEqual(self.folder_b.mapped(
lambda d: (d.access_internal, d.access_via_link, d.is_access_via_link_hidden)), [('edit', 'edit', False)])
self.assertEqual(self.folder_b.access_ids.filtered(lambda a: a.partner_id == doc_partner).mapped(
lambda a: (a.role, a.expiration_date)), [('edit', IN_ONE_DAY)])
self.assertEqual(self.document_gif.mapped(
lambda d: (d.access_internal, d.access_via_link, d.is_access_via_link_hidden)), [('view', 'view', False)])
self.assertEqual(self.document_txt.mapped(
lambda d: (d.access_internal, d.access_via_link, d.is_access_via_link_hidden)), [('view', 'view', True)])
self.assertEqual(self.document_gif.access_ids.filtered(lambda a: a.partner_id == doc_partner).mapped(
lambda a: (a.role, a.expiration_date)), [('view', False)])
def test_action_update_access_rights_link_propagation(self):
self.assertEqual(set((self.folder_b + self.document_gif).mapped('access_via_link')), {'none'})
self.folder_b.action_update_access_rights(access_via_link='view')
self.assertEqual(set((self.folder_b + self.document_gif).mapped('access_via_link')), {'view'})
@mute_logger('odoo.addons.base.models.ir_rule')
def test_action_update_access_rights_sudo(self):
"""Test that the action will update rights on restricted access folder when using sudo."""
self.folder_a.write({
'access_internal': 'none',
'access_via_link': 'none',
'folder_id': False,
'owner_id': self.document_manager,
})
self.folder_a.access_ids = False
self._assert_no_members(self.folder_a)
folder_a_as_internal = self.folder_a.with_user(self.internal_user)
# Members
with self.assertRaises(AccessError):
folder_a_as_internal.action_update_access_rights(partners={self.internal_user.partner_id: ('edit', None)})
folder_a_as_internal.sudo().action_update_access_rights(partners={self.internal_user.partner_id: ('edit', None)})
@users('[email protected]')
def test_moving_documents(self):
"""Check that documents can be moved to a new location and have their rights updated except discoverability."""
self.folder_b.write({
'access_internal': 'none',
'access_via_link': 'none',
'is_access_via_link_hidden': True,
})
self.folder_a.write({'access_via_link': 'view', 'is_access_via_link_hidden': False})
self.folder_a_a.folder_id = self.folder_b.id
self.assertEqual(self.folder_a_a.folder_id, self.folder_b)
self.folder_b.folder_id = self.folder_a.id
self.assertEqual(self.folder_b.folder_id, self.folder_a)
self.assertEqual(self.folder_b.access_internal, 'view', 'Internal access should have been updated.')
self.assertEqual(self.folder_b.access_via_link, 'view', 'link access should have been updated.')
self.assertEqual(self.folder_b.is_access_via_link_hidden, True, 'Discoverability should not be updated')
self.document_gif.folder_id = False
shortcut = self.folder_b.with_user(self.doc_user).action_create_shortcut(location_user_folder_id='MY')
self.document_gif.folder_id = shortcut.id
self.assertEqual(self.document_gif.folder_id, self.folder_b)
doc_shortcut = self.document_gif.action_create_shortcut(str(shortcut.id))
self.assertEqual(doc_shortcut.folder_id, self.folder_b)
# making a shortcut of a shortcut use the target instead
shortcut = shortcut.action_create_shortcut(location_user_folder_id='MY')
self.assertEqual(shortcut.shortcut_document_id, self.folder_b)
# Can't move out if not editor on folder
self.folder_a.action_update_access_rights(partners={self.internal_user.partner_id.id: ('edit', False)})
self.folder_b.action_update_access_rights(partners={self.internal_user.partner_id.id: ('view', False)})
self.document_gif.action_update_access_rights(partners={self.internal_user.partner_id.id: ('edit', False)})
with self.assertRaises(AccessError):
self.document_gif.with_user(self.internal_user).folder_id = self.folder_a
# Unless user is the owner
self.document_gif.owner_id = self.internal_user
self.document_gif.with_user(self.internal_user).folder_id = self.folder_a
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.addons.base.models.ir_rule')
def test_create_document_access(self):
with self.assertRaises(AccessError):
self.folder_a.with_user(self.internal_user).name = 'test'
with self.assertRaises(AccessError):
self.env['documents.document'].with_user(self.internal_user).create({
'name': 'folder',
'folder_id': self.folder_a.id,
'owner_id': self.internal_user.id,
'type': 'folder',
})
# internal user can create a file on the root
self.env['documents.document'].with_user(self.internal_user).create({
'name': 'document',
'folder_id': False,
'owner_id': self.internal_user.id,
'type': 'binary',
})
with self.assertRaises(UserError):
# portal can not be the owner of a document on the root
self.env['documents.document'].with_user(self.portal_user).create({
'name': 'document',
'folder_id': False,
'owner_id': self.portal_user.id,
'type': 'binary',
})
with self.assertRaises(UserError):
# and portal can not create a document on the root for an other user
self.env['documents.document'].with_user(self.portal_user).create({
'name': 'document',
'folder_id': False,
'owner_id': self.internal_user.id,
'type': 'binary',
})
# but in SUDO, portal should be able to create a document on the root
self.env['documents.document'].with_user(self.portal_user).sudo().create({
'name': 'document',
'folder_id': False,
'owner_id': self.internal_user.id,
'type': 'binary',
})
with self.assertRaises(UserError):
# even in SUDO, portal can not be set as the owner of a root document
self.env['documents.document'].with_user(self.portal_user).sudo().create({
'name': 'document',
'folder_id': False,
'owner_id': self.portal_user.id,
'type': 'binary',
})
@mute_logger('odoo.addons.base.models.ir_rule')
def test_restrict_write_on_pinned_folders(self):
"""Check that editing pinned folders is restricted to managers
Creating inside depends on regular access rights
"""
self.assertFalse(self.folder_a.folder_id)
self.folder_a.owner_id = False
self.folder_a.action_update_access_rights(partners={self.internal_user.partner_id.id: ('edit', False)})
access = self.env['documents.access'].search([
('document_id', '=', self.folder_a.id),
('partner_id', '=', self.internal_user.partner_id.id),
])
self.assertEqual(len(access), 1)
self.assertEqual(access.role, 'edit')
with self.assertRaises(AccessError): # Un-pin attempt
self.folder_a.with_user(self.internal_user).folder_id = self.folder_b
# That's because
self._assert_raises_check_access_rule(self.folder_a.with_user(self.internal_user), 'write')
# but user_permission = 'edit' and can create inside
self.assertEqual(self.folder_a.with_user(self.internal_user).user_permission, 'edit')
self.env['documents.document'].with_user(self.internal_user).create({
'type': 'folder', 'name': 'a folder', 'folder_id': self.folder_a.id
})
# or create a shortcut inside
shortcut = self.document_txt.with_user(self.internal_user).action_create_shortcut(
location_user_folder_id=str(self.folder_a.id)
)
self.assertEqual(shortcut.folder_id, self.folder_a)
# Managers can unpin by moving to another root folder
self.folder_b.owner_id = False
self.folder_a.with_user(self.document_manager).folder_id = self.folder_b
self.assertFalse(self.folder_a._is_company_root_folder())
# Or moving to their own drive
self.folder_a.with_user(self.document_manager).folder_id = False
self.folder_a.with_user(self.document_manager).owner_id = self.document_manager
@mute_logger('odoo.addons.base.models.ir_rule')
def test_pin_folder_create(self):
"""Check that a normal user can not create a pinned folder."""
folder = self.env['documents.document'].create({
'folder_id': False,
'name': 'folder',
'owner_id': False,
'type': 'folder',
})
self.assertTrue(folder._is_company_root_folder())
with self.assertRaises(AccessError):
self.env['documents.document'].with_user(self.internal_user).create({
'folder_id': False,
'name': 'folder',
'owner_id': False,
'type': 'folder',
})
@mute_logger('odoo.addons.base.models.ir_rule')
def test_pin_folder_folder_id(self):
"""Check that non-admins cannot (un-)pin company root folders."""
self.assertFalse(self.folder_a.folder_id)
self.folder_a.owner_id = self.document_manager
self.folder_a.access_ids = False
self.folder_a.action_update_access_rights(
access_internal='none',
partners={self.internal_user.partner_id.id: ('edit', False)}
)
children_access_before = [access_ids.filtered(lambda a: a.role)
for access_ids in self.folder_a.children_ids.mapped(lambda f: f.access_ids)]
for child_access_before in children_access_before:
self.assertFalse(self.document_manager.partner_id & child_access_before.partner_id)
# managers pins from their drive
self.folder_a.with_user(self.document_manager).user_folder_id = "COMPANY"
self.assertEqual(
(self.document_manager | self.internal_user).partner_id,
self.folder_a.access_ids.filtered(lambda a: a.role).partner_id)
# Manager access was not added on children
self.assertEqual(
[access_ids.filtered(lambda a: a.role)
for access_ids in self.folder_a.children_ids.mapped(lambda f: f.access_ids)],
children_access_before)
# internal user cannot write on pinned folders
self._assert_raises_check_access_rule(self.folder_a.with_user(self.internal_user), 'write')
self.folder_b.children_ids.action_update_access_rights(partners={self.doc_user.partner_id: (False, False)})
self.folder_b.folder_id = False
self.assertEqual(self.folder_b.owner_id, self.doc_user)
self.folder_b.action_update_access_rights(
access_internal='edit',
partners={self.internal_user.partner_id.id: ('edit', False)}
)
children_access_before = [access_ids.filtered(lambda a: a.role)
for access_ids in self.folder_b.children_ids.mapped(lambda f: f.access_ids)]
for child_access_before in children_access_before:
self.assertFalse(self.doc_user.partner_id & child_access_before.partner_id)
# managers can pin accessible folders (here in other user's drive)
self.folder_b.with_user(self.document_manager).user_folder_id = "COMPANY"
# previous owner is added as member
self.assertEqual(
(self.doc_user | self.internal_user).partner_id,
self.folder_b.access_ids.filtered(lambda a: a.role).partner_id)
# Previous owner access was not added on children
self.assertEqual(
[access_ids.filtered(lambda a: a.role)
for access_ids in self.folder_b.children_ids.mapped(lambda f: f.access_ids)],
children_access_before)
# set_as_company_root changed owner_id
self.assertFalse((self.folder_a | self.folder_b).owner_id)
# Normal user cannot pin
self.folder_a.with_user(self.document_manager).owner_id = self.internal_user
self.folder_a.with_user(self.internal_user).check_access('write')
with self.assertRaises(AccessError):
self.folder_a.with_user(self.internal_user).user_folder_id = "COMPANY"
# with SUDO, a normal user can pin a folder
self.folder_a.with_user(self.internal_user).sudo().user_folder_id = "COMPANY"
# with SUDO, a normal user can move a pinned a folder
self.folder_a.with_user(self.internal_user).sudo().owner_id = self.internal_user
# Internal user cannot copy a company root folder
with self.assertRaises(AccessError):
self.company_root_folder.with_user(self.internal_user).copy(default={'user_folder_id': "COMPANY"})
@mute_logger('odoo.addons.base.models.ir_rule')
def test_unlink_with_children(self):
"""Check that deletion handles children items and checks access rights
This is necessary to avoid handling changes of access rights when moving
records to parent folders, showing them in the trash etc. This is also
important to make sure that an 2nd level folder without owner cannot be
pinned by deleting its parent
"""
self.folder_a.action_update_access_rights(access_internal='edit')
self.folder_a_a.action_update_access_rights(access_internal='none')
with self.assertRaises(AccessError):
self.folder_a.with_user(self.internal_user).unlink()
self.folder_a_a.action_update_access_rights(access_internal='edit')
self.folder_a.with_user(self.internal_user).unlink()
self.assertFalse(self.folder_a_a.exists())
@mute_logger('odoo.addons.base.models.ir_rule')
def test_archiving_as_portal(self):
"""Check that (un)archiving is not allowed to portal users."""
self.folder_a.action_update_access_rights(partners={self.portal_user.partner_id: ("edit", False)})
with self.assertRaises(UserError):
self.folder_a.with_user(self.portal_user).action_archive()
self.folder_a.action_archive()
self.assertFalse(self.folder_a.active)
with self.assertRaises(UserError):
self.folder_a.with_user(self.portal_user).action_unarchive()
self.folder_a.with_user(self.portal_user).sudo().action_unarchive()
@mute_logger('odoo.addons.base.models.ir_rule')
def test_archiving_with_children(self):
"""Check that archiving handles children items and checks access rights.
See also ``test_unlink_with_children``
"""
self.folder_a.action_update_access_rights(access_internal='edit')
self.folder_a_a.action_update_access_rights(access_internal='none')
with self.assertRaises(AccessError):
self.folder_a.with_user(self.internal_user).action_archive()
self.folder_a_a.action_update_access_rights(access_internal='edit')
self.folder_a.with_user(self.internal_user).action_archive()
self.assertFalse(self.folder_a_a.active)
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_expiration(self):
"""Check that expired access_ids no longer provide access."""
self._assert_no_members(self.folder_a)
self.folder_a.action_update_access_rights(partners={self.portal_user.partner_id.id: ('view', False)})
folder_a_as_portal = self.folder_a.with_user(self.portal_user)
folder_a_as_portal.check_access('read')
first_child_as_portal = folder_a_as_portal.children_ids
self.folder_a.action_update_access_rights(partners={
self.portal_user.partner_id.id: ('view', fields.Datetime.now() - datetime.timedelta(days=1))})
self.assertEqual(len(first_child_as_portal), 1)
self._assert_raises_check_access_rule(folder_a_as_portal, 'read')
# As we did update children
self._assert_raises_check_access_rule(first_child_as_portal, 'read')
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_via_link_from_parent_folder(self):
"""Check that a document accessible via link is accessible to users having access to the parent folder.
Note that access via link to the parent is only checked if there is an access record (albeit without role).
This is used to:
* share a folder with its similarly-configured contents to a public user with a single token
(incl. download a zip).
* make files discoverable when navigating the parent folder in the client without creating `access` records
for every file when 'reading' the folder.
"""
self._assert_no_members(self.folder_b)
self.folder_b.action_update_access_rights(access_internal='none', access_via_link='none') # with propagation
self.assertEqual(self.folder_b.access_internal, 'none')
self.assertEqual(self.document_gif.access_internal, 'none')
self.assertEqual(self.document_txt.access_internal, 'none')
self.assertEqual(self.document_gif.access_via_link, 'none')
self.assertEqual(self.document_txt.access_via_link, 'view')
with mute_logger('odoo.addons.documents.models.documents_document'): # Creating document(s) as superuser
document_txt_private = self.document_txt.copy()
self.assertIn(document_txt_private.access_ids.role, {False, 'edit'})
document_txt_private.is_access_via_link_hidden = True
self.assertEqual(document_txt_private.access_via_link, 'view')
gif_as_internal = self.document_gif.with_user(self.internal_user)
txt_as_internal = self.document_txt.with_user(self.internal_user)
txt_private_as_internal = document_txt_private.with_user(self.internal_user)
self._assert_raises_check_access_rule(gif_as_internal | txt_as_internal, 'read')
access_values = {'document_id': self.folder_b.id, 'partner_id': self.internal_user.partner_id.id}
folder_accesses_values = [
('internal user access', {'access_internal': 'view'}),
('link accessed', {
'access_via_link': 'view',
'access_ids': [Command.create(access_values | {'last_access_date': fields.Datetime.now()})]
}),
('member', {'access_ids': [Command.create(access_values | {'role': 'view'})]}),
]
for case_name, folder_access_vals in folder_accesses_values:
with self.subTest(case_name=case_name):
self.folder_b.write(folder_access_vals)
self._assert_raises_check_access_rule(gif_as_internal, 'read')
self._assert_raises_check_access_rule(txt_private_as_internal, 'read')
self.assertEqual((gif_as_internal | txt_private_as_internal).mapped('user_permission'), ['none', 'none'])
self.assertEqual(txt_as_internal.search([('folder_id', '=', self.folder_b.id)]), self.document_txt)
txt_as_internal.check_access('read')
if case_name == 'internal user access': # Difference between internal and portal
self._assert_raises_check_access_rule(self.document_txt.with_user(self.portal_user), 'read')
# cleanup
self.folder_b.write({'access_internal': 'none', 'access_via_link': 'none'})
self.folder_b.access_ids.unlink()
def test_access_documents_and_attachment(self):
self.document_gif.action_update_access_rights(access_internal='none', access_via_link='none')
gif_as_internal = self.document_gif.with_user(self.internal_user)
self.assertFalse(self.document_gif.res_id)
self.assertFalse(self.document_gif.res_model)
self.assertEqual(self.document_gif.attachment_id.res_id, self.document_gif.id)
self.assertEqual(self.document_gif.attachment_id.res_model, 'documents.document')
self._assert_raises_check_access_rule(gif_as_internal, 'read')
# Check that the user can not read the attachment
self.assertFalse(self.document_gif.res_model)
with self.assertRaises(AccessError):
self.assertEqual(self.document_gif.attachment_id.with_user(self.internal_user).name, 'file.gif')
# If we give access to the document, the user should be able to read both the document and the attachment
self.document_gif.access_internal = 'view'
self.assertEqual(gif_as_internal.with_user(self.internal_user).name, 'file.gif')
self.assertFalse(gif_as_internal.res_model)
self.env.invalidate_all()
self.assertEqual(self.document_gif.attachment_id.with_user(self.internal_user).name, 'file.gif')
@mute_logger('odoo.addons.base.models.ir_rule')
def test_access_rights_shortcuts_and_discoverability(self):
"""Check access rights related to shortcuts:
* A shortcut pointing to a non-accessible document is hidden.
* A shortcut pointing to a non-accessible document but discoverable with link should be visible.
See also ``test_access_via_link_from_parent_folder``.
"""
self._assert_no_members(self.folder_b)
self.folder_b.folder_id = self.folder_a.id
self.assertEqual((self.folder_b + self.document_txt).access_ids.mapped('role'), [False, 'edit'])
self.folder_a.owner_id = False
self.assertEqual(self.folder_a.access_internal, 'view')
self.assertEqual(self.document_txt.folder_id, self.folder_b)
self.folder_a.action_update_access_rights(partners={self.portal_user.partner_id: ('view', False)})
(self.folder_b + self.document_txt).action_update_access_rights(
access_internal='none', access_via_link='none', partners={self.portal_user.partner_id: (False, False)})
self.assertEqual(self.document_txt.with_user(self.portal_user).user_permission, 'none')
self._assert_raises_check_access_rule(self.document_txt.with_user(self.portal_user), 'read')
# Create a shortcut to document_txt in folder_a
shortcut = self.document_txt.action_create_shortcut(location_user_folder_id=str(self.folder_a.id))
self.assertEqual(
shortcut.access_ids.mapped(lambda a: (a.role, a.partner_id)), [('edit', self.doc_user.partner_id)])
self._assert_raises_check_access_rule(shortcut.with_user(self.portal_user), 'read',
"Shortcut shouldn't be visible as source is inaccessible")
self.assertEqual(shortcut.with_user(self.portal_user).user_permission, 'none')
docs = self.document_txt + shortcut
# Neither internal nor manager can't see any one of the two
self.assertSetEqual(set(docs.with_user(self.internal_user).mapped('user_permission')), {'none'})
self._assert_raises_check_access_rule(docs.with_user(self.internal_user), 'read')
self.assertSetEqual(set(docs.with_user(self.document_manager).mapped('user_permission')), {'none'})
self._assert_raises_check_access_rule(docs.with_user(self.document_manager), 'read')
# Let internal see both
self.document_txt.action_update_access_rights(access_internal='view')
self.assertSetEqual(set(docs.with_user(self.internal_user).mapped('user_permission')), {'view'})
docs.with_user(self.internal_user).check_access('read')
# Manager now has edit right
self.assertSetEqual(set(docs.with_user(self.document_manager).mapped('user_permission')), {'edit'})
docs.with_user(self.document_manager).check_access('write')
# Still not portal
self.assertSetEqual(set(docs.with_user(self.portal_user).mapped('user_permission')), {'none'})
self._assert_raises_check_access_rule(docs.with_user(self.portal_user), 'read')
self.document_txt.action_update_access_rights(access_via_link='view', is_access_via_link_hidden=True)
self.assertEqual(shortcut.access_via_link, 'view')
self.assertEqual(shortcut.is_access_via_link_hidden, True)
self._assert_raises_check_access_rule(shortcut.with_user(self.portal_user), 'read',
"Shortcut shouldn't be visible as source is still inaccessible")
self.assertEqual(shortcut.with_user(self.portal_user).user_permission, 'none')
self.document_txt.action_update_access_rights(is_access_via_link_hidden=False)
self.assertEqual(self.document_txt.is_access_via_link_hidden, False)
self.assertEqual(shortcut.is_access_via_link_hidden, False)
self.assertEqual(shortcut.with_user(self.portal_user).user_permission, 'view')
shortcut.with_user(self.portal_user).check_access('read')
# Shortcut target is still inaccessible, it will be made available when logging access
self._assert_raises_check_access_rule(self.document_txt.with_user(self.portal_user), 'read')
# Updating the access on the target update the access on the shortcut itself
partner = self.env['res.partner'].create({'name': 'Test'})
shortcut.shortcut_document_id.action_update_access_rights(
access_internal='edit', partners={partner: ('edit', False)})
self.assertEqual(shortcut.shortcut_document_id.access_internal, 'edit')
self.assertEqual(shortcut.access_internal, 'edit')
access = self.env['documents.access'].search([('partner_id', '=', partner.id)])
self.assertEqual(len(access), 2)
self.assertEqual(access.document_id, shortcut | shortcut.shortcut_document_id)
shortcut.shortcut_document_id.action_update_access_rights(
access_internal='edit', partners={partner: (False, False)})
self.assertFalse(access.exists())
# Check that own shortcut is deleted when access to the target is removed
# (avoids client fetches on inaccessible previews & others).
self.folder_a.action_update_access_rights(access_internal='edit')
self.document_txt.action_update_access_rights(access_internal='view', access_via_link='none')
# Access via access_internal
shortcut = self.document_txt.with_user(self.internal_user).action_create_shortcut(
location_user_folder_id=str(self.folder_a.id)
)
self.assertEqual(shortcut.owner_id, self.internal_user)
self.document_txt.action_update_access_rights(access_internal='none')
self.assertEqual(self.document_txt.with_user(self.internal_user).user_permission, 'none')
self.assertFalse(
self.env['documents.document'].with_user(self.internal_user).search([('id', '=', shortcut.id)]))
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
# Access via membership
self.document_txt.action_update_access_rights(
partners={self.internal_user.partner_id: ('view', False)})
shortcut = self.document_txt.with_user(self.internal_user).action_create_shortcut(
location_user_folder_id=str(self.folder_a.id)
)
self.assertEqual(shortcut.owner_id, self.internal_user)
self.document_txt.action_update_access_rights(partners={self.internal_user.partner_id: (False, False)})
self.assertFalse(
self.env['documents.document'].with_user(self.internal_user).search([('id', '=', shortcut.id)]))
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
# Access via ownership
self.document_txt.owner_id = self.internal_user
shortcut = self.document_txt.with_user(self.internal_user).action_create_shortcut(
location_user_folder_id=str(self.folder_a.id)
)
self.assertEqual(shortcut.owner_id, self.internal_user)
self.document_txt.owner_id = self.document_manager
self.assertFalse(
self.env['documents.document'].with_user(self.internal_user).search([('id', '=', shortcut.id)]))
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
def test_access_rights_shortcuts_target(self):
Doc_as_internal = self.env['documents.document'].with_user(self.internal_user)
# Check in SUDO to remove the added `user_permission` domain from the access rules
Doc_as_internal_sudo = Doc_as_internal.sudo()
shortcut = self.document_txt.action_create_shortcut(location_user_folder_id=str(self.folder_a.id))
# Check edit access on the shortcut when we can only read the target
self.document_txt.action_update_access_rights(access_internal='view', access_via_link='none')
shortcut.sudo().owner_id = self.internal_user
self.assertEqual(self.document_txt.with_user(self.internal_user).user_permission, 'view')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'edit')
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'edit')]))
doc_class = Doc_as_internal_sudo.pool['documents.document']
original_search = doc_class._search_user_permission
with patch.object(
doc_class,
"_search_user_permission",
autospec=True,
side_effect=original_search,
) as patched_search_user_permission:
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '!=', 'none')]))
self.assertEqual(patched_search_user_permission.call_count, 1)
self.document_txt.action_update_access_rights(access_internal='none')
self.assertEqual(self.document_txt.with_user(self.internal_user).user_permission, 'none')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
self.assertFalse(Doc_as_internal.search([('id', '=', shortcut.id)]))
with patch.object(
doc_class,
"_search_user_permission",
autospec=True,
side_effect=original_search,
) as patched_search_user_permission:
self.assertFalse(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'edit')]))
self.assertEqual(patched_search_user_permission.call_count, 2)
self.assertFalse(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'view')]))
self.document_txt.sudo().owner_id = self.internal_user
self.assertEqual(self.document_txt.with_user(self.internal_user).user_permission, 'edit')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'edit')
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'edit')]))
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '!=', 'none')]))
self.assertFalse(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'view')]))
def test_access_rights_shortcuts_target_accessible_via_link(self):
Doc_as_internal_sudo = self.env['documents.document'].with_user(self.internal_user).sudo()
self.folder_a.access_internal = 'edit'
self.document_txt.folder_id.action_update_access_rights(access_internal='view')
self.document_txt.action_update_access_rights(
access_internal='none',
access_via_link='view',
is_access_via_link_hidden=False,
)
self.env['documents.access'].create([{
'document_id': self.document_txt.id,
'partner_id': self.internal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
}])
shortcut = self.document_txt.with_user(self.internal_user).action_create_shortcut(
location_user_folder_id=str(self.folder_a.id))
self.assertEqual(self.document_txt.with_user(self.internal_user).user_permission, 'view')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'edit')
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '=', 'edit')]))
self.assertTrue(Doc_as_internal_sudo.search([('id', '=', shortcut.id), ('user_permission', '!=', 'none')]))
# We remove the access on the target
self.document_txt.action_update_access_rights(access_via_link='none')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
self.assertEqual(shortcut.with_user(self.internal_user).user_permission, 'none')
@mute_logger('odoo.addons.base.models.ir_rule')
@users('[email protected]')
def test_access_rights_shortcuts_propagation(self):
"""Test that we update shortcuts if we have edit access on the document"""
target = self.env['documents.document'].create({
'name': 'Target',
'access_internal': 'edit',
})
root = self.env['documents.document'].create({
'name': 'Root',
'type': 'folder',
'access_internal': 'edit',
'children_ids': [Command.create({
'name': 'A',
'type': 'folder',
'access_internal': 'edit',
'is_access_via_link_hidden': True,
'children_ids': [Command.create({
'name': 'File 1',
'access_internal': 'edit',
}), Command.create({
'name': 'File 2',
'access_internal': 'view',
}), Command.create({
'name': 'File 3',
'access_internal': 'none',
'access_via_link': 'edit',
'is_access_via_link_hidden': False,
}), Command.create({
'name': 'Sub-folder',
'type': 'folder',
'access_internal': 'edit',
'children_ids': [Command.create({
'name': 'Sub-file',
'access_internal': 'edit',
})]
}), Command.create({
'name': 'Shortcut',
'access_internal': 'edit',
'shortcut_document_id': target.id,
})],
})],
'owner_id': self.document_manager.id # not False
})
file_1 = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'File 1')])
file_2 = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'File 2')])
file_3 = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'File 3')])
shortcut = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'Shortcut')])
sub_folder = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'Sub-folder')])
sub_file = self.env['documents.document'].search([('id', 'child_of', root.id), ('name', '=', 'Sub-file')])
shortcut_1 = file_1.action_create_shortcut(location_user_folder_id='MY')
shortcut_2 = file_2.action_create_shortcut(location_user_folder_id='MY')
shortcut_3 = file_3.action_create_shortcut(location_user_folder_id='MY')
self.assertEqual(shortcut_1.access_internal, 'edit')
self.assertEqual(shortcut_2.access_internal, 'view')
self.assertEqual(shortcut_3.access_internal, 'none')
# ** sanity check **
# no write access, we should not update it
self.assertEqual(file_2.with_user(self.internal_user).user_permission, 'view')
self.assertEqual(shortcut_2.with_user(self.internal_user).user_permission, 'view')
# we have write access on the target but not on the shortcut
# (the shortcut should be updated in "SUDO" in order to keep them synchronized)
self.assertEqual(file_3.with_user(self.internal_user).user_permission, 'edit')
self.assertEqual(shortcut_3.with_user(self.internal_user).user_permission, 'none')
# ******************
root.with_user(self.internal_user).action_update_access_rights(access_via_link='view')
self.assertEqual(root.access_via_link, 'view')
self.assertEqual(file_1.access_via_link, 'view')
self.assertEqual(file_2.access_via_link, 'none')
self.assertEqual(file_3.access_via_link, 'view')
self.assertEqual(shortcut_1.access_via_link, 'view')
self.assertEqual(shortcut_2.access_via_link, 'none')
self.assertEqual(shortcut_3.access_via_link, 'view')
self.assertEqual(sub_folder.access_via_link, 'view')
self.assertEqual(sub_file.access_via_link, 'view')
# The access update is done only from the target to the shortcut,
# and not from the shortcut to the target
self.assertEqual(target.access_via_link, 'none')
self.assertEqual(shortcut.access_via_link, 'none')
# test the propagation of the members
root.with_user(self.internal_user).action_update_access_rights(
partners={self.internal_user.partner_id: ('edit', None)})
def get_access(document):
return document.access_ids.filtered(
lambda a: a.partner_id == self.internal_user.partner_id).role
self.assertEqual(get_access(root), 'edit')
self.assertEqual(get_access(file_1), 'edit')
self.assertEqual(get_access(file_2), False, 'The user can not write on that document')
self.assertEqual(get_access(file_3), False)
self.assertEqual(get_access(sub_folder), 'edit')
self.assertEqual(get_access(sub_file), 'edit')
# shortcut always synchronized
self.assertEqual(get_access(shortcut_1), 'edit')
self.assertEqual(get_access(shortcut_2), False)
self.assertEqual(get_access(shortcut_3), False)
# The members' update is done only from the target to the shortcut,
# and not from the shortcut to the target
self.assertEqual(get_access(target), False)
self.assertEqual(get_access(shortcut), False)
def test_shortcuts_cant_have_children(self):
folder_a_shortcut = self.folder_a.action_create_shortcut()
with self.assertRaises(ValidationError):
self.env['documents.document'].create({'type': 'folder', 'folder_id': folder_a_shortcut.id})
self.folder_b.folder_id = folder_a_shortcut
self.assertEqual(self.folder_b.folder_id, self.folder_a, "The shortcut's target should have been used")
def test_portal_cant_own_root_documents(self):
self.assertFalse(self.folder_a.folder_id)
with self.assertRaises(UserError):
self.folder_a.owner_id = self.portal_user
@mute_logger('odoo.addons.base.models.ir_rule')
def test_copy_document_access(self):
""" Test that the copy method of Document also copies access rights. """
IN_ONE_DAY = fields.Datetime.now() + datetime.timedelta(days=1)
documents = self.document_gif | self.document_txt
self.folder_b.access_internal = 'none'
self.folder_b.action_update_access_rights(partners={self.internal_user.partner_id: ('view', False)})
# If we have no access on the root folder, copy in "My Drive"
copied_document = documents.with_user(self.internal_user).copy()
self.assertFalse(copied_document.folder_id)
self.assertEqual(copied_document.owner_id, self.internal_user)
self.folder_b.action_update_access_rights(partners={self.internal_user.partner_id: ('edit', False)})
self.document_gif.action_update_access_rights(partners={self.internal_user.partner_id: (False, False)})
self.document_gif.access_internal = 'none'
with self.assertRaises(AccessError, msg='No access to one of the document'):
documents.with_user(self.internal_user).copy()
self.document_txt.action_update_access_rights(partners={self.portal_user.partner_id: ('view', IN_ONE_DAY)})
self.document_gif.action_update_access_rights(partners={self.internal_user.partner_id: ('view', False)})
# Copying folders is also possible
(documents | self.folder_b).with_user(self.internal_user).copy()
gif_copy, txt_copy = documents.with_user(self.internal_user).copy()
self.assertEqual((gif_copy | txt_copy).owner_id, self.internal_user)
self.assertTrue(gif_copy.attachment_id)
gif_copy_access_ids = gif_copy.access_ids
self.assertEqual(
set((a.partner_id.name, a.role) for a in gif_copy_access_ids),
{(self.internal_user.name, False), (self.doc_user.name, 'edit')},
"Existing member and original owner should have access"
)
self.assertSetEqual({*gif_copy_access_ids.mapped('expiration_date')}, {False})
txt_copy_access_by_partner = {a.partner_id: (a.role, a.expiration_date) for a in txt_copy.access_ids}
self.assertEqual(len(txt_copy_access_by_partner), 3)
self.assertEqual(txt_copy_access_by_partner.get(self.portal_user.partner_id), ('view', IN_ONE_DAY))
self.assertEqual(txt_copy_access_by_partner.get(self.doc_user.partner_id), ('edit', False))
self.assertEqual(txt_copy_access_by_partner.get(self.internal_user.partner_id), (False, False))
self.document_txt.action_archive()
with self.assertRaises(UserError, msg='Cannot copy document in the Trash'):
documents.with_user(self.internal_user).copy()
url_document_in_my_folder = self.env['documents.document'].create(
{'name': 'url', 'type': 'url', 'url': 'https://www.odoo.com/', 'owner_id': self.internal_user.id})
_, url_copied = (self.document_gif | url_document_in_my_folder).with_user(self.internal_user).copy()
self.assertEqual(url_copied.owner_id, self.internal_user)
shortcut = self.env['documents.document'].create(
{'name': 'shortcut', 'shortcut_document_id': self.document_gif.id, 'owner_id': self.internal_user.id})
# Copying shortcuts is also supported
shortcuts = url_document_in_my_folder | shortcut
shortcuts.folder_id = self.folder_a
with mute_logger('odoo.addons.documents.models.documents_document'): # Creating document(s) as superuser
copied_shortcuts = shortcuts.copy()
self.assertEqual(copied_shortcuts.folder_id, self.folder_a)
# If we have no access on the root folder, create the shortcuts in "My Drive"
self.document_gif.owner_id = self.internal_user
copied_shortcuts = shortcuts.with_user(self.internal_user).copy()
self.assertFalse(copied_shortcuts.folder_id)
self.assertEqual(copied_shortcuts.owner_id, self.internal_user)
# If we have no access on the root folder, create the folder copy in "My Drive"
self.folder_b.folder_id = self.folder_a
for access in ('edit', 'view'):
self.folder_b.action_update_access_rights(partners={self.internal_user.partner_id: (access, False)})
copied_folder = self.folder_b.with_user(self.internal_user).copy()
self.assertFalse(copied_folder.folder_id)
self.assertEqual(copied_folder.owner_id, self.internal_user)
self.assertEqual(len(copied_folder.children_ids), 6)
self.assertNotEqual(copied_folder.children_ids, self.folder_b.children_ids)
# If a manager copies a root folder, do not change the owner
self.folder_b.folder_id = False
self.assertEqual(self.folder_b.owner_id, self.doc_user)
self.assertFalse(self.folder_b._is_company_root_folder())
copied_folder = self.folder_b.with_user(self.document_manager).copy()
self.assertFalse(copied_folder.folder_id)
self.assertEqual(copied_folder.owner_id, self.document_manager)
self.folder_b.owner_id = False
self.assertTrue(self.folder_b._is_company_root_folder())
copied_folder = self.folder_b.with_user(self.document_manager).copy()
self.assertFalse(copied_folder.folder_id)
self.assertFalse(copied_folder.owner_id)
# The user has the access on the documents, but he can not create pinned folders
# Create the copy in "My Drive"
self.folder_b.access_internal = 'edit'
copied_folder = self.folder_b.with_user(self.internal_user).copy()
self.assertFalse(copied_folder.folder_id)
self.assertEqual(copied_folder.owner_id, self.internal_user)
# Check that portal can not copy in his drive (no drive)
self.folder_b.action_update_access_rights(access_via_link = 'edit', partners={self.portal_user.partner_id: ('edit', False)})
with self.assertRaises(ValidationError):
self.folder_b.with_user(self.portal_user).copy()
@mute_logger('odoo.addons.base.models.ir_rule')
def test_deleting_in_non_edit_folder(self):
"""Check required rights on document and containing folder to archive/unlink.
Archiving a document requires being the owner of the document or having
edit permission on both the document and its containing folder.
"""
# Manager creates a folder in their drive and gives editor right to user
folder = self.env['documents.document'].with_user(self.document_manager).create({
'folder_id': False,
'name': 'Folder with User',
'type': 'folder',
'access_ids': [Command.create({'partner_id': self.doc_user.partner_id.id, 'role': 'edit'})],
})
# User creates two documents in that folder
user_doc_1, user_doc_2, user_doc_3 = user_docs = self.env['documents.document'].with_user(self.doc_user).create([{
'folder_id': folder.id,
'name': f'User doc #{idx}',
'type': 'binary',
} for idx in range(3)])
self.assertEqual(user_docs.owner_id, self.doc_user)
# Manager removes user's role on the folder
folder.with_user(self.document_manager).action_update_access_rights(
partners={self.doc_user.partner_id: (False, None)},
)
self.assertEqual(folder.with_user(self.doc_user).user_permission, 'none')
user_doc_1.with_user(self.doc_user).action_archive()
self.assertFalse(user_doc_1.active, 'Owner should be able to archive the document')
user_doc_1.with_user(self.doc_user).action_unarchive()
self.assertTrue(user_doc_1.active, 'Owner should be able to restore the document')
user_doc_1.with_user(self.doc_user).unlink()
self.assertFalse(user_doc_1.exists(), 'Owner should be able to unlink the document')
user_doc_2.with_user(self.document_manager).owner_id = self.document_manager
user_doc_2.with_user(self.document_manager).action_update_access_rights(
partners={self.doc_user.partner_id: ('edit', None)},
)
self.assertEqual(user_doc_2.with_user(self.doc_user).user_permission, 'edit')
with self.assertRaises(UserError, msg="Editor without edit permission on folder shouldn't be able to archive"):
user_doc_2.with_user(self.doc_user).action_archive()
with self.assertRaises(AccessError, msg="Editor without edit permission on folder shouldn't be able to archive"):
user_doc_2.with_user(self.doc_user).active = False
with self.assertRaises(UserError, msg="Editor without edit permission on folder shouldn't be able to delete"):
user_doc_2.with_user(self.doc_user).unlink()
user_doc_2.folder_id.sudo().access_internal = 'none'
with self.assertRaises(UserError, msg="Editor with no permission on folder shouldn't be able to delete"):
user_doc_2.with_user(self.doc_user).unlink()
user_doc_2.folder_id.sudo().access_internal = 'view'
with self.assertRaises(UserError, msg="Editor with view permission on folder shouldn't be able to delete"):
user_doc_2.with_user(self.doc_user).unlink()
user_doc_3.with_user(self.document_manager).owner_id = self.document_manager
user_doc_3.with_user(self.document_manager).action_update_access_rights(
partners={self.doc_user.partner_id: ('view', None)},
)
self.assertFalse((user_doc_2 | user_doc_3)._filtered_access('unlink'))
def test_updating_owner(self):
self.assertEqual(self.folder_a_a.owner_id, self.doc_user)
self.folder_a.action_update_access_rights(access_internal='edit')
self.assertEqual(self.folder_a_a.with_user(self.internal_user).user_permission, 'edit')
with self.assertRaises(AccessError):
self.folder_a_a.with_user(self.internal_user).owner_id = self.internal_user
self.folder_a_a.action_update_access_rights(access_internal='none')
self.assertEqual(self.folder_a_a.with_user(self.internal_user).user_permission, 'none')
# Add a document in their folder but set their access to viewer.
self.document_txt.folder_id = self.folder_a_a
self.document_txt.owner_id = self.document_manager
self.document_txt.action_update_access_rights(partners={self.doc_user.partner_id: ('view', False)})
self.assertEqual(self.document_txt.with_user(self.doc_user).user_permission, 'view')
self.folder_a_a.with_user(self.doc_user).owner_id = self.internal_user
self.assertEqual(self.folder_a_a.with_user(self.doc_user).user_permission, 'edit',
"Previous owner should have kept edit permission on folder")
self.assertEqual(self.document_txt.with_user(self.doc_user).user_permission, 'view',
"Previous owner should not have gained rights on folder content")
@mute_logger('odoo.addons.base.models.ir_rule')
def test_embedded_action(self):
"""Test embedding and running actions on the right records"""
self.folder_a.action_update_access_rights(
access_internal="edit",
partners={self.portal_user.partner_id: ('edit', False)}
)
with self.assertRaises(AccessError):
self.env['documents.document'].with_user(self.internal_user).action_folder_embed_action(
self.folder_a.id, self.server_action.id)
self.server_action.group_ids = self.env.ref('documents.group_documents_manager')
with self.assertRaises(UserError):
self.env['documents.document'].with_user(self.doc_user).action_folder_embed_action(
self.folder_a.id, self.server_action.id)
self.server_action.group_ids = self.env.ref('base.group_user')
self.env['documents.document'].with_user(self.doc_user).action_folder_embed_action(
self.folder_a.id, self.server_action.id)
doc = self.env['documents.document'].create({'name': 'A request', 'folder_id': self.folder_a.id})
embedded_action = doc.available_embedded_actions_ids
self.assertEqual(len(embedded_action), 1)
doc_in_context = self.env['documents.document'].with_context(
active_model='documents.document', active_id=doc.id)
with self.assertRaises(AccessError):
doc_in_context.with_user(self.portal_user).action_execute_embedded_action(embedded_action.id)
doc_in_context.with_user(self.internal_user).action_execute_embedded_action(embedded_action.id)
with self.assertRaises(UserError):
doc_in_context.with_context(
active_ids=(doc | self.folder_a).ids,
).with_user(self.internal_user).action_execute_embedded_action(embedded_action.id)
doc.action_update_access_rights(access_internal='none')
with self.assertRaises(AccessError):
doc_in_context.with_context(
active_id=doc.id
).with_user(self.internal_user).action_execute_embedded_action(embedded_action.id)
@mute_logger('odoo.addons.base.models.ir_rule')
def test_embedded_action_shortcut_folder(self):
"""Test embedding and running actions on the right records"""
self.folder_a.action_update_access_rights(
access_internal="edit",
partners={self.portal_user.partner_id: ('edit', False)}
)
doc = self.env['documents.document'].create({'name': 'A request', 'folder_id': self.folder_a.id})
self.assertFalse(doc.available_embedded_actions_ids)
folder_a_shortcut = self.folder_a.action_create_shortcut()
self.assertFalse(self.folder_a._get_folder_embedded_actions([self.folder_a.id]).get(self.folder_a.id))
self.assertFalse(self.folder_a._get_folder_embedded_actions([folder_a_shortcut.id]).get(folder_a_shortcut.id))
# From here, same as `test_embedded_action` but using the shortcut instead
with self.assertRaises(AccessError):
self.env['documents.document'].with_user(self.internal_user).action_folder_embed_action(
folder_a_shortcut.id, self.server_action.id)
self.server_action.group_ids = self.env.ref('documents.group_documents_manager')
with self.assertRaises(UserError):
self.env['documents.document'].with_user(self.doc_user).action_folder_embed_action(
folder_a_shortcut.id, self.server_action.id)
self.server_action.group_ids = self.env.ref('base.group_user')
self.env['documents.document'].with_user(self.doc_user).action_folder_embed_action(
folder_a_shortcut.id, self.server_action.id)
self.assertEqual(len(doc.available_embedded_actions_ids), 1)
self.assertTrue(self.folder_a._get_folder_embedded_actions([self.folder_a.id])[self.folder_a.id])
self.assertEqual(self.folder_a._get_folder_embedded_actions([folder_a_shortcut.id])[folder_a_shortcut.id],
self.folder_a._get_folder_embedded_actions([self.folder_a.id])[self.folder_a.id])
self.folder_a.action_update_access_rights(access_internal="view")
self.internal_user.group_ids |= self.env.ref('documents.group_documents_user')
document = self.document_gif.with_user(self.internal_user)
document.sudo().access_internal = 'edit'
action = self.env['ir.actions.server'].create({
'name': 'Test Action',
'model_id': self.env['ir.model']._get_id('documents.document'),
'update_path': 'folder_id',
'usage': 'documents_embedded',
'state': 'object_write',
'resource_ref': f'documents.document,{self.folder_a.id}',
}).with_user(self.internal_user)
# Check that we need write access on the folder to pin it
self.assertEqual(document.folder_id.user_permission, 'view')
with self.assertRaises(AccessError):
document.env['documents.document'].action_folder_embed_action(document.folder_id.id, action.id)
document.folder_id.sudo().access_internal = 'edit'
with RecordCapturer(self.env['ir.embedded.actions'], []) as capturer:
document.env['documents.document'].action_folder_embed_action(document.folder_id.id, action.id)
embedded = capturer.records
self.assertEqual(len(embedded), 1)
# Try to move an existing embedded action
self.assertEqual(self.folder_a.with_user(self.internal_user).user_permission, 'view')
self.assertEqual(self.folder_b.with_user(self.internal_user).user_permission, 'edit')
with self.assertRaises(AccessError):
embedded.with_user(self.internal_user).parent_res_id = self.folder_a.id
embedded.parent_res_id = self.folder_a.id
with self.assertRaises(AccessError):
embedded.with_user(self.internal_user).parent_res_id = self.folder_b.id
def test_groupless_embedded_action_availability(self):
""" Ensure that an embedded action which should otherwise be visible to a given document
record remains visible in the case where it has `groups_ids=[]`.
"""
embedded_action = self.env['ir.embedded.actions'].create({
'name': 'public action',
'parent_action_id': self.env.ref('documents.document_action').id,
'action_id': self.server_action.id,
'parent_res_model': 'documents.document',
'groups_ids': [Command.clear()],
'parent_res_id': self.document_txt.folder_id.id,
})
self.assertIn(embedded_action, self.document_txt.available_embedded_actions_ids)
@freeze_time('2025-02-26 15:02:00')
def test_log_owner_access_when_passing_access_ids(self):
doc = self.env['documents.document'].with_user(self.doc_user).create({
'access_ids': [Command.create({'partner_id': self.document_manager.partner_id.id, 'role': 'edit'})]})
self.assertEqual(
set(doc.access_ids.mapped(lambda a: (a.partner_id, a.role, a.last_access_date))),
{
(self.document_manager.partner_id, 'edit', False),
(self.doc_user.partner_id, False, fields.Datetime.now())
}
)
doc = self.env['documents.document'].with_user(self.doc_user).create({'access_ids': False})
self.assertEqual(set(doc.access_ids.mapped(lambda a: (a.partner_id, a.role, a.last_access_date))),
{(self.doc_user.partner_id, False, fields.Datetime.now())})
@users('[email protected]')
def test_embedded_actions_unembed(self):
"""Check that actions can be un-embedded as records may exist with/without these groups.
Groups defined on embedded actions are not supported while those on the server actions
are (tested in test_embedded_action)."""
folder = self.document_txt.folder_id
self.assertFalse(self.document_txt.available_embedded_actions_ids)
self.document_txt.invalidate_recordset(['available_embedded_actions_ids'])
embedded_action_vals = {
'name': 'public action',
'parent_action_id': self.env.ref('documents.document_action').id,
'action_id': self.server_action.id,
'parent_res_model': 'documents.document',
'parent_res_id': folder.id,
}
embedded_action_with_group = self.env['ir.embedded.actions'].create({
**embedded_action_vals,
'groups_ids': [self.env.ref('documents.group_documents_manager').id],
})
self.assertIn(embedded_action_with_group, self.document_txt.available_embedded_actions_ids)
self.document_txt.invalidate_recordset(['available_embedded_actions_ids'])
self.env['documents.document'].action_folder_embed_action(folder.id, self.server_action.id)
self.assertFalse(self.document_txt.available_embedded_actions_ids,
"Embedded action with group should have been removed.")
self.document_txt.invalidate_recordset(['available_embedded_actions_ids'])
self.env['documents.document'].action_folder_embed_action(folder.id, self.server_action.id)
self.assertTrue(self.document_txt.available_embedded_actions_ids,
"Embedded action should have been created.")
self.document_txt.available_embedded_actions_ids.groups_ids = False
self.env['documents.document'].action_folder_embed_action(folder.id, self.server_action.id)
self.assertFalse(self.document_txt.available_embedded_actions_ids,
"Embedded action without group should have been removed.")
self.document_txt.invalidate_recordset(['available_embedded_actions_ids'])
self.env['ir.embedded.actions'].create([{
**embedded_action_vals,
'groups_ids': [self.env.ref('documents.group_documents_manager').id],
},
embedded_action_vals
])
self.assertTrue(self.document_txt.available_embedded_actions_ids)
self.document_txt.invalidate_recordset(['available_embedded_actions_ids'])
self.env['documents.document'].action_folder_embed_action(folder.id, self.server_action.id)
self.assertFalse(self.document_txt.available_embedded_actions_ids,
"Embedded action with and without group should have been removed.")
def test_restricted_folder_dependant_fields(self):
"""Check that reading fields depending on restricted folder doesn't raise (access) error."""
self.folder_b.access_internal = 'none'
self.assertEqual(self.folder_b.with_user(self.internal_user).user_permission, 'none')
self.assertFalse(self.env['documents.document'].with_user(self.internal_user)._get_folder_embedded_actions(self.folder_b.id))
self.document_gif.access_internal = 'view'
self.assertEqual(self.document_gif.with_user(self.internal_user).user_permission, 'view')
self.document_gif.invalidate_recordset() # cache pollution
self.assertFalse(self.document_gif.with_user(self.internal_user).available_embedded_actions_ids)
# Private method now returns actions because there are accessible child(ren)
self.env['documents.document'].with_user(self.internal_user)._get_folder_embedded_actions(self.folder_b.id)
with self.assertRaises(UserError):
# Public method doesn't need to, actions are only embeddable by folder editors.
self.env['documents.document'].with_user(self.internal_user).get_documents_actions(self.folder_b.id)
folder_b_embedded_server_action_ids = {
server_action['id']
for server_action in self.folder_b.action_folder_embed_action(self.folder_b.id, self.server_action.id)
if server_action['is_embedded']
}
embedded_action_folder_b = self.document_gif.with_user(self.internal_user).available_embedded_actions_ids
self.assertIn(embedded_action_folder_b.action_id.id, folder_b_embedded_server_action_ids)
def test_tracking_creation_on_access_rights_changes(self):
self.folder_a_a.unlink()
# 10 sub folders
sub_folder_ids = self.env['documents.document'].create([
{
'type': 'folder',
'name': f'sub folder A-{letter}',
'folder_id': self.folder_a.id,
} for letter in ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'j')
])
# 10 sub sub folders = 100 sub sub folders in total
for sub_folder in sub_folder_ids:
self.env['documents.document'].create([
{
'type': 'folder',
'name': f'{sub_folder.name}-{count}',
'folder_id': sub_folder.id,
'children_ids': [Command.create({
'name': f'File {count}',
}) for count in range(10)]
# 10 files in each sub sub folders = 1000 files
} for count in range(10)
])
self.assertEqual(len(self.folder_a.children_ids), 10)
for child in self.folder_a.children_ids:
self.assertEqual(len(child.children_ids), 10)
self.assertEqual(self.folder_a.access_internal, 'view')
def _get_mail_messages(doc_ids):
return self.env['mail.message'].search([
('res_id', 'in', doc_ids),
('tracking_value_ids', '!=', False)
])
folder_a_d = sub_folder_ids[3]
self.assertEqual(folder_a_d.access_internal, 'view')
folder_a_d.action_update_access_rights(
access_internal='edit', partners={self.portal_user.partner_id.id: ('view', False)})
with self.enter_registry_test_mode():
self.env.ref('documents.ir_cron_documents_access_tracking').method_direct_trigger()
folder_a_d_message_ids = _get_mail_messages(folder_a_d.ids + folder_a_d.children_ids.ids)
self.assertTrue(folder_a_d_message_ids.exists())
for message in folder_a_d_message_ids:
self.assertTracking(message, [
('access_internal', 'char', 'Viewer', 'Editor')
], strict=True)
self.assertIn('Portal user has gained access as\nViewer', html2plaintext(message.body))
self.folder_a.action_update_access_rights(
access_internal='edit', access_via_link='view', is_access_via_link_hidden=True,
partners={self.portal_user.partner_id.id: ('edit', False)})
while self.env['documents.access.tracking'].search_count([]) > 0:
with self.enter_registry_test_mode():
self.env.ref('documents.ir_cron_documents_access_tracking').method_direct_trigger()
other_folders_ids = sub_folder_ids - folder_a_d
message_ids = _get_mail_messages(other_folders_ids.ids + other_folders_ids.children_ids.ids)
self.assertTrue(message_ids.exists())
for message in message_ids:
self.assertTracking(message, [
('access_internal', 'char', 'Viewer', 'Editor'),
('access_via_link', 'char', 'None', 'Viewer'),
], strict=True)
self.assertIn('Portal user has gained access as\nEditor', html2plaintext(message.body))
folder_a_d_latest_message_ids = _get_mail_messages(folder_a_d.ids + folder_a_d.children_ids.ids).filtered(
lambda message: message.id not in folder_a_d_message_ids.ids
)
for message in folder_a_d_latest_message_ids:
self.assertTracking(message, [
('access_via_link', 'char', 'None', 'Viewer'),
# 'is_access_via_link_hidden' is not propagated
], strict=True)
self.assertIn('Portal user rights changed from\nViewer\nto\nEditor', html2plaintext(message.body))
def test_members_access_on_move_documents(self):
self.document_txt.action_update_access_rights(partners={self.internal_user.partner_id.id: ('view', False)})
self.env['documents.access'].create({
'document_id': self.folder_a.id,
'partner_id': self.internal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
})
self.assertIn(
(self.internal_user.partner_id, 'view'),
self.document_txt.access_ids.mapped(lambda a: (a.partner_id, a.role)))
self.assertIn(
(self.internal_user.partner_id, False),
self.folder_a.access_ids.mapped(lambda a: (a.partner_id, a.role)))
self.document_txt.folder_id = self.folder_a.id
self.assertIn(
(self.internal_user.partner_id, 'view'),
self.document_txt.access_ids.mapped(lambda a: (a.partner_id, a.role)))
def test_members_invitation(self):
Access = self.env['documents.access']
internal_access = Access.create({
'document_id': self.folder_a.id,
'partner_id': self.internal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
'role': 'view',
})
with self.assertRaises(UserError):
internal_access._get_member_signup_token()
portal_access = Access.create({
'document_id': self.folder_a.id,
'partner_id': self.portal_user.partner_id.id,
'last_access_date': fields.Datetime.now(),
'role': 'view',
})
with self.assertRaises(UserError):
portal_access._get_member_signup_token()
public_access = Access.create({
'document_id': self.folder_a.id,
'partner_id': self.env["res.partner"].create({'name': 'Test'}).id,
'last_access_date': fields.Datetime.now(),
'role': 'view',
})
token = public_access._get_member_signup_token()
self.assertEqual(Access._get_member_from_token(public_access.id, token), public_access)
self.assertNotEqual(Access._get_member_from_token(public_access.id, 'invalid' + token), public_access)
public_access.expiration_date = fields.Datetime.now() + datetime.timedelta(days=1)
token = public_access._get_member_signup_token()
self.assertEqual(Access._get_member_from_token(public_access.id, token), public_access)
# use an old token
token = public_access._get_member_signup_token()
public_access.expiration_date = fields.Datetime.now() - datetime.timedelta(days=1)
self.assertFalse(Access._get_member_from_token(public_access.id, token))
public_access = Access.create({
'document_id': self.folder_a.id,
'partner_id': self.env["res.partner"].create({'name': 'Test'}).id,
'last_access_date': fields.Datetime.now(),
})
with self.assertRaises(UserError):
public_access._get_member_signup_token()
def test_permissions_internal_propagation_on_folder_moves(self):
not_secret_folder = self.env['documents.document'].create({
'name': 'Not Secret Folder',
'type': 'folder',
'access_internal': 'edit',
'children_ids': [
Command.create({
'name': 'Secret Folder',
'type': 'folder',
'access_internal': 'none',
}),
],
'owner_id': self.document_manager.id # not odoobot
})
secret_folder = not_secret_folder.children_ids[0]
self.assertEqual(secret_folder.with_user(self.internal_user).user_permission, 'none')
none = self.env['documents.document'].with_user(self.internal_user).create({
'name': 'none',
'type': 'folder',
'access_internal': 'none',
})
editor = self.env['documents.document'].with_user(self.internal_user).create({
'name': 'editor',
'type': 'folder',
'access_internal': 'edit',
})
not_secret_folder.with_user(self.internal_user).action_move_folder(target=str(none.id))
none.with_user(self.internal_user).action_move_folder(target=str(editor.id))
with self.assertRaises(AccessError):
secret_folder.with_user(self.internal_user).check_access('read')
self.assertEqual(secret_folder.access_internal, 'none')
def test_permissions_member_and_owner_propagation_on_folder_moves(self):
member_folder = self.env['documents.document'].create({
'name': 'Member Folder',
'type': 'folder',
'access_internal': 'none',
'children_ids': [
Command.create({
'name': 'Secret Folder',
'type': 'folder',
'access_internal': 'none',
'access_ids': False,
}),
],
'access_ids': [Command.create({'partner_id': self.internal_user.partner_id.id, 'role': 'edit'})],
'owner_id': self.document_manager.id # not odoobot
})
secret_folder = member_folder.children_ids[0]
self.assertEqual(secret_folder.with_user(self.doc_user).user_permission, 'none')
none = self.env['documents.document'].with_user(self.internal_user).create({
'name': 'none',
'type': 'folder',
'access_internal': 'none',
})
doc_user_editor = self.env['documents.document'].with_user(self.internal_user).create({
'name': 'editor',
'type': 'folder',
'access_internal': 'none',
'access_ids': [Command.create({'partner_id': self.doc_user.partner_id.id, 'role': 'edit'})]
})
member_folder.with_user(self.internal_user).action_move_folder(target=str(none.id))
none.with_user(self.internal_user).action_move_folder(target=str(doc_user_editor.id))
none.with_user(self.doc_user).check_access('read')
member_folder.with_user(self.doc_user).check_access("read")
self.assertIn(self.doc_user.partner_id, member_folder.access_ids.partner_id)
with self.assertRaises(AccessError):
secret_folder.with_user(self.internal_user).check_access('read')
with self.assertRaises(AccessError):
secret_folder.with_user(self.doc_user).check_access('read')
self.assertNotIn(self.internal_user.partner_id, secret_folder.access_ids.partner_id)
self.assertNotIn(self.doc_user.partner_id, secret_folder.access_ids.partner_id)
@users('[email protected]')
def test_owner_edition(self):
"""Check that with edit permission, users can change the owner of a document he owns or without owners."""
document = self.env['documents.document'].create({'name': 'test'})
self.assertEqual(document.owner_id, self.env.user)
self.assertEqual(document.user_folder_id, 'MY')
document.owner_id = False
self.assertEqual(document.user_permission, 'edit', 'Removing the owner gives him edit permission')
self.assertEqual(document.user_folder_id, 'COMPANY')
document.owner_id = self.env.user
self.assertEqual(document.user_folder_id, 'MY')
document.owner_id = False
document.owner_id = self.document_manager
with self.assertRaises(AccessError, msg='You cannot change the owner of documents you do not own.'):
document.owner_id = self.env.user