Completed
Pull Request — develop (#320)
by Michiel
04:42
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(
524
                    'Registrant second factor of type %s with ID %s does not exist',
525
                    get_class($secondFactorIdentifier),
526
                    $secondFactorIdentifier->getValue()
527
                )
528
            );
529
        }
530
531
        if (!$registeringSecondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
532
            throw new DomainException('The verified second factors registration code or identifier do not match.');
533
        }
534
        $authoringSecondFactor = $this->getVettedSecondFactor($authoringSecondFactorId);
535
        if ($authoringSecondFactor === null) {
536
            throw new DomainException(
537
                sprintf('Authorizing second factor with ID %s does not exist', $authoringSecondFactorId)
538
            );
539
        }
540
        if ($authoringSecondFactor->hasEqualOrHigherLoaComparedTo($registeringSecondFactor, $secondFactorTypeService)) {
541
            throw new DomainException("The second factor to be vetted has a higher LoA then the Token used for proving posession");
542
        }
543
        $registeringSecondFactor->vet(DocumentNumber::unknown(), true);
544
    }
545
546
    public function complyWithVettingOfSecondFactor(
547
        SecondFactorId $secondFactorId,
548
        SecondFactorType $secondFactorType,
549
        SecondFactorIdentifier $secondFactorIdentifier,
550
        $registrationCode,
551
        DocumentNumber $documentNumber,
552
        $provePossessionSkipped
553
    ) {
554
        $this->assertNotForgotten();
555
556
        $secondFactorToVet = null;
557
        foreach ($this->verifiedSecondFactors as $secondFactor) {
558
            /** @var VerifiedSecondFactor $secondFactor */
559
            if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
560
                $secondFactorToVet = $secondFactor;
561
            }
562
        }
563
564
        if (!$secondFactorToVet) {
565
            throw new DomainException(
566
                'Cannot vet second factor, no verified second factor can be vetted using the given registration code ' .
567
                'and second factor identifier'
568
            );
569
        }
570
571
        if (!$secondFactorToVet->canBeVettedNow()) {
572
            throw new DomainException('Cannot vet second factor, the registration window is closed.');
573
        }
574
575
        $secondFactorToVet->vet($documentNumber, $provePossessionSkipped);
576
    }
577
578
    public function revokeSecondFactor(SecondFactorId $secondFactorId)
579
    {
580
        $this->assertNotForgotten();
581
582
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
583
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
584
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
585
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
586
        /** @var VettedSecondFactor|null $vettedSecondFactor */
587
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
588
589
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
590
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
591
        }
592
593
        if ($unverifiedSecondFactor) {
594
            $unverifiedSecondFactor->revoke();
595
596
            return;
597
        }
598
599
        if ($verifiedSecondFactor) {
600
            $verifiedSecondFactor->revoke();
601
602
            return;
603
        }
604
605
        $vettedSecondFactor->revoke();
606
607
        if ($this->vettedSecondFactors->isEmpty()) {
608
            $this->allVettedSecondFactorsRemoved();
609
        }
610
    }
611
612
    public function complyWithSecondFactorRevocation(SecondFactorId $secondFactorId, IdentityId $authorityId)
613
    {
614
        $this->assertNotForgotten();
615
616
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
617
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
618
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
619
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
620
        /** @var VettedSecondFactor|null $vettedSecondFactor */
621
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
622
623
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
624
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
625
        }
626
627
        if ($unverifiedSecondFactor) {
628
            $unverifiedSecondFactor->complyWithRevocation($authorityId);
629
630
            return;
631
        }
632
633
        if ($verifiedSecondFactor) {
634
            $verifiedSecondFactor->complyWithRevocation($authorityId);
635
636
            return;
637
        }
638
639
        $vettedSecondFactor->complyWithRevocation($authorityId);
640
641
        if ($this->vettedSecondFactors->isEmpty()) {
642
            $this->allVettedSecondFactorsRemoved();
643
        }
644
    }
645
646
    /**
647
     * @param RegistrationAuthorityRole $role
648
     * @param Institution $institution
649
     * @param Location $location
650
     * @param ContactInformation $contactInformation
651
     * @param InstitutionConfiguration $institutionConfiguration
652
     * @return void
653
     */
654
    public function accreditWith(
655
        RegistrationAuthorityRole $role,
656
        Institution $institution,
657
        Location $location,
658
        ContactInformation $contactInformation,
659
        InstitutionConfiguration $institutionConfiguration
660
    ) {
661
        $this->assertNotForgotten();
662
663
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
664
            throw new DomainException('An Identity may only be accredited by configured institutions.');
665
        }
666
667
        if (!$this->vettedSecondFactors->count()) {
668
            throw new DomainException(
669
                'An Identity must have at least one vetted second factor before it can be accredited'
670
            );
671
        }
672
673
        if ($this->registrationAuthorities->exists($institution)) {
674
            throw new DomainException('Cannot accredit Identity as it has already been accredited for institution');
675
        }
676
677
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
678
            $this->apply(new IdentityAccreditedAsRaForInstitutionEvent(
679
                $this->id,
680
                $this->nameId,
681
                $this->institution,
682
                $role,
683
                $location,
684
                $contactInformation,
685
                $institution
686
            ));
687
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
688
            $this->apply(new IdentityAccreditedAsRaaForInstitutionEvent(
689
                $this->id,
690
                $this->nameId,
691
                $this->institution,
692
                $role,
693
                $location,
694
                $contactInformation,
695
                $institution
696
            ));
697
        } else {
698
            throw new DomainException('An Identity can only be accredited with either the RA or RAA role');
699
        }
700
    }
701
702
    public function amendRegistrationAuthorityInformation(Institution $institution, Location $location, ContactInformation $contactInformation)
703
    {
704
        $this->assertNotForgotten();
705
706
        if (!$this->registrationAuthorities->exists($institution)) {
707
            throw new DomainException(
708
                'Cannot amend registration authority information: identity is not a registration authority for institution'
709
            );
710
        }
711
712
        $this->apply(
713
            new RegistrationAuthorityInformationAmendedForInstitutionEvent(
714
                $this->id,
715
                $this->institution,
716
                $this->nameId,
717
                $location,
718
                $contactInformation,
719
                $institution
720
            )
721
        );
722
    }
723
724
    /**
725
     * This method will appoint an institution to become ra or raa for another institution
726
     *
727
     * @param Institution $institution
728
     * @param RegistrationAuthorityRole $role
729
     * @param InstitutionConfiguration $institutionConfiguration
730
     */
731
    public function appointAs(
732
        Institution $institution,
733
        RegistrationAuthorityRole $role,
734
        InstitutionConfiguration $institutionConfiguration
735
    ) {
736
        $this->assertNotForgotten();
737
738
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
739
            throw new DomainException(
740
                'Cannot appoint as different RegistrationAuthorityRole: identity is not a registration authority for institution'
741
            );
742
        }
743
744
        $registrationAuthority = $this->registrationAuthorities->get($institution);
745
746
        if ($registrationAuthority->isAppointedAs($role)) {
747
            return;
748
        }
749
750
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
751
            $this->apply(new AppointedAsRaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
752
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
753
            $this->apply(new AppointedAsRaaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
754
        } else {
755
            throw new DomainException('An Identity can only be appointed as either RA or RAA');
756
        }
757
    }
758
759
    public function retractRegistrationAuthority(Institution $institution)
760
    {
761
        $this->assertNotForgotten();
762
763
        if (!$this->registrationAuthorities->exists($institution)) {
764
            throw new DomainException(
765
                'Cannot Retract Registration Authority as the Identity is not a registration authority'
766
            );
767
        }
768
769
        $this->apply(new RegistrationAuthorityRetractedForInstitutionEvent(
770
            $this->id,
771
            $this->institution,
772
            $this->nameId,
773
            $this->commonName,
774
            $this->email,
775
            $institution
776
        ));
777
    }
778
779
    public function expressPreferredLocale(Locale $preferredLocale)
780
    {
781
        $this->assertNotForgotten();
782
783
        if ($this->preferredLocale === $preferredLocale) {
784
            return;
785
        }
786
787
        $this->apply(new LocalePreferenceExpressedEvent($this->id, $this->institution, $preferredLocale));
788
    }
789
790
    public function forget()
791
    {
792
        $this->assertNotForgotten();
793
794
        if ($this->registrationAuthorities->count()) {
795
            throw new DomainException('Cannot forget an identity that is currently accredited as an RA(A)');
796
        }
797
798
        $this->apply(new IdentityForgottenEvent($this->id, $this->institution));
799
    }
800
801
    public function allVettedSecondFactorsRemoved()
802
    {
803
        $this->apply(
804
            new VettedSecondFactorsAllRevokedEvent(
805
                $this->id,
806
                $this->institution
807
            )
808
        );
809
    }
810
811
    protected function applyIdentityCreatedEvent(IdentityCreatedEvent $event)
812
    {
813
        $this->id = $event->identityId;
814
        $this->institution = $event->identityInstitution;
815
        $this->nameId = $event->nameId;
816
        $this->commonName = $event->commonName;
817
        $this->email = $event->email;
818
        $this->preferredLocale = $event->preferredLocale;
819
        $this->forgotten = false;
820
821
        $this->unverifiedSecondFactors = new SecondFactorCollection();
822
        $this->verifiedSecondFactors = new SecondFactorCollection();
823
        $this->vettedSecondFactors = new SecondFactorCollection();
824
        $this->registrationAuthorities = new RegistrationAuthorityCollection();
825
    }
826
827
    public function applyIdentityRenamedEvent(IdentityRenamedEvent $event)
828
    {
829
        $this->commonName = $event->commonName;
830
    }
831
832
    public function applyIdentityEmailChangedEvent(IdentityEmailChangedEvent $event)
833
    {
834
        $this->email = $event->email;
835
    }
836
837
    protected function applyYubikeySecondFactorBootstrappedEvent(YubikeySecondFactorBootstrappedEvent $event)
838
    {
839
        $secondFactor = VettedSecondFactor::create(
840
            $event->secondFactorId,
841
            $this,
842
            new SecondFactorType('yubikey'),
843
            $event->yubikeyPublicId
844
        );
845
846
        $this->vettedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
847
    }
848
849
    protected function applyYubikeyPossessionProvenEvent(YubikeyPossessionProvenEvent $event)
850
    {
851
        $secondFactor = UnverifiedSecondFactor::create(
852
            $event->secondFactorId,
853
            $this,
854
            new SecondFactorType('yubikey'),
855
            $event->yubikeyPublicId,
856
            $event->emailVerificationWindow,
857
            $event->emailVerificationNonce
858
        );
859
860
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
861
    }
862
863
    protected function applyYubikeyPossessionProvenAndVerifiedEvent(YubikeyPossessionProvenAndVerifiedEvent $event)
864
    {
865
        $secondFactor = VerifiedSecondFactor::create(
866
            $event->secondFactorId,
867
            $this,
868
            new SecondFactorType('yubikey'),
869
            $event->yubikeyPublicId,
870
            $event->registrationRequestedAt,
871
            $event->registrationCode
872
        );
873
874
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
875
    }
876
877
    protected function applyPhonePossessionProvenEvent(PhonePossessionProvenEvent $event)
878
    {
879
        $secondFactor = UnverifiedSecondFactor::create(
880
            $event->secondFactorId,
881
            $this,
882
            new SecondFactorType('sms'),
883
            $event->phoneNumber,
884
            $event->emailVerificationWindow,
885
            $event->emailVerificationNonce
886
        );
887
888
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
889
    }
890
891
    protected function applyPhonePossessionProvenAndVerifiedEvent(PhonePossessionProvenAndVerifiedEvent $event)
892
    {
893
        $secondFactor = VerifiedSecondFactor::create(
894
            $event->secondFactorId,
895
            $this,
896
            new SecondFactorType('sms'),
897
            $event->phoneNumber,
898
            $event->registrationRequestedAt,
899
            $event->registrationCode
900
        );
901
902
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
903
    }
904
905
    protected function applyGssfPossessionProvenEvent(GssfPossessionProvenEvent $event)
906
    {
907
        $secondFactor = UnverifiedSecondFactor::create(
908
            $event->secondFactorId,
909
            $this,
910
            new SecondFactorType((string)$event->stepupProvider),
911
            $event->gssfId,
912
            $event->emailVerificationWindow,
913
            $event->emailVerificationNonce
914
        );
915
916
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
917
    }
918
919
    protected function applyGssfPossessionProvenAndVerifiedEvent(GssfPossessionProvenAndVerifiedEvent $event)
920
    {
921
        $secondFactor = VerifiedSecondFactor::create(
922
            $event->secondFactorId,
923
            $this,
924
            new SecondFactorType((string)$event->stepupProvider),
925
            $event->gssfId,
926
            $event->registrationRequestedAt,
927
            $event->registrationCode
928
        );
929
930
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
931
    }
932
933
    protected function applyU2fDevicePossessionProvenEvent(U2fDevicePossessionProvenEvent $event)
934
    {
935
        $secondFactor = UnverifiedSecondFactor::create(
936
            $event->secondFactorId,
937
            $this,
938
            new SecondFactorType('u2f'),
939
            $event->keyHandle,
940
            $event->emailVerificationWindow,
941
            $event->emailVerificationNonce
942
        );
943
944
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
945
    }
946
947
    protected function applyU2fDevicePossessionProvenAndVerifiedEvent(U2fDevicePossessionProvenAndVerifiedEvent $event)
948
    {
949
        $secondFactor = VerifiedSecondFactor::create(
950
            $event->secondFactorId,
951
            $this,
952
            new SecondFactorType('u2f'),
953
            $event->keyHandle,
954
            $event->registrationRequestedAt,
955
            $event->registrationCode
956
        );
957
958
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
959
    }
960
961
    protected function applyEmailVerifiedEvent(EmailVerifiedEvent $event)
962
    {
963
        $secondFactorId = (string)$event->secondFactorId;
964
965
        /** @var UnverifiedSecondFactor $unverified */
966
        $unverified = $this->unverifiedSecondFactors->get($secondFactorId);
967
        $verified = $unverified->asVerified($event->registrationRequestedAt, $event->registrationCode);
968
969
        $this->unverifiedSecondFactors->remove($secondFactorId);
970
        $this->verifiedSecondFactors->set($secondFactorId, $verified);
971
    }
972
973
    protected function applySecondFactorVettedEvent(SecondFactorVettedEvent $event)
974
    {
975
        $secondFactorId = (string)$event->secondFactorId;
976
977
        /** @var VerifiedSecondFactor $verified */
978
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
979
        $vetted = $verified->asVetted();
980
981
        $this->verifiedSecondFactors->remove($secondFactorId);
982
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
983
    }
984
985
    protected function applySecondFactorVettedWithoutTokenProofOfPossession(SecondFactorVettedWithoutTokenProofOfPossession $event)
986
    {
987
        $secondFactorId = (string)$event->secondFactorId;
988
989
        /** @var VerifiedSecondFactor $verified */
990
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
991
        $vetted = $verified->asVetted();
992
993
        $this->verifiedSecondFactors->remove($secondFactorId);
994
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
995
    }
996
997
    protected function applyUnverifiedSecondFactorRevokedEvent(UnverifiedSecondFactorRevokedEvent $event)
998
    {
999
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
1000
    }
1001
1002
    protected function applyCompliedWithUnverifiedSecondFactorRevocationEvent(
1003
        CompliedWithUnverifiedSecondFactorRevocationEvent $event
1004
    ) {
1005
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
1006
    }
1007
1008
    protected function applyVerifiedSecondFactorRevokedEvent(VerifiedSecondFactorRevokedEvent $event)
1009
    {
1010
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1011
    }
1012
1013
    protected function applyCompliedWithVerifiedSecondFactorRevocationEvent(
1014
        CompliedWithVerifiedSecondFactorRevocationEvent $event
1015
    ) {
1016
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1017
    }
1018
1019
    protected function applyVettedSecondFactorRevokedEvent(VettedSecondFactorRevokedEvent $event)
1020
    {
1021
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1022
    }
1023
1024
    protected function applyCompliedWithVettedSecondFactorRevocationEvent(
1025
        CompliedWithVettedSecondFactorRevocationEvent $event
1026
    ) {
1027
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1028
    }
1029
1030
    protected function applyIdentityAccreditedAsRaForInstitutionEvent(IdentityAccreditedAsRaForInstitutionEvent $event)
1031
    {
1032
        $this->registrationAuthorities->set($event->raInstitution, RegistrationAuthority::accreditWith(
1033
            $event->registrationAuthorityRole,
1034
            $event->location,
1035
            $event->contactInformation,
1036
            $event->raInstitution
1037
        ));
1038
    }
1039
1040
    protected function applyIdentityAccreditedAsRaaForInstitutionEvent(IdentityAccreditedAsRaaForInstitutionEvent $event)
1041
    {
1042
        $this->registrationAuthorities->set($event->raInstitution, RegistrationAuthority::accreditWith(
1043
            $event->registrationAuthorityRole,
1044
            $event->location,
1045
            $event->contactInformation,
1046
            $event->raInstitution
1047
        ));
1048
    }
1049
1050
    protected function applyRegistrationAuthorityInformationAmendedForInstitutionEvent(
1051
        RegistrationAuthorityInformationAmendedForInstitutionEvent $event
1052
    ) {
1053
        $this->registrationAuthorities->get($event->raInstitution)->amendInformation($event->location, $event->contactInformation);
1054
    }
1055
1056
    protected function applyAppointedAsRaaForInstitutionEvent(AppointedAsRaaForInstitutionEvent $event)
1057
    {
1058
        $this->registrationAuthorities->get($event->raInstitution)->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1059
    }
1060
1061
    protected function applyRegistrationAuthorityRetractedForInstitutionEvent(RegistrationAuthorityRetractedForInstitutionEvent $event)
1062
    {
1063
        $this->registrationAuthorities->remove($event->raInstitution);
1064
    }
1065
1066
    protected function applyLocalePreferenceExpressedEvent(LocalePreferenceExpressedEvent $event)
1067
    {
1068
        $this->preferredLocale = $event->preferredLocale;
1069
    }
1070
1071
    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...
1072
    {
1073
        $this->commonName = CommonName::unknown();
1074
        $this->email = Email::unknown();
1075
        $this->forgotten = true;
1076
    }
1077
1078
    /**
1079
     * This method is kept to be backwards compatible for changes before FGA
1080
     *
1081
     * @param AppointedAsRaEvent $event
1082
     */
1083
    protected function applyAppointedAsRaEvent(AppointedAsRaEvent $event)
1084
    {
1085
        $this->registrationAuthorities->get($event->identityInstitution)
1086
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1087
    }
1088
1089
    /**
1090
     * This method is kept to be backwards compatible for changes before FGA
1091
     *
1092
     * @param AppointedAsRaaEvent $event
1093
     */
1094
    protected function applyAppointedAsRaaEvent(AppointedAsRaaEvent $event)
1095
    {
1096
        $this->registrationAuthorities->get($event->identityInstitution)
1097
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1098
    }
1099
1100
    /**
1101
     * This method is kept to be backwards compatible for changes before FGA
1102
     *
1103
     * @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...
1104
     */
1105
    protected function applyIdentityAccreditedAsRaEvent(IdentityAccreditedAsRaEvent $event)
1106
    {
1107
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1108
            $event->registrationAuthorityRole,
1109
            $event->location,
1110
            $event->contactInformation,
1111
            $event->identityInstitution
1112
        ));
1113
    }
1114
1115
    /**
1116
     * This method is kept to be backwards compatible for changes before FGA
1117
     *
1118
     * @param IdentityAccreditedAsRaaEvent $event
1119
     */
1120
    protected function applyIdentityAccreditedAsRaaEvent(IdentityAccreditedAsRaaEvent $event)
1121
    {
1122
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1123
            $event->registrationAuthorityRole,
1124
            $event->location,
1125
            $event->contactInformation,
1126
            $event->identityInstitution
1127
        ));
1128
    }
1129
1130
    /**
1131
     * This method is kept to be backwards compatible for changes before FGA
1132
     *
1133
     * @param AppointedAsRaForInstitutionEvent $event
1134
     */
1135
    protected function applyAppointedAsRaForInstitutionEvent(AppointedAsRaForInstitutionEvent $event)
1136
    {
1137
        $this->registrationAuthorities->get($event->identityInstitution)
1138
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1139
    }
1140
1141
    /**
1142
     * This method is kept to be backwards compatible for changes before FGA
1143
     *
1144
     * @param RegistrationAuthorityInformationAmendedEvent $event
1145
     */
1146
    protected function applyRegistrationAuthorityInformationAmendedEvent(
1147
        RegistrationAuthorityInformationAmendedEvent $event
1148
    ) {
1149
        $this->registrationAuthorities->get($event->identityInstitution)->amendInformation($event->location, $event->contactInformation);
1150
    }
1151
1152
    /**
1153
     * This method is kept to be backwards compatible for changes before FGA
1154
     *
1155
     * @param RegistrationAuthorityRetractedEvent $event
1156
     */
1157
    protected function applyRegistrationAuthorityRetractedEvent(RegistrationAuthorityRetractedEvent $event)
1158
    {
1159
        $this->registrationAuthorities->remove($event->identityInstitution);
1160
    }
1161
1162
1163
    public function getAggregateRootId(): string
1164
    {
1165
        return $this->id->getIdentityId();
1166
    }
1167
1168
    protected function getChildEntities(): array
1169
    {
1170
        return array_merge(
1171
            $this->unverifiedSecondFactors->getValues(),
1172
            $this->verifiedSecondFactors->getValues(),
1173
            $this->vettedSecondFactors->getValues(),
1174
            $this->registrationAuthorities->getValues()
1175
        );
1176
    }
1177
1178
    /**
1179
     * @throws DomainException
1180
     */
1181
    private function assertNotForgotten()
1182
    {
1183
        if ($this->forgotten) {
1184
            throw new DomainException('Operation on this Identity is not allowed: it has been forgotten');
1185
        }
1186
    }
1187
1188
    /**
1189
     * @throws DomainException
1190
     */
1191
    private function assertUserMayAddSecondFactor()
1192
    {
1193
        if (count($this->unverifiedSecondFactors) +
1194
            count($this->verifiedSecondFactors) +
1195
            count($this->vettedSecondFactors) >= $this->maxNumberOfTokens
1196
        ) {
1197
            throw new DomainException(
1198
                sprintf('User may not have more than %d token(s)', $this->maxNumberOfTokens)
1199
            );
1200
        }
1201
    }
1202
1203
    public function getId()
1204
    {
1205
        return $this->id;
1206
    }
1207
1208
    /**
1209
     * @return NameId
1210
     */
1211
    public function getNameId()
1212
    {
1213
        return $this->nameId;
1214
    }
1215
1216
    /**
1217
     * @return Institution
1218
     */
1219
    public function getInstitution()
1220
    {
1221
        return $this->institution;
1222
    }
1223
1224
    public function getCommonName()
1225
    {
1226
        return $this->commonName;
1227
    }
1228
1229
    public function getEmail()
1230
    {
1231
        return $this->email;
1232
    }
1233
1234
    public function getPreferredLocale()
1235
    {
1236
        return $this->preferredLocale;
1237
    }
1238
1239
    /**
1240
     * @param SecondFactorId $secondFactorId
1241
     * @return VerifiedSecondFactor|null
1242
     */
1243
    public function getVerifiedSecondFactor(SecondFactorId $secondFactorId)
1244
    {
1245
        return $this->verifiedSecondFactors->get((string)$secondFactorId);
1246
    }
1247
1248
    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...
1249
    {
1250
        return $this->vettedSecondFactors->get((string)$secondFactorId);
1251
    }
1252
}
1253