1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from odoo.exceptions import AccessError
from odoo.tests.common import TransactionCase
from odoo.tools.misc import mute_logger
# test group that demo user should not have
USER_DEMO = 'base.user_demo'
GROUP_SYSTEM = 'base.group_system'
class TestACL(TransactionCase):
def setUp(self):
super(TestACL, self).setUp()
self.demo_user = self.env.ref(USER_DEMO)
self.erp_system_group = self.env.ref(GROUP_SYSTEM)
def _set_field_groups(self, model, field_name, groups):
field = model._fields[field_name]
self.patch(field, 'groups', groups)
def test_field_visibility_restriction(self):
"""Check that model-level ``groups`` parameter effectively restricts access to that
field for users who do not belong to one of the explicitly allowed groups"""
currency = self.env['res.currency'].sudo(self.demo_user)
# Verify the test environment first
original_fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group before the test")
self.assertIn('decimal_places', 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(currency, 'decimal_places', GROUP_SYSTEM)
fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
self.assertNotIn('decimal_places', fields, "'decimal_places' field should be gone")
self.assertEquals(view_arch.xpath("//field[@name='decimal_places']"), [],
"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.erp_system_group.users += self.demo_user
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
fields = currency.fields_get([])
form_view = currency.fields_view_get(False, 'form')
view_arch = etree.fromstring(form_view.get('arch'))
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
self.assertIn('decimal_places', 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")
@mute_logger('odoo.models')
def test_field_crud_restriction(self):
"Read/Write RPC access to restricted field should be forbidden"
partner = self.env['res.partner'].browse(1).sudo(self.demo_user)
# Verify the test environment first
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertFalse(has_group_system, "`demo` user should not belong to the restricted group")
self.assert_(partner.read(['bank_ids']))
self.assert_(partner.write({'bank_ids': []}))
# Now restrict access to the field and check it's forbidden
self._set_field_groups(partner, 'bank_ids', GROUP_SYSTEM)
with self.assertRaises(AccessError):
partner.read(['bank_ids'])
with self.assertRaises(AccessError):
partner.write({'bank_ids': []})
# Add the restricted group, and check that it works again
self.erp_system_group.users += self.demo_user
has_group_system = self.demo_user.has_group(GROUP_SYSTEM)
self.assertTrue(has_group_system, "`demo` user should now belong to the restricted group")
self.assert_(partner.read(['bank_ids']))
self.assert_(partner.write({'bank_ids': []}))
@mute_logger('odoo.models')
def test_fields_browse_restriction(self):
"""Test access to records having restricted fields"""
partner = self.env['res.partner'].sudo(self.demo_user)
self._set_field_groups(partner, 'email', GROUP_SYSTEM)
# accessing fields must no raise exceptions...
partner = partner.search([], limit=1)
partner.name
# ... except if they are restricted
with self.assertRaises(AccessError):
with mute_logger('odoo.models'):
partner.email
def test_view_create_edit_button_invisibility(self):
""" Test form view Create, Edit, Delete button visibility based on access right of model"""
methods = ['create', 'edit', 'delete']
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
for method in methods:
self.assertEqual(view_arch.get(method), 'false')
def test_view_create_edit_button_visibility(self):
""" Test form view Create, Edit, Delete button visibility based on access right of model"""
self.erp_system_group.users += self.demo_user
methods = ['create', 'edit', 'delete']
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
for method in methods:
self.assertIsNone(view_arch.get(method))
def test_m2o_field_create_edit_invisibility(self):
""" Test many2one field Create and Edit option visibility based on access rights of relation field"""
methods = ['create', 'write']
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
field_node = view_arch.xpath("//field[@name='currency_id']")
self.assertTrue(len(field_node), "currency_id field should be in company from view")
for method in methods:
self.assertEqual(field_node[0].get('can_' + method), 'false')
def test_m2o_field_create_edit_visibility(self):
""" Test many2one field Create and Edit option visibility based on access rights of relation field"""
self.erp_system_group.users += self.demo_user
methods = ['create', 'write']
company = self.env['res.company'].sudo(self.demo_user)
company_view = company.fields_view_get(False, 'form')
view_arch = etree.fromstring(company_view['arch'])
field_node = view_arch.xpath("//field[@name='currency_id']")
self.assertTrue(len(field_node), "currency_id field should be in company from view")
for method in methods:
self.assertEqual(field_node[0].get('can_' + method), 'true')
class TestIrRule(TransactionCase):
def test_ir_rule(self):
model_res_partner = self.env.ref('base.model_res_partner')
group_user = self.env.ref('base.group_user')
user_demo = self.env.ref('base.user_demo')
# create an ir_rule for the Employee group with an blank domain
rule1 = self.env['ir.rule'].create({
'name': 'test_rule1',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read as demo user the partners (one blank domain)
partners_demo = self.env['res.partner'].sudo(user_demo)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domain 1=1
rule1.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domain []
rule1.domain_force = "[]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create another ir_rule for the Employee group (to test multiple rules)
rule2 = self.env['ir.rule'].create({
'name': 'test_rule2',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read as demo user with domains [] and blank
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1 and blank
rule1.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1 and 1=1
rule2.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create another ir_rule for the Employee group (to test multiple rules)
rule3 = self.env['ir.rule'].create({
'name': 'test_rule3',
'model_id': model_res_partner.id,
'domain_force': False,
'groups': [(6, 0, group_user.ids)],
})
# read the partners as demo user
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# same with domains 1=1, 1=1 and 1=1
rule3.domain_force = "[(1,'=',1)]"
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# modify the global rule on res_company which triggers a recursive check
# of the rules on company
global_rule = self.env.ref('base.res_company_rule_employee')
global_rule.domain_force = "[('id','child_of',[user.company_id.id])]"
# read as demo user (exercising the global company rule)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# Modify the ir_rule for employee to have a rule that fordids seeing any
# record. We use a domain with implicit AND operator for later tests on
# normalization.
rule2.domain_force = "[('id','=',False),('name','=',False)]"
# check that demo user still sees partners, because group-rules are OR'ed
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partner.")
# create a new group with demo user in it, and a complex rule
group_test = self.env['res.groups'].create({
'name': 'Test Group',
'users': [(6, 0, user_demo.ids)],
})
# add the rule to the new group, with a domain containing an implicit
# AND operator, which is more tricky because it will have to be
# normalized before combining it
rule3.write({
'domain_force': "[('name','!=',False),('id','!=',False)]",
'groups': [(6, 0, group_test.ids)],
})
# read the partners again as demo user, which should give results
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see partners even with the combined rules.")
# delete global domains (to combine only group domains)
self.env['ir.rule'].search([('groups', '=', False)]).unlink()
# read the partners as demo user (several group domains, no global domain)
partners = partners_demo.search([])
self.assertTrue(partners, "Demo user should see some partners.")
|