Completed
Push — feature/ra-optional-vetting ( 7c61c5...804421 )
by
unknown
03:13
created

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