Passed
Pull Request — 4.0 (#7878)
by Damian
09:36
created

InheritedPermissionsTest::tearDown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Security\Tests;
4
5
use SilverStripe\Core\Injector\Injector;
6
use SilverStripe\Dev\SapphireTest;
7
use SilverStripe\Security\Group;
8
use SilverStripe\Security\InheritedPermissions;
9
use SilverStripe\Security\Member;
10
use SilverStripe\Security\PermissionChecker;
11
use SilverStripe\Security\Test\InheritedPermissionsTest\TestPermissionNode;
12
use SilverStripe\Security\Test\InheritedPermissionsTest\TestDefaultPermissionChecker;
13
use SilverStripe\Security\Test\InheritedPermissionsTest\UnstagedNode;
14
use SilverStripe\Versioned\Versioned;
15
16
class InheritedPermissionsTest extends SapphireTest
17
{
18
    protected static $fixture_file = 'InheritedPermissionsTest.yml';
19
20
    protected static $extra_dataobjects = [
21
        TestPermissionNode::class,
22
        UnstagedNode::class,
23
    ];
24
25
    /**
26
     * @var TestDefaultPermissionChecker
27
     */
28
    protected $rootPermissions = null;
29
30
    protected function setUp()
31
    {
32
        $this->rootPermissions = new TestDefaultPermissionChecker();
33
34
        // Register root permissions
35
        $permission1 = InheritedPermissions::create(TestPermissionNode::class)
0 ignored issues
show
Bug introduced by
SilverStripe\Security\Te...stPermissionNode::class of type string is incompatible with the type array expected by parameter $args of SilverStripe\Security\In...edPermissions::create(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

35
        $permission1 = InheritedPermissions::create(/** @scrutinizer ignore-type */ TestPermissionNode::class)
Loading history...
36
            ->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
37
            ->setDefaultPermissions($this->rootPermissions);
38
        Injector::inst()->registerService(
39
            $permission1,
40
            PermissionChecker::class . '.testpermissions'
41
        );
42
43
        // Reset root permission
44
        $permission2 = InheritedPermissions::create(UnstagedNode::class)
45
            ->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
46
            ->setDefaultPermissions($this->rootPermissions);
47
        Injector::inst()->registerService(
48
            $permission2,
49
            PermissionChecker::class . '.unstagedpermissions'
50
        );
51
52
        parent::setUp();
53
54
        $permission1->clearCache();
55
        $permission2->clearCache();
56
    }
57
58
    protected function tearDown()
59
    {
60
        Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.testpermissions');
61
        Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.unstagedpermissions');
62
        $this->rootPermissions = null;
63
        parent::tearDown();
64
    }
65
66
    public function testEditPermissions()
67
    {
68
        $editor = $this->objFromFixture(Member::class, 'editor');
69
70
        $about = $this->objFromFixture(TestPermissionNode::class, 'about');
71
        $aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
72
        $history = $this->objFromFixture(TestPermissionNode::class, 'history');
73
        $products = $this->objFromFixture(TestPermissionNode::class, 'products');
74
        $product1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
75
        $product4 = $this->objFromFixture(TestPermissionNode::class, 'products-product4');
76
77
        // Test logged out users cannot edit
78
        Member::actAs(null, function () use ($aboutStaff) {
79
            $this->assertFalse($aboutStaff->canEdit());
80
        });
81
82
        // Can't edit a page that is locked to admins
83
        $this->assertFalse($about->canEdit($editor));
84
85
        // Can edit a page that is locked to editors
86
        $this->assertTrue($products->canEdit($editor));
87
88
        // Can edit a child of that page that inherits
89
        $this->assertTrue($product1->canEdit($editor));
90
91
        // Can't edit a child of that page that has its permissions overridden
92
        $this->assertFalse($product4->canEdit($editor));
93
94
        // Test that root node respects root permissions
95
        $this->assertTrue($history->canEdit($editor));
96
97
        TestPermissionNode::getInheritedPermissions()->clearCache();
98
        $this->rootPermissions->setCanEdit(false);
99
100
        // With root edit false, permissions are now denied for CanEditType = Inherit
101
        $this->assertFalse($history->canEdit($editor));
102
    }
103
104
    public function testDeletePermissions()
105
    {
106
        $editor = $this->objFromFixture(Member::class, 'editor');
107
108
        $about = $this->objFromFixture(TestPermissionNode::class, 'about');
109
        $aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
110
        $history = $this->objFromFixture(TestPermissionNode::class, 'history');
111
        $products = $this->objFromFixture(TestPermissionNode::class, 'products');
112
        $product1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
113
        $product4 = $this->objFromFixture(TestPermissionNode::class, 'products-product4');
114
115
        // Test logged out users cannot edit
116
        Member::actAs(null, function () use ($aboutStaff) {
117
            $this->assertFalse($aboutStaff->canDelete());
118
        });
119
120
        // Can't edit a page that is locked to admins
121
        $this->assertFalse($about->canDelete($editor));
122
123
        // Can't delete a page if a child (product4) is un-deletable
124
        $this->assertFalse($products->canDelete($editor));
125
126
        // Can edit a child of that page that inherits
127
        $this->assertTrue($product1->canDelete($editor));
128
129
        // Can't edit a child of that page that has its permissions overridden
130
        $this->assertFalse($product4->canDelete($editor));
131
132
        // Test that root node respects root permissions
133
        $this->assertTrue($history->canDelete($editor));
134
135
        TestPermissionNode::getInheritedPermissions()->clearCache();
136
        $this->rootPermissions->setCanEdit(false);
137
138
        // With root edit false, permissions are now denied for CanEditType = Inherit
139
        $this->assertFalse($history->canDelete($editor));
140
    }
141
142
    public function testViewPermissions()
143
    {
144
        $history = $this->objFromFixture(TestPermissionNode::class, 'history');
145
        $contact = $this->objFromFixture(TestPermissionNode::class, 'contact');
146
        $contactForm = $this->objFromFixture(TestPermissionNode::class, 'contact-form');
147
        $secret = $this->objFromFixture(TestPermissionNode::class, 'secret');
148
        $secretNested = $this->objFromFixture(TestPermissionNode::class, 'secret-nested');
149
        $protected = $this->objFromFixture(TestPermissionNode::class, 'protected');
150
        $protectedChild = $this->objFromFixture(TestPermissionNode::class, 'protected-child');
151
        $editor = $this->objFromFixture(Member::class, 'editor');
152
153
        // Not logged in user can only access Inherit or Anyone pages
154
        Member::actAs(
155
            null,
156
            function () use ($protectedChild, $secretNested, $protected, $secret, $history, $contact, $contactForm) {
157
                $this->assertTrue($history->canView());
158
                $this->assertTrue($contact->canView());
159
                $this->assertTrue($contactForm->canView());
160
                // Protected
161
                $this->assertFalse($secret->canView());
162
                $this->assertFalse($secretNested->canView());
163
                $this->assertFalse($protected->canView());
164
                $this->assertFalse($protectedChild->canView());
165
            }
166
        );
167
168
        // Editor can view pages restricted to logged in users
169
        $this->assertTrue($secret->canView($editor));
170
        $this->assertTrue($secretNested->canView($editor));
171
172
        // Cannot read admin-only pages
173
        $this->assertFalse($protected->canView($editor));
174
        $this->assertFalse($protectedChild->canView($editor));
175
176
        // Check root permissions
177
        $this->assertTrue($history->canView($editor));
178
179
        TestPermissionNode::getInheritedPermissions()->clearCache();
180
        $this->rootPermissions->setCanView(false);
181
182
        $this->assertFalse($history->canView($editor));
183
    }
184
185
    public function testUnstagedViewPermissions()
186
    {
187
        $history = $this->objFromFixture(UnstagedNode::class, 'history');
188
        $contact = $this->objFromFixture(UnstagedNode::class, 'contact');
189
        $contactForm = $this->objFromFixture(UnstagedNode::class, 'contact-form');
190
        $secret = $this->objFromFixture(UnstagedNode::class, 'secret');
191
        $secretNested = $this->objFromFixture(UnstagedNode::class, 'secret-nested');
192
        $protected = $this->objFromFixture(UnstagedNode::class, 'protected');
193
        $protectedChild = $this->objFromFixture(UnstagedNode::class, 'protected-child');
194
        $editor = $this->objFromFixture(Member::class, 'editor');
195
196
        // Not logged in user can only access Inherit or Anyone pages
197
        Member::actAs(
198
            null,
199
            function () use ($protectedChild, $secretNested, $protected, $secret, $history, $contact, $contactForm) {
200
                $this->assertTrue($history->canView());
201
                $this->assertTrue($contact->canView());
202
                $this->assertTrue($contactForm->canView());
203
                // Protected
204
                $this->assertFalse($secret->canView());
205
                $this->assertFalse($secretNested->canView());
206
                $this->assertFalse($protected->canView());
207
                $this->assertFalse($protectedChild->canView());
208
            }
209
        );
210
211
        // Editor can view pages restricted to logged in users
212
        $this->assertTrue($secret->canView($editor));
213
        $this->assertTrue($secretNested->canView($editor));
214
215
        // Cannot read admin-only pages
216
        $this->assertFalse($protected->canView($editor));
217
        $this->assertFalse($protectedChild->canView($editor));
218
219
        // Check root permissions
220
        $this->assertTrue($history->canView($editor));
221
222
        UnstagedNode::getInheritedPermissions()->clearCache();
223
        $this->rootPermissions->setCanView(false);
224
225
        $this->assertFalse($history->canView($editor));
226
    }
227
228
    /**
229
     * Test that draft permissions deny unrestricted live permissions
230
     */
231
    public function testRestrictedDraftUnrestrictedLive()
232
    {
233
        Versioned::set_stage(Versioned::DRAFT);
234
235
        // Should be editable by non-admin editor
236
        /** @var TestPermissionNode $products */
237
        $products = $this->objFromFixture(TestPermissionNode::class, 'products');
238
        /** @var TestPermissionNode $products1 */
239
        $products1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
240
        $editor = $this->objFromFixture(Member::class, 'editor');
241
242
        // Ensure the editor can edit
243
        $this->assertTrue($products->canEdit($editor));
244
        $this->assertTrue($products1->canEdit($editor));
245
246
        // Write current version to live
247
        $products->writeToStage(Versioned::LIVE);
0 ignored issues
show
Bug introduced by
The method writeToStage() does not exist on SilverStripe\Security\Te...Test\TestPermissionNode. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

247
        $products->/** @scrutinizer ignore-call */ 
248
                   writeToStage(Versioned::LIVE);
Loading history...
248
        $products1->writeToStage(Versioned::LIVE);
249
250
        // Draft version restrict to admins
251
        $products->EditorGroups()->setByIDList([
0 ignored issues
show
Bug introduced by
The method EditorGroups() does not exist on SilverStripe\Security\Te...Test\TestPermissionNode. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

251
        $products->/** @scrutinizer ignore-call */ 
252
                   EditorGroups()->setByIDList([
Loading history...
252
            $this->idFromFixture(Group::class, 'admins')
253
        ]);
254
        $products->write();
255
256
        // Ensure editor can no longer edit
257
        TestPermissionNode::getInheritedPermissions()->clearCache();
258
        $this->assertFalse($products->canEdit($editor));
259
        $this->assertFalse($products1->canEdit($editor));
260
    }
261
262
    /**
263
     * Test that draft permissions permit access over live permissions
264
     */
265
    public function testUnrestrictedDraftOverridesLive()
266
    {
267
        Versioned::set_stage(Versioned::DRAFT);
268
269
        // Should be editable by non-admin editor
270
        /** @var TestPermissionNode $about */
271
        $about = $this->objFromFixture(TestPermissionNode::class, 'about');
272
        /** @var TestPermissionNode $aboutStaff */
273
        $aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
274
        $editor = $this->objFromFixture(Member::class, 'editor');
275
276
        // Ensure the editor can't edit
277
        $this->assertFalse($about->canEdit($editor));
278
        $this->assertFalse($aboutStaff->canEdit($editor));
279
280
        // Write current version to live
281
        $about->writeToStage(Versioned::LIVE);
282
        $aboutStaff->writeToStage(Versioned::LIVE);
283
284
        // Unrestrict draft
285
        $about->CanEditType = InheritedPermissions::LOGGED_IN_USERS;
0 ignored issues
show
Bug Best Practice introduced by
The property CanEditType does not exist on SilverStripe\Security\Te...Test\TestPermissionNode. Since you implemented __set, consider adding a @property annotation.
Loading history...
286
        $about->write();
287
288
        // Ensure editor can no longer edit
289
        TestPermissionNode::getInheritedPermissions()->clearCache();
290
        $this->assertTrue($about->canEdit($editor));
291
        $this->assertTrue($aboutStaff->canEdit($editor));
292
    }
293
294
    /**
295
     * Ensure that flipping parent / child relationship on live doesn't
296
     * cause infinite loop
297
     */
298
    public function testMobiusHierarchy()
299
    {
300
        Versioned::set_stage(Versioned::DRAFT);
301
302
        /** @var TestPermissionNode $history */
303
        $history = $this->objFromFixture(TestPermissionNode::class, 'history');
304
        /** @var TestPermissionNode $historyGallery */
305
        $historyGallery = $this->objFromFixture(TestPermissionNode::class, 'history-gallery');
306
307
        // Publish current state to live
308
        $history->writeToStage(Versioned::LIVE);
309
        $historyGallery->writeToStage(Versioned::LIVE);
310
311
        // Flip relation
312
        $historyGallery->ParentID = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\Security\Te...Test\TestPermissionNode. Since you implemented __set, consider adding a @property annotation.
Loading history...
313
        $historyGallery->write();
314
        $history->ParentID = $historyGallery->ID;
315
        $history->write();
316
317
        // Test viewability (not logged in users)
318
        Member::actAs(null, function () use ($history, $historyGallery) {
319
            $this->assertTrue($history->canView());
320
            $this->assertTrue($historyGallery->canView());
321
        });
322
323
        // Change permission on draft root and ensure it affects both
324
        $historyGallery->CanViewType = InheritedPermissions::LOGGED_IN_USERS;
0 ignored issues
show
Bug Best Practice introduced by
The property CanViewType does not exist on SilverStripe\Security\Te...Test\TestPermissionNode. Since you implemented __set, consider adding a @property annotation.
Loading history...
325
        $historyGallery->write();
326
        TestPermissionNode::getInheritedPermissions()->clearCache();
327
328
        // Test viewability (not logged in users)
329
        Member::actAs(null, function () use ($history, $historyGallery) {
330
            $this->assertFalse($historyGallery->canView());
331
            $this->assertFalse($history->canView());
332
        });
333
    }
334
}
335