diff --git a/addons/base_import/models.py b/addons/base_import/models.py
index 7463a02028e..7cbd0d79e09 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 40263e93b87..a539e4d429f 100644
--- a/addons/mail/security/mail_security.xml
+++ b/addons/mail/security/mail_security.xml
@@ -23,17 +23,6 @@
-
-
- mail.followers: read and write others entries
-
-
- []
-
-
-
-
-
mail.message.subtype: portal/public: read public subtypes
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index bdb5b636f1c..f29e1bd0f4e 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 560d1608848..f9db2707371 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 cd955d23ac3..6923c1a6ddb 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 f704a772e55..0f49dff02a2 100644
--- a/openerp/addons/base/security/base_security.xml
+++ b/openerp/addons/base/security/base_security.xml
@@ -42,6 +42,9 @@
Technical Features
+
+
+
User
diff --git a/openerp/addons/base/tests/test_acl.py b/openerp/addons/base/tests/test_acl.py
index 9f5e27d8d56..b003a204156 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 4fc412f8ca2..2cdd284d071 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 6aaecba62be..14692869c14 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