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.
Passed
Push — master ( 4d285c...edf968 )
by Damien
03:23
created

ResponseAssertion::createAuthnStatement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 38
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 11
nc 1
nop 1
dl 0
loc 38
ccs 12
cts 12
cp 1
crap 1
rs 9.9
c 2
b 0
f 0
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
        ResponseMessage $response,
27
        ProviderRecord $identityProvider,
28
        ProviderRecord $serviceProvider,
29
        Settings $settings,
30
        SamlAuthnRequest $authnRequest = null
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
                $serviceProvider,
45 1
                $user,
46 1
                $settings,
47 1
                $authnRequest
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->keychainPrivateXmlSecurityKey()
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 AbstractProvider $serviceProvider
103
     * @param User $user
104
     * @param Settings $settings
105
     * @param SamlAuthnRequest|null $authnRequest
106
     * @return SubjectConfirmation
107
     * @throws \Throwable
108
     * @throws \yii\base\Exception
109
     */
110 2
    protected function createSubjectConfirmation(
111
        AbstractProvider $serviceProvider,
112
        User $user,
113
        Settings $settings,
114
        SamlAuthnRequest $authnRequest = null
115
    ) {
116
        /**
117
         * Subject Confirmation
118
         * Reference: https://stackoverflow.com/a/29546696/1590910
119
         *
120
         * The times in the <SubjectConfirmationData> signals for how long time assertion can be tied to the subject.
121
         * In Web SSO where the subject confirmation method "bearer" is usually used, it means that within this time
122
         * we can trust that the assertion applies to the one providing the assertion. The assertion might be valid
123
         * for a longer time, but we must create a session within this time frame. This is described in the Web SSO
124
         * Profile section 4.1.4.3. The times in <SubjectConfirmationData> must fall within the interval of
125
         * those in <Conditions>.
126
         */
127
128 2
        $subjectConfirmation = new SubjectConfirmation();
129
130 2
        $subjectConfirmation->setMethod(
131 2
            Constants::CM_BEARER
132
        );
133
134
135
        // Add Subject Confirmation Data
136 2
        $subjectConfirmation->setSubjectConfirmationData(
137 2
            $subjectConfirmationData = new SubjectConfirmationData()
138
        );
139
140 2
        $subjectConfirmationData->setNotOnOrAfter(
141 2
            (new \DateTime(
142 2
                $settings->messageNotOnOrAfter
143 2
            ))->getTimestamp()
144
        );
145
146 2
        if ($authnRequest) {
147 2
            $subjectConfirmationData->setInResponseTo($authnRequest->getId());
148 2
            $subjectConfirmationData->setRecipient(
149 2
                $authnRequest->getAssertionConsumerServiceURL()
150
            );
151
        }
152
153 2
        $subjectConfirmation->setNameID(
154 2
            $nameId = new NameID()
155
        );
156
157 2
        $nameId->setFormat(Constants::NAMEID_UNSPECIFIED);
158 2
        $nameId->setValue(
159 2
            $serviceProvider->assignNameId($user)
160
        );
161
162 2
        return $subjectConfirmation;
163
    }
164
165
    /**
166
     * @param Assertion $assertion
167
     * @throws \Exception
168
     */
169 2
    protected function createConditions(
170
        Assertion $assertion,
171
        Settings $settings
172
    ) {
173
        /**
174
         * Conditions
175
         * Reference: https://stackoverflow.com/a/29546696/1590910
176
         *
177
         * The times in <Conditions> is the validity of the entire assertion.
178
         * It should not be consumed after this time. There is nothing preventing a user
179
         * session on an SP to extend beyond this point in time though.
180
         */
181
182 2
        $assertion->setNotBefore(
183 2
            (new \DateTime(
184 2
                $settings->messageNotBefore
185 2
            ))->getTimestamp()
186
        );
187
188 2
        $assertion->setNotOnOrAfter(
189 2
            (new \DateTime(
190 2
                $settings->messageNotOnOrAfter
191 2
            ))->getTimestamp()
192
        );
193 2
    }
194
195
    /**
196
     * @param Assertion $assertion
197
     * @throws \yii\base\Exception
198
     * @throws \yii\base\InvalidConfigException
199
     */
200 2
    protected function createAuthnStatement(Assertion $assertion)
201
    {
202
        /**
203
         * Reference: https://stackoverflow.com/a/29546696/1590910
204
         *
205
         * SessionNotOnOrAfter is something completely different that is not directly related to the lifetime of
206
         * the assertion or the subject. It is a parameter the idp can use to control how long an SP session may be.
207
         * Please note that this parameter is defined that it SHOULD be handled by an SP according to the SAML2Core
208
         * spec, but far from all SP implementations do. An example of an implementation that does is as usual
209
         * Shibboleth, that always will respect the occurence of this parameter. When using Single Logout, this
210
         * parameter is more critical, as it synchronizes the timeout of the session on both the SP and the
211
         * Idp, to ensure that an SP does not issue a logout request for a session no longer known to the Idp.
212
         */
213 2
        $sessionEnd = (new \DateTime())->setTimestamp(
214 2
            ConfigHelper::durationInSeconds(
215
                /**
216
                * Use crafts user session duration
217
                */
218 2
                \Craft::$app->config->getGeneral()->userSessionDuration
219
            )
220
            + // Math!
221 2
            (new \DateTime())->getTimestamp()
222
        );
223
224
        /**
225
         * Add AuthnStatement attributes and AuthnContext
226
         */
227 2
        $assertion->setAuthnInstant((new \DateTime())->getTimestamp());
228 2
        $assertion->setSessionNotOnOrAfter(
229 2
            $sessionEnd->getTimestamp()
230
        );
231
232 2
        $assertion->setSessionIndex(
233 2
            Saml::getInstance()->getSession()->getId()
234
        );
235
236 2
        $assertion->setAuthnContextClassRef(
237 2
            Constants::AC_PASSWORD
238
        );
239 2
    }
240
241 2
    public function setAssertionAttributes(
242
        User $user,
243
        Assertion $assertion,
244
        ProviderRecord $serviceProvider,
245
        Settings $settings
246
    ) {
247
248
        // set on the assertion and the subject confirmations
249 2
        $assertion->setNameID(
250 2
            $nameId = new NameID()
251
        );
252
253 2
        $nameId->setFormat(Constants::NAMEID_UNSPECIFIED);
254 2
        $nameId->setValue(
255 2
            $serviceProvider->assignNameId($user)
256
        );
257
258
259
        // Check the provider first
260
        $attributeMap =
261 2
            $serviceProvider->hasMapping() ? $serviceProvider->getMapping() : $settings->responseAttributeMap;
262
263 2
        $attributes = [];
264 2
        foreach ($attributeMap as $map) {
265 2
            $map = new AttributeMap($map);
266 2
            $attributes[$map->attributeName] = $this->assignProperty(
267 2
                $user,
268 1
                $map
269
            );
270
        }
271
272
        // Add groups if configured
273 2
        if ($serviceProvider->syncGroups &&
274 2
            ($groupAttribute = $this->groupsToAttributes($user, $serviceProvider))
275
        ) {
276
            $attributes[$serviceProvider->groupsAttributeName] = $groupAttribute;
277
        }
278
279 2
        Saml::debug(json_encode($attributes));
280 2
        $assertion->setAttributes($attributes);
281 2
    }
282
283
    /**
284
     * @param User $user
285
     * @param AbstractProvider $serviceProvider
286
     * @return array|bool
287
     */
288
    protected function groupsToAttributes(User $user, AbstractProvider $serviceProvider)
289
    {
290
        if (count($user->getGroups()) === 0) {
291
            return false;
292
        }
293
        $attribute = [];
294
        foreach ($user->getGroups() as $group) {
295
            $options = $serviceProvider->getGroupOptions();
296
            if (! $options->shouldSync($group->id)) {
297
                continue;
298
            }
299
300
            $attribute[] = $group->handle;
301
        }
302
303
        return $attribute;
304
    }
305
306
    /**
307
     * Utilities
308
     */
309
310
    /**
311
     * @param User $user
312
     * @param $attributeName
313
     * @param $craftProperty
314
     * @return array
315
     */
316 2
    protected function assignProperty(
317
        User $user,
318
        AttributeMap $map
319
    ) {
320 2
        $value = $map->renderValue($user);
321
322 2
        if ($value instanceof \DateTime) {
323
            $value = $value->format(\DateTime::ISO8601);
324
        }
325
326
        return [
327 2
            $map->attributeName => $value,
328
        ];
329
    }
330
331
    /**
332
     * @return User|false|\yii\web\IdentityInterface|null
333
     */
334
    protected function getUser()
335
    {
336
        return \Craft::$app->getUser()->getIdentity();
337
    }
338
}
339