Completed
Pull Request — develop (#320)
by Michiel
05:27 queued 02:41
created

Identity::vetSecondFactor()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 62
rs 7.8957
c 0
b 0
f 0
cc 7
nc 6
nop 10

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Copyright 2014 SURFnet bv
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace Surfnet\Stepup\Identity;
20
21
use Broadway\EventSourcing\EventSourcedAggregateRoot;
22
use Surfnet\Stepup\Configuration\InstitutionConfiguration;
23
use Surfnet\Stepup\Configuration\Value\Institution as ConfigurationInstitution;
24
use Surfnet\Stepup\DateTime\DateTime;
25
use Surfnet\Stepup\Exception\DomainException;
26
use Surfnet\Stepup\Helper\SecondFactorProvePossessionHelper;
27
use Surfnet\Stepup\Identity\Api\Identity as IdentityApi;
28
use Surfnet\Stepup\Identity\Entity\RegistrationAuthority;
29
use Surfnet\Stepup\Identity\Entity\RegistrationAuthorityCollection;
30
use Surfnet\Stepup\Identity\Entity\SecondFactorCollection;
31
use Surfnet\Stepup\Identity\Entity\UnverifiedSecondFactor;
32
use Surfnet\Stepup\Identity\Entity\VerifiedSecondFactor;
33
use Surfnet\Stepup\Identity\Entity\VettedSecondFactor;
34
use Surfnet\Stepup\Identity\Event\AppointedAsRaaEvent;
35
use Surfnet\Stepup\Identity\Event\AppointedAsRaEvent;
36
use Surfnet\Stepup\Identity\Event\AppointedAsRaaForInstitutionEvent;
37
use Surfnet\Stepup\Identity\Event\AppointedAsRaForInstitutionEvent;
38
use Surfnet\Stepup\Identity\Event\CompliedWithUnverifiedSecondFactorRevocationEvent;
39
use Surfnet\Stepup\Identity\Event\CompliedWithVerifiedSecondFactorRevocationEvent;
40
use Surfnet\Stepup\Identity\Event\CompliedWithVettedSecondFactorRevocationEvent;
41
use Surfnet\Stepup\Identity\Event\EmailVerifiedEvent;
42
use Surfnet\Stepup\Identity\Event\GssfPossessionProvenAndVerifiedEvent;
43
use Surfnet\Stepup\Identity\Event\GssfPossessionProvenEvent;
44
use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaaEvent;
45
use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaaForInstitutionEvent;
46
use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaEvent;
47
use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaForInstitutionEvent;
48
use Surfnet\Stepup\Identity\Event\IdentityCreatedEvent;
49
use Surfnet\Stepup\Identity\Event\IdentityEmailChangedEvent;
50
use Surfnet\Stepup\Identity\Event\IdentityForgottenEvent;
51
use Surfnet\Stepup\Identity\Event\IdentityRenamedEvent;
52
use Surfnet\Stepup\Identity\Event\LocalePreferenceExpressedEvent;
53
use Surfnet\Stepup\Identity\Event\PhonePossessionProvenAndVerifiedEvent;
54
use Surfnet\Stepup\Identity\Event\PhonePossessionProvenEvent;
55
use Surfnet\Stepup\Identity\Event\RegistrationAuthorityInformationAmendedEvent;
56
use Surfnet\Stepup\Identity\Event\RegistrationAuthorityInformationAmendedForInstitutionEvent;
57
use Surfnet\Stepup\Identity\Event\RegistrationAuthorityRetractedEvent;
58
use Surfnet\Stepup\Identity\Event\RegistrationAuthorityRetractedForInstitutionEvent;
59
use Surfnet\Stepup\Identity\Event\SecondFactorVettedWithoutTokenProofOfPossession;
60
use Surfnet\Stepup\Identity\Event\SecondFactorVettedEvent;
61
use Surfnet\Stepup\Identity\Event\U2fDevicePossessionProvenAndVerifiedEvent;
62
use Surfnet\Stepup\Identity\Event\U2fDevicePossessionProvenEvent;
63
use Surfnet\Stepup\Identity\Event\UnverifiedSecondFactorRevokedEvent;
64
use Surfnet\Stepup\Identity\Event\VerifiedSecondFactorRevokedEvent;
65
use Surfnet\Stepup\Identity\Event\VettedSecondFactorRevokedEvent;
66
use Surfnet\Stepup\Identity\Event\VettedSecondFactorsAllRevokedEvent;
67
use Surfnet\Stepup\Identity\Event\YubikeyPossessionProvenAndVerifiedEvent;
68
use Surfnet\Stepup\Identity\Event\YubikeyPossessionProvenEvent;
69
use Surfnet\Stepup\Identity\Event\YubikeySecondFactorBootstrappedEvent;
70
use Surfnet\Stepup\Identity\Value\CommonName;
71
use Surfnet\Stepup\Identity\Value\ContactInformation;
72
use Surfnet\Stepup\Identity\Value\DocumentNumber;
73
use Surfnet\Stepup\Identity\Value\Email;
74
use Surfnet\Stepup\Identity\Value\EmailVerificationWindow;
75
use Surfnet\Stepup\Identity\Value\GssfId;
76
use Surfnet\Stepup\Identity\Value\IdentityId;
77
use Surfnet\Stepup\Identity\Value\Institution;
78
use Surfnet\Stepup\Identity\Value\Locale;
79
use Surfnet\Stepup\Identity\Value\Location;
80
use Surfnet\Stepup\Identity\Value\NameId;
81
use Surfnet\Stepup\Identity\Value\PhoneNumber;
82
use Surfnet\Stepup\Identity\Value\RegistrationAuthorityRole;
83
use Surfnet\Stepup\Identity\Value\SecondFactorId;
84
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifier;
85
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifierFactory;
86
use Surfnet\Stepup\Identity\Value\StepupProvider;
87
use Surfnet\Stepup\Identity\Value\U2fKeyHandle;
88
use Surfnet\Stepup\Identity\Value\YubikeyPublicId;
89
use Surfnet\Stepup\Token\TokenGenerator;
90
use Surfnet\StepupBundle\Security\OtpGenerator;
91
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
92
use Surfnet\StepupBundle\Value\SecondFactorType;
93
use Surfnet\StepupMiddleware\ApiBundle\Identity\Service\SecondFactorService;
94
use function sprintf;
95
96
/**
97
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
98
 * @SuppressWarnings(PHPMD.TooManyMethods)
99
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
100
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
101
 * @SuppressWarnings(PHPMD.ExcessiveClassLength)
102
 */
103
class Identity extends EventSourcedAggregateRoot implements IdentityApi
104
{
105
    /**
106
     * @var IdentityId
107
     */
108
    private $id;
109
110
    /**
111
     * @var Institution
112
     */
113
    private $institution;
114
115
    /**
116
     * @var NameId
117
     */
118
    private $nameId;
119
120
    /**
121
     * @var \Surfnet\Stepup\Identity\Value\CommonName
122
     */
123
    private $commonName;
124
125
    /**
126
     * @var \Surfnet\Stepup\Identity\Value\Email
127
     */
128
    private $email;
129
130
    /**
131
     * @var SecondFactorCollection|UnverifiedSecondFactor[]
132
     */
133
    private $unverifiedSecondFactors;
134
135
    /**
136
     * @var SecondFactorCollection|VerifiedSecondFactor[]
137
     */
138
    private $verifiedSecondFactors;
139
140
    /**
141
     * @var SecondFactorCollection|VettedSecondFactor[]
142
     */
143
    private $vettedSecondFactors;
144
145
    /**
146
     * @var RegistrationAuthorityCollection
147
     */
148
    private $registrationAuthorities;
149
150
    /**
151
     * @var Locale
152
     */
153
    private $preferredLocale;
154
155
    /**
156
     * @var boolean
157
     */
158
    private $forgotten;
159
160
    /**
161
     * @var int
162
     */
163
    private $maxNumberOfTokens = 1;
164
165
    public static function create(
166
        IdentityId $id,
167
        Institution $institution,
168
        NameId $nameId,
169
        CommonName $commonName,
170
        Email $email,
171
        Locale $preferredLocale
172
    ) {
173
        $identity = new self();
174
        $identity->apply(new IdentityCreatedEvent($id, $institution, $nameId, $commonName, $email, $preferredLocale));
175
176
        return $identity;
177
    }
178
179
    final public function __construct()
180
    {
181
    }
182
183
    public function rename(CommonName $commonName)
184
    {
185
        $this->assertNotForgotten();
186
187
        if ($this->commonName->equals($commonName)) {
188
            return;
189
        }
190
191
        $this->commonName = $commonName;
192
        $this->apply(new IdentityRenamedEvent($this->id, $this->institution, $commonName));
193
    }
194
195
    public function changeEmail(Email $email)
196
    {
197
        $this->assertNotForgotten();
198
199
        if ($this->email->equals($email)) {
200
            return;
201
        }
202
203
        $this->email = $email;
204
        $this->apply(new IdentityEmailChangedEvent($this->id, $this->institution, $email));
205
    }
206
207
    /**
208
     * @param int $numberOfTokens
209
     */
210
    public function setMaxNumberOfTokens($numberOfTokens)
211
    {
212
        $this->maxNumberOfTokens = $numberOfTokens;
213
    }
214
215
    public function bootstrapYubikeySecondFactor(SecondFactorId $secondFactorId, YubikeyPublicId $yubikeyPublicId)
216
    {
217
        $this->assertNotForgotten();
218
        $this->assertUserMayAddSecondFactor();
219
220
        $this->apply(
221
            new YubikeySecondFactorBootstrappedEvent(
222
                $this->id,
223
                $this->nameId,
224
                $this->institution,
225
                $this->commonName,
226
                $this->email,
227
                $this->preferredLocale,
228
                $secondFactorId,
229
                $yubikeyPublicId
230
            )
231
        );
232
    }
233
234
    public function provePossessionOfYubikey(
235
        SecondFactorId $secondFactorId,
236
        YubikeyPublicId $yubikeyPublicId,
237
        $emailVerificationRequired,
238
        EmailVerificationWindow $emailVerificationWindow
239
    ) {
240
        $this->assertNotForgotten();
241
        $this->assertUserMayAddSecondFactor();
242
243
        if ($emailVerificationRequired) {
244
            $emailVerificationNonce = TokenGenerator::generateNonce();
245
246
            $this->apply(
247
                new YubikeyPossessionProvenEvent(
248
                    $this->id,
249
                    $this->institution,
250
                    $secondFactorId,
251
                    $yubikeyPublicId,
252
                    $emailVerificationRequired,
253
                    $emailVerificationWindow,
254
                    $emailVerificationNonce,
255
                    $this->commonName,
256
                    $this->email,
257
                    $this->preferredLocale
258
                )
259
            );
260
        } else {
261
            $this->apply(
262
                new YubikeyPossessionProvenAndVerifiedEvent(
263
                    $this->id,
264
                    $this->institution,
265
                    $secondFactorId,
266
                    $yubikeyPublicId,
267
                    $this->commonName,
268
                    $this->email,
269
                    $this->preferredLocale,
270
                    DateTime::now(),
271
                    OtpGenerator::generate(8)
272
                )
273
            );
274
        }
275
    }
276
277
    public function provePossessionOfPhone(
278
        SecondFactorId $secondFactorId,
279
        PhoneNumber $phoneNumber,
280
        $emailVerificationRequired,
281
        EmailVerificationWindow $emailVerificationWindow
282
    ) {
283
        $this->assertNotForgotten();
284
        $this->assertUserMayAddSecondFactor();
285
286
        if ($emailVerificationRequired) {
287
            $emailVerificationNonce = TokenGenerator::generateNonce();
288
289
            $this->apply(
290
                new PhonePossessionProvenEvent(
291
                    $this->id,
292
                    $this->institution,
293
                    $secondFactorId,
294
                    $phoneNumber,
295
                    $emailVerificationRequired,
296
                    $emailVerificationWindow,
297
                    $emailVerificationNonce,
298
                    $this->commonName,
299
                    $this->email,
300
                    $this->preferredLocale
301
                )
302
            );
303
        } else {
304
            $this->apply(
305
                new PhonePossessionProvenAndVerifiedEvent(
306
                    $this->id,
307
                    $this->institution,
308
                    $secondFactorId,
309
                    $phoneNumber,
310
                    $this->commonName,
311
                    $this->email,
312
                    $this->preferredLocale,
313
                    DateTime::now(),
314
                    OtpGenerator::generate(8)
315
                )
316
            );
317
        }
318
    }
319
320
    public function provePossessionOfGssf(
321
        SecondFactorId $secondFactorId,
322
        StepupProvider $provider,
323
        GssfId $gssfId,
324
        $emailVerificationRequired,
325
        EmailVerificationWindow $emailVerificationWindow
326
    ) {
327
        $this->assertNotForgotten();
328
        $this->assertUserMayAddSecondFactor();
329
330
        if ($emailVerificationRequired) {
331
            $emailVerificationNonce = TokenGenerator::generateNonce();
332
333
            $this->apply(
334
                new GssfPossessionProvenEvent(
335
                    $this->id,
336
                    $this->institution,
337
                    $secondFactorId,
338
                    $provider,
339
                    $gssfId,
340
                    $emailVerificationRequired,
341
                    $emailVerificationWindow,
342
                    $emailVerificationNonce,
343
                    $this->commonName,
344
                    $this->email,
345
                    $this->preferredLocale
346
                )
347
            );
348
        } else {
349
            $this->apply(
350
                new GssfPossessionProvenAndVerifiedEvent(
351
                    $this->id,
352
                    $this->institution,
353
                    $secondFactorId,
354
                    $provider,
355
                    $gssfId,
356
                    $this->commonName,
357
                    $this->email,
358
                    $this->preferredLocale,
359
                    DateTime::now(),
360
                    OtpGenerator::generate(8)
361
                )
362
            );
363
        }
364
    }
365
366
    public function provePossessionOfU2fDevice(
367
        SecondFactorId $secondFactorId,
368
        U2fKeyHandle $keyHandle,
369
        $emailVerificationRequired,
370
        EmailVerificationWindow $emailVerificationWindow
371
    ) {
372
        $this->assertNotForgotten();
373
        $this->assertUserMayAddSecondFactor();
374
375
        if ($emailVerificationRequired) {
376
            $emailVerificationNonce = TokenGenerator::generateNonce();
377
378
            $this->apply(
379
                new U2fDevicePossessionProvenEvent(
380
                    $this->id,
381
                    $this->institution,
382
                    $secondFactorId,
383
                    $keyHandle,
384
                    $emailVerificationRequired,
385
                    $emailVerificationWindow,
386
                    $emailVerificationNonce,
387
                    $this->commonName,
388
                    $this->email,
389
                    $this->preferredLocale
390
                )
391
            );
392
        } else {
393
            $this->apply(
394
                new U2fDevicePossessionProvenAndVerifiedEvent(
395
                    $this->id,
396
                    $this->institution,
397
                    $secondFactorId,
398
                    $keyHandle,
399
                    $this->commonName,
400
                    $this->email,
401
                    $this->preferredLocale,
402
                    DateTime::now(),
403
                    OtpGenerator::generate(8)
404
                )
405
            );
406
        }
407
    }
408
409
    public function verifyEmail($verificationNonce)
410
    {
411
        $this->assertNotForgotten();
412
413
        $secondFactorToVerify = null;
414
        foreach ($this->unverifiedSecondFactors as $secondFactor) {
415
            /** @var Entity\UnverifiedSecondFactor $secondFactor */
416
            if ($secondFactor->hasNonce($verificationNonce)) {
417
                $secondFactorToVerify = $secondFactor;
418
            }
419
        }
420
421
        if (!$secondFactorToVerify) {
422
            throw new DomainException(
423
                'Cannot verify second factor, no unverified second factor can be verified using the given nonce'
424
            );
425
        }
426
427
        /** @var Entity\UnverifiedSecondFactor $secondFactorToVerify */
428
        if (!$secondFactorToVerify->canBeVerifiedNow()) {
429
            throw new DomainException('Cannot verify second factor, the verification window is closed.');
430
        }
431
432
        $secondFactorToVerify->verifyEmail();
433
    }
434
435
    /**
436
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
437
     */
438
    public function vetSecondFactor(
439
        IdentityApi $registrant,
440
        SecondFactorId $registrantsSecondFactorId,
441
        SecondFactorType $registrantsSecondFactorType,
442
        SecondFactorIdentifier $registrantsSecondFactorIdentifier,
443
        $registrationCode,
444
        DocumentNumber $documentNumber,
445
        $identityVerified,
446
        SecondFactorTypeService $secondFactorTypeService,
447
        SecondFactorProvePossessionHelper $secondFactorProvePossessionHelper,
448
        $provePossessionSkipped
449
    ) {
450
        $this->assertNotForgotten();
451
452
        /** @var VettedSecondFactor|null $secondFactorWithHighestLoa */
453
        $secondFactorWithHighestLoa = $this->vettedSecondFactors->getSecondFactorWithHighestLoa($secondFactorTypeService);
454
        $registrantsSecondFactor = $registrant->getVerifiedSecondFactor($registrantsSecondFactorId);
455
456
        if ($registrantsSecondFactor === null) {
457
            throw new DomainException(
458
                sprintf('Registrant second factor with ID %s does not exist', $registrantsSecondFactorId)
459
            );
460
        }
461
462
        if ($secondFactorWithHighestLoa === null) {
463
            throw new DomainException(
464
                sprintf(
465
                    'Vetting failed: authority %s has %d vetted second factors!',
466
                    $this->id,
467
                    count($this->vettedSecondFactors)
468
                )
469
            );
470
        }
471
472
        if (!$secondFactorWithHighestLoa->hasEqualOrHigherLoaComparedTo(
473
            $registrantsSecondFactor,
474
            $secondFactorTypeService
475
        )) {
476
            throw new DomainException("Authority does not have the required LoA to vet the registrant's second factor");
477
        }
478
479
        if (!$identityVerified) {
480
            throw new DomainException('Will not vet second factor when physical identity has not been verified.');
481
        }
482
483
        if ($provePossessionSkipped && !$secondFactorProvePossessionHelper->canSkipProvePossession($registrantsSecondFactorType)) {
484
            throw new DomainException(sprintf(
485
                "The possession of registrants second factor with ID '%s' of type '%s' has to be physically proven",
486
                $registrantsSecondFactorId,
487
                $registrantsSecondFactorType->getSecondFactorType()
488
            ));
489
        }
490
491
        $registrant->complyWithVettingOfSecondFactor(
492
            $registrantsSecondFactorId,
493
            $registrantsSecondFactorType,
494
            $registrantsSecondFactorIdentifier,
495
            $registrationCode,
496
            $documentNumber,
497
            $provePossessionSkipped
498
        );
499
    }
500
501
    /**
502
     * Self vetting, is when the user uses it's own token to vet another.
503
     *
504
     * Here the new token should have a lower or equal LoA to that of the one in posession of the user
505
     */
506
    public function selfVetSecondFactor(
507
        SecondFactorId $authoringSecondFactorId,
508
        string $registrationCode,
509
        SecondFactorIdentifier $secondFactorIdentifier,
510
        SecondFactorTypeService $secondFactorTypeService
511
    ): void {
512
        $this->assertNotForgotten();
513
        $registeringSecondFactor = null;
514
        foreach ($this->verifiedSecondFactors as $secondFactor) {
515
            /** @var VerifiedSecondFactor $secondFactor */
516
            if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
517
                $registeringSecondFactor = $secondFactor;
518
            }
519
        }
520
521
        if ($registeringSecondFactor === null) {
522
            throw new DomainException(
523
                sprintf('Registrant second factor of type %s with ID %s does not exist', get_class($secondFactorIdentifier), $secondFactorIdentifier->getValue())
524
            );
525
        }
526
527
        if (!$registeringSecondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
528
            throw new DomainException('The verified second factors registration code or identifier do not match.');
529
        }
530
        $authoringSecondFactor = $this->getVettedSecondFactor($authoringSecondFactorId);
531
        if ($authoringSecondFactor === null) {
532
            throw new DomainException(
533
                sprintf('Authorizing second factor with ID %s does not exist', $authoringSecondFactorId)
534
            );
535
        }
536
        if ($authoringSecondFactor->hasEqualOrHigherLoaComparedTo($registeringSecondFactor, $secondFactorTypeService)) {
537
            throw new DomainException("The second factor to be vetted has a higher LoA then the Token used for proving posession");
538
        }
539
        $registeringSecondFactor->vet(DocumentNumber::unknown(), true);
540
    }
541
542
    public function complyWithVettingOfSecondFactor(
543
        SecondFactorId $secondFactorId,
544
        SecondFactorType $secondFactorType,
545
        SecondFactorIdentifier $secondFactorIdentifier,
546
        $registrationCode,
547
        DocumentNumber $documentNumber,
548
        $provePossessionSkipped
549
    ) {
550
        $this->assertNotForgotten();
551
552
        $secondFactorToVet = null;
553
        foreach ($this->verifiedSecondFactors as $secondFactor) {
554
            /** @var VerifiedSecondFactor $secondFactor */
555
            if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
556
                $secondFactorToVet = $secondFactor;
557
            }
558
        }
559
560
        if (!$secondFactorToVet) {
561
            throw new DomainException(
562
                'Cannot vet second factor, no verified second factor can be vetted using the given registration code ' .
563
                'and second factor identifier'
564
            );
565
        }
566
567
        if (!$secondFactorToVet->canBeVettedNow()) {
568
            throw new DomainException('Cannot vet second factor, the registration window is closed.');
569
        }
570
571
        $secondFactorToVet->vet($documentNumber, $provePossessionSkipped);
572
    }
573
574
    public function revokeSecondFactor(SecondFactorId $secondFactorId)
575
    {
576
        $this->assertNotForgotten();
577
578
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
579
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
580
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
581
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
582
        /** @var VettedSecondFactor|null $vettedSecondFactor */
583
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
584
585
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
586
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
587
        }
588
589
        if ($unverifiedSecondFactor) {
590
            $unverifiedSecondFactor->revoke();
591
592
            return;
593
        }
594
595
        if ($verifiedSecondFactor) {
596
            $verifiedSecondFactor->revoke();
597
598
            return;
599
        }
600
601
        $vettedSecondFactor->revoke();
602
603
        if ($this->vettedSecondFactors->isEmpty()) {
604
            $this->allVettedSecondFactorsRemoved();
605
        }
606
    }
607
608
    public function complyWithSecondFactorRevocation(SecondFactorId $secondFactorId, IdentityId $authorityId)
609
    {
610
        $this->assertNotForgotten();
611
612
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
613
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
614
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
615
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
616
        /** @var VettedSecondFactor|null $vettedSecondFactor */
617
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
618
619
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
620
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
621
        }
622
623
        if ($unverifiedSecondFactor) {
624
            $unverifiedSecondFactor->complyWithRevocation($authorityId);
625
626
            return;
627
        }
628
629
        if ($verifiedSecondFactor) {
630
            $verifiedSecondFactor->complyWithRevocation($authorityId);
631
632
            return;
633
        }
634
635
        $vettedSecondFactor->complyWithRevocation($authorityId);
636
637
        if ($this->vettedSecondFactors->isEmpty()) {
638
            $this->allVettedSecondFactorsRemoved();
639
        }
640
    }
641
642
    /**
643
     * @param RegistrationAuthorityRole $role
644
     * @param Institution $institution
645
     * @param Location $location
646
     * @param ContactInformation $contactInformation
647
     * @param InstitutionConfiguration $institutionConfiguration
648
     * @return void
649
     */
650
    public function accreditWith(
651
        RegistrationAuthorityRole $role,
652
        Institution $institution,
653
        Location $location,
654
        ContactInformation $contactInformation,
655
        InstitutionConfiguration $institutionConfiguration
656
    ) {
657
        $this->assertNotForgotten();
658
659
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
660
            throw new DomainException('An Identity may only be accredited by configured institutions.');
661
        }
662
663
        if (!$this->vettedSecondFactors->count()) {
664
            throw new DomainException(
665
                'An Identity must have at least one vetted second factor before it can be accredited'
666
            );
667
        }
668
669
        if ($this->registrationAuthorities->exists($institution)) {
670
            throw new DomainException('Cannot accredit Identity as it has already been accredited for institution');
671
        }
672
673
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
674
            $this->apply(new IdentityAccreditedAsRaForInstitutionEvent(
675
                $this->id,
676
                $this->nameId,
677
                $this->institution,
678
                $role,
679
                $location,
680
                $contactInformation,
681
                $institution
682
            ));
683
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
684
            $this->apply(new IdentityAccreditedAsRaaForInstitutionEvent(
685
                $this->id,
686
                $this->nameId,
687
                $this->institution,
688
                $role,
689
                $location,
690
                $contactInformation,
691
                $institution
692
            ));
693
        } else {
694
            throw new DomainException('An Identity can only be accredited with either the RA or RAA role');
695
        }
696
    }
697
698
    public function amendRegistrationAuthorityInformation(Institution $institution, Location $location, ContactInformation $contactInformation)
699
    {
700
        $this->assertNotForgotten();
701
702
        if (!$this->registrationAuthorities->exists($institution)) {
703
            throw new DomainException(
704
                'Cannot amend registration authority information: identity is not a registration authority for institution'
705
            );
706
        }
707
708
        $this->apply(
709
            new RegistrationAuthorityInformationAmendedForInstitutionEvent(
710
                $this->id,
711
                $this->institution,
712
                $this->nameId,
713
                $location,
714
                $contactInformation,
715
                $institution
716
            )
717
        );
718
    }
719
720
    /**
721
     * This method will appoint an institution to become ra or raa for another institution
722
     *
723
     * @param Institution $institution
724
     * @param RegistrationAuthorityRole $role
725
     * @param InstitutionConfiguration $institutionConfiguration
726
     */
727
    public function appointAs(
728
        Institution $institution,
729
        RegistrationAuthorityRole $role,
730
        InstitutionConfiguration $institutionConfiguration
731
    ) {
732
        $this->assertNotForgotten();
733
734
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
735
            throw new DomainException(
736
                'Cannot appoint as different RegistrationAuthorityRole: identity is not a registration authority for institution'
737
            );
738
        }
739
740
        $registrationAuthority = $this->registrationAuthorities->get($institution);
741
742
        if ($registrationAuthority->isAppointedAs($role)) {
743
            return;
744
        }
745
746
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
747
            $this->apply(new AppointedAsRaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
748
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
749
            $this->apply(new AppointedAsRaaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
750
        } else {
751
            throw new DomainException('An Identity can only be appointed as either RA or RAA');
752
        }
753
    }
754
755
    public function retractRegistrationAuthority(Institution $institution)
756
    {
757
        $this->assertNotForgotten();
758
759
        if (!$this->registrationAuthorities->exists($institution)) {
760
            throw new DomainException(
761
                'Cannot Retract Registration Authority as the Identity is not a registration authority'
762
            );
763
        }
764
765
        $this->apply(new RegistrationAuthorityRetractedForInstitutionEvent(
766
            $this->id,
767
            $this->institution,
768
            $this->nameId,
769
            $this->commonName,
770
            $this->email,
771
            $institution
772
        ));
773
    }
774
775
    public function expressPreferredLocale(Locale $preferredLocale)
776
    {
777
        $this->assertNotForgotten();
778
779
        if ($this->preferredLocale === $preferredLocale) {
780
            return;
781
        }
782
783
        $this->apply(new LocalePreferenceExpressedEvent($this->id, $this->institution, $preferredLocale));
784
    }
785
786
    public function forget()
787
    {
788
        $this->assertNotForgotten();
789
790
        if ($this->registrationAuthorities->count()) {
791
            throw new DomainException('Cannot forget an identity that is currently accredited as an RA(A)');
792
        }
793
794
        $this->apply(new IdentityForgottenEvent($this->id, $this->institution));
795
    }
796
797
    public function allVettedSecondFactorsRemoved()
798
    {
799
        $this->apply(
800
            new VettedSecondFactorsAllRevokedEvent(
801
                $this->id,
802
                $this->institution
803
            )
804
        );
805
    }
806
807
    protected function applyIdentityCreatedEvent(IdentityCreatedEvent $event)
808
    {
809
        $this->id = $event->identityId;
810
        $this->institution = $event->identityInstitution;
811
        $this->nameId = $event->nameId;
812
        $this->commonName = $event->commonName;
813
        $this->email = $event->email;
814
        $this->preferredLocale = $event->preferredLocale;
815
        $this->forgotten = false;
816
817
        $this->unverifiedSecondFactors = new SecondFactorCollection();
818
        $this->verifiedSecondFactors = new SecondFactorCollection();
819
        $this->vettedSecondFactors = new SecondFactorCollection();
820
        $this->registrationAuthorities = new RegistrationAuthorityCollection();
821
    }
822
823
    public function applyIdentityRenamedEvent(IdentityRenamedEvent $event)
824
    {
825
        $this->commonName = $event->commonName;
826
    }
827
828
    public function applyIdentityEmailChangedEvent(IdentityEmailChangedEvent $event)
829
    {
830
        $this->email = $event->email;
831
    }
832
833
    protected function applyYubikeySecondFactorBootstrappedEvent(YubikeySecondFactorBootstrappedEvent $event)
834
    {
835
        $secondFactor = VettedSecondFactor::create(
836
            $event->secondFactorId,
837
            $this,
838
            new SecondFactorType('yubikey'),
839
            $event->yubikeyPublicId
840
        );
841
842
        $this->vettedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
843
    }
844
845
    protected function applyYubikeyPossessionProvenEvent(YubikeyPossessionProvenEvent $event)
846
    {
847
        $secondFactor = UnverifiedSecondFactor::create(
848
            $event->secondFactorId,
849
            $this,
850
            new SecondFactorType('yubikey'),
851
            $event->yubikeyPublicId,
852
            $event->emailVerificationWindow,
853
            $event->emailVerificationNonce
854
        );
855
856
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
857
    }
858
859
    protected function applyYubikeyPossessionProvenAndVerifiedEvent(YubikeyPossessionProvenAndVerifiedEvent $event)
860
    {
861
        $secondFactor = VerifiedSecondFactor::create(
862
            $event->secondFactorId,
863
            $this,
864
            new SecondFactorType('yubikey'),
865
            $event->yubikeyPublicId,
866
            $event->registrationRequestedAt,
867
            $event->registrationCode
868
        );
869
870
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
871
    }
872
873
    protected function applyPhonePossessionProvenEvent(PhonePossessionProvenEvent $event)
874
    {
875
        $secondFactor = UnverifiedSecondFactor::create(
876
            $event->secondFactorId,
877
            $this,
878
            new SecondFactorType('sms'),
879
            $event->phoneNumber,
880
            $event->emailVerificationWindow,
881
            $event->emailVerificationNonce
882
        );
883
884
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
885
    }
886
887
    protected function applyPhonePossessionProvenAndVerifiedEvent(PhonePossessionProvenAndVerifiedEvent $event)
888
    {
889
        $secondFactor = VerifiedSecondFactor::create(
890
            $event->secondFactorId,
891
            $this,
892
            new SecondFactorType('sms'),
893
            $event->phoneNumber,
894
            $event->registrationRequestedAt,
895
            $event->registrationCode
896
        );
897
898
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
899
    }
900
901
    protected function applyGssfPossessionProvenEvent(GssfPossessionProvenEvent $event)
902
    {
903
        $secondFactor = UnverifiedSecondFactor::create(
904
            $event->secondFactorId,
905
            $this,
906
            new SecondFactorType((string)$event->stepupProvider),
907
            $event->gssfId,
908
            $event->emailVerificationWindow,
909
            $event->emailVerificationNonce
910
        );
911
912
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
913
    }
914
915
    protected function applyGssfPossessionProvenAndVerifiedEvent(GssfPossessionProvenAndVerifiedEvent $event)
916
    {
917
        $secondFactor = VerifiedSecondFactor::create(
918
            $event->secondFactorId,
919
            $this,
920
            new SecondFactorType((string)$event->stepupProvider),
921
            $event->gssfId,
922
            $event->registrationRequestedAt,
923
            $event->registrationCode
924
        );
925
926
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
927
    }
928
929
    protected function applyU2fDevicePossessionProvenEvent(U2fDevicePossessionProvenEvent $event)
930
    {
931
        $secondFactor = UnverifiedSecondFactor::create(
932
            $event->secondFactorId,
933
            $this,
934
            new SecondFactorType('u2f'),
935
            $event->keyHandle,
936
            $event->emailVerificationWindow,
937
            $event->emailVerificationNonce
938
        );
939
940
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
941
    }
942
943
    protected function applyU2fDevicePossessionProvenAndVerifiedEvent(U2fDevicePossessionProvenAndVerifiedEvent $event)
944
    {
945
        $secondFactor = VerifiedSecondFactor::create(
946
            $event->secondFactorId,
947
            $this,
948
            new SecondFactorType('u2f'),
949
            $event->keyHandle,
950
            $event->registrationRequestedAt,
951
            $event->registrationCode
952
        );
953
954
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
955
    }
956
957
    protected function applyEmailVerifiedEvent(EmailVerifiedEvent $event)
958
    {
959
        $secondFactorId = (string)$event->secondFactorId;
960
961
        /** @var UnverifiedSecondFactor $unverified */
962
        $unverified = $this->unverifiedSecondFactors->get($secondFactorId);
963
        $verified = $unverified->asVerified($event->registrationRequestedAt, $event->registrationCode);
964
965
        $this->unverifiedSecondFactors->remove($secondFactorId);
966
        $this->verifiedSecondFactors->set($secondFactorId, $verified);
967
    }
968
969
    protected function applySecondFactorVettedEvent(SecondFactorVettedEvent $event)
970
    {
971
        $secondFactorId = (string)$event->secondFactorId;
972
973
        /** @var VerifiedSecondFactor $verified */
974
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
975
        $vetted = $verified->asVetted();
976
977
        $this->verifiedSecondFactors->remove($secondFactorId);
978
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
979
    }
980
981
    protected function applySecondFactorVettedWithoutTokenProofOfPossession(SecondFactorVettedWithoutTokenProofOfPossession $event)
982
    {
983
        $secondFactorId = (string)$event->secondFactorId;
984
985
        /** @var VerifiedSecondFactor $verified */
986
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
987
        $vetted = $verified->asVetted();
988
989
        $this->verifiedSecondFactors->remove($secondFactorId);
990
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
991
    }
992
993
    protected function applyUnverifiedSecondFactorRevokedEvent(UnverifiedSecondFactorRevokedEvent $event)
994
    {
995
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
996
    }
997
998
    protected function applyCompliedWithUnverifiedSecondFactorRevocationEvent(
999
        CompliedWithUnverifiedSecondFactorRevocationEvent $event
1000
    ) {
1001
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
1002
    }
1003
1004
    protected function applyVerifiedSecondFactorRevokedEvent(VerifiedSecondFactorRevokedEvent $event)
1005
    {
1006
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1007
    }
1008
1009
    protected function applyCompliedWithVerifiedSecondFactorRevocationEvent(
1010
        CompliedWithVerifiedSecondFactorRevocationEvent $event
1011
    ) {
1012
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1013
    }
1014
1015
    protected function applyVettedSecondFactorRevokedEvent(VettedSecondFactorRevokedEvent $event)
1016
    {
1017
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1018
    }
1019
1020
    protected function applyCompliedWithVettedSecondFactorRevocationEvent(
1021
        CompliedWithVettedSecondFactorRevocationEvent $event
1022
    ) {
1023
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1024
    }
1025
1026
    protected function applyIdentityAccreditedAsRaForInstitutionEvent(IdentityAccreditedAsRaForInstitutionEvent $event)
1027
    {
1028
        $this->registrationAuthorities->set($event->raInstitution, RegistrationAuthority::accreditWith(
1029
            $event->registrationAuthorityRole,
1030
            $event->location,
1031
            $event->contactInformation,
1032
            $event->raInstitution
1033
        ));
1034
    }
1035
1036
    protected function applyIdentityAccreditedAsRaaForInstitutionEvent(IdentityAccreditedAsRaaForInstitutionEvent $event)
1037
    {
1038
        $this->registrationAuthorities->set($event->raInstitution, RegistrationAuthority::accreditWith(
1039
            $event->registrationAuthorityRole,
1040
            $event->location,
1041
            $event->contactInformation,
1042
            $event->raInstitution
1043
        ));
1044
    }
1045
1046
    protected function applyRegistrationAuthorityInformationAmendedForInstitutionEvent(
1047
        RegistrationAuthorityInformationAmendedForInstitutionEvent $event
1048
    ) {
1049
        $this->registrationAuthorities->get($event->raInstitution)->amendInformation($event->location, $event->contactInformation);
1050
    }
1051
1052
    protected function applyAppointedAsRaaForInstitutionEvent(AppointedAsRaaForInstitutionEvent $event)
1053
    {
1054
        $this->registrationAuthorities->get($event->raInstitution)->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1055
    }
1056
1057
    protected function applyRegistrationAuthorityRetractedForInstitutionEvent(RegistrationAuthorityRetractedForInstitutionEvent $event)
1058
    {
1059
        $this->registrationAuthorities->remove($event->raInstitution);
1060
    }
1061
1062
    protected function applyLocalePreferenceExpressedEvent(LocalePreferenceExpressedEvent $event)
1063
    {
1064
        $this->preferredLocale = $event->preferredLocale;
1065
    }
1066
1067
    protected function applyIdentityForgottenEvent(IdentityForgottenEvent $event)
0 ignored issues
show
Unused Code introduced by
The parameter $event 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...
1068
    {
1069
        $this->commonName = CommonName::unknown();
1070
        $this->email = Email::unknown();
1071
        $this->forgotten = true;
1072
    }
1073
1074
    /**
1075
     * This method is kept to be backwards compatible for changes before FGA
1076
     *
1077
     * @param AppointedAsRaEvent $event
1078
     */
1079
    protected function applyAppointedAsRaEvent(AppointedAsRaEvent $event)
1080
    {
1081
        $this->registrationAuthorities->get($event->identityInstitution)
1082
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1083
    }
1084
1085
    /**
1086
     * This method is kept to be backwards compatible for changes before FGA
1087
     *
1088
     * @param AppointedAsRaaEvent $event
1089
     */
1090
    protected function applyAppointedAsRaaEvent(AppointedAsRaaEvent $event)
1091
    {
1092
        $this->registrationAuthorities->get($event->identityInstitution)
1093
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1094
    }
1095
1096
    /**
1097
     * This method is kept to be backwards compatible for changes before FGA
1098
     *
1099
     * @param AppointedAsRaaEvent $event
0 ignored issues
show
Documentation introduced by
Should the type for parameter $event not be IdentityAccreditedAsRaEvent?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1100
     */
1101
    protected function applyIdentityAccreditedAsRaEvent(IdentityAccreditedAsRaEvent $event)
1102
    {
1103
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1104
            $event->registrationAuthorityRole,
1105
            $event->location,
1106
            $event->contactInformation,
1107
            $event->identityInstitution
1108
        ));
1109
    }
1110
1111
    /**
1112
     * This method is kept to be backwards compatible for changes before FGA
1113
     *
1114
     * @param IdentityAccreditedAsRaaEvent $event
1115
     */
1116
    protected function applyIdentityAccreditedAsRaaEvent(IdentityAccreditedAsRaaEvent $event)
1117
    {
1118
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1119
            $event->registrationAuthorityRole,
1120
            $event->location,
1121
            $event->contactInformation,
1122
            $event->identityInstitution
1123
        ));
1124
    }
1125
1126
    /**
1127
     * This method is kept to be backwards compatible for changes before FGA
1128
     *
1129
     * @param AppointedAsRaForInstitutionEvent $event
1130
     */
1131
    protected function applyAppointedAsRaForInstitutionEvent(AppointedAsRaForInstitutionEvent $event)
1132
    {
1133
        $this->registrationAuthorities->get($event->identityInstitution)
1134
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1135
    }
1136
1137
    /**
1138
     * This method is kept to be backwards compatible for changes before FGA
1139
     *
1140
     * @param RegistrationAuthorityInformationAmendedEvent $event
1141
     */
1142
    protected function applyRegistrationAuthorityInformationAmendedEvent(
1143
        RegistrationAuthorityInformationAmendedEvent $event
1144
    ) {
1145
        $this->registrationAuthorities->get($event->identityInstitution)->amendInformation($event->location, $event->contactInformation);
1146
    }
1147
1148
    /**
1149
     * This method is kept to be backwards compatible for changes before FGA
1150
     *
1151
     * @param RegistrationAuthorityRetractedEvent $event
1152
     */
1153
    protected function applyRegistrationAuthorityRetractedEvent(RegistrationAuthorityRetractedEvent $event)
1154
    {
1155
        $this->registrationAuthorities->remove($event->identityInstitution);
1156
    }
1157
1158
1159
    public function getAggregateRootId(): string
1160
    {
1161
        return $this->id->getIdentityId();
1162
    }
1163
1164
    protected function getChildEntities(): array
1165
    {
1166
        return array_merge(
1167
            $this->unverifiedSecondFactors->getValues(),
1168
            $this->verifiedSecondFactors->getValues(),
1169
            $this->vettedSecondFactors->getValues(),
1170
            $this->registrationAuthorities->getValues()
1171
        );
1172
    }
1173
1174
    /**
1175
     * @throws DomainException
1176
     */
1177
    private function assertNotForgotten()
1178
    {
1179
        if ($this->forgotten) {
1180
            throw new DomainException('Operation on this Identity is not allowed: it has been forgotten');
1181
        }
1182
    }
1183
1184
    /**
1185
     * @throws DomainException
1186
     */
1187
    private function assertUserMayAddSecondFactor()
1188
    {
1189
        if (count($this->unverifiedSecondFactors) +
1190
            count($this->verifiedSecondFactors) +
1191
            count($this->vettedSecondFactors) >= $this->maxNumberOfTokens
1192
        ) {
1193
            throw new DomainException(
1194
                sprintf('User may not have more than %d token(s)', $this->maxNumberOfTokens)
1195
            );
1196
        }
1197
    }
1198
1199
    public function getId()
1200
    {
1201
        return $this->id;
1202
    }
1203
1204
    /**
1205
     * @return NameId
1206
     */
1207
    public function getNameId()
1208
    {
1209
        return $this->nameId;
1210
    }
1211
1212
    /**
1213
     * @return Institution
1214
     */
1215
    public function getInstitution()
1216
    {
1217
        return $this->institution;
1218
    }
1219
1220
    public function getCommonName()
1221
    {
1222
        return $this->commonName;
1223
    }
1224
1225
    public function getEmail()
1226
    {
1227
        return $this->email;
1228
    }
1229
1230
    public function getPreferredLocale()
1231
    {
1232
        return $this->preferredLocale;
1233
    }
1234
1235
    /**
1236
     * @param SecondFactorId $secondFactorId
1237
     * @return VerifiedSecondFactor|null
1238
     */
1239
    public function getVerifiedSecondFactor(SecondFactorId $secondFactorId)
1240
    {
1241
        return $this->verifiedSecondFactors->get((string)$secondFactorId);
1242
    }
1243
1244
    private function getVettedSecondFactor(SecondFactorId $secondFactorId): ?VettedSecondFactor
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1245
    {
1246
        return $this->vettedSecondFactors->get((string)$secondFactorId);
1247
    }
1248
}
1249