Completed
Push — 4.2.0 ( f03d5e...24bd0a )
by Daniel
12:22 queued 05:26
created

GroupTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Security\Tests;
4
5
use InvalidArgumentException;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Dev\FunctionalTest;
8
use SilverStripe\ORM\ArrayList;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\Security\Group;
11
use SilverStripe\Security\Member;
12
use SilverStripe\Security\Permission;
13
use SilverStripe\Security\Tests\GroupTest\TestMember;
14
15
class GroupTest extends FunctionalTest
16
{
17
    protected static $fixture_file = 'GroupTest.yml';
18
19
    protected static $extra_dataobjects = [
20
        TestMember::class
21
    ];
22
23
    protected function setUp()
24
    {
25
        parent::setUp();
26
    }
27
28
    public function testGroupCodeDefaultsToTitle()
29
    {
30
        $g1 = new Group();
31
        $g1->Title = "My Title";
32
        $g1->write();
33
        $this->assertEquals('my-title', $g1->Code, 'Custom title gets converted to code if none exists already');
34
35
        $g2 = new Group();
36
        $g2->Title = "My Title";
37
        $g2->Code = "my-code";
38
        $g2->write();
39
        $this->assertEquals('my-code', $g2->Code, 'Custom attributes are not overwritten by Title field');
40
41
        $g3 = new Group();
42
        $g3->Title = _t('SilverStripe\\Admin\\SecurityAdmin.NEWGROUP', "New Group");
43
        $g3->write();
44
        $this->assertNull($g3->Code, 'Default title doesnt trigger attribute setting');
45
    }
46
47
    /**
48
     * @skipUpgrade
49
     */
50
    public function testMemberGroupRelationForm()
51
    {
52
        $this->logInAs($this->idFromFixture(TestMember::class, 'admin'));
53
54
        $adminGroup = $this->objFromFixture(Group::class, 'admingroup');
55
        $parentGroup = $this->objFromFixture(Group::class, 'parentgroup');
56
57
        // Test single group relation through checkboxsetfield
58
        $form = new GroupTest\MemberForm(Controller::curr(), 'Form');
59
        /** @var Member $member */
60
        $member = $this->objFromFixture(TestMember::class, 'admin');
61
        $form->loadDataFrom($member);
62
        $checkboxSetField = $form->Fields()->fieldByName('Groups');
63
        $checkboxSetField->setValue(
64
            array(
65
            $adminGroup->ID => $adminGroup->ID, // keep existing relation
66
            $parentGroup->ID => $parentGroup->ID, // add new relation
67
            )
68
        );
69
        $form->saveInto($member);
70
        $updatedGroups = $member->Groups();
71
72
        $this->assertEquals(
73
            2,
74
            count($updatedGroups->column()),
75
            "Adding a toplevel group works"
76
        );
77
        $this->assertContains($adminGroup->ID, $updatedGroups->column('ID'));
78
        $this->assertContains($parentGroup->ID, $updatedGroups->column('ID'));
79
80
        // Test unsetting relationship
81
        $form->loadDataFrom($member);
82
        $checkboxSetField = $form->Fields()->fieldByName('Groups');
83
        $checkboxSetField->setValue(
84
            array(
85
            $adminGroup->ID => $adminGroup->ID, // keep existing relation
86
            //$parentGroup->ID => $parentGroup->ID, // remove previously set relation
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
87
            )
88
        );
89
        $form->saveInto($member);
90
        $member->flushCache();
91
        $updatedGroups = $member->Groups();
92
        $this->assertEquals(
93
            1,
94
            count($updatedGroups->column()),
95
            "Removing a previously added toplevel group works"
96
        );
97
        $this->assertContains($adminGroup->ID, $updatedGroups->column('ID'));
98
    }
99
100
    public function testUnsavedGroups()
101
    {
102
        $member = $this->objFromFixture(TestMember::class, 'admin');
103
        $group = new Group();
104
105
        // Can save user to unsaved group
106
        $group->Members()->add($member);
107
        $this->assertEquals(array($member->ID), array_values($group->Members()->getIDList()));
108
109
        // Persists after writing to DB
110
        $group->write();
111
112
        /** @var Group $group */
113
        $group = Group::get()->byID($group->ID);
114
        $this->assertEquals(array($member->ID), array_values($group->Members()->getIDList()));
115
    }
116
117
    public function testCollateAncestorIDs()
118
    {
119
        /** @var Group $parentGroup */
120
        $parentGroup = $this->objFromFixture(Group::class, 'parentgroup');
121
        /** @var Group $childGroup */
122
        $childGroup = $this->objFromFixture(Group::class, 'childgroup');
123
        $orphanGroup = new Group();
124
        $orphanGroup->ParentID = 99999;
125
        $orphanGroup->write();
126
127
        $this->assertEquals(
128
            1,
129
            count($parentGroup->collateAncestorIDs()),
130
            'Root node only contains itself'
131
        );
132
        $this->assertContains($parentGroup->ID, $parentGroup->collateAncestorIDs());
133
134
        $this->assertEquals(
135
            2,
136
            count($childGroup->collateAncestorIDs()),
137
            'Contains parent nodes, with child node first'
138
        );
139
        $this->assertContains($parentGroup->ID, $childGroup->collateAncestorIDs());
140
        $this->assertContains($childGroup->ID, $childGroup->collateAncestorIDs());
141
142
        $this->assertEquals(
143
            1,
144
            count($orphanGroup->collateAncestorIDs()),
145
            'Orphaned nodes dont contain invalid parent IDs'
146
        );
147
        $this->assertContains($orphanGroup->ID, $orphanGroup->collateAncestorIDs());
148
    }
149
150
    /**
151
     * Test that Groups including their children (recursively) are collated and returned
152
     */
153
    public function testCollateFamilyIds()
154
    {
155
        /** @var Group $group */
156
        $group = $this->objFromFixture(Group::class, 'parentgroup');
157
        $groupIds = $this->allFixtureIDs(Group::class);
158
        $ids = array_intersect_key($groupIds, array_flip(['parentgroup', 'childgroup', 'grandchildgroup']));
159
        $this->assertEquals(array_values($ids), $group->collateFamilyIDs());
160
    }
161
162
    /**
163
     * Test that an exception is thrown if collateFamilyIDs is called on an unsaved Group
164
     */
165
    public function testCannotCollateUnsavedGroupFamilyIds()
166
    {
167
        $this->expectException(InvalidArgumentException::class);
168
        $this->expectExceptionMessage('Cannot call collateFamilyIDs on unsaved Group.');
169
        $group = new Group;
170
        $group->collateFamilyIDs();
171
    }
172
173
    /**
174
     * Test that a Group's children can be retrieved
175
     */
176
    public function testGetAllChildren()
177
    {
178
        /** @var Group $group */
179
        $group = $this->objFromFixture(Group::class, 'parentgroup');
180
        $children = $group->getAllChildren();
181
        $this->assertInstanceOf(ArrayList::class, $children);
182
        $this->assertSame(['childgroup', 'grandchildgroup'], $children->column('Code'));
183
    }
184
185
    public function testGroupInGroupMethods()
186
    {
187
        $parentGroup = $this->objFromFixture(Group::class, 'parentgroup');
188
        $childGroup = $this->objFromFixture(Group::class, 'childgroup');
189
        $grandchildGroup = $this->objFromFixture(Group::class, 'grandchildgroup');
190
        $adminGroup = $this->objFromFixture(Group::class, 'admingroup');
191
        $group1 = $this->objFromFixture(Group::class, 'group1');
192
193
        $this->assertTrue($grandchildGroup->inGroup($childGroup));
0 ignored issues
show
Bug introduced by
The method inGroup() does not exist on SilverStripe\ORM\DataObject. 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

193
        $this->assertTrue($grandchildGroup->/** @scrutinizer ignore-call */ inGroup($childGroup));
Loading history...
194
        $this->assertTrue($grandchildGroup->inGroup($childGroup->ID));
195
        $this->assertTrue($grandchildGroup->inGroup($childGroup->Code));
0 ignored issues
show
Bug Best Practice introduced by
The property Code does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
196
197
        $this->assertTrue($grandchildGroup->inGroup($parentGroup));
198
        $this->assertTrue($grandchildGroup->inGroups([$parentGroup, $childGroup]));
0 ignored issues
show
Bug introduced by
The method inGroups() does not exist on SilverStripe\ORM\DataObject. 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

198
        $this->assertTrue($grandchildGroup->/** @scrutinizer ignore-call */ inGroups([$parentGroup, $childGroup]));
Loading history...
199
        $this->assertTrue($grandchildGroup->inGroups([$childGroup, $parentGroup]));
200
        $this->assertTrue($grandchildGroup->inGroups([$parentGroup, $childGroup], true));
201
202
        $this->assertFalse($grandchildGroup->inGroup($adminGroup));
203
        $this->assertFalse($grandchildGroup->inGroups([$adminGroup, $group1]));
204
        $this->assertFalse($grandchildGroup->inGroups([$adminGroup, $childGroup], true));
205
206
        $this->assertFalse($grandchildGroup->inGroup('NotARealGroup'));
207
        $this->assertFalse($grandchildGroup->inGroup(99999999999));
208
        $this->assertFalse($grandchildGroup->inGroup(new TestMember()));
209
210
        // Edgecases
211
        $this->assertTrue($grandchildGroup->inGroup($grandchildGroup));
212
        $this->assertFalse($grandchildGroup->inGroups([]));
213
        $this->assertFalse($grandchildGroup->inGroups([], true));
214
    }
215
216
    public function testDelete()
217
    {
218
        $group = $this->objFromFixture(Group::class, 'parentgroup');
219
        $groupID = $group->ID;
220
        $childGroupID = $this->idFromFixture(Group::class, 'childgroup');
221
        $group->delete();
222
223
        $this->assertEquals(
224
            0,
225
            DataObject::get(Group::class, "\"ID\" = {$groupID}")->count(),
226
            'Group is removed'
227
        );
228
        $this->assertEquals(
229
            0,
230
            DataObject::get(Permission::class, "\"GroupID\" = {$groupID}")->count(),
231
            'Permissions removed along with the group'
232
        );
233
        $this->assertEquals(
234
            0,
235
            DataObject::get(Group::class, "\"ParentID\" = {$groupID}")->count(),
236
            'Child groups are removed'
237
        );
238
        $this->assertEquals(
239
            0,
240
            DataObject::get(Group::class, "\"ParentID\" = {$childGroupID}")->count(),
241
            'Grandchild groups are removed'
242
        );
243
    }
244
245
    public function testValidatesPrivilegeLevelOfParent()
246
    {
247
        /** @var Group $nonAdminGroup */
248
        $nonAdminGroup = $this->objFromFixture(Group::class, 'childgroup');
249
        /** @var Group $adminGroup */
250
        $adminGroup = $this->objFromFixture(Group::class, 'admingroup');
251
252
        // Making admin group parent of a non-admin group, effectively expanding is privileges
253
        $nonAdminGroup->ParentID = $adminGroup->ID;
254
255
        $this->logInWithPermission('APPLY_ROLES');
256
        $result = $nonAdminGroup->validate();
257
        $this->assertFalse(
258
            $result->isValid(),
259
            'Members with only APPLY_ROLES can\'t assign parent groups with direct ADMIN permissions'
260
        );
261
262
        $this->logInWithPermission('ADMIN');
263
        $result = $nonAdminGroup->validate();
264
        $this->assertTrue(
265
            $result->isValid(),
266
            'Members with ADMIN can assign parent groups with direct ADMIN permissions'
267
        );
268
        $nonAdminGroup->write();
269
270
        $this->logInWithPermission('ADMIN');
271
        /** @var Group $inheritedAdminGroup */
272
        $inheritedAdminGroup = $this->objFromFixture(Group::class, 'group1');
273
        $inheritedAdminGroup->ParentID = $adminGroup->ID;
274
        $inheritedAdminGroup->write(); // only works with ADMIN login
275
276
        $this->logInWithPermission('APPLY_ROLES');
277
        $result = $nonAdminGroup->validate();
278
        $this->assertFalse(
279
            $result->isValid(),
280
            'Members with only APPLY_ROLES can\'t assign parent groups with inherited ADMIN permission'
281
        );
282
    }
283
}
284