Completed
Push — master ( b0095b...64e902 )
by Damien
05:32
created

UserGroups::sync()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 10
cts 10
cp 1
rs 9.7
c 0
b 0
f 0
cc 2
nc 2
nop 4
crap 2
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 3
    protected function findOrCreate($groupName)
34
    {
35
36 3
        $groupHandle = StringHelper::camelCase($groupName);
37
38 3
        if (! $userGroup = \Craft::$app->getUserGroups()->getGroupByHandle($groupHandle)) {
39 3
            if (Saml::getInstance()->getSettings()->autoCreateGroups !== true) {
40
                return null;
41
            }
42 3
            $userGroup = new UserGroup(
43
                [
44 3
                    'name' => $groupName,
45 3
                    'handle' => StringHelper::toAscii($groupHandle),
46
                ]
47
            );
48 3
            if (! \Craft::$app->getUserGroups()->saveGroup(
49 3
                $userGroup
50
            )
51
            ) {
52
                Saml::error(json_encode($userGroup->getErrors()));
53
                throw new UserException("Error saving new group {$groupHandle}");
54
            }
55
        }
56
57 3
        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 3
    public function sync(UserElement $user, Response $response, ProviderRecord $serviceProvider, Settings $settings)
68
    {
69 3
        foreach ($this->getAssertions($response, $serviceProvider) as $assertion) {
70 3
            $this->syncByAssertion(
71 3
                $user,
72 1
                $assertion,
73 1
                $settings
74
            );
75
        }
76
77 3
        $this->assignDefaultGroups(
78 3
            $user,
79 1
            $settings
80
        );
81
82 3
        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 3
    protected function syncByAssertion(
93
        UserElement $user,
94
        Assertion $assertion,
95
        Settings $settings
96
    ) {
97
        /**
98
         * Nothing to do, move on
99
         */
100 3
        if (false === $settings->syncGroups) {
101
            return true;
102
        }
103
104 3
        $groupNames = $settings->groupAttributeNames;
105 3
        $groups = [];
106
        /**
107
         * Make sure there is an attribute statement
108
         */
109 3
        if (! $assertion->getAttributes()) {
110
            Saml::debug(
111
                'No attribute statement found, moving on.'
112
            );
113
            return true;
114
        }
115
116 3
        foreach ($assertion->getAttributes() as $attributeName => $attributeValue) {
117 3
            Saml::debug(
118 3
                sprintf(
119 3
                    'Is attribute group? "%s" in %s',
120 1
                    $attributeName,
121 3
                    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 3
            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 3
                if (! is_array($attributeValue)) {
144
                    $attributeValue = [$attributeValue];
145
                }
146
147 3
                foreach ($attributeValue as $groupName) {
148 3
                    if ($group = $this->findOrCreate($groupName)) {
149 3
                        Saml::debug(
150 3
                            sprintf(
151 3
                                'Assigning group: %s',
152 3
                                $group->name
153
                            )
154
                        );
155 3
                        $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 3
        if (empty($groups)) {
171
            return true;
172
        }
173
174
        /**
175
         * Get existing groups
176
         */
177 3
        $existingGroupIds = array_map(
178
            function ($group) {
179
                return (int)$group->id;
180 3
            },
181 3
            $user->getGroups()
182
        );
183
184 3
        return \Craft::$app->getUsers()->assignUserToGroups(
185 3
            $user->id,
186 3
            array_unique(
187 3
                array_merge(
188 3
                    $existingGroupIds,
189 1
                    $groups
190
                )
191
            )
192
        );
193
    }
194
195
    /**
196
     * @param UserElement $user
197
     * @return bool|null
198
     */
199 3
    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 3
        $groups = array_merge(
202 3
            $user->getGroups(),
203 3
            $newGroups = $this->getDefaultGroups()
204
        );
205
206
        /**
207
         * if it's not empty add the groups
208
         */
209 3
        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 3
        return null;
223
    }
224
225
    /**
226
     * @return array
227
     */
228 3
    protected function getDefaultGroups()
229
    {
230 3
        $groups = [];
231 3
        foreach (Saml::getInstance()->getSettings()->defaultGroupAssignments as $groupId) {
232
            $groups[$groupId] = \Craft::$app->getUserGroups()->getGroupById($groupId);
233
        }
234
235 3
        return $groups;
236
    }
237
}
238