From 1fea55934f62ac62ad1d3b54995178ec9786f103 Mon Sep 17 00:00:00 2001 From: Ted Salmon Date: Fri, 16 Jun 2017 11:20:24 -0700 Subject: [PATCH] [ADD] user_threshold: Create module (#836) * [ADD] user_threshold: Create module * [IMP] user_threshold: Fixes per PR * Fix README * Rename poorly named methods * Syntactic sugar * Remove direct SQL in favor of ORM API * Docstrings * [IMP] user_threshold: Fixes per PR * Update verbiage in README * Simplify access check calls * Use the same verbiage throughout error messages * Add Link to related modules in README --- user_threshold/README.rst | 95 +++++++++++++ user_threshold/__init__.py | 5 + user_threshold/__manifest__.py | 20 +++ .../data/ir_config_parameter_data.xml | 14 ++ user_threshold/data/user_threshold_data.xml | 13 ++ user_threshold/models/__init__.py | 9 ++ user_threshold/models/ir_config_parameter.py | 45 ++++++ user_threshold/models/res_company.py | 47 +++++++ user_threshold/models/res_groups.py | 27 ++++ user_threshold/models/res_users.py | 130 ++++++++++++++++++ user_threshold/tests/__init__.py | 8 ++ user_threshold/tests/common.py | 31 +++++ .../tests/test_ir_config_parameter.py | 87 ++++++++++++ user_threshold/tests/test_res_company.py | 44 ++++++ user_threshold/tests/test_res_groups.py | 34 +++++ user_threshold/tests/test_res_users.py | 127 +++++++++++++++++ user_threshold/views/res_company_view.xml | 19 +++ user_threshold/views/res_users_view.xml | 22 +++ 18 files changed, 777 insertions(+) create mode 100644 user_threshold/README.rst create mode 100644 user_threshold/__init__.py create mode 100644 user_threshold/__manifest__.py create mode 100644 user_threshold/data/ir_config_parameter_data.xml create mode 100644 user_threshold/data/user_threshold_data.xml create mode 100644 user_threshold/models/__init__.py create mode 100644 user_threshold/models/ir_config_parameter.py create mode 100644 user_threshold/models/res_company.py create mode 100644 user_threshold/models/res_groups.py create mode 100644 user_threshold/models/res_users.py create mode 100644 user_threshold/tests/__init__.py create mode 100644 user_threshold/tests/common.py create mode 100644 user_threshold/tests/test_ir_config_parameter.py create mode 100644 user_threshold/tests/test_res_company.py create mode 100644 user_threshold/tests/test_res_groups.py create mode 100644 user_threshold/tests/test_res_users.py create mode 100644 user_threshold/views/res_company_view.xml create mode 100644 user_threshold/views/res_users_view.xml diff --git a/user_threshold/README.rst b/user_threshold/README.rst new file mode 100644 index 000000000..28cf6f40e --- /dev/null +++ b/user_threshold/README.rst @@ -0,0 +1,95 @@ +.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +============== +User Threshold +============== + +This module adds the ability to limit the amount of non-portal/public +users that exist in the database and per-company. + +It adds a group named `User Threshold Managers` which are the only users +who can alter the thresholds. + +This module also limits the ability of users to add membership +to the manager group to pre-existing members. By default, `Administrator` +is the only member of this group. + +Additionally, there is a flag that can be set on users so that they do not +count towards the user threshold. + +Using the `USER_THRESHOLD_HIDE` environment variable, you can also hide the +threshold exemption flag from users and the company setting for user +threshold. Setting this flag will also remove threshold exemptions for any +users who are not defined in the `USER_THRESHOLD_USER` environment variable. + +There are two modules available that also implement functionality similar to +what is provided in this module but in a more abstract way. They are: + +https://github.com/it-projects-llc/access-addons/tree/10.0/access_limit_records_number +https://github.com/it-projects-llc/access-addons/tree/10.0/access_restricted + + +Usage +===== + +A system parameter named `user.threshold.database` is added by default with +the value of '0' (Unlimited). Set this value to the total number of users +you wish to allow in the database. + +A field has been added to users to allow you to exempt them from the +thresholds. + +A field has been added to all companies, which allows you to define the max +number of users that the company can have. + +The following environment variables are available for your configuration ease: + +| Name | Description | +|------|-------------| +| USER_THRESHOLD_HIDE | Hide all threshold settings and default the exempt users to those defined by the USER_THRESHOLD_USERS variable +| USER_THRESHOLD_USER | White list of users who are exempt from the threshold. + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/10.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +``_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Ted Salmon + + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/user_threshold/__init__.py b/user_threshold/__init__.py new file mode 100644 index 000000000..fd02263ce --- /dev/null +++ b/user_threshold/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import models diff --git a/user_threshold/__manifest__.py b/user_threshold/__manifest__.py new file mode 100644 index 000000000..4ab471a07 --- /dev/null +++ b/user_threshold/__manifest__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). +{ + "name": "User Threshold", + "summary": "Add Configurable User Threshold Support", + "version": "10.0.1.0.0", + "category": "Authentication", + "website": "https://www.laslabs.com", + "author": "LasLabs, Odoo Community Association (OCA)", + "license": "LGPL-3", + "application": False, + 'installable': True, + "data": [ + 'data/user_threshold_data.xml', + 'data/ir_config_parameter_data.xml', + 'views/res_company_view.xml', + 'views/res_users_view.xml', + ], +} diff --git a/user_threshold/data/ir_config_parameter_data.xml b/user_threshold/data/ir_config_parameter_data.xml new file mode 100644 index 000000000..015b27ed3 --- /dev/null +++ b/user_threshold/data/ir_config_parameter_data.xml @@ -0,0 +1,14 @@ + + + + + + + user.threshold.database + 0 + + + + diff --git a/user_threshold/data/user_threshold_data.xml b/user_threshold/data/user_threshold_data.xml new file mode 100644 index 000000000..69b864391 --- /dev/null +++ b/user_threshold/data/user_threshold_data.xml @@ -0,0 +1,13 @@ + + + + + + + User Threshold Manager + + + + + diff --git a/user_threshold/models/__init__.py b/user_threshold/models/__init__.py new file mode 100644 index 000000000..c7d946f48 --- /dev/null +++ b/user_threshold/models/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import ir_config_parameter +from . import res_company +from . import res_groups +from . import res_users + diff --git a/user_threshold/models/ir_config_parameter.py b/user_threshold/models/ir_config_parameter.py new file mode 100644 index 000000000..d33d30660 --- /dev/null +++ b/user_threshold/models/ir_config_parameter.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +import os + +from odoo import _, api, models +from odoo.exceptions import AccessError + +from .res_groups import THRESHOLD_MANAGER + +MAX_DB_USER_PARAM = 'user.threshold.database' +THRESHOLD_HIDE = str(os.environ.get('USER_THRESHOLD_HIDE', '')) == '1' + + +class IrConfigParameter(models.Model): + _inherit = 'ir.config_parameter' + + @api.multi + def unlink(self): + """ + Override to disallow deletion of the user threshold parameter + when the user does not have the right access + """ + for rec in self.filtered(lambda r: r.key == MAX_DB_USER_PARAM): + if not self.env.user.has_group(THRESHOLD_MANAGER): + raise AccessError(_( + 'You must be a member of the `User Threshold Manager` ' + 'to delete this parameter' + )) + return super(IrConfigParameter, self).unlink() + + @api.multi + def write(self, vals): + """ + Override to disallow manipulation of the user threshold parameter + when the user does not have the right access + """ + for rec in self.filtered(lambda r: r.key == MAX_DB_USER_PARAM): + if not self.env.user.has_group(THRESHOLD_MANAGER): + raise AccessError(_( + 'You must be a member of the `User Threshold Manager` ' + 'to set this parameter' + )) + return super(IrConfigParameter, self).write(vals) diff --git a/user_threshold/models/res_company.py b/user_threshold/models/res_company.py new file mode 100644 index 000000000..8b66d325b --- /dev/null +++ b/user_threshold/models/res_company.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from lxml import etree + +from odoo import _, api, fields, models +from odoo.exceptions import AccessError + +from .ir_config_parameter import THRESHOLD_HIDE +from .res_groups import THRESHOLD_MANAGER + + +class ResCompany(models.Model): + _inherit = 'res.company' + + max_users = fields.Integer( + 'Maximum Number of users allowed for this company', + ) + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, + submenu=False): + """ Hide Max User Field when the env var to hide the field is set """ + res = super(ResCompany, self).fields_view_get( + view_id, view_type, toolbar, submenu + ) + if THRESHOLD_HIDE: + doc = etree.XML(res['arch']) + for node in doc.xpath("//field[@name='max_users']"): + node.getparent().remove(node) + res['arch'] = etree.tostring(doc, pretty_print=True) + return res + + @api.multi + def write(self, vals): + """ + Override to disallow manipulation of the user threshold parameter + when the user does not have the right access + """ + is_manager = self.env.user.has_group(THRESHOLD_MANAGER) + if vals.get('max_users') and not is_manager: + raise AccessError(_( + 'You must be a member of the `User Threshold Manager` to set ' + 'this parameter' + )) + return super(ResCompany, self).write(vals) diff --git a/user_threshold/models/res_groups.py b/user_threshold/models/res_groups.py new file mode 100644 index 000000000..6cc08e7c2 --- /dev/null +++ b/user_threshold/models/res_groups.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import _, api, models +from odoo.exceptions import AccessError + +THRESHOLD_MANAGER = 'user_threshold.group_threshold_manager' + + +class ResGroups(models.Model): + _inherit = 'res.groups' + + @api.multi + def write(self, vals): + """ Override write to verify that membership of the Threshold Manager + group is not able to be set by users outside that group + """ + manager = self.env.ref(THRESHOLD_MANAGER, raise_if_not_found=False) + if manager: + is_manager = self.env.user.has_group(THRESHOLD_MANAGER) + if not is_manager and manager in self: + raise AccessError(_( + 'You must be a member of the `User Threshold Manager` ' + 'group to grant access to it.' + )) + return super(ResGroups, self).write(vals) diff --git a/user_threshold/models/res_users.py b/user_threshold/models/res_users.py new file mode 100644 index 000000000..b26151215 --- /dev/null +++ b/user_threshold/models/res_users.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +import os +from csv import reader +from lxml import etree + +from odoo import SUPERUSER_ID, _, api, fields, models, registry +from odoo.exceptions import AccessError, ValidationError + +from .ir_config_parameter import THRESHOLD_HIDE, MAX_DB_USER_PARAM +from .res_groups import THRESHOLD_MANAGER + + +class ResUsers(models.Model): + _inherit = 'res.users' + + threshold_exempt = fields.Boolean( + 'Exempt User From User Count Thresholds', + ) + + def __init__(self, pool, cr): + """ + Override to check if env var to hide threshold configuration and + reset the database state is set. If it is, run those actions + """ + if THRESHOLD_HIDE: + exempt_users_var = os.environ.get('USER_THRESHOLD_USER', '') + exempt_users = reader([exempt_users_var]) + with api.Environment.manage(): + with registry(cr.dbname).cursor() as new_cr: + new_env = api.Environment(new_cr, SUPERUSER_ID, {}) + installed = new_env['ir.module.module'].search_count([ + ('name', '=', 'user_threshold'), + ('state', '=', 'installed'), + ]) + if installed: + users = new_env['res.users'].search([ + ('share', '=', False), + ('threshold_exempt', '=', True), + ]) + non_ex = users.filtered( + lambda r: r.login not in exempt_users + ) + for user in non_ex: + user.threshold_exempt = False + new_cr.commit() + + def _check_thresholds(self): + """ + Check to see if any user thresholds are met + Returns: + False when the thresholds aren't met and True when they are + """ + domain = [ + ('threshold_exempt', '=', False), + ('share', '=', False), + ] + users = self.env['res.users'].search(domain) + max_db_users = int(self.env['ir.config_parameter'].get_param( + MAX_DB_USER_PARAM + )) + if max_db_users > 0 and len(users) >= max_db_users: + return True + company = self.env.user.company_id + company_users = users.filtered(lambda r: r.company_id.id == company.id) + if company.max_users > 0 and len(company_users) >= company.max_users: + return True + return False + + @api.multi + def copy(self, default=None): + """ + Override method to make sure the Thresholds aren't met before + creating a new user + """ + if self._check_thresholds(): + raise ValidationError(_( + 'Cannot add user - Maximum number of allowed users reached' + )) + return super(ResUsers, self).copy(default=default) + + @api.multi + def create(self, vals): + """ + Override method to make sure the Thresholds aren't met before + creating a new user + """ + if self._check_thresholds(): + raise ValidationError(_( + 'Cannot add user - Maximum number of allowed users reached' + )) + return super(ResUsers, self).create(vals) + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, + submenu=False): + """ Hide Max User Field when the env var to hide the field is set """ + res = super(ResUsers, self).fields_view_get( + view_id, view_type, toolbar, submenu + ) + if THRESHOLD_HIDE: + doc = etree.XML(res['arch']) + for node in doc.xpath("//group[@name='user_threshold']"): + node.getparent().remove(node) + res['arch'] = etree.tostring(doc, pretty_print=True) + return res + + @api.multi + def write(self, vals): + """ + Override write to verify that membership of the Threshold Manager + group is not able to be set by users outside that group + """ + thold_group = self.env.ref(THRESHOLD_MANAGER, raise_if_not_found=False) + if thold_group: + user_is_manager = self.env.user.has_group(THRESHOLD_MANAGER) + if vals.get('threshold_exempt') and not user_is_manager: + raise AccessError(_( + 'You must be a member of the `User Threshold Manager`' + ' group to grant threshold exemptions.' + )) + is_add_group = vals.get('in_group_%s' % thold_group.id) + if is_add_group and not user_is_manager: + raise AccessError(_( + 'You must be a member of the `User Threshold Manager`' + ' group to grant access to it.' + )) + return super(ResUsers, self).write(vals) diff --git a/user_threshold/tests/__init__.py b/user_threshold/tests/__init__.py new file mode 100644 index 000000000..0eb360d80 --- /dev/null +++ b/user_threshold/tests/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import test_ir_config_parameter +from . import test_res_company +from . import test_res_groups +from . import test_res_users diff --git a/user_threshold/tests/common.py b/user_threshold/tests/common.py new file mode 100644 index 000000000..7c57c56d2 --- /dev/null +++ b/user_threshold/tests/common.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +import random +import string + +from odoo.tests.common import TransactionCase + +MAX_DB_USER_PARAM = 'user.threshold.database' + + +class Common(TransactionCase): + + def _create_test_user(self): + """ Create a user for testing """ + user = self.env.ref('base.user_demo').copy() + rand_name = ''.join([ + random.choice(string.ascii_letters) for n in xrange(10) + ]) + user.write({'login': rand_name}) + return user + + def _add_user_to_group(self, user): + """ Add a given user Record to the threshold manager group """ + th_group = self.env.ref('user_threshold.group_threshold_manager') + system_group = self.env.ref('base.group_system') + user.write({ + 'in_group_%s' % th_group.id: True, + 'in_group_%s' % system_group.id: True + }) diff --git a/user_threshold/tests/test_ir_config_parameter.py b/user_threshold/tests/test_ir_config_parameter.py new file mode 100644 index 000000000..1bf391b33 --- /dev/null +++ b/user_threshold/tests/test_ir_config_parameter.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo.exceptions import AccessError +from .common import Common, MAX_DB_USER_PARAM + + +class TestIrConfigParameter(Common): + + def _get_param(self): + return self.env['ir.config_parameter'].search([ + ('key', '=', MAX_DB_USER_PARAM), + ]) + + def test_can_set(self): + """ + It should test that users in the Threshold Manager group can + update the parameter + """ + mdl = self.env['ir.config_parameter'] + u = self._create_test_user() + self._add_user_to_group(u) + exp = '20' + mdl.sudo(u.id).set_param(MAX_DB_USER_PARAM, exp) + self.assertEquals(mdl.get_param(MAX_DB_USER_PARAM), exp) + + def test_cannot_set(self): + """ + It should test that users NOT in the Threshold Manager group + cannot alter the parameter + """ + u = self._create_test_user() + with self.assertRaises(AccessError): + self.env['ir.config_parameter'].sudo(u.id).set_param( + MAX_DB_USER_PARAM, 20 + ) + + def test_can_unlink(self): + """ + It should test that users in the Threshold Manager group can + unlink the Threshold Param + """ + u = self._create_test_user() + self._add_user_to_group(u) + param = self._get_param() + self.assertTrue(param.sudo(u.id).unlink()) + + def test_cannot_unlink(self): + """ + It should test that users outside the Threshold Manager group + cannot unlink the Threshold Param + """ + u = self._create_test_user() + param = self._get_param() + system_group = self.env.ref('base.group_system') + u.write({'in_group_%s' % system_group.id: True}) + with self.assertRaises(AccessError): + param.sudo(u.id).unlink() + + def test_can_write(self): + """ + It should test that users in the Threshold Manager group can + write the Threshold Param + """ + u = self._create_test_user() + self._add_user_to_group(u) + param = self._get_param() + res = '10' + param.sudo(u.id).write({'value': res}) + self.assertEquals(param.value, res) + + def test_cannot_write(self): + """ + It should test that users outside the Threshold Manager group + cannot write the Threshold Param + """ + u = self._create_test_user() + system_group = self.env.ref('base.group_system') + access_group = self.env.ref('base.group_erp_manager') + u.write({ + 'in_group_%s' % system_group.id: True, + 'in_group_%s' % access_group.id: True, + }) + param = self._get_param() + with self.assertRaises(AccessError): + param.sudo(u.id).write({'value': '10'}) diff --git a/user_threshold/tests/test_res_company.py b/user_threshold/tests/test_res_company.py new file mode 100644 index 000000000..900b72d2f --- /dev/null +++ b/user_threshold/tests/test_res_company.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from lxml import etree + +from odoo.exceptions import AccessError +from .common import Common + + +class TestResCompany(Common): + + def test_fields_view_get(self): + """ + It should verify that setting THRESHOLD_HIDE removes the parameter + from the view + """ + import odoo.addons.user_threshold.models.res_company as mdl + mdl.THRESHOLD_HIDE = True + view = self.env.ref('user_threshold.view_company_form') + c = self.env['res.company'].browse(1) + ret = c.fields_view_get(view.id) + doc = etree.XML(ret['arch']) + self.assertEquals(doc.xpath("//field[@name='max_users']"), []) + + def test_can_write_max_users(self): + """ + It should restrict the max users parameter to Threshold Managers + """ + u = self._create_test_user() + self._add_user_to_group(u) + c = self.env['res.company'].browse(1) + res = 10 + c.sudo(u.id).write({'max_users': res}) + self.assertEquals(c.max_users, res) + + def test_cannot_write_max_users(self): + """ + It should restrict the max users parameter to Threshold Managers + """ + u = self._create_test_user() + c = self.env['res.company'].browse(1) + with self.assertRaises(AccessError): + c.sudo(u.id).write({'max_users': 10}) diff --git a/user_threshold/tests/test_res_groups.py b/user_threshold/tests/test_res_groups.py new file mode 100644 index 000000000..baca024f7 --- /dev/null +++ b/user_threshold/tests/test_res_groups.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo.exceptions import AccessError +from .common import Common + + +class TestResGroups(Common): + + def test_can_write_max_users(self): + """ + It should restrict membership additions to Threshold Managers to + pre-existing members of that group + """ + u = self._create_test_user() + u.write({ + 'in_group_%s' % self.env.ref('base.group_erp_manager').id: True + }) + g = self.env.ref('user_threshold.group_threshold_manager') + with self.assertRaises(AccessError): + g.sudo(u.id).write({'users': self.env.ref('base.user_demo').id}) + + def test_cannot_write_max_users(self): + """ + It should restrict membership additions to Threshold Managers to + pre-existing members of that group + """ + u = self._create_test_user() + self._add_user_to_group(u) + g = self.env.ref('user_threshold.group_threshold_manager') + demo_user = self.env.ref('base.user_demo') + g.sudo(u.id).write({'users': [(4, [demo_user.id])]}) + self.assertTrue(demo_user.id in g.users.ids) diff --git a/user_threshold/tests/test_res_users.py b/user_threshold/tests/test_res_users.py new file mode 100644 index 000000000..88dbd8b4e --- /dev/null +++ b/user_threshold/tests/test_res_users.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from lxml import etree + +from odoo.exceptions import AccessError, ValidationError +from .common import Common, MAX_DB_USER_PARAM + + +class TestResUsers(Common): + + def setUp(self): + super(TestResUsers, self).setUp() + self.env['ir.config_parameter'].set_param(MAX_DB_USER_PARAM, '0') + + def test_copy_global(self): + """ + It should restrict the user count in copy() as prescribed by the + global threshold parameter + """ + self.env['ir.config_parameter'].set_param(MAX_DB_USER_PARAM, 3) + self._create_test_user() + with self.assertRaises(ValidationError): + self._create_test_user() + + def test_create_global(self): + """ + It should restrict the user count as prescribed by the global + threshold parameter + """ + self.env['ir.config_parameter'].set_param(MAX_DB_USER_PARAM, 3) + self._create_test_user() + with self.assertRaises(ValidationError): + self.env['res.users'].create({ + 'login': 'Derp Derpington', + 'email': 'dderpington@example.com', + 'notify_email': 'always', + }) + + def test_copy_company(self): + """ + It should restrict the user count in copy() as prescribed by the + companies threshold parameter + """ + c = self.env['res.company'].browse(1) + c.max_users = 3 + self._create_test_user() + with self.assertRaises(ValidationError): + self._create_test_user() + + def test_create_company(self): + """ + It should restrict the user count as prescribed by the companies + threshold parameter + """ + c = self.env['res.company'].browse(1) + c.max_users = 3 + self._create_test_user() + with self.assertRaises(ValidationError): + self.env['res.users'].create({ + 'login': 'Derp Derpington', + 'email': 'dderpington@example.com', + 'notify_email': 'always', + }) + + def test_fields_view_get(self): + """ + It should verify that setting THRESHOLD_HIDE removes the parameter + from the view + """ + import odoo.addons.user_threshold.models.res_users as mdl + mdl.THRESHOLD_HIDE = True + view = self.env.ref('user_threshold.view_users_form') + u = self._create_test_user() + ret = u.fields_view_get(view.id) + doc = etree.XML(ret['arch']) + self.assertEquals(doc.xpath("//group[@name='user_threshold']"), []) + + def test_cannot_write_exempt(self): + """ + It should restrict the threshold exempt parameter to Threshold + Managers + """ + u = self._create_test_user() + tu = self._create_test_user() + with self.assertRaises(AccessError): + tu.sudo(u.id).write({'threshold_exempt': True}) + + def test_can_write_exempt(self): + """ + It should restrict the threshold exempt parameter to Threshold + Managers + """ + u = self._create_test_user() + self._add_user_to_group(u) + tu = self._create_test_user() + tu.sudo(u.id).write({'threshold_exempt': True}) + self.assertEquals(tu.threshold_exempt, True) + + def test_cannot_write_group(self): + """ + It should restrict additions to the Threshold Managers to users in + that group + """ + u = self._create_test_user() + u.write({ + 'in_group_%s' % self.env.ref('base.group_erp_manager').id: True + }) + tu = self._create_test_user() + th_group = self.env.ref('user_threshold.group_threshold_manager') + with self.assertRaises(AccessError): + tu.sudo(u.id).write({'in_group_%s' % th_group.id: True}) + + def test_can_write_group(self): + """ + It should restrict additions to the Threshold Managers to users in + that group + """ + u = self._create_test_user() + self._add_user_to_group(u) + tu = self._create_test_user() + th_group = self.env.ref('user_threshold.group_threshold_manager') + tu.sudo(u.id).write({'in_group_%s' % th_group.id: True}) + self.assertEquals( + tu.has_group('user_threshold.group_threshold_manager'), True + ) diff --git a/user_threshold/views/res_company_view.xml b/user_threshold/views/res_company_view.xml new file mode 100644 index 000000000..e1f1d8a01 --- /dev/null +++ b/user_threshold/views/res_company_view.xml @@ -0,0 +1,19 @@ + + + + + + + res.company.form + res.company + + + + + + + + + diff --git a/user_threshold/views/res_users_view.xml b/user_threshold/views/res_users_view.xml new file mode 100644 index 000000000..3a3c75049 --- /dev/null +++ b/user_threshold/views/res_users_view.xml @@ -0,0 +1,22 @@ + + + + + + + res.users.form + res.users + + + + + + + + + + +