Completed
Pull Request — feature/fine-grained-authoriza... (#246)
by
unknown
40:56 queued 21:52
created

Identity   F

Complexity

Total Complexity 108

Size/Duplication

Total Lines 1017
Duplicated Lines 30.19 %

Coupling/Cohesion

Components 1
Dependencies 51

Importance

Changes 0
Metric Value
dl 307
loc 1017
c 0
b 0
f 0
wmc 108
lcom 1
cbo 51
rs 1.532

63 Methods

Rating   Name   Duplication   Size   Complexity  
A create() 0 15 1
A __construct() 0 3 1
A rename() 0 11 2
A changeEmail() 0 11 2
A setMaxNumberOfTokens() 0 4 1
A bootstrapYubikeySecondFactor() 0 18 1
A provePossessionOfYubikey() 42 43 2
A provePossessionOfPhone() 42 43 2
A provePossessionOfGssf() 45 46 2
A provePossessionOfU2fDevice() 42 43 2
A verifyEmail() 0 25 5
B vetSecondFactor() 0 52 5
A complyWithVettingOfSecondFactor() 0 31 5
B revokeSecondFactor() 0 29 6
B complyWithSecondFactorRevocation() 0 29 6
B accreditWith() 11 47 6
A amendRegistrationAuthorityInformation() 0 21 2
A appointAs() 3 24 5
A retractRegistrationAuthority() 0 19 2
A expressPreferredLocale() 0 10 2
A forget() 0 10 2
A applyIdentityCreatedEvent() 0 19 1
A applyIdentityRenamedEvent() 0 4 1
A applyIdentityEmailChangedEvent() 0 4 1
A applyYubikeySecondFactorBootstrappedEvent() 0 11 1
A applyYubikeyPossessionProvenEvent() 13 13 1
A applyYubikeyPossessionProvenAndVerifiedEvent() 13 13 1
A applyPhonePossessionProvenEvent() 13 13 1
A applyPhonePossessionProvenAndVerifiedEvent() 13 13 1
A applyGssfPossessionProvenEvent() 13 13 1
A applyGssfPossessionProvenAndVerifiedEvent() 13 13 1
A applyU2fDevicePossessionProvenEvent() 13 13 1
A applyU2fDevicePossessionProvenAndVerifiedEvent() 13 13 1
A applyEmailVerifiedEvent() 0 11 1
A applySecondFactorVettedEvent() 0 11 1
A applyUnverifiedSecondFactorRevokedEvent() 0 4 1
A applyCompliedWithUnverifiedSecondFactorRevocationEvent() 0 6 1
A applyVerifiedSecondFactorRevokedEvent() 0 4 1
A applyCompliedWithVerifiedSecondFactorRevocationEvent() 0 6 1
A applyVettedSecondFactorRevokedEvent() 0 4 1
A applyCompliedWithVettedSecondFactorRevocationEvent() 0 6 1
A applyIdentityAccreditedAsRaEvent() 9 9 1
A applyIdentityAccreditedAsRaaEvent() 9 9 1
A applyRegistrationAuthorityInformationAmendedEvent() 0 6 1
A applyAppointedAsRaEvent() 0 4 1
A applyAppointedAsRaaEvent() 0 4 1
A applyRegistrationAuthorityRetractedEvent() 0 4 1
A applyLocalePreferenceExpressedEvent() 0 4 1
A applyIdentityForgottenEvent() 0 6 1
A applySAccreditedInstitutionsAddedToIdentityEvent() 0 4 1
A applySelectRaaOptionChangedEvent() 0 12 3
A applyInstitutionConfigurationRemovedEvent() 0 4 1
A getAggregateRootId() 0 4 1
A getChildEntities() 0 9 1
A assertNotForgotten() 0 6 2
A assertUserMayAddSecondFactor() 0 11 2
A getId() 0 4 1
A getNameId() 0 4 1
A getInstitution() 0 4 1
A getCommonName() 0 4 1
A getEmail() 0 4 1
A getPreferredLocale() 0 4 1
A getVerifiedSecondFactor() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Identity often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Identity, and based on these observations, apply Extract Interface, too.

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