From 31518bc09b1ae2be37c3555799e2c122f0a4dbcd Mon Sep 17 00:00:00 2001 From: Ajay Javiya <aja@openerp.com> Date: Wed, 17 Jun 2015 15:12:28 +0530 Subject: [PATCH] [IMP] base: make group "Technical Features" effective in debug mode only In method `user_has_groups`, make "Technical Features" effective in debug mode. Make the group "Employees" inherit "Technical Features". Make the group "Technical Features" invisible in the user form view. Remove useless `ir.rule` attached on group "Technical Features". Fix `test_acl` by avoiding the tricks around the group "Technical Features". --- addons/base_import/models.py | 2 +- addons/mail/security/mail_security.xml | 11 ----- addons/web/controllers/main.py | 2 +- openerp/addons/base/ir/ir_ui_menu.py | 13 +++--- openerp/addons/base/res/res_users.py | 7 +++- .../addons/base/security/base_security.xml | 3 ++ openerp/addons/base/tests/test_acl.py | 42 +++++++++---------- openerp/http.py | 5 ++- openerp/models.py | 13 +++++- 9 files changed, 54 insertions(+), 44 deletions(-) diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 7463a02028e4..7cbd0d79e09f 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -149,7 +149,7 @@ class ir_import(orm.TransientModel): elif field['type'] == 'one2many' and depth: f['fields'] = self.get_fields( cr, uid, field['relation'], context=context, depth=depth-1) - if self.pool['res.users'].has_group(cr, uid, 'base.group_no_one'): + if self.user_has_groups(cr, uid, 'base.group_no_one'): f['fields'].append({'id' : '.id', 'name': '.id', 'string': _("Database ID"), 'required': False, 'fields': []}) fields.append(f) diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml index 40263e93b874..a539e4d429f8 100644 --- a/addons/mail/security/mail_security.xml +++ b/addons/mail/security/mail_security.xml @@ -23,17 +23,6 @@ <field name="perm_read" eval="False"/> </record> - <!-- If technical rights then read and write others subscriptions --> - <record id="mail_followers_rule_write_admin" model="ir.rule"> - <field name="name">mail.followers: read and write others entries</field> - <field name="model_id" ref="model_mail_followers"/> - <field name="groups" eval="[(4, ref('base.group_no_one'))]"/> - <field name="domain_force">[]</field> - <field name="perm_create" eval="False"/> - <field name="perm_unlink" eval="False"/> - <field name="perm_read" eval="False"/> - </record> - <record id="mail_message_subtype_rule_public" model="ir.rule"> <field name="name">mail.message.subtype: portal/public: read public subtypes</field> <field name="model_id" ref="model_mail_message_subtype"/> diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index bdb5b636f1c0..f29e1bd0f4e5 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -528,7 +528,7 @@ class Home(http.Controller): return werkzeug.utils.redirect(kw.get('redirect'), 303) request.uid = request.session.uid - menu_data = request.registry['ir.ui.menu'].load_menus(request.cr, request.uid, context=request.context) + menu_data = request.registry['ir.ui.menu'].load_menus(request.cr, request.uid, request.debug, context=request.context) return request.render('web.webclient_bootstrap', qcontext={'menu_data': menu_data}) @http.route('/web/dbredirect', type='http', auth="none") diff --git a/openerp/addons/base/ir/ir_ui_menu.py b/openerp/addons/base/ir/ir_ui_menu.py index 560d16088481..f9db2707371a 100644 --- a/openerp/addons/base/ir/ir_ui_menu.py +++ b/openerp/addons/base/ir/ir_ui_menu.py @@ -9,6 +9,7 @@ import threading import openerp.modules from openerp.osv import fields, osv from openerp import api, tools +from openerp.http import request from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ @@ -23,15 +24,15 @@ class ir_ui_menu(osv.osv): self.pool['ir.model.access'].register_cache_clearing_method(self._name, 'clear_caches') @api.model - @tools.ormcache('frozenset(self.env.user.groups_id.ids)') - def _visible_menu_ids(self): + @tools.ormcache('frozenset(self.env.user.groups_id.ids)', 'debug') + def _visible_menu_ids(self, debug=False): """ Return the ids of the menu items visible to the user. """ # retrieve all menus, and determine which ones are visible context = {'ir.ui.menu.full_list': True} menus = self.with_context(context).search([]) + groups = self.env.user.groups_id if debug else self.env.user.groups_id - self.env.ref('base.group_no_one') # first discard all menus with groups the user does not have - groups = self.env.user.groups_id menus = menus.filtered( lambda menu: not menu.groups_id or menu.groups_id & groups) @@ -68,7 +69,7 @@ class ir_ui_menu(osv.osv): the menu hierarchy of the current user. Uses a cache for speeding up the computation. """ - visible_ids = self._visible_menu_ids() + visible_ids = self._visible_menu_ids(request.debug if request else False) return self.filtered(lambda menu: menu.id in visible_ids) def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): @@ -271,8 +272,8 @@ class ir_ui_menu(osv.osv): } @api.cr_uid_context - @tools.ormcache_context('uid', keys=('lang',)) - def load_menus(self, cr, uid, context=None): + @tools.ormcache_context('uid', 'debug', keys=('lang',)) + def load_menus(self, cr, uid, debug, context=None): """ Loads all menu items (all applications and their sub-menus). :return: the menu root diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index cd955d23ac38..6923c1a6ddb4 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -780,6 +780,7 @@ class groups_view(osv.osv): user_context.update(self.pool['res.users'].context_get(cr, uid)) view = self.pool['ir.model.data'].xmlid_to_object(cr, SUPERUSER_ID, 'base.user_groups_view', context=user_context) if view and view.exists() and view._name == 'ir.ui.view': + group_no_one = view.env.ref('base.group_no_one') xml1, xml2 = [], [] xml1.append(E.separator(string=_('Application'), colspan="2")) for app, kind, gs in self.get_groups_by_application(cr, uid, user_context): @@ -796,7 +797,11 @@ class groups_view(osv.osv): xml2.append(E.separator(string=app_name, colspan="4", **attrs)) for g in gs: field_name = name_boolean_group(g.id) - xml2.append(E.field(name=field_name, **attrs)) + if g == group_no_one: + # make the group_no_one invisible in the form view + xml2.append(E.field(name=field_name, invisible="1", **attrs)) + else: + xml2.append(E.field(name=field_name, **attrs)) xml2.append({'class': "o_label_nowrap"}) xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace") diff --git a/openerp/addons/base/security/base_security.xml b/openerp/addons/base/security/base_security.xml index f704a772e55c..0f49dff02a25 100644 --- a/openerp/addons/base/security/base_security.xml +++ b/openerp/addons/base/security/base_security.xml @@ -42,6 +42,9 @@ <record model="res.groups" id="group_no_one"> <field name="name">Technical Features</field> </record> + <record model="res.groups" id="group_user"> + <field name="implied_ids" eval="[(4, ref('group_no_one'))]"/> + </record> <record id="group_sale_salesman" model="res.groups"> <field name="name">User</field> diff --git a/openerp/addons/base/tests/test_acl.py b/openerp/addons/base/tests/test_acl.py index 9f5e27d8d567..b003a2041565 100644 --- a/openerp/addons/base/tests/test_acl.py +++ b/openerp/addons/base/tests/test_acl.py @@ -6,7 +6,8 @@ from openerp.tools.misc import mute_logger from openerp.tests import common # test group that demo user should not have -GROUP_TECHNICAL_FEATURES = 'base.group_no_one' +GROUP_SYSTEM = 'base.group_system' +GROUP_ERP_MANAGER = 'base.group_erp_manager' class TestACL(common.TransactionCase): @@ -17,17 +18,16 @@ class TestACL(common.TransactionCase): self.res_partner = self.registry('res.partner') self.res_users = self.registry('res.users') self.res_company = self.registry('res.company') - self.demo_uid = self.registry('ir.model.data').xmlid_to_res_id(self.cr, self.uid, 'base.user_demo') - self.tech_group = self.registry('ir.model.data').xmlid_to_object(self.cr, self.uid, GROUP_TECHNICAL_FEATURES) - self.erp_manager_group = self.registry('ir.model.data').xmlid_to_object(self.cr, self.uid, 'base.group_erp_manager') - self.erp_system_group = self.registry('ir.model.data').xmlid_to_object(self.cr, self.uid, 'base.group_system') + self.demo_uid = self.env.ref('base.user_demo').id + self.erp_system_group = self.env.ref(GROUP_SYSTEM) + self.erp_manager_group = self.env.ref(GROUP_ERP_MANAGER) def _set_field_groups(self, model, field_name, groups): field = model._fields[field_name] column = model._columns[field_name] old_groups = field.groups old_prefetch = column._prefetch - + field.groups = groups column.groups = groups column._prefetch = False @@ -45,14 +45,14 @@ class TestACL(common.TransactionCase): original_fields = self.res_currency.fields_get(self.cr, self.demo_uid, []) form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form') view_arch = etree.fromstring(form_view.get('arch')) - has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES) - self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group before the test") + has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM) + self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group before the test") self.assertTrue('decimal_places' in original_fields, "'decimal_places' field must be properly visible before the test") self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [], "Field 'decimal_places' must be found in view definition before the test") # restrict access to the field and check it's gone - self._set_field_groups(self.res_currency, 'decimal_places', GROUP_TECHNICAL_FEATURES) + self._set_field_groups(self.res_currency, 'decimal_places', GROUP_SYSTEM) fields = self.res_currency.fields_get(self.cr, self.demo_uid, []) form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form') @@ -62,31 +62,31 @@ class TestACL(common.TransactionCase): "Field 'decimal_places' must not be found in view definition") # Make demo user a member of the restricted group and check that the field is back - self.tech_group.write({'users': [(4, self.demo_uid)]}) - has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES) + self.erp_system_group.write({'users': [(4, self.demo_uid)]}) + has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM) fields = self.res_currency.fields_get(self.cr, self.demo_uid, []) form_view = self.res_currency.fields_view_get(self.cr, self.demo_uid, False, 'form') view_arch = etree.fromstring(form_view.get('arch')) #import pprint; pprint.pprint(fields); pprint.pprint(form_view) - self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group") + self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group") self.assertTrue('decimal_places' in fields, "'decimal_places' field must be properly visible again") self.assertNotEquals(view_arch.xpath("//field[@name='decimal_places']"), [], "Field 'decimal_places' must be found in view definition again") #cleanup - self.tech_group.write({'users': [(3, self.demo_uid)]}) + self.erp_system_group.write({'users': [(3, self.demo_uid)]}) @mute_logger('openerp.models') def test_field_crud_restriction(self): "Read/Write RPC access to restricted field should be forbidden" # Verify the test environment first - has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES) - self.assertFalse(has_tech_feat, "`demo` user should not belong to the restricted group") + has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM) + self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group") self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])) self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})) # Now restrict access to the field and check it's forbidden - self._set_field_groups(self.res_partner, 'bank_ids', GROUP_TECHNICAL_FEATURES) + self._set_field_groups(self.res_partner, 'bank_ids', GROUP_SYSTEM) with self.assertRaises(AccessError): self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids']) @@ -94,19 +94,19 @@ class TestACL(common.TransactionCase): self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []}) # Add the restricted group, and check that it works again - self.tech_group.write({'users': [(4, self.demo_uid)]}) - has_tech_feat = self.res_users.has_group(self.cr, self.demo_uid, GROUP_TECHNICAL_FEATURES) - self.assertTrue(has_tech_feat, "`demo` user should now belong to the restricted group") + self.erp_system_group.write({'users': [(4, self.demo_uid)]}) + has_group_system = self.res_users.has_group(self.cr, self.demo_uid, GROUP_SYSTEM) + self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group") self.assert_(self.res_partner.read(self.cr, self.demo_uid, [1], ['bank_ids'])) self.assert_(self.res_partner.write(self.cr, self.demo_uid, [1], {'bank_ids': []})) #cleanup - self.tech_group.write({'users': [(3, self.demo_uid)]}) + self.erp_system_group.write({'users': [(3, self.demo_uid)]}) @mute_logger('openerp.models') def test_fields_browse_restriction(self): """Test access to records having restricted fields""" - self._set_field_groups(self.res_partner, 'email', GROUP_TECHNICAL_FEATURES) + self._set_field_groups(self.res_partner, 'email', GROUP_SYSTEM) pid = self.res_partner.search(self.cr, self.demo_uid, [], limit=1)[0] part = self.res_partner.browse(self.cr, self.demo_uid, pid) diff --git a/openerp/http.py b/openerp/http.py index 4fc412f8ca28..2cdd284d0717 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -314,7 +314,10 @@ class WebRequest(object): def debug(self): """ Indicates whether the current request is in "debug" mode """ - return 'debug' in self.httprequest.args + debug = 'debug' in self.httprequest.args + if not debug and self.httprequest.referrer: + debug = bool(urlparse.parse_qs(urlparse.urlparse(self.httprequest.referrer).query, keep_blank_values=True).get('debug')) + return debug @contextlib.contextmanager def registry_cr(self): diff --git a/openerp/models.py b/openerp/models.py index 6aaecba62bee..14692869c14c 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -1339,8 +1339,17 @@ class BaseModel(object): :return: True if the current user is a member of one of the given groups """ - return any(self.pool['res.users'].has_group(cr, uid, group_ext_id) - for group_ext_id in groups.split(',')) + from openerp.http import request + Users = self.pool['res.users'] + for group_ext_id in groups.split(','): + if group_ext_id == 'base.group_no_one': + # check: the group_no_one is effective in debug mode only + if Users.has_group(cr, uid, group_ext_id) and request and request.debug: + return True + else: + if Users.has_group(cr, uid, group_ext_id): + return True + return False def _get_default_form_view(self, cr, user, context=None): """ Generates a default single-line form view using all fields -- GitLab