File: test_acl.py

package info (click to toggle)
oca-core 11.0.20180730-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 509,684 kB
  • sloc: xml: 258,806; python: 164,081; sql: 217; sh: 92; makefile: 16
file content (255 lines) | stat: -rw-r--r-- 11,873 bytes parent folder | download
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.")