Completed
Push — master ( 40460f...579788 )
by Damien
05:11 queued 11s
created

UserGroups::sync()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 0
cts 10
cp 0
rs 9.7
c 0
b 0
f 0
cc 2
nc 2
nop 4
crap 6
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 */
6
7
namespace flipbox\saml\sp\services\login;
8
9
use craft\elements\User as UserElement;
10
use craft\helpers\StringHelper;
11
use craft\models\UserGroup;
12
use flipbox\saml\sp\models\Settings;
13
use flipbox\saml\sp\records\ProviderRecord;
14
use flipbox\saml\sp\Saml;
15
use SAML2\Assertion;
16
use SAML2\Response;
17
use yii\base\UserException;
18
19
/**
20
 * Class UserGroups
21
 * @package flipbox\saml\sp\services
22
 */
23
class UserGroups
24
{
25
    use AssertionTrait;
26
27
    /**
28
     * @param string $groupName
29
     * @return UserGroup|null
30
     * @throws UserException
31
     * @throws \craft\errors\WrongEditionException
32
     */
33
    protected function findOrCreate($groupName)
34
    {
35
36
        $groupHandle = StringHelper::camelCase($groupName);
37
38
        if (! $userGroup = \Craft::$app->getUserGroups()->getGroupByHandle($groupHandle)) {
39
            if (Saml::getInstance()->getSettings()->autoCreateGroups !== true) {
40
                return null;
41
            }
42
            $userGroup = new UserGroup(
43
                [
44
                    'name' => $groupName,
45
                    'handle' => StringHelper::toAscii($groupHandle),
46
                ]
47
            );
48
            if (! \Craft::$app->getUserGroups()->saveGroup(
49
                $userGroup
50
            )
51
            ) {
52
                Saml::error(json_encode($userGroup->getErrors()));
53
                throw new UserException("Error saving new group {$groupHandle}");
54
            }
55
        }
56
57
        return $userGroup;
58
    }
59
60
    /**
61
     * @param UserElement $user
62
     * @param Response $response
63
     * @return bool
64
     * @throws UserException
65
     * @throws \craft\errors\WrongEditionException
66
     */
67
    public function sync(UserElement $user, Response $response, ProviderRecord $serviceProvider, Settings $settings)
68
    {
69
        foreach ($this->getAssertions($response, $serviceProvider) as $assertion) {
70
            $this->syncByAssertion(
71
                $user,
72
                $assertion,
73
                $settings
74
            );
75
        }
76
77
        $this->assignDefaultGroups(
78
            $user,
79
            $settings
80
        );
81
82
        return true;
83
    }
84
85
    /**
86
     * @param UserElement $user
87
     * @param Assertion $assertion
88
     * @return bool
89
     * @throws UserException
90
     * @throws \craft\errors\WrongEditionException
91
     */
92
    protected function syncByAssertion(
93
        UserElement $user,
94
        Assertion $assertion,
95
        Settings $settings
96
    ) {
97
        /**
98
         * Nothing to do, move on
99
         */
100
        if (false === $settings->syncGroups) {
101
            return true;
102
        }
103
104
        $groupNames = $settings->groupAttributeNames;
105
        $groups = [];
106
        /**
107
         * Make sure there is an attribute statement
108
         */
109
        if (! $assertion->getAttributes()) {
110
            Saml::debug(
111
                'No attribute statement found, moving on.'
112
            );
113
            return true;
114
        }
115
116
        foreach ($assertion->getAttributes() as $attributeName => $attributeValue) {
117
            Saml::debug(
118
                sprintf(
119
                    'Is attribute group? "%s" in %s',
120
                    $attributeName,
121
                    json_encode($groupNames)
122
                )
123
            );
124
            /**
125
             * Is there a group name match?
126
             * Match the attribute name to the specified name in the plugin settings
127
             */
128
            if (in_array($attributeName, $groupNames)) {
129
                /**
130
                 * Loop thru all of the attributes values because they could have multiple values.
131
                 * Example XML:
132
                 * <saml2:Attribute Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
133
                 *   <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
134
                 *           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
135
                 *           craft_admin
136
                 *           </saml2:AttributeValue>
137
                 *   <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
138
                 *           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
139
                 *           craft_member
140
                 *           </saml2:AttributeValue>
141
                 * </saml2:Attribute>
142
                 */
143
                if (! is_array($attributeValue)) {
144
                    $attributeValue = [$attributeValue];
145
                }
146
147
                foreach ($attributeValue as $groupName) {
148
                    if ($group = $this->findOrCreate($groupName)) {
149
                        Saml::debug(
150
                            sprintf(
151
                                'Assigning group: %s',
152
                                $group->name
153
                            )
154
                        );
155
                        $groups[] = $group->id;
156
                    } else {
157
                        Saml::debug(
158
                            sprintf(
159
                                'Group not found or created for %s',
160
                                $groupName
161
                            )
162
                        );
163
                    }
164
                }
165
            }
166
        }
167
        /**
168
         * just return if this is empty
169
         */
170
        if (empty($groups)) {
171
            return true;
172
        }
173
174
        /**
175
         * Get existing groups
176
         */
177
        $existingGroupIds = array_map(
178
            function ($group) {
179
                return (int)$group->id;
180
            },
181
            $user->getGroups()
182
        );
183
184
        return \Craft::$app->getUsers()->assignUserToGroups(
185
            $user->id,
186
            array_unique(
187
                array_merge(
188
                    $existingGroupIds,
189
                    $groups
190
                )
191
            )
192
        );
193
    }
194
195
    /**
196
     * @param UserElement $user
197
     * @return bool|null
198
     */
199
    protected function assignDefaultGroups(\craft\elements\User $user, Settings $settings)
0 ignored issues
show
Unused Code introduced by
The parameter $settings is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
200
    {
201
        $groups = array_merge(
202
            $user->getGroups(),
203
            $newGroups = $this->getDefaultGroups()
204
        );
205
206
        /**
207
         * if it's not empty add the groups
208
         */
209
        if (! empty($newGroups)) {
210
            $groupIds = array_map(
211
                function ($group) {
212
                    return (int)$group->id;
213
                },
214
                $groups
215
            );
216
217
            if (\Craft::$app->getUsers()->assignUserToGroups($user->id, array_unique($groupIds))) {
218
                $user->setGroups($groups);
219
            }
220
        }
221
222
        return null;
223
    }
224
225
    /**
226
     * @return array
227
     */
228
    protected function getDefaultGroups()
229
    {
230
        $groups = [];
231
        foreach (Saml::getInstance()->getSettings()->defaultGroupAssignments as $groupId) {
232
            $groups[$groupId] = \Craft::$app->getUserGroups()->getGroupById($groupId);
233
        }
234
235
        return $groups;
236
    }
237
}
238