[FIX] core: avoid access error when flushing

This patch guarantees that computed fields with `compute_sudo=True` do
not cause access errors when fetched from database.

This fixes a non-deterministic access error upon `commit`, where fields
are recomputed and flushed with some random environment.  The method
`recompute` performs its duty by accessing the fields to recompute.  The
compute method of a field is called but does not assign it.  As the
field is not in cache, its value is read from database.  However, as the
latter is not done in superuser mode, it may cause some access error.

closes odoo/odoo#61842

X-original-commit: cfb942ab5902e0992434eb2c7db2940526df404e
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Raphael Collet
2020-11-17 11:32:42 +00:00
parent a5fc632682
commit af6c56711f
2 changed files with 27 additions and 0 deletions
@@ -636,6 +636,29 @@ class TestFields(TransactionCaseWithUserDemo):
self.assertEqual(record.bars, False)
self.assertEqual(record.bares, False)
def test_16_compute_unassigned_access_error(self):
# create a real record
record = self.env['test_new_api.compute.unassigned'].create({})
record.flush()
# alter access rights: regular users cannot read 'record'
access = self.env.ref('test_new_api.access_test_new_api_compute_unassigned')
access.perm_read = False
# switch to environment with user demo
record = record.with_user(self.user_demo)
record.env.cache.invalidate()
# check that the record is not accessible
with self.assertRaises(AccessError):
record.bars
# modify the record and flush() changes with the current environment:
# this should not trigger an access error, even if unassigned computed
# fields are fetched from database
record.foo = "X"
record.flush()
def test_20_float(self):
""" test rounding of float fields """
record = self.env['test_new_api.mixed'].create({})
+4
View File
@@ -929,6 +929,7 @@ class Field(MetaField('DummyField', (object,), {})):
# only a single record may be accessed
record.ensure_one()
recomputed = False
if self.compute and (record.id in env.all.tocompute.get(self, ())) \
and not env.is_protected(self, record):
# self must be computed on record
@@ -941,6 +942,7 @@ class Field(MetaField('DummyField', (object,), {})):
self.compute_value(recs)
except (AccessError, MissingError):
self.compute_value(record)
recomputed = True
try:
value = env.cache.get(record, self)
@@ -949,6 +951,8 @@ class Field(MetaField('DummyField', (object,), {})):
if self.store and record.id:
# real record: fetch from database
recs = record._in_cache_without(self)
if recomputed and self.compute_sudo:
recs = recs.sudo()
try:
recs._fetch_field(self)
except AccessError: