Completed
Push — feature/self-vet ( 7edffb...85e549 )
by Michiel
03:37
created

Identity::getVettedSecondFactor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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\OnPremiseVettingType;
82
use Surfnet\Stepup\Identity\Value\PhoneNumber;
83
use Surfnet\Stepup\Identity\Value\RegistrationAuthorityRole;
84
use Surfnet\Stepup\Identity\Value\SecondFactorId;
85
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifier;
86
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifierFactory;
87
use Surfnet\Stepup\Identity\Value\SelfVetVettingType;
88
use Surfnet\Stepup\Identity\Value\StepupProvider;
89
use Surfnet\Stepup\Identity\Value\U2fKeyHandle;
90
use Surfnet\Stepup\Identity\Value\VettingType;
91
use Surfnet\Stepup\Identity\Value\YubikeyPublicId;
92
use Surfnet\Stepup\Token\TokenGenerator;
93
use Surfnet\StepupBundle\Security\OtpGenerator;
94
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
95
use Surfnet\StepupBundle\Value\Loa;
96
use Surfnet\StepupBundle\Value\SecondFactorType;
97
use Surfnet\StepupMiddleware\ApiBundle\Identity\Service\SecondFactorService;
98
use function sprintf;
99
100
/**
101
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
102
 * @SuppressWarnings(PHPMD.TooManyMethods)
103
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
104
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
105
 * @SuppressWarnings(PHPMD.ExcessiveClassLength)
106
 */
107
class Identity extends EventSourcedAggregateRoot implements IdentityApi
108
{
109
    /**
110
     * @var IdentityId
111
     */
112
    private $id;
113
114
    /**
115
     * @var Institution
116
     */
117
    private $institution;
118
119
    /**
120
     * @var NameId
121
     */
122
    private $nameId;
123
124
    /**
125
     * @var \Surfnet\Stepup\Identity\Value\CommonName
126
     */
127
    private $commonName;
128
129
    /**
130
     * @var \Surfnet\Stepup\Identity\Value\Email
131
     */
132
    private $email;
133
134
    /**
135
     * @var SecondFactorCollection|UnverifiedSecondFactor[]
136
     */
137
    private $unverifiedSecondFactors;
138
139
    /**
140
     * @var SecondFactorCollection|VerifiedSecondFactor[]
141
     */
142
    private $verifiedSecondFactors;
143
144
    /**
145
     * @var SecondFactorCollection|VettedSecondFactor[]
146
     */
147
    private $vettedSecondFactors;
148
149
    /**
150
     * @var RegistrationAuthorityCollection
151
     */
152
    private $registrationAuthorities;
153
154
    /**
155
     * @var Locale
156
     */
157
    private $preferredLocale;
158
159
    /**
160
     * @var boolean
161
     */
162
    private $forgotten;
163
164
    /**
165
     * @var int
166
     */
167
    private $maxNumberOfTokens = 1;
168
169
    public static function create(
170
        IdentityId $id,
171
        Institution $institution,
172
        NameId $nameId,
173
        CommonName $commonName,
174
        Email $email,
175
        Locale $preferredLocale
176
    ) {
177
        $identity = new self();
178
        $identity->apply(new IdentityCreatedEvent($id, $institution, $nameId, $commonName, $email, $preferredLocale));
179
180
        return $identity;
181
    }
182
183
    final public function __construct()
184
    {
185
    }
186
187
    public function rename(CommonName $commonName)
188
    {
189
        $this->assertNotForgotten();
190
191
        if ($this->commonName->equals($commonName)) {
192
            return;
193
        }
194
195
        $this->commonName = $commonName;
196
        $this->apply(new IdentityRenamedEvent($this->id, $this->institution, $commonName));
197
    }
198
199
    public function changeEmail(Email $email)
200
    {
201
        $this->assertNotForgotten();
202
203
        if ($this->email->equals($email)) {
204
            return;
205
        }
206
207
        $this->email = $email;
208
        $this->apply(new IdentityEmailChangedEvent($this->id, $this->institution, $email));
209
    }
210
211
    /**
212
     * @param int $numberOfTokens
213
     */
214
    public function setMaxNumberOfTokens($numberOfTokens)
215
    {
216
        $this->maxNumberOfTokens = $numberOfTokens;
217
    }
218
219
    public function bootstrapYubikeySecondFactor(SecondFactorId $secondFactorId, YubikeyPublicId $yubikeyPublicId)
220
    {
221
        $this->assertNotForgotten();
222
        $this->assertUserMayAddSecondFactor();
223
224
        $this->apply(
225
            new YubikeySecondFactorBootstrappedEvent(
226
                $this->id,
227
                $this->nameId,
228
                $this->institution,
229
                $this->commonName,
230
                $this->email,
231
                $this->preferredLocale,
232
                $secondFactorId,
233
                $yubikeyPublicId
234
            )
235
        );
236
    }
237
238 View Code Duplication
    public function provePossessionOfYubikey(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
        SecondFactorId $secondFactorId,
240
        YubikeyPublicId $yubikeyPublicId,
241
        $emailVerificationRequired,
242
        EmailVerificationWindow $emailVerificationWindow
243
    ) {
244
        $this->assertNotForgotten();
245
        $this->assertUserMayAddSecondFactor();
246
247
        if ($emailVerificationRequired) {
248
            $emailVerificationNonce = TokenGenerator::generateNonce();
249
250
            $this->apply(
251
                new YubikeyPossessionProvenEvent(
252
                    $this->id,
253
                    $this->institution,
254
                    $secondFactorId,
255
                    $yubikeyPublicId,
256
                    $emailVerificationRequired,
257
                    $emailVerificationWindow,
258
                    $emailVerificationNonce,
259
                    $this->commonName,
260
                    $this->email,
261
                    $this->preferredLocale
262
                )
263
            );
264
        } else {
265
            $this->apply(
266
                new YubikeyPossessionProvenAndVerifiedEvent(
267
                    $this->id,
268
                    $this->institution,
269
                    $secondFactorId,
270
                    $yubikeyPublicId,
271
                    $this->commonName,
272
                    $this->email,
273
                    $this->preferredLocale,
274
                    DateTime::now(),
275
                    OtpGenerator::generate(8)
276
                )
277
            );
278
        }
279
    }
280
281 View Code Duplication
    public function provePossessionOfPhone(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
        SecondFactorId $secondFactorId,
283
        PhoneNumber $phoneNumber,
284
        $emailVerificationRequired,
285
        EmailVerificationWindow $emailVerificationWindow
286
    ) {
287
        $this->assertNotForgotten();
288
        $this->assertUserMayAddSecondFactor();
289
290
        if ($emailVerificationRequired) {
291
            $emailVerificationNonce = TokenGenerator::generateNonce();
292
293
            $this->apply(
294
                new PhonePossessionProvenEvent(
295
                    $this->id,
296
                    $this->institution,
297
                    $secondFactorId,
298
                    $phoneNumber,
299
                    $emailVerificationRequired,
300
                    $emailVerificationWindow,
301
                    $emailVerificationNonce,
302
                    $this->commonName,
303
                    $this->email,
304
                    $this->preferredLocale
305
                )
306
            );
307
        } else {
308
            $this->apply(
309
                new PhonePossessionProvenAndVerifiedEvent(
310
                    $this->id,
311
                    $this->institution,
312
                    $secondFactorId,
313
                    $phoneNumber,
314
                    $this->commonName,
315
                    $this->email,
316
                    $this->preferredLocale,
317
                    DateTime::now(),
318
                    OtpGenerator::generate(8)
319
                )
320
            );
321
        }
322
    }
323
324 View Code Duplication
    public function provePossessionOfGssf(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
325
        SecondFactorId $secondFactorId,
326
        StepupProvider $provider,
327
        GssfId $gssfId,
328
        $emailVerificationRequired,
329
        EmailVerificationWindow $emailVerificationWindow
330
    ) {
331
        $this->assertNotForgotten();
332
        $this->assertUserMayAddSecondFactor();
333
334
        if ($emailVerificationRequired) {
335
            $emailVerificationNonce = TokenGenerator::generateNonce();
336
337
            $this->apply(
338
                new GssfPossessionProvenEvent(
339
                    $this->id,
340
                    $this->institution,
341
                    $secondFactorId,
342
                    $provider,
343
                    $gssfId,
344
                    $emailVerificationRequired,
345
                    $emailVerificationWindow,
346
                    $emailVerificationNonce,
347
                    $this->commonName,
348
                    $this->email,
349
                    $this->preferredLocale
350
                )
351
            );
352
        } else {
353
            $this->apply(
354
                new GssfPossessionProvenAndVerifiedEvent(
355
                    $this->id,
356
                    $this->institution,
357
                    $secondFactorId,
358
                    $provider,
359
                    $gssfId,
360
                    $this->commonName,
361
                    $this->email,
362
                    $this->preferredLocale,
363
                    DateTime::now(),
364
                    OtpGenerator::generate(8)
365
                )
366
            );
367
        }
368
    }
369
370 View Code Duplication
    public function provePossessionOfU2fDevice(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
        SecondFactorId $secondFactorId,
372
        U2fKeyHandle $keyHandle,
373
        $emailVerificationRequired,
374
        EmailVerificationWindow $emailVerificationWindow
375
    ) {
376
        $this->assertNotForgotten();
377
        $this->assertUserMayAddSecondFactor();
378
379
        if ($emailVerificationRequired) {
380
            $emailVerificationNonce = TokenGenerator::generateNonce();
381
382
            $this->apply(
383
                new U2fDevicePossessionProvenEvent(
384
                    $this->id,
385
                    $this->institution,
386
                    $secondFactorId,
387
                    $keyHandle,
388
                    $emailVerificationRequired,
389
                    $emailVerificationWindow,
390
                    $emailVerificationNonce,
391
                    $this->commonName,
392
                    $this->email,
393
                    $this->preferredLocale
394
                )
395
            );
396
        } else {
397
            $this->apply(
398
                new U2fDevicePossessionProvenAndVerifiedEvent(
399
                    $this->id,
400
                    $this->institution,
401
                    $secondFactorId,
402
                    $keyHandle,
403
                    $this->commonName,
404
                    $this->email,
405
                    $this->preferredLocale,
406
                    DateTime::now(),
407
                    OtpGenerator::generate(8)
408
                )
409
            );
410
        }
411
    }
412
413
    public function verifyEmail($verificationNonce)
414
    {
415
        $this->assertNotForgotten();
416
417
        $secondFactorToVerify = null;
418
        foreach ($this->unverifiedSecondFactors as $secondFactor) {
419
            /** @var Entity\UnverifiedSecondFactor $secondFactor */
420
            if ($secondFactor->hasNonce($verificationNonce)) {
421
                $secondFactorToVerify = $secondFactor;
422
            }
423
        }
424
425
        if (!$secondFactorToVerify) {
426
            throw new DomainException(
427
                'Cannot verify second factor, no unverified second factor can be verified using the given nonce'
428
            );
429
        }
430
431
        /** @var Entity\UnverifiedSecondFactor $secondFactorToVerify */
432
        if (!$secondFactorToVerify->canBeVerifiedNow()) {
433
            throw new DomainException('Cannot verify second factor, the verification window is closed.');
434
        }
435
436
        $secondFactorToVerify->verifyEmail();
437
    }
438
439
    /**
440
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
441
     */
442
    public function vetSecondFactor(
443
        IdentityApi $registrant,
444
        SecondFactorId $registrantsSecondFactorId,
445
        SecondFactorType $registrantsSecondFactorType,
446
        SecondFactorIdentifier $registrantsSecondFactorIdentifier,
447
        $registrationCode,
448
        DocumentNumber $documentNumber,
449
        $identityVerified,
450
        SecondFactorTypeService $secondFactorTypeService,
451
        SecondFactorProvePossessionHelper $secondFactorProvePossessionHelper,
452
        $provePossessionSkipped
453
    ) {
454
        $this->assertNotForgotten();
455
456
        /** @var VettedSecondFactor|null $secondFactorWithHighestLoa */
457
        $secondFactorWithHighestLoa = $this->vettedSecondFactors->getSecondFactorWithHighestLoa($secondFactorTypeService);
458
        $registrantsSecondFactor = $registrant->getVerifiedSecondFactor($registrantsSecondFactorId);
459
460
        if ($registrantsSecondFactor === null) {
461
            throw new DomainException(
462
                sprintf('Registrant second factor with ID %s does not exist', $registrantsSecondFactorId)
463
            );
464
        }
465
466
        if ($secondFactorWithHighestLoa === null) {
467
            throw new DomainException(
468
                sprintf(
469
                    'Vetting failed: authority %s has %d vetted second factors!',
470
                    $this->id,
471
                    count($this->vettedSecondFactors)
472
                )
473
            );
474
        }
475
476
        if (!$secondFactorWithHighestLoa->hasEqualOrHigherLoaComparedTo(
477
            $registrantsSecondFactor,
478
            $secondFactorTypeService
479
        )) {
480
            throw new DomainException("Authority does not have the required LoA to vet the registrant's second factor");
481
        }
482
483
        if (!$identityVerified) {
484
            throw new DomainException('Will not vet second factor when physical identity has not been verified.');
485
        }
486
487
        if ($provePossessionSkipped && !$secondFactorProvePossessionHelper->canSkipProvePossession($registrantsSecondFactorType)) {
488
            throw new DomainException(sprintf(
489
                "The possession of registrants second factor with ID '%s' of type '%s' has to be physically proven",
490
                $registrantsSecondFactorId,
491
                $registrantsSecondFactorType->getSecondFactorType()
492
            ));
493
        }
494
495
        $registrant->complyWithVettingOfSecondFactor(
496
            $registrantsSecondFactorId,
497
            $registrantsSecondFactorType,
498
            $registrantsSecondFactorIdentifier,
499
            $registrationCode,
500
            $documentNumber,
501
            $provePossessionSkipped
502
        );
503
    }
504
505
    /**
506
     * Self vetting, is when the user uses it's own token to vet another.
507
     *
508
     * Here the new token should have a lower or equal LoA to that of the one in posession of the user
509
     */
510
    public function selfVetSecondFactor(
511
        Loa $authoringSecondFactorLoa,
512
        string $registrationCode,
513
        SecondFactorIdentifier $secondFactorIdentifier,
514
        SecondFactorTypeService $secondFactorTypeService
515
    ): void {
516
        $this->assertNotForgotten();
517
        $registeringSecondFactor = null;
518
        foreach ($this->verifiedSecondFactors as $secondFactor) {
519
            /** @var VerifiedSecondFactor $secondFactor */
520
            if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
521
                $registeringSecondFactor = $secondFactor;
522
            }
523
        }
524
525
        if ($registeringSecondFactor === null) {
526
            throw new DomainException(
527
                sprintf(
528
                    'Registrant second factor of type %s with ID %s does not exist',
529
                    get_class($secondFactorIdentifier),
530
                    $secondFactorIdentifier->getValue()
531
                )
532
            );
533
        }
534
535
        if (!$registeringSecondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
536
            throw new DomainException('The verified second factors registration code or identifier do not match.');
537
        }
538
539
        $selfVettingIsAllowed = $authoringSecondFactorLoa->levelIsHigherOrEqualTo(
540
            $registeringSecondFactor->getLoaLevel($secondFactorTypeService)
541
        );
542
543
        if (!$selfVettingIsAllowed) {
544
            throw new DomainException(
545
                "The second factor to be vetted has a higher LoA then the Token used for proving possession"
546
            );
547
        }
548
549
        $registeringSecondFactor->vet(true, new SelfVetVettingType($authoringSecondFactorLoa));
550
    }
551
552
    public function complyWithVettingOfSecondFactor(
553
        SecondFactorId $secondFactorId,
554
        SecondFactorType $secondFactorType,
555
        SecondFactorIdentifier $secondFactorIdentifier,
556
        $registrationCode,
557
        DocumentNumber $documentNumber,
558
        $provePossessionSkipped
559
    ) {
560
        $this->assertNotForgotten();
561
562
        $secondFactorToVet = null;
563
        foreach ($this->verifiedSecondFactors as $secondFactor) {
564
            /** @var VerifiedSecondFactor $secondFactor */
565
            if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) {
566
                $secondFactorToVet = $secondFactor;
567
            }
568
        }
569
570
        if (!$secondFactorToVet) {
571
            throw new DomainException(
572
                'Cannot vet second factor, no verified second factor can be vetted using the given registration code ' .
573
                'and second factor identifier'
574
            );
575
        }
576
577
        if (!$secondFactorToVet->canBeVettedNow()) {
578
            throw new DomainException('Cannot vet second factor, the registration window is closed.');
579
        }
580
581
        $secondFactorToVet->vet($provePossessionSkipped, new OnPremiseVettingType($documentNumber));
582
    }
583
584
    public function revokeSecondFactor(SecondFactorId $secondFactorId)
585
    {
586
        $this->assertNotForgotten();
587
588
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
589
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
590
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
591
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
592
        /** @var VettedSecondFactor|null $vettedSecondFactor */
593
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
594
595
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
596
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
597
        }
598
599
        if ($unverifiedSecondFactor) {
600
            $unverifiedSecondFactor->revoke();
601
602
            return;
603
        }
604
605
        if ($verifiedSecondFactor) {
606
            $verifiedSecondFactor->revoke();
607
608
            return;
609
        }
610
611
        $vettedSecondFactor->revoke();
612
613
        if ($this->vettedSecondFactors->isEmpty()) {
614
            $this->allVettedSecondFactorsRemoved();
615
        }
616
    }
617
618
    public function complyWithSecondFactorRevocation(SecondFactorId $secondFactorId, IdentityId $authorityId)
619
    {
620
        $this->assertNotForgotten();
621
622
        /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */
623
        $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string)$secondFactorId);
624
        /** @var VerifiedSecondFactor|null $verifiedSecondFactor */
625
        $verifiedSecondFactor = $this->verifiedSecondFactors->get((string)$secondFactorId);
626
        /** @var VettedSecondFactor|null $vettedSecondFactor */
627
        $vettedSecondFactor = $this->vettedSecondFactors->get((string)$secondFactorId);
628
629
        if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) {
630
            throw new DomainException('Cannot revoke second factor: no second factor with given id exists.');
631
        }
632
633
        if ($unverifiedSecondFactor) {
634
            $unverifiedSecondFactor->complyWithRevocation($authorityId);
635
636
            return;
637
        }
638
639
        if ($verifiedSecondFactor) {
640
            $verifiedSecondFactor->complyWithRevocation($authorityId);
641
642
            return;
643
        }
644
645
        $vettedSecondFactor->complyWithRevocation($authorityId);
646
647
        if ($this->vettedSecondFactors->isEmpty()) {
648
            $this->allVettedSecondFactorsRemoved();
649
        }
650
    }
651
652
    /**
653
     * @param RegistrationAuthorityRole $role
654
     * @param Institution $institution
655
     * @param Location $location
656
     * @param ContactInformation $contactInformation
657
     * @param InstitutionConfiguration $institutionConfiguration
658
     * @return void
659
     */
660
    public function accreditWith(
661
        RegistrationAuthorityRole $role,
662
        Institution $institution,
663
        Location $location,
664
        ContactInformation $contactInformation,
665
        InstitutionConfiguration $institutionConfiguration
666
    ) {
667
        $this->assertNotForgotten();
668
669
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
670
            throw new DomainException('An Identity may only be accredited by configured institutions.');
671
        }
672
673
        if (!$this->vettedSecondFactors->count()) {
674
            throw new DomainException(
675
                'An Identity must have at least one vetted second factor before it can be accredited'
676
            );
677
        }
678
679
        if ($this->registrationAuthorities->exists($institution)) {
680
            throw new DomainException('Cannot accredit Identity as it has already been accredited for institution');
681
        }
682
683
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
684
            $this->apply(new IdentityAccreditedAsRaForInstitutionEvent(
685
                $this->id,
686
                $this->nameId,
687
                $this->institution,
688
                $role,
689
                $location,
690
                $contactInformation,
691
                $institution
692
            ));
693 View Code Duplication
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
694
            $this->apply(new IdentityAccreditedAsRaaForInstitutionEvent(
695
                $this->id,
696
                $this->nameId,
697
                $this->institution,
698
                $role,
699
                $location,
700
                $contactInformation,
701
                $institution
702
            ));
703
        } else {
704
            throw new DomainException('An Identity can only be accredited with either the RA or RAA role');
705
        }
706
    }
707
708
    public function amendRegistrationAuthorityInformation(Institution $institution, Location $location, ContactInformation $contactInformation)
709
    {
710
        $this->assertNotForgotten();
711
712
        if (!$this->registrationAuthorities->exists($institution)) {
713
            throw new DomainException(
714
                'Cannot amend registration authority information: identity is not a registration authority for institution'
715
            );
716
        }
717
718
        $this->apply(
719
            new RegistrationAuthorityInformationAmendedForInstitutionEvent(
720
                $this->id,
721
                $this->institution,
722
                $this->nameId,
723
                $location,
724
                $contactInformation,
725
                $institution
726
            )
727
        );
728
    }
729
730
    /**
731
     * This method will appoint an institution to become ra or raa for another institution
732
     *
733
     * @param Institution $institution
734
     * @param RegistrationAuthorityRole $role
735
     * @param InstitutionConfiguration $institutionConfiguration
736
     */
737
    public function appointAs(
738
        Institution $institution,
739
        RegistrationAuthorityRole $role,
740
        InstitutionConfiguration $institutionConfiguration
741
    ) {
742
        $this->assertNotForgotten();
743
744
        if (!$institutionConfiguration->isInstitutionAllowedToAccreditRoles(new ConfigurationInstitution($this->institution->getInstitution()))) {
745
            throw new DomainException(
746
                'Cannot appoint as different RegistrationAuthorityRole: identity is not a registration authority for institution'
747
            );
748
        }
749
750
        $registrationAuthority = $this->registrationAuthorities->get($institution);
751
752
        if ($registrationAuthority->isAppointedAs($role)) {
753
            return;
754
        }
755
756
        if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) {
757
            $this->apply(new AppointedAsRaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
758 View Code Duplication
        } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
759
            $this->apply(new AppointedAsRaaForInstitutionEvent($this->id, $this->institution, $this->nameId, $institution));
760
        } else {
761
            throw new DomainException('An Identity can only be appointed as either RA or RAA');
762
        }
763
    }
764
765
    public function retractRegistrationAuthority(Institution $institution)
766
    {
767
        $this->assertNotForgotten();
768
769
        if (!$this->registrationAuthorities->exists($institution)) {
770
            throw new DomainException(
771
                'Cannot Retract Registration Authority as the Identity is not a registration authority'
772
            );
773
        }
774
775
        $this->apply(new RegistrationAuthorityRetractedForInstitutionEvent(
776
            $this->id,
777
            $this->institution,
778
            $this->nameId,
779
            $this->commonName,
780
            $this->email,
781
            $institution
782
        ));
783
    }
784
785
    public function expressPreferredLocale(Locale $preferredLocale)
786
    {
787
        $this->assertNotForgotten();
788
789
        if ($this->preferredLocale === $preferredLocale) {
790
            return;
791
        }
792
793
        $this->apply(new LocalePreferenceExpressedEvent($this->id, $this->institution, $preferredLocale));
794
    }
795
796
    public function forget()
797
    {
798
        $this->assertNotForgotten();
799
800
        if ($this->registrationAuthorities->count()) {
801
            throw new DomainException('Cannot forget an identity that is currently accredited as an RA(A)');
802
        }
803
804
        $this->apply(new IdentityForgottenEvent($this->id, $this->institution));
805
    }
806
807
    public function allVettedSecondFactorsRemoved()
808
    {
809
        $this->apply(
810
            new VettedSecondFactorsAllRevokedEvent(
811
                $this->id,
812
                $this->institution
813
            )
814
        );
815
    }
816
817
    protected function applyIdentityCreatedEvent(IdentityCreatedEvent $event)
818
    {
819
        $this->id = $event->identityId;
820
        $this->institution = $event->identityInstitution;
821
        $this->nameId = $event->nameId;
822
        $this->commonName = $event->commonName;
823
        $this->email = $event->email;
824
        $this->preferredLocale = $event->preferredLocale;
825
        $this->forgotten = false;
826
827
        $this->unverifiedSecondFactors = new SecondFactorCollection();
828
        $this->verifiedSecondFactors = new SecondFactorCollection();
829
        $this->vettedSecondFactors = new SecondFactorCollection();
830
        $this->registrationAuthorities = new RegistrationAuthorityCollection();
831
    }
832
833
    public function applyIdentityRenamedEvent(IdentityRenamedEvent $event)
834
    {
835
        $this->commonName = $event->commonName;
836
    }
837
838
    public function applyIdentityEmailChangedEvent(IdentityEmailChangedEvent $event)
839
    {
840
        $this->email = $event->email;
841
    }
842
843
    protected function applyYubikeySecondFactorBootstrappedEvent(YubikeySecondFactorBootstrappedEvent $event)
844
    {
845
        $secondFactor = VettedSecondFactor::create(
846
            $event->secondFactorId,
847
            $this,
848
            new SecondFactorType('yubikey'),
849
            $event->yubikeyPublicId
850
        );
851
852
        $this->vettedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
853
    }
854
855 View Code Duplication
    protected function applyYubikeyPossessionProvenEvent(YubikeyPossessionProvenEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
856
    {
857
        $secondFactor = UnverifiedSecondFactor::create(
858
            $event->secondFactorId,
859
            $this,
860
            new SecondFactorType('yubikey'),
861
            $event->yubikeyPublicId,
862
            $event->emailVerificationWindow,
863
            $event->emailVerificationNonce
864
        );
865
866
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
867
    }
868
869 View Code Duplication
    protected function applyYubikeyPossessionProvenAndVerifiedEvent(YubikeyPossessionProvenAndVerifiedEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
870
    {
871
        $secondFactor = VerifiedSecondFactor::create(
872
            $event->secondFactorId,
873
            $this,
874
            new SecondFactorType('yubikey'),
875
            $event->yubikeyPublicId,
876
            $event->registrationRequestedAt,
877
            $event->registrationCode
878
        );
879
880
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
881
    }
882
883 View Code Duplication
    protected function applyPhonePossessionProvenEvent(PhonePossessionProvenEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
884
    {
885
        $secondFactor = UnverifiedSecondFactor::create(
886
            $event->secondFactorId,
887
            $this,
888
            new SecondFactorType('sms'),
889
            $event->phoneNumber,
890
            $event->emailVerificationWindow,
891
            $event->emailVerificationNonce
892
        );
893
894
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
895
    }
896
897 View Code Duplication
    protected function applyPhonePossessionProvenAndVerifiedEvent(PhonePossessionProvenAndVerifiedEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
898
    {
899
        $secondFactor = VerifiedSecondFactor::create(
900
            $event->secondFactorId,
901
            $this,
902
            new SecondFactorType('sms'),
903
            $event->phoneNumber,
904
            $event->registrationRequestedAt,
905
            $event->registrationCode
906
        );
907
908
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
909
    }
910
911 View Code Duplication
    protected function applyGssfPossessionProvenEvent(GssfPossessionProvenEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
912
    {
913
        $secondFactor = UnverifiedSecondFactor::create(
914
            $event->secondFactorId,
915
            $this,
916
            new SecondFactorType((string)$event->stepupProvider),
917
            $event->gssfId,
918
            $event->emailVerificationWindow,
919
            $event->emailVerificationNonce
920
        );
921
922
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
923
    }
924
925 View Code Duplication
    protected function applyGssfPossessionProvenAndVerifiedEvent(GssfPossessionProvenAndVerifiedEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
926
    {
927
        $secondFactor = VerifiedSecondFactor::create(
928
            $event->secondFactorId,
929
            $this,
930
            new SecondFactorType((string)$event->stepupProvider),
931
            $event->gssfId,
932
            $event->registrationRequestedAt,
933
            $event->registrationCode
934
        );
935
936
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
937
    }
938
939 View Code Duplication
    protected function applyU2fDevicePossessionProvenEvent(U2fDevicePossessionProvenEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
940
    {
941
        $secondFactor = UnverifiedSecondFactor::create(
942
            $event->secondFactorId,
943
            $this,
944
            new SecondFactorType('u2f'),
945
            $event->keyHandle,
946
            $event->emailVerificationWindow,
947
            $event->emailVerificationNonce
948
        );
949
950
        $this->unverifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
951
    }
952
953 View Code Duplication
    protected function applyU2fDevicePossessionProvenAndVerifiedEvent(U2fDevicePossessionProvenAndVerifiedEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
954
    {
955
        $secondFactor = VerifiedSecondFactor::create(
956
            $event->secondFactorId,
957
            $this,
958
            new SecondFactorType('u2f'),
959
            $event->keyHandle,
960
            $event->registrationRequestedAt,
961
            $event->registrationCode
962
        );
963
964
        $this->verifiedSecondFactors->set((string)$secondFactor->getId(), $secondFactor);
965
    }
966
967
    protected function applyEmailVerifiedEvent(EmailVerifiedEvent $event)
968
    {
969
        $secondFactorId = (string)$event->secondFactorId;
970
971
        /** @var UnverifiedSecondFactor $unverified */
972
        $unverified = $this->unverifiedSecondFactors->get($secondFactorId);
973
        $verified = $unverified->asVerified($event->registrationRequestedAt, $event->registrationCode);
974
975
        $this->unverifiedSecondFactors->remove($secondFactorId);
976
        $this->verifiedSecondFactors->set($secondFactorId, $verified);
977
    }
978
979 View Code Duplication
    protected function applySecondFactorVettedEvent(SecondFactorVettedEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
980
    {
981
        $secondFactorId = (string)$event->secondFactorId;
982
983
        /** @var VerifiedSecondFactor $verified */
984
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
985
        $vetted = $verified->asVetted();
986
987
        $this->verifiedSecondFactors->remove($secondFactorId);
988
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
989
    }
990
991 View Code Duplication
    protected function applySecondFactorVettedWithoutTokenProofOfPossession(SecondFactorVettedWithoutTokenProofOfPossession $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
992
    {
993
        $secondFactorId = (string)$event->secondFactorId;
994
995
        /** @var VerifiedSecondFactor $verified */
996
        $verified = $this->verifiedSecondFactors->get($secondFactorId);
997
        $vetted = $verified->asVetted();
998
999
        $this->verifiedSecondFactors->remove($secondFactorId);
1000
        $this->vettedSecondFactors->set($secondFactorId, $vetted);
1001
    }
1002
1003
    protected function applyUnverifiedSecondFactorRevokedEvent(UnverifiedSecondFactorRevokedEvent $event)
1004
    {
1005
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
1006
    }
1007
1008
    protected function applyCompliedWithUnverifiedSecondFactorRevocationEvent(
1009
        CompliedWithUnverifiedSecondFactorRevocationEvent $event
1010
    ) {
1011
        $this->unverifiedSecondFactors->remove((string)$event->secondFactorId);
1012
    }
1013
1014
    protected function applyVerifiedSecondFactorRevokedEvent(VerifiedSecondFactorRevokedEvent $event)
1015
    {
1016
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1017
    }
1018
1019
    protected function applyCompliedWithVerifiedSecondFactorRevocationEvent(
1020
        CompliedWithVerifiedSecondFactorRevocationEvent $event
1021
    ) {
1022
        $this->verifiedSecondFactors->remove((string)$event->secondFactorId);
1023
    }
1024
1025
    protected function applyVettedSecondFactorRevokedEvent(VettedSecondFactorRevokedEvent $event)
1026
    {
1027
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1028
    }
1029
1030
    protected function applyCompliedWithVettedSecondFactorRevocationEvent(
1031
        CompliedWithVettedSecondFactorRevocationEvent $event
1032
    ) {
1033
        $this->vettedSecondFactors->remove((string)$event->secondFactorId);
1034
    }
1035
1036 View Code Duplication
    protected function applyIdentityAccreditedAsRaForInstitutionEvent(IdentityAccreditedAsRaForInstitutionEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    protected function applyIdentityAccreditedAsRaaForInstitutionEvent(IdentityAccreditedAsRaaForInstitutionEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1047
    {
1048
        $this->registrationAuthorities->set($event->raInstitution, RegistrationAuthority::accreditWith(
1049
            $event->registrationAuthorityRole,
1050
            $event->location,
1051
            $event->contactInformation,
1052
            $event->raInstitution
1053
        ));
1054
    }
1055
1056
    protected function applyRegistrationAuthorityInformationAmendedForInstitutionEvent(
1057
        RegistrationAuthorityInformationAmendedForInstitutionEvent $event
1058
    ) {
1059
        $this->registrationAuthorities->get($event->raInstitution)->amendInformation($event->location, $event->contactInformation);
1060
    }
1061
1062
    protected function applyAppointedAsRaaForInstitutionEvent(AppointedAsRaaForInstitutionEvent $event)
1063
    {
1064
        $this->registrationAuthorities->get($event->raInstitution)->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1065
    }
1066
1067
    protected function applyRegistrationAuthorityRetractedForInstitutionEvent(RegistrationAuthorityRetractedForInstitutionEvent $event)
1068
    {
1069
        $this->registrationAuthorities->remove($event->raInstitution);
1070
    }
1071
1072
    protected function applyLocalePreferenceExpressedEvent(LocalePreferenceExpressedEvent $event)
1073
    {
1074
        $this->preferredLocale = $event->preferredLocale;
1075
    }
1076
1077
    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...
1078
    {
1079
        $this->commonName = CommonName::unknown();
1080
        $this->email = Email::unknown();
1081
        $this->forgotten = true;
1082
    }
1083
1084
    /**
1085
     * This method is kept to be backwards compatible for changes before FGA
1086
     *
1087
     * @param AppointedAsRaEvent $event
1088
     */
1089
    protected function applyAppointedAsRaEvent(AppointedAsRaEvent $event)
1090
    {
1091
        $this->registrationAuthorities->get($event->identityInstitution)
1092
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1093
    }
1094
1095
    /**
1096
     * This method is kept to be backwards compatible for changes before FGA
1097
     *
1098
     * @param AppointedAsRaaEvent $event
1099
     */
1100
    protected function applyAppointedAsRaaEvent(AppointedAsRaaEvent $event)
1101
    {
1102
        $this->registrationAuthorities->get($event->identityInstitution)
1103
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA));
1104
    }
1105
1106
    /**
1107
     * This method is kept to be backwards compatible for changes before FGA
1108
     *
1109
     * @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...
1110
     */
1111 View Code Duplication
    protected function applyIdentityAccreditedAsRaEvent(IdentityAccreditedAsRaEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1112
    {
1113
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1114
            $event->registrationAuthorityRole,
1115
            $event->location,
1116
            $event->contactInformation,
1117
            $event->identityInstitution
1118
        ));
1119
    }
1120
1121
    /**
1122
     * This method is kept to be backwards compatible for changes before FGA
1123
     *
1124
     * @param IdentityAccreditedAsRaaEvent $event
1125
     */
1126 View Code Duplication
    protected function applyIdentityAccreditedAsRaaEvent(IdentityAccreditedAsRaaEvent $event)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1127
    {
1128
        $this->registrationAuthorities->set($event->identityInstitution, RegistrationAuthority::accreditWith(
1129
            $event->registrationAuthorityRole,
1130
            $event->location,
1131
            $event->contactInformation,
1132
            $event->identityInstitution
1133
        ));
1134
    }
1135
1136
    /**
1137
     * This method is kept to be backwards compatible for changes before FGA
1138
     *
1139
     * @param AppointedAsRaForInstitutionEvent $event
1140
     */
1141
    protected function applyAppointedAsRaForInstitutionEvent(AppointedAsRaForInstitutionEvent $event)
1142
    {
1143
        $this->registrationAuthorities->get($event->identityInstitution)
1144
            ->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA));
1145
    }
1146
1147
    /**
1148
     * This method is kept to be backwards compatible for changes before FGA
1149
     *
1150
     * @param RegistrationAuthorityInformationAmendedEvent $event
1151
     */
1152
    protected function applyRegistrationAuthorityInformationAmendedEvent(
1153
        RegistrationAuthorityInformationAmendedEvent $event
1154
    ) {
1155
        $this->registrationAuthorities->get($event->identityInstitution)->amendInformation($event->location, $event->contactInformation);
1156
    }
1157
1158
    /**
1159
     * This method is kept to be backwards compatible for changes before FGA
1160
     *
1161
     * @param RegistrationAuthorityRetractedEvent $event
1162
     */
1163
    protected function applyRegistrationAuthorityRetractedEvent(RegistrationAuthorityRetractedEvent $event)
1164
    {
1165
        $this->registrationAuthorities->remove($event->identityInstitution);
1166
    }
1167
1168
1169
    public function getAggregateRootId(): string
1170
    {
1171
        return $this->id->getIdentityId();
1172
    }
1173
1174
    protected function getChildEntities(): array
1175
    {
1176
        return array_merge(
1177
            $this->unverifiedSecondFactors->getValues(),
1178
            $this->verifiedSecondFactors->getValues(),
1179
            $this->vettedSecondFactors->getValues(),
1180
            $this->registrationAuthorities->getValues()
1181
        );
1182
    }
1183
1184
    /**
1185
     * @throws DomainException
1186
     */
1187
    private function assertNotForgotten()
1188
    {
1189
        if ($this->forgotten) {
1190
            throw new DomainException('Operation on this Identity is not allowed: it has been forgotten');
1191
        }
1192
    }
1193
1194
    /**
1195
     * @throws DomainException
1196
     */
1197
    private function assertUserMayAddSecondFactor()
1198
    {
1199
        if (count($this->unverifiedSecondFactors) +
1200
            count($this->verifiedSecondFactors) +
1201
            count($this->vettedSecondFactors) >= $this->maxNumberOfTokens
1202
        ) {
1203
            throw new DomainException(
1204
                sprintf('User may not have more than %d token(s)', $this->maxNumberOfTokens)
1205
            );
1206
        }
1207
    }
1208
1209
    public function getId()
1210
    {
1211
        return $this->id;
1212
    }
1213
1214
    /**
1215
     * @return NameId
1216
     */
1217
    public function getNameId()
1218
    {
1219
        return $this->nameId;
1220
    }
1221
1222
    /**
1223
     * @return Institution
1224
     */
1225
    public function getInstitution()
1226
    {
1227
        return $this->institution;
1228
    }
1229
1230
    public function getCommonName()
1231
    {
1232
        return $this->commonName;
1233
    }
1234
1235
    public function getEmail()
1236
    {
1237
        return $this->email;
1238
    }
1239
1240
    public function getPreferredLocale()
1241
    {
1242
        return $this->preferredLocale;
1243
    }
1244
1245
    /**
1246
     * @param SecondFactorId $secondFactorId
1247
     * @return VerifiedSecondFactor|null
1248
     */
1249
    public function getVerifiedSecondFactor(SecondFactorId $secondFactorId)
1250
    {
1251
        return $this->verifiedSecondFactors->get((string)$secondFactorId);
1252
    }
1253
}
1254