Completed
Push — master ( 0ee3b2...b21ccb )
by Damien
06:19 queued 05:11
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 find($groupName)
34
    {
35
36 3
        $groupHandle = StringHelper::camelCase($groupName);
37
38 3
        if (! $userGroup = \Craft::$app->getUserGroups()->getGroupByHandle($groupHandle)) {
39 3
            Saml::warning(
40 3
                sprintf(
41 2
                    "Group handle %s not found.".
42 3
                    " This group must be created by an admin users before user can be assigned to it.",
43 1
                    $groupHandle
44
                )
45
            );
46
        }
47
48 3
        return $userGroup;
49
    }
50
51
    /**
52
     * @param UserElement $user
53
     * @param Response $response
54
     * @return bool
55
     * @throws UserException
56
     * @throws \craft\errors\WrongEditionException
57
     */
58 3
    public function sync(UserElement $user, Response $response, ProviderRecord $serviceProvider, Settings $settings)
59
    {
60 3
        foreach ($this->getAssertions($response, $serviceProvider) as $assertion) {
61 3
            $this->syncByAssertion(
62 3
                $user,
63 1
                $assertion,
64 1
                $settings
65
            );
66
        }
67
68 3
        $this->assignDefaultGroups(
69 3
            $user,
70 1
            $settings
71
        );
72
73 3
        return true;
74
    }
75
76
    /**
77
     * @param UserElement $user
78
     * @param Assertion $assertion
79
     * @return bool
80
     * @throws UserException
81
     * @throws \craft\errors\WrongEditionException
82
     */
83 3
    protected function syncByAssertion(
84
        UserElement $user,
85
        Assertion $assertion,
86
        Settings $settings
87
    ) {
88
        /**
89
         * Nothing to do, move on
90
         */
91 3
        if (false === $settings->syncGroups) {
92
            return true;
93
        }
94
95 3
        $groupNames = $settings->groupAttributeNames;
96 3
        $groups = [];
97
        /**
98
         * Make sure there is an attribute statement
99
         */
100 3
        if (! $assertion->getAttributes()) {
101
            Saml::debug(
102
                'No attribute statement found, moving on.'
103
            );
104
            return true;
105
        }
106
107 3
        foreach ($assertion->getAttributes() as $attributeName => $attributeValue) {
108 3
            Saml::debug(
109 3
                sprintf(
110 3
                    'Is attribute group? "%s" in %s',
111 1
                    $attributeName,
112 3
                    json_encode($groupNames)
113
                )
114
            );
115
            /**
116
             * Is there a group name match?
117
             * Match the attribute name to the specified name in the plugin settings
118
             */
119 3
            if (in_array($attributeName, $groupNames)) {
120
                /**
121
                 * Loop thru all of the attributes values because they could have multiple values.
122
                 * Example XML:
123
                 * <saml2:Attribute Name="groups" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
124
                 *   <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
125
                 *           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
126
                 *           craft_admin
127
                 *           </saml2:AttributeValue>
128
                 *   <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
129
                 *           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
130
                 *           craft_member
131
                 *           </saml2:AttributeValue>
132
                 * </saml2:Attribute>
133
                 */
134 3
                if (! is_array($attributeValue)) {
135
                    $attributeValue = [$attributeValue];
136
                }
137
138 3
                foreach ($attributeValue as $groupName) {
139 3
                    if ($group = $this->find($groupName)) {
140
                        Saml::debug(
141
                            sprintf(
142
                                'Assigning group: %s',
143
                                $group->name
144
                            )
145
                        );
146
                        $groups[] = $group->id;
147
                    } else {
148 3
                        Saml::debug(
149 3
                            sprintf(
150 3
                                'Group not found: %s',
151 1
                                $groupName
152
                            )
153
                        );
154
                    }
155
                }
156
            }
157
        }
158
        /**
159
         * just return if this is empty
160
         */
161 3
        if (empty($groups)) {
162 3
            return true;
163
        }
164
165
        /**
166
         * Get existing groups
167
         */
168
        $existingGroupIds = array_map(
169
            function ($group) {
170
                return (int)$group->id;
171
            },
172
            $user->getGroups()
173
        );
174
175
        return \Craft::$app->getUsers()->assignUserToGroups(
176
            $user->id,
177
            array_unique(
178
                array_merge(
179
                    $existingGroupIds,
180
                    $groups
181
                )
182
            )
183
        );
184
    }
185
186
    /**
187
     * @param UserElement $user
188
     * @return bool|null
189
     */
190 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...
191
    {
192 3
        $groups = array_merge(
193 3
            $user->getGroups(),
194 3
            $newGroups = $this->getDefaultGroups()
195
        );
196
197
        /**
198
         * if it's not empty add the groups
199
         */
200 3
        if (! empty($newGroups)) {
201
            $groupIds = array_map(
202
                function ($group) {
203
                    return (int)$group->id;
204
                },
205
                $groups
206
            );
207
208
            if (\Craft::$app->getUsers()->assignUserToGroups($user->id, array_unique($groupIds))) {
209
                $user->setGroups($groups);
210
            }
211
        }
212
213 3
        return null;
214
    }
215
216
    /**
217
     * @return array
218
     */
219 3
    protected function getDefaultGroups()
220
    {
221 3
        $groups = [];
222 3
        foreach (Saml::getInstance()->getSettings()->defaultGroupAssignments as $groupId) {
223
            $groups[$groupId] = \Craft::$app->getUserGroups()->getGroupById($groupId);
224
        }
225
226 3
        return $groups;
227
    }
228
}
229