GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 310b67...f3cbc4 )
by Damien
16:56 queued 13:08
created

ResponseAssertion::createSubjectConfirmation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 4
dl 0
loc 51
ccs 19
cts 19
cp 1
crap 1
rs 9.6666
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace flipbox\saml\idp\services\messages;
4
5
use craft\base\Component;
6
use craft\elements\User;
7
use craft\helpers\ConfigHelper;
8
use flipbox\saml\core\models\AttributeMap;
9
use flipbox\saml\core\records\AbstractProvider;
10
use flipbox\saml\idp\models\Settings;
11
use flipbox\saml\idp\records\ProviderRecord;
12
use flipbox\saml\idp\Saml;
13
use SAML2\Assertion;
14
use SAML2\AuthnRequest as SamlAuthnRequest;
15
use SAML2\Constants;
16
use SAML2\EncryptedAssertion;
17
use SAML2\Response as ResponseMessage;
18
use SAML2\XML\saml\NameID;
19
use SAML2\XML\saml\SubjectConfirmation;
20
use SAML2\XML\saml\SubjectConfirmationData;
21
22
class ResponseAssertion extends Component
23
{
24 2
    public function create(
25
        User $user,
26
        SamlAuthnRequest $authnRequest,
27
        ResponseMessage $response,
28
        ProviderRecord $identityProvider,
29
        ProviderRecord $serviceProvider,
30
        Settings $settings
31
    ) {
32 2
        $assertion = new Assertion();
33
34 2
        $issuer = $response->getIssuer();
35 2
        if (! is_null($issuer)) {
36 2
            $assertion->setIssuer(
37 2
                $issuer
38
            );
39
        }
40
41
42 2
        $assertion->setSubjectConfirmation([
43 2
            $this->createSubjectConfirmation(
44 2
                $authnRequest,
45 1
                $serviceProvider,
46 1
                $user,
47 1
                $settings
48
            ),
49
        ]);
50
51 2
        $this->createConditions($assertion, $settings);
52
53
54 2
        $this->createAuthnStatement($assertion);
55
56
57 2
        $this->setAssertionAttributes(
58 2
            $user,
59 1
            $assertion,
60 1
            $serviceProvider,
61 1
            $settings
62
        );
63
64 2
        $firstDescriptor = $serviceProvider->spSsoDescriptors()[0];
65
66
        // Sign Assertions
67 2
        if ($firstDescriptor->wantAssertionsSigned()) {
68
            $assertion->setSignatureKey(
69
                $identityProvider->signingXMLSecurityKey()
70
            );
71
        }
72
73
74
        // Encrypt Assertions
75 2
        if ($serviceProvider->encryptAssertions) {
76
            $unencrypted = $assertion;
77
78
            if (is_null($serviceProvider->encryptionKey())) {
79
                throw new \Exception('No encryption key found for the service provider.');
80
            }
81
            $unencrypted->setEncryptionKey(
82
                $serviceProvider->encryptionKey()
83
            );
84
85
            $assertion = new EncryptedAssertion();
86
            $assertion->setAssertion(
87
                $unencrypted,
88
                $serviceProvider->encryptionKey()
89
            );
90
        }
91
92 2
        $response->setAssertions(
93
            [
94 2
                $assertion,
95
            ]
96
        );
97
98 2
        return $assertion;
99
    }
100
101
    /**
102
     * @param SamlAuthnRequest $authnRequest
103
     * @param User $user
104
     * @return SubjectConfirmation
105
     * @throws \Exception
106
     */
107 2
    protected function createSubjectConfirmation(
108
        SamlAuthnRequest $authnRequest,
109
        AbstractProvider $serviceProvider,
110
        User $user,
111
        Settings $settings
112
    ) {
113
        /**
114
         * Subject Confirmation
115
         * Reference: https://stackoverflow.com/a/29546696/1590910
116
         *
117
         * The times in the <SubjectConfirmationData> signals for how long time assertion can be tied to the subject.
118
         * In Web SSO where the subject confirmation method "bearer" is usually used, it means that within this time
119
         * we can trust that the assertion applies to the one providing the assertion. The assertion might be valid
120
         * for a longer time, but we must create a session within this time frame. This is described in the Web SSO
121
         * Profile section 4.1.4.3. The times in <SubjectConfirmationData> must fall within the interval of
122
         * those in <Conditions>.
123
         */
124
125 2
        $subjectConfirmation = new SubjectConfirmation();
126
127 2
        $subjectConfirmation->setMethod(
128 2
            Constants::CM_BEARER
129
        );
130
131
132
        // Add Subject Confirmation Data
133 2
        $subjectConfirmation->setSubjectConfirmationData(
134 2
            $subjectConfirmationData = new SubjectConfirmationData()
135
        );
136
137 2
        $subjectConfirmationData->setInResponseTo($authnRequest->getId());
138 2
        $subjectConfirmationData->setNotOnOrAfter(
139 2
            (new \DateTime(
140 2
                $settings->messageNotOnOrAfter
141 2
            ))->getTimestamp()
142
        );
143
144 2
        $subjectConfirmationData->setRecipient(
145 2
            $authnRequest->getAssertionConsumerServiceURL()
146
        );
147
148 2
        $subjectConfirmation->setNameID(
149 2
            $nameId = new NameID()
150
        );
151
152 2
        $nameId->setFormat(Constants::NAMEID_UNSPECIFIED);
153 2
        $nameId->setValue(
154 2
            $serviceProvider->assignNameId($user)
155
        );
156
157 2
        return $subjectConfirmation;
158
    }
159
160
    /**
161
     * @param Assertion $assertion
162
     * @throws \Exception
163
     */
164 2
    protected function createConditions(
165
        Assertion $assertion,
166
        Settings $settings
167
    ) {
168
        /**
169
         * Conditions
170
         * Reference: https://stackoverflow.com/a/29546696/1590910
171
         *
172
         * The times in <Conditions> is the validity of the entire assertion.
173
         * It should not be consumed after this time. There is nothing preventing a user
174
         * session on an SP to extend beyond this point in time though.
175
         */
176
177 2
        $assertion->setNotBefore(
178 2
            (new \DateTime(
179 2
                $settings->messageNotBefore
180 2
            ))->getTimestamp()
181
        );
182
183 2
        $assertion->setNotOnOrAfter(
184 2
            (new \DateTime(
185 2
                $settings->messageNotOnOrAfter
186 2
            ))->getTimestamp()
187
        );
188 2
    }
189
190
    /**
191
     * @param Assertion $assertion
192
     * @throws \yii\base\Exception
193
     * @throws \yii\base\InvalidConfigException
194
     */
195 2
    protected function createAuthnStatement(Assertion $assertion)
196
    {
197
        /**
198
         * Reference: https://stackoverflow.com/a/29546696/1590910
199
         *
200
         * SessionNotOnOrAfter is something completely different that is not directly related to the lifetime of
201
         * the assertion or the subject. It is a parameter the idp can use to control how long an SP session may be.
202
         * Please note that this parameter is defined that it SHOULD be handled by an SP according to the SAML2Core
203
         * spec, but far from all SP implementations do. An example of an implementation that does is as usual
204
         * Shibboleth, that always will respect the occurence of this parameter. When using Single Logout, this
205
         * parameter is more critical, as it synchronizes the timeout of the session on both the SP and the
206
         * Idp, to ensure that an SP does not issue a logout request for a session no longer known to the Idp.
207
         */
208 2
        $sessionEnd = (new \DateTime())->setTimestamp(
209 2
            ConfigHelper::durationInSeconds(
210
                /**
211
                * Use crafts user session duration
212
                */
213 2
                \Craft::$app->config->getGeneral()->userSessionDuration
214
            )
215
            + // Math!
216 2
            (new \DateTime())->getTimestamp()
217
        );
218
219
        /**
220
         * Add AuthnStatement attributes and AuthnContext
221
         */
222 2
        $assertion->setAuthnInstant((new \DateTime())->getTimestamp());
223 2
        $assertion->setSessionNotOnOrAfter(
224 2
            $sessionEnd->getTimestamp()
225
        );
226
227 2
        $assertion->setSessionIndex(
228 2
            Saml::getInstance()->getSession()->getId()
229
        );
230
231 2
        $assertion->setAuthnContextClassRef(
232 2
            Constants::AC_PASSWORD
233
        );
234 2
    }
235
236 2
    public function setAssertionAttributes(
237
        User $user,
238
        Assertion $assertion,
239
        ProviderRecord $serviceProvider,
240
        Settings $settings
241
    ) {
242
243
        // set on the assertion and the subject confirmations
244 2
        $assertion->setNameID(
245 2
            $nameId = new NameID()
246
        );
247
248 2
        $nameId->setFormat(Constants::NAMEID_UNSPECIFIED);
249 2
        $nameId->setValue(
250 2
            $serviceProvider->assignNameId($user)
251
        );
252
253
254
        // Check the provider first
255
        $attributeMap =
256 2
            $serviceProvider->hasMapping() ? $serviceProvider->getMapping() : $settings->responseAttributeMap;
257
258 2
        $attributes = [];
259 2
        foreach ($attributeMap as $map) {
260 2
            $map = new AttributeMap($map);
261 2
            $attributes[$map->attributeName] = $this->assignProperty(
262 2
                $user,
263 1
                $map
264
            );
265
        }
266
267
        // Add groups if configured
268 2
        if ($serviceProvider->syncGroups &&
269 2
            ($groupAttribute = $this->groupsToAttributes($user, $serviceProvider))
270
        ) {
271
            $attributes[$serviceProvider->groupsAttributeName] = $groupAttribute;
272
        }
273
274 2
        Saml::debug(json_encode($attributes));
275 2
        $assertion->setAttributes($attributes);
276 2
    }
277
278
    /**
279
     * @param User $user
280
     * @param AbstractProvider $serviceProvider
281
     * @return array|bool
282
     */
283
    protected function groupsToAttributes(User $user, AbstractProvider $serviceProvider)
284
    {
285
        if (count($user->getGroups()) === 0) {
286
            return false;
287
        }
288
        $attribute = [];
289
        foreach ($user->getGroups() as $group) {
290
            $options = $serviceProvider->getGroupOptions();
291
            if (! $options->shouldSync($group->id)) {
292
                continue;
293
            }
294
295
            $attribute[] = $group->name;
296
        }
297
298
        return $attribute;
299
    }
300
301
    /**
302
     * Utilities
303
     */
304
305
    /**
306
     * @param User $user
307
     * @param $attributeName
308
     * @param $craftProperty
309
     * @return array
310
     */
311 2
    protected function assignProperty(
312
        User $user,
313
        AttributeMap $map
314
    ) {
315 2
        $value = $map->renderValue($user);
316
317 2
        if ($value instanceof \DateTime) {
318
            $value = $value->format(\DateTime::ISO8601);
319
        }
320
321
        return [
322 2
            $map->attributeName => $value,
323
        ];
324
    }
325
326
    /**
327
     * @return User|false|\yii\web\IdentityInterface|null
328
     */
329
    protected function getUser()
330
    {
331
        return \Craft::$app->getUser()->getIdentity();
332
    }
333
}
334