Completed
Push — feature/ra-optional-vetting ( 241f76...b2f623 )
by
unknown
03:37 queued 12s
created

applySecondFactorPossessionSkippedEvent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 11
Ratio 100 %

Importance

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