From c248594ee5255b232e42438783b9061d235153cb Mon Sep 17 00:00:00 2001 From: Mitali Patel <mpa@odoo.com> Date: Wed, 12 Feb 2020 14:09:12 +0000 Subject: [PATCH] [IMP] base: allow restricting / removing exports right Add a new group allowing administrators to remove the ability for users to bulk-export data from the database. It's pretty minor as technically the user can still access the underlying object through the basic methods but it's still a bit of a roadblock. Users can export by default. Task 2170900 closes odoo/odoo#45400 Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com> --- addons/test_xlsx_export/tests/test_export.py | 2 +- .../src/js/views/list/list_controller.js | 14 ++++-- addons/web/static/tests/views/list_tests.js | 46 +++++++++++++++---- odoo/addons/base/data/res_users_demo.xml | 2 +- odoo/addons/base/security/base_groups.xml | 7 ++- odoo/addons/base/security/ir.model.access.csv | 2 +- odoo/models.py | 2 + 7 files changed, 58 insertions(+), 17 deletions(-) diff --git a/addons/test_xlsx_export/tests/test_export.py b/addons/test_xlsx_export/tests/test_export.py index 7a2e50a80e3c..9ba0960be5ab 100644 --- a/addons/test_xlsx_export/tests/test_export.py +++ b/addons/test_xlsx_export/tests/test_export.py @@ -22,7 +22,7 @@ class XlsxCreatorCase(common.HttpCase): super().setUp() self.model = self.env[self.model_name] - mail_new_test_user(self.env, login='fof', password='123456789') + u = mail_new_test_user(self.env, login='fof', password='123456789', groups='base.group_user,base.group_allow_export') self.authenticate('fof', '123456789') self.worksheet = {} # mock worksheet diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js index 4a3972d50eb0..988291fdb1e2 100644 --- a/addons/web/static/src/js/views/list/list_controller.js +++ b/addons/web/static/src/js/views/list/list_controller.js @@ -58,6 +58,9 @@ var ListController = BasicController.extend({ this.fieldChangedPrevented = false; this.isPageSelected = false; // true iff all records of the page are selected this.isDomainSelected = false; // true iff the user selected all records matching the domain + session.user_has_group('base.group_allow_export').then(has_group => { + this.isExportEnable = has_group; + }); Object.defineProperty(this, 'mode', { get: () => this.renderer.isEditable() ? 'edit' : 'readonly', set: () => {}, @@ -372,10 +375,13 @@ var ListController = BasicController.extend({ return null; } const props = this._super(...arguments); - const otherActionItems = [{ - description: _t("Export"), - callback: () => this._onExportData(), - }]; + const otherActionItems = []; + if (this.isExportEnable) { + otherActionItems.push({ + description: _t("Export"), + callback: () => this._onExportData() + }); + } if (this.archiveEnabled) { otherActionItems.push({ description: _t("Archive"), diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 94a7a3bb1569..b2340f370652 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -177,7 +177,7 @@ QUnit.module('Views', { }); QUnit.test('list with delete="0"', async function (assert) { - assert.expect(4); + assert.expect(3); const list = await createView({ View: ListView, @@ -192,13 +192,7 @@ QUnit.module('Views', { assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record'); await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); - assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus'); - await cpHelpers.toggleActionMenu(list); - assert.deepEqual( - cpHelpers.getMenuItemTexts(list), - ['Export'], - 'action menu should not have Delete button' - ); + assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .o_dropdown_menu'); list.destroy(); }); @@ -222,6 +216,40 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('list with export button', async function (assert) { + assert.expect(4); + + const list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + viewOptions: {hasActionMenus: true}, + arch: '<tree><field name="foo"/></tree>', + session: { + async user_has_group(group) { + if (group === 'base.group_allow_export') { + return true; + } + return this._super(...arguments); + }, + }, + }); + + assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus'); + assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record'); + + await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); + assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus'); + await cpHelpers.toggleActionMenu(list); + assert.deepEqual( + cpHelpers.getMenuItemTexts(list), + ['Export', 'Delete'], + 'action menu should have Export button' + ); + + list.destroy(); + }); + QUnit.test('simple editable rendering', async function (assert) { assert.expect(15); @@ -4538,7 +4566,7 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_list_record_selector:first input')); await cpHelpers.toggleActionMenu(list); - assert.deepEqual(cpHelpers.getMenuItemTexts(list), ['Export', 'Delete', 'Action event']); + assert.deepEqual(cpHelpers.getMenuItemTexts(list), ['Delete', 'Action event']); list.destroy(); }); diff --git a/odoo/addons/base/data/res_users_demo.xml b/odoo/addons/base/data/res_users_demo.xml index 980c4d2b3bc0..4d3a65721deb 100644 --- a/odoo/addons/base/data/res_users_demo.xml +++ b/odoo/addons/base/data/res_users_demo.xml @@ -35,7 +35,7 @@ <field name="password">demo</field> <field name="signature" type="xml"><span>-- <br/>+Mr Demo</span></field> <field name="company_id" ref="main_company"/> - <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/> + <field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager'), ref('base.group_allow_export')])]"/> <field name="image_1920" type="base64" file="base/static/img/user_demo-image.jpg"/> </record> diff --git a/odoo/addons/base/security/base_groups.xml b/odoo/addons/base/security/base_groups.xml index 3bdf8ee39282..5d184c0564dc 100644 --- a/odoo/addons/base/security/base_groups.xml +++ b/odoo/addons/base/security/base_groups.xml @@ -36,6 +36,11 @@ <record model="res.groups" id="group_no_one"> <field name="name">Technical Features</field> </record> + <record id="group_allow_export" model="res.groups"> + <field name="name">Access to export feature</field> + <field name="category_id" ref="base.module_category_hidden"/> + <field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> + </record> <record model="res.groups" id="group_user"> <field name="implied_ids" eval="[(4, ref('group_no_one'))]"/> <field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> @@ -47,7 +52,7 @@ </record> <record id="default_user" model="res.users"> - <field name="groups_id" eval="[(4,ref('base.group_partner_manager'))]"/> + <field name="groups_id" eval="[(4, ref('base.group_partner_manager')), (4, ref('base.group_allow_export'))]"/> </record> <!-- diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv index 861ebbf5aac8..a3405311a5fa 100644 --- a/odoo/addons/base/security/ir.model.access.csv +++ b/odoo/addons/base/security/ir.model.access.csv @@ -3,7 +3,7 @@ "access_ir_attachment_group_user","ir_attachment group_user","model_ir_attachment","group_user",1,1,1,1 "access_ir_attachment_group_portal_public","ir_attachment group_portal_public","model_ir_attachment",,0,0,0,0 "access_ir_cron_group_cron","ir_cron group_cron","model_ir_cron","group_system",1,1,1,1 -"access_ir_exports_group_system","ir_exports group_system","model_ir_exports","base.group_user",1,1,1,1 +"access_ir_exports_group_system","ir_exports group_system","model_ir_exports","base.group_allow_export",1,1,1,1 "access_ir_exports_line_group_system","ir_exports_line group_system","model_ir_exports_line","base.group_user",1,1,1,1 "access_ir_model_group_erp_manager","ir_model group_erp_manager","model_ir_model","group_erp_manager",1,1,1,1 "access_ir_model_constraint_group_erp_manager","ir_model_constraint group_erp_manager","model_ir_model_constraint","group_erp_manager",1,1,1,1 diff --git a/odoo/models.py b/odoo/models.py index 52dadd4b041a..8a46be32f1f8 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -912,6 +912,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): This method is used when exporting data via client menu """ + if not (self.env.user._is_admin() or self.env.user.has_group('base.group_allow_export')): + raise UserError(_("You don't have the rights to export data. Please contact an Administrator.")) fields_to_export = [fix_import_export_id_paths(f) for f in fields_to_export] return {'datas': self._export_rows(fields_to_export)} -- GitLab