From 7a15e52cea8133fedf632edf4fd37fda930b58fc Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 9 Mar 2017 15:42:00 +0100 Subject: [PATCH] [FIX] base: check ir.ui.view recursion --- openerp/addons/base/ir/ir_ui_view.py | 6 ++++++ openerp/addons/base/tests/test_views.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index ea90e3b2fbe..410f1acf987 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -111,6 +111,7 @@ xpath_utils['hasclass'] = _hasclass class view(osv.osv): _name = 'ir.ui.view' + _parent_name = 'inherit_id' # used for recursion check def _get_model_data(self, cr, uid, ids, fname, args, context=None): result = dict.fromkeys(ids, False) @@ -188,6 +189,10 @@ class view(osv.osv): return self._relaxng_validator def _check_xml(self, cr, uid, ids, context=None): + # As all constraints are verified on create/write, we must re-check that there is no + # recursion before calling `read_combined` to avoid an infinite loop. + if not self._check_recursion(cr, uid, ids, context=context): + return True # pretend arch is valid to avoid misleading user about the error. if context is None: context = {} context = dict(context, check_view_ids=ids) @@ -225,6 +230,7 @@ class view(osv.osv): ] _constraints = [ (_check_xml, 'Invalid view definition', ['arch']), + (osv.osv._check_recursion, 'You cannot create recursive inherited views.', ['inherit_id']), ] def _auto_init(self, cr, context=None): diff --git a/openerp/addons/base/tests/test_views.py b/openerp/addons/base/tests/test_views.py index 0de418b6f5a..bd297d2789c 100644 --- a/openerp/addons/base/tests/test_views.py +++ b/openerp/addons/base/tests/test_views.py @@ -9,6 +9,7 @@ from lxml.builder import E from psycopg2 import IntegrityError +from openerp.exceptions import ValidationError from openerp.tests import common import openerp.tools @@ -243,6 +244,25 @@ class TestViewInheritance(ViewCase): self.View.default_view( self.cr, self.uid, model=self.model, view_type='graph')) + def test_no_recursion(self): + r1 = self.makeView('R1') + with self.assertRaises(ValidationError), self.cr.savepoint(): + self.View.write(self.cr, self.uid, r1, {'inherit_id': r1}) + + r2 = self.makeView('R2', r1) + r3 = self.makeView('R3', r2) + with self.assertRaises(ValidationError), self.cr.savepoint(): + self.View.write(self.cr, self.uid, r2, {'inherit_id': r3}) + + with self.assertRaises(ValidationError), self.cr.savepoint(): + self.View.write(self.cr, self.uid, r1, {'inherit_id': r3}) + + with self.assertRaises(ValidationError), self.cr.savepoint(): + self.View.write(self.cr, self.uid, r1, { + 'inherit_id': r1, + 'arch': self.arch_for('itself', parent=True), + }) + class TestApplyInheritanceSpecs(ViewCase): """ Applies a sequence of inheritance specification nodes to a base architecture. IO state parameters (cr, uid, model, context) are used for