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