From b7865502e49be27aa1c72adb0a49676dc7c6b747 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 20 Nov 2013 11:06:27 +0100 Subject: [PATCH] [FIX] orm.browse_record: access error due to prefetch of indirectly referenced records (OPW #596679) The browse_record prefetching algorithm attempts to load data for all known records from the requested model (i.e. all IDs present in the browse cache), regardless of how indirectly/remotely they were referenced. An indirect parent record may therefore be prefetched along with its directly browsed children, possibly crossing company boundaries involuntarily. This patch implements a fallback mechanism when the prefetching failed due to what looks like an ACL restriction. This being a fuzzy concept at the moment, it does its best to only catch a restricted set of exceptions, and retry loading the data for the directly requested ID only. This may cause a small performance penalty in case of real errors (with some spurious logging too), but should only be triggered in very few cases. The downside when this happens is that the prefetching for that model gets effectively disabled, requiring multiple SQL queries for further access to the data of the other directly browsed records. This EAFP approach seems safer and faster than a LBYL technique where we would have to filter all indirect m2o references according to ACLs before allowing them to enter the cache. lp bug: https://launchpad.net/bugs/1238042 fixed lp bug: https://launchpad.net/bugs/1212429 fixed bzr revid: odo@openerp.com-20131120100627-031fljyf4ckprc9b --- openerp/osv/orm.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index d250588cede..e46b385b1b3 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -398,7 +398,15 @@ class browse_record(object): ids = filter(lambda id: name not in self._data[id], self._data.keys()) # read the results field_names = map(lambda x: x[0], fields_to_fetch) - field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write") + try: + field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write") + except (openerp.exceptions.AccessError, except_orm): + if len(ids) == 1: + raise + # prefetching attempt failed, perhaps we're violating ACL restrictions involuntarily + _logger.info('Prefetching attempt for fields %s on %s failed for ids %s, re-trying just for id %s', field_names, self._model._name, ids, self._id) + ids = [self._id] + field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write") # TODO: improve this, very slow for reports if self._fields_process: