[FIX] core: access to ir.attachment without res_model/res_id

This revision restores the behavior of 16.0 regarding
the access to attachments with no `res_model`/`res_id`
set.

It was changed unexpectedly during the refactoring
of `read`/`fetch`.

Basically, the below used to raise an AccessError in 16.0:

Create an attachment without res_model/res_id and access it
as another user
```py
attachment = self.env['ir.attachment'].create({'name': 'foo'})
attachment.invalidate_recordset()
attachment.with_user(self.env.ref('base.user_demo')).datas
```

While it no longer raises the AccessError in saas-16.2

The reason is that the override of `_read` calling
`check` has been removed in saas-16.2:
https://github.com/odoo/odoo/commit/e962860c6f0d8ec9e50bb376e1faab5c7bc69374#diff-ab3dadc37163820f8e863edb1dd216f8b0e7ccf19cf4e2052577a7bae7ddd7e8L606-L608

in favor to do the check in `_search`:

https://github.com/odoo/odoo/commit/ae31aebf095392d8c91f49ac279e1949997dc245

But there is a difference of behavior between the checks in `check` and `_search`
regarding attachments without `res_model`/`res_id`:

https://github.com/odoo/odoo-security/blob/bc538f6944461a642bac0f757ec96d7f8cc14c9a/odoo/addons/base/models/ir_attachment.py#L454-L465

https://github.com/odoo/odoo-security/blob/bc538f6944461a642bac0f757ec96d7f8cc14c9a/odoo/addons/base/models/ir_attachment.py#L566-L573

The goal of this revision is to have an unified behavior regarding the treatment
of attachments without `res_model`/`res_id` in both `check` and `_search`.

closes odoo/odoo#119768

X-original-commit: 82c36285b897b72a956da0585ebd62f4c2d33784
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2023-04-26 07:53:03 +02:00
parent 67669db8d5
commit aee3af0abe
2 changed files with 31 additions and 4 deletions
+3 -3
View File
@@ -544,13 +544,13 @@ class IrAttachment(models.Model):
model_attachments = defaultdict(lambda: defaultdict(set)) # {res_model: {res_id: set(ids)}}
for id_, res_model, res_id, res_field, public, create_uid in rows:
all_ids.append(id_)
if not res_model or public:
if public:
allowed_ids.add(id_)
continue
if res_model and not res_id and create_uid == self.env.uid:
if not res_id and (self.env.is_system() or create_uid == self.env.uid):
allowed_ids.add(id_)
continue
if not (res_field and disable_binary_fields_attachments):
if not (res_field and disable_binary_fields_attachments) and res_model and res_id:
model_attachments[res_model][res_id].add(id_)
# check permissions on records model by model
+28 -1
View File
@@ -284,8 +284,10 @@ class TestPermissions(TransactionCase):
self.env.flush_all()
a.invalidate_recordset()
def test_no_read_permission(self):
def test_read_permission(self):
"""If the record can't be read, the attachment can't be read either
If the attachment is public, the attachment can be read even if the record can't be read
If the attachment has no res_model/res_id, it can be read by its author and admins only
"""
# check that the information can be read out of the box
self.attachment.datas
@@ -295,6 +297,31 @@ class TestPermissions(TransactionCase):
with self.assertRaises(AccessError):
self.attachment.datas
# Make the attachment public
self.attachment.sudo().public = True
# Check the information can be read again
self.attachment.datas
# Remove the public access
self.attachment.sudo().public = False
# Check the record can no longer be accessed
with self.assertRaises(AccessError):
self.attachment.datas
# Create an attachment as user without res_model/res_id
attachment_user = self.Attachments.create({'name': 'foo'})
# Check the user can access his own attachment
attachment_user.datas
# Create an attachment as superuser without res_model/res_id
attachment_admin = self.Attachments.with_user(odoo.SUPERUSER_ID).create({'name': 'foo'})
# Check the record cannot be accessed by a regular user
with self.assertRaises(AccessError):
attachment_admin.with_user(self.env.user).datas
# Check the record can be accessed by an admin (other than superuser)
admin_user = self.env.ref('base.user_admin')
# Safety assert that base.user_admin is not the superuser, otherwise the test is useless
self.assertNotEqual(odoo.SUPERUSER_ID, admin_user.id)
attachment_admin.with_user(admin_user).datas
def test_with_write_permissions(self):
"""With write permissions to the linked record, attachment can be
created, updated, or deleted (or copied).