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