Completed
Push — feature/fga-ra-management ( e11d53...de408d )
by
unknown
19:18 queued 10:50
created

Identity::applyAppointedInstitutionAsRaaEvent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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