OpenConext /
Stepup-Middleware
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 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\Exception\DomainException; |
||
| 23 | use Surfnet\Stepup\Identity\Api\Identity as IdentityApi; |
||
| 24 | use Surfnet\Stepup\Identity\Entity\RegistrationAuthority; |
||
| 25 | use Surfnet\Stepup\Identity\Entity\SecondFactorCollection; |
||
| 26 | use Surfnet\Stepup\Identity\Entity\UnverifiedSecondFactor; |
||
| 27 | use Surfnet\Stepup\Identity\Entity\VerifiedSecondFactor; |
||
| 28 | use Surfnet\Stepup\Identity\Entity\VettedSecondFactor; |
||
| 29 | use Surfnet\Stepup\Identity\Event\AppointedAsRaaEvent; |
||
| 30 | use Surfnet\Stepup\Identity\Event\AppointedAsRaEvent; |
||
| 31 | use Surfnet\Stepup\Identity\Event\CompliedWithUnverifiedSecondFactorRevocationEvent; |
||
| 32 | use Surfnet\Stepup\Identity\Event\CompliedWithVerifiedSecondFactorRevocationEvent; |
||
| 33 | use Surfnet\Stepup\Identity\Event\CompliedWithVettedSecondFactorRevocationEvent; |
||
| 34 | use Surfnet\Stepup\Identity\Event\EmailVerifiedEvent; |
||
| 35 | use Surfnet\Stepup\Identity\Event\GssfPossessionProvenEvent; |
||
| 36 | use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaaEvent; |
||
| 37 | use Surfnet\Stepup\Identity\Event\IdentityAccreditedAsRaEvent; |
||
| 38 | use Surfnet\Stepup\Identity\Event\IdentityCreatedEvent; |
||
| 39 | use Surfnet\Stepup\Identity\Event\IdentityEmailChangedEvent; |
||
| 40 | use Surfnet\Stepup\Identity\Event\IdentityForgottenEvent; |
||
| 41 | use Surfnet\Stepup\Identity\Event\IdentityRenamedEvent; |
||
| 42 | use Surfnet\Stepup\Identity\Event\LocalePreferenceExpressedEvent; |
||
| 43 | use Surfnet\Stepup\Identity\Event\PhonePossessionProvenEvent; |
||
| 44 | use Surfnet\Stepup\Identity\Event\RegistrationAuthorityInformationAmendedEvent; |
||
| 45 | use Surfnet\Stepup\Identity\Event\RegistrationAuthorityRetractedEvent; |
||
| 46 | use Surfnet\Stepup\Identity\Event\SecondFactorVettedEvent; |
||
| 47 | use Surfnet\Stepup\Identity\Event\U2fDevicePossessionProvenEvent; |
||
| 48 | use Surfnet\Stepup\Identity\Event\UnverifiedSecondFactorRevokedEvent; |
||
| 49 | use Surfnet\Stepup\Identity\Event\VerifiedSecondFactorRevokedEvent; |
||
| 50 | use Surfnet\Stepup\Identity\Event\VettedSecondFactorRevokedEvent; |
||
| 51 | use Surfnet\Stepup\Identity\Event\YubikeyPossessionProvenEvent; |
||
| 52 | use Surfnet\Stepup\Identity\Event\YubikeySecondFactorBootstrappedEvent; |
||
| 53 | use Surfnet\Stepup\Identity\Value\CommonName; |
||
| 54 | use Surfnet\Stepup\Identity\Value\ContactInformation; |
||
| 55 | use Surfnet\Stepup\Identity\Value\DocumentNumber; |
||
| 56 | use Surfnet\Stepup\Identity\Value\Email; |
||
| 57 | use Surfnet\Stepup\Identity\Value\EmailVerificationWindow; |
||
| 58 | use Surfnet\Stepup\Identity\Value\GssfId; |
||
| 59 | use Surfnet\Stepup\Identity\Value\IdentityId; |
||
| 60 | use Surfnet\Stepup\Identity\Value\Institution; |
||
| 61 | use Surfnet\Stepup\Identity\Value\Locale; |
||
| 62 | use Surfnet\Stepup\Identity\Value\Location; |
||
| 63 | use Surfnet\Stepup\Identity\Value\NameId; |
||
| 64 | use Surfnet\Stepup\Identity\Value\PhoneNumber; |
||
| 65 | use Surfnet\Stepup\Identity\Value\RegistrationAuthorityRole; |
||
| 66 | use Surfnet\Stepup\Identity\Value\SecondFactorId; |
||
| 67 | use Surfnet\Stepup\Identity\Value\SecondFactorIdentifier; |
||
| 68 | use Surfnet\Stepup\Identity\Value\StepupProvider; |
||
| 69 | use Surfnet\Stepup\Identity\Value\U2fKeyHandle; |
||
| 70 | use Surfnet\Stepup\Identity\Value\YubikeyPublicId; |
||
| 71 | use Surfnet\Stepup\Token\TokenGenerator; |
||
| 72 | use Surfnet\StepupBundle\Value\SecondFactorType; |
||
| 73 | |||
| 74 | /** |
||
| 75 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
||
| 76 | * @SuppressWarnings(PHPMD.TooManyMethods) |
||
| 77 | * @SuppressWarnings(PHPMD.TooManyPublicMethods) |
||
| 78 | * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
||
| 79 | */ |
||
| 80 | class Identity extends EventSourcedAggregateRoot implements IdentityApi |
||
| 81 | { |
||
| 82 | /** |
||
| 83 | * @var IdentityId |
||
| 84 | */ |
||
| 85 | private $id; |
||
| 86 | |||
| 87 | /** |
||
| 88 | * @var Institution |
||
| 89 | */ |
||
| 90 | private $institution; |
||
| 91 | |||
| 92 | /** |
||
| 93 | * @var NameId |
||
| 94 | */ |
||
| 95 | private $nameId; |
||
| 96 | |||
| 97 | /** |
||
| 98 | * @var \Surfnet\Stepup\Identity\Value\CommonName |
||
| 99 | */ |
||
| 100 | private $commonName; |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @var \Surfnet\Stepup\Identity\Value\Email |
||
| 104 | */ |
||
| 105 | private $email; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * @var SecondFactorCollection|UnverifiedSecondFactor[] |
||
| 109 | */ |
||
| 110 | private $unverifiedSecondFactors; |
||
| 111 | |||
| 112 | /** |
||
| 113 | * @var SecondFactorCollection|VerifiedSecondFactor[] |
||
| 114 | */ |
||
| 115 | private $verifiedSecondFactors; |
||
| 116 | |||
| 117 | /** |
||
| 118 | * @var SecondFactorCollection|VettedSecondFactor[] |
||
| 119 | */ |
||
| 120 | private $vettedSecondFactors; |
||
| 121 | |||
| 122 | /** |
||
| 123 | * @var RegistrationAuthority |
||
| 124 | */ |
||
| 125 | private $registrationAuthority; |
||
| 126 | |||
| 127 | /** |
||
| 128 | * @var Locale |
||
| 129 | */ |
||
| 130 | private $preferredLocale; |
||
| 131 | |||
| 132 | /** |
||
| 133 | * @var boolean |
||
| 134 | */ |
||
| 135 | private $forgotten; |
||
| 136 | |||
| 137 | public static function create( |
||
| 138 | IdentityId $id, |
||
| 139 | Institution $institution, |
||
| 140 | NameId $nameId, |
||
| 141 | CommonName $commonName, |
||
| 142 | Email $email, |
||
| 143 | Locale $preferredLocale |
||
| 144 | ) { |
||
| 145 | $identity = new self(); |
||
| 146 | $identity->apply(new IdentityCreatedEvent($id, $institution, $nameId, $commonName, $email, $preferredLocale)); |
||
| 147 | |||
| 148 | return $identity; |
||
| 149 | } |
||
| 150 | |||
| 151 | final public function __construct() |
||
| 152 | { |
||
| 153 | } |
||
| 154 | |||
| 155 | public function rename(CommonName $commonName) |
||
| 156 | { |
||
| 157 | $this->assertNotForgotten(); |
||
| 158 | |||
| 159 | if ($this->commonName->equals($commonName)) { |
||
| 160 | return; |
||
| 161 | } |
||
| 162 | |||
| 163 | $this->commonName = $commonName; |
||
| 164 | $this->apply(new IdentityRenamedEvent($this->id, $this->institution, $commonName)); |
||
| 165 | } |
||
| 166 | |||
| 167 | public function changeEmail(Email $email) |
||
| 168 | { |
||
| 169 | $this->assertNotForgotten(); |
||
| 170 | |||
| 171 | if ($this->email->equals($email)) { |
||
| 172 | return; |
||
| 173 | } |
||
| 174 | |||
| 175 | $this->email = $email; |
||
| 176 | $this->apply(new IdentityEmailChangedEvent($this->id, $this->institution, $email)); |
||
| 177 | } |
||
| 178 | |||
| 179 | public function bootstrapYubikeySecondFactor(SecondFactorId $secondFactorId, YubikeyPublicId $yubikeyPublicId) |
||
| 180 | { |
||
| 181 | $this->assertNotForgotten(); |
||
| 182 | $this->assertUserMayAddSecondFactor(); |
||
| 183 | |||
| 184 | $this->apply( |
||
| 185 | new YubikeySecondFactorBootstrappedEvent( |
||
| 186 | $this->id, |
||
| 187 | $this->nameId, |
||
| 188 | $this->institution, |
||
| 189 | $this->commonName, |
||
| 190 | $this->email, |
||
| 191 | $this->preferredLocale, |
||
| 192 | $secondFactorId, |
||
| 193 | $yubikeyPublicId |
||
| 194 | ) |
||
| 195 | ); |
||
| 196 | } |
||
| 197 | |||
| 198 | View Code Duplication | public function provePossessionOfYubikey( |
|
| 199 | SecondFactorId $secondFactorId, |
||
| 200 | YubikeyPublicId $yubikeyPublicId, |
||
| 201 | EmailVerificationWindow $emailVerificationWindow |
||
| 202 | ) { |
||
| 203 | $this->assertNotForgotten(); |
||
| 204 | $this->assertUserMayAddSecondFactor(); |
||
| 205 | |||
| 206 | $this->apply( |
||
| 207 | new YubikeyPossessionProvenEvent( |
||
| 208 | $this->id, |
||
| 209 | $this->institution, |
||
| 210 | $secondFactorId, |
||
| 211 | $yubikeyPublicId, |
||
| 212 | $emailVerificationWindow, |
||
| 213 | TokenGenerator::generateNonce(), |
||
| 214 | $this->commonName, |
||
| 215 | $this->email, |
||
| 216 | $this->preferredLocale |
||
| 217 | ) |
||
| 218 | ); |
||
| 219 | } |
||
| 220 | |||
| 221 | View Code Duplication | public function provePossessionOfPhone( |
|
| 222 | SecondFactorId $secondFactorId, |
||
| 223 | PhoneNumber $phoneNumber, |
||
| 224 | EmailVerificationWindow $emailVerificationWindow |
||
| 225 | ) { |
||
| 226 | $this->assertNotForgotten(); |
||
| 227 | $this->assertUserMayAddSecondFactor(); |
||
| 228 | |||
| 229 | $this->apply( |
||
| 230 | new PhonePossessionProvenEvent( |
||
| 231 | $this->id, |
||
| 232 | $this->institution, |
||
| 233 | $secondFactorId, |
||
| 234 | $phoneNumber, |
||
| 235 | $emailVerificationWindow, |
||
| 236 | TokenGenerator::generateNonce(), |
||
| 237 | $this->commonName, |
||
| 238 | $this->email, |
||
| 239 | $this->preferredLocale |
||
| 240 | ) |
||
| 241 | ); |
||
| 242 | } |
||
| 243 | |||
| 244 | View Code Duplication | public function provePossessionOfGssf( |
|
| 245 | SecondFactorId $secondFactorId, |
||
| 246 | StepupProvider $provider, |
||
| 247 | GssfId $gssfId, |
||
| 248 | EmailVerificationWindow $emailVerificationWindow |
||
| 249 | ) { |
||
| 250 | $this->assertNotForgotten(); |
||
| 251 | $this->assertUserMayAddSecondFactor(); |
||
| 252 | |||
| 253 | $this->apply( |
||
| 254 | new GssfPossessionProvenEvent( |
||
| 255 | $this->id, |
||
| 256 | $this->institution, |
||
| 257 | $secondFactorId, |
||
| 258 | $provider, |
||
| 259 | $gssfId, |
||
| 260 | $emailVerificationWindow, |
||
| 261 | TokenGenerator::generateNonce(), |
||
| 262 | $this->commonName, |
||
| 263 | $this->email, |
||
| 264 | $this->preferredLocale |
||
| 265 | ) |
||
| 266 | ); |
||
| 267 | } |
||
| 268 | |||
| 269 | View Code Duplication | public function provePossessionOfU2fDevice( |
|
| 270 | SecondFactorId $secondFactorId, |
||
| 271 | U2fKeyHandle $keyHandle, |
||
| 272 | EmailVerificationWindow $emailVerificationWindow |
||
| 273 | ) { |
||
| 274 | $this->assertNotForgotten(); |
||
| 275 | $this->assertUserMayAddSecondFactor(); |
||
| 276 | |||
| 277 | $this->apply( |
||
| 278 | new U2fDevicePossessionProvenEvent( |
||
| 279 | $this->id, |
||
| 280 | $this->institution, |
||
| 281 | $secondFactorId, |
||
| 282 | $keyHandle, |
||
| 283 | $emailVerificationWindow, |
||
| 284 | TokenGenerator::generateNonce(), |
||
| 285 | $this->commonName, |
||
| 286 | $this->email, |
||
| 287 | $this->preferredLocale |
||
| 288 | ) |
||
| 289 | ); |
||
| 290 | } |
||
| 291 | |||
| 292 | public function verifyEmail($verificationNonce) |
||
| 293 | { |
||
| 294 | $this->assertNotForgotten(); |
||
| 295 | |||
| 296 | $secondFactorToVerify = null; |
||
| 297 | foreach ($this->unverifiedSecondFactors as $secondFactor) { |
||
| 298 | /** @var Entity\UnverifiedSecondFactor $secondFactor */ |
||
| 299 | if ($secondFactor->hasNonce($verificationNonce)) { |
||
| 300 | $secondFactorToVerify = $secondFactor; |
||
| 301 | } |
||
| 302 | } |
||
| 303 | |||
| 304 | if (!$secondFactorToVerify) { |
||
| 305 | throw new DomainException( |
||
| 306 | 'Cannot verify second factor, no unverified second factor can be verified using the given nonce' |
||
| 307 | ); |
||
| 308 | } |
||
| 309 | |||
| 310 | /** @var Entity\UnverifiedSecondFactor $secondFactorToVerify */ |
||
| 311 | if (!$secondFactorToVerify->canBeVerifiedNow()) { |
||
| 312 | throw new DomainException('Cannot verify second factor, the verification window is closed.'); |
||
| 313 | } |
||
| 314 | |||
| 315 | $secondFactorToVerify->verifyEmail(); |
||
| 316 | } |
||
| 317 | |||
| 318 | public function vetSecondFactor( |
||
| 319 | IdentityApi $registrant, |
||
| 320 | SecondFactorId $registrantsSecondFactorId, |
||
| 321 | SecondFactorType $registrantsSecondFactorType, |
||
| 322 | SecondFactorIdentifier $registrantsSecondFactorIdentifier, |
||
|
0 ignored issues
–
show
|
|||
| 323 | $registrationCode, |
||
| 324 | DocumentNumber $documentNumber, |
||
| 325 | $identityVerified |
||
| 326 | ) { |
||
| 327 | $this->assertNotForgotten(); |
||
| 328 | |||
| 329 | /** @var VettedSecondFactor|null $secondFactorWithHighestLoa */ |
||
| 330 | $secondFactorWithHighestLoa = $this->vettedSecondFactors->getSecondFactorWithHighestLoa(); |
||
| 331 | $registrantsSecondFactor = $registrant->getVerifiedSecondFactor($registrantsSecondFactorId); |
||
| 332 | |||
| 333 | if ($registrantsSecondFactor === null) { |
||
| 334 | throw new DomainException( |
||
| 335 | sprintf('Registrant second factor with ID %s does not exist', $registrantsSecondFactorId) |
||
| 336 | ); |
||
| 337 | } |
||
| 338 | |||
| 339 | if (!$secondFactorWithHighestLoa->hasEqualOrHigherLoaComparedTo($registrantsSecondFactor)) { |
||
| 340 | throw new DomainException("Authority does not have the required LoA to vet the registrant's second factor"); |
||
| 341 | } |
||
| 342 | |||
| 343 | if (!$identityVerified) { |
||
| 344 | throw new DomainException('Will not vet second factor when physical identity has not been verified.'); |
||
| 345 | } |
||
| 346 | |||
| 347 | $registrant->complyWithVettingOfSecondFactor( |
||
| 348 | $registrantsSecondFactorId, |
||
| 349 | $registrantsSecondFactorType, |
||
| 350 | $registrantsSecondFactorIdentifier, |
||
| 351 | $registrationCode, |
||
| 352 | $documentNumber |
||
| 353 | ); |
||
| 354 | } |
||
| 355 | |||
| 356 | public function complyWithVettingOfSecondFactor( |
||
| 357 | SecondFactorId $secondFactorId, |
||
| 358 | SecondFactorType $secondFactorType, |
||
| 359 | SecondFactorIdentifier $secondFactorIdentifier, |
||
| 360 | $registrationCode, |
||
| 361 | DocumentNumber $documentNumber |
||
| 362 | ) { |
||
| 363 | $this->assertNotForgotten(); |
||
| 364 | |||
| 365 | $secondFactorToVet = null; |
||
| 366 | foreach ($this->verifiedSecondFactors as $secondFactor) { |
||
| 367 | /** @var VerifiedSecondFactor $secondFactor */ |
||
| 368 | if ($secondFactor->hasRegistrationCodeAndIdentifier($registrationCode, $secondFactorIdentifier)) { |
||
| 369 | $secondFactorToVet = $secondFactor; |
||
| 370 | } |
||
| 371 | } |
||
| 372 | |||
| 373 | if (!$secondFactorToVet) { |
||
| 374 | throw new DomainException( |
||
| 375 | 'Cannot vet second factor, no verified second factor can be vetted using the given registration code ' . |
||
| 376 | 'and second factor identifier' |
||
| 377 | ); |
||
| 378 | } |
||
| 379 | |||
| 380 | if (!$secondFactorToVet->canBeVettedNow()) { |
||
| 381 | throw new DomainException('Cannot vet second factor, the registration window is closed.'); |
||
| 382 | } |
||
| 383 | |||
| 384 | $secondFactorToVet->vet($documentNumber); |
||
| 385 | } |
||
| 386 | |||
| 387 | public function revokeSecondFactor(SecondFactorId $secondFactorId) |
||
| 388 | { |
||
| 389 | $this->assertNotForgotten(); |
||
| 390 | |||
| 391 | /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */ |
||
| 392 | $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string) $secondFactorId); |
||
| 393 | /** @var VerifiedSecondFactor|null $verifiedSecondFactor */ |
||
| 394 | $verifiedSecondFactor = $this->verifiedSecondFactors->get((string) $secondFactorId); |
||
| 395 | /** @var VettedSecondFactor|null $vettedSecondFactor */ |
||
| 396 | $vettedSecondFactor = $this->vettedSecondFactors->get((string) $secondFactorId); |
||
| 397 | |||
| 398 | if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) { |
||
| 399 | throw new DomainException('Cannot revoke second factor: no second factor with given id exists.'); |
||
| 400 | } |
||
| 401 | |||
| 402 | if ($unverifiedSecondFactor) { |
||
| 403 | $unverifiedSecondFactor->revoke(); |
||
| 404 | |||
| 405 | return; |
||
| 406 | } |
||
| 407 | |||
| 408 | if ($verifiedSecondFactor) { |
||
| 409 | $verifiedSecondFactor->revoke(); |
||
| 410 | |||
| 411 | return; |
||
| 412 | } |
||
| 413 | |||
| 414 | $vettedSecondFactor->revoke(); |
||
| 415 | } |
||
| 416 | |||
| 417 | public function complyWithSecondFactorRevocation(SecondFactorId $secondFactorId, IdentityId $authorityId) |
||
| 418 | { |
||
| 419 | $this->assertNotForgotten(); |
||
| 420 | |||
| 421 | /** @var UnverifiedSecondFactor|null $unverifiedSecondFactor */ |
||
| 422 | $unverifiedSecondFactor = $this->unverifiedSecondFactors->get((string) $secondFactorId); |
||
| 423 | /** @var VerifiedSecondFactor|null $verifiedSecondFactor */ |
||
| 424 | $verifiedSecondFactor = $this->verifiedSecondFactors->get((string) $secondFactorId); |
||
| 425 | /** @var VettedSecondFactor|null $vettedSecondFactor */ |
||
| 426 | $vettedSecondFactor = $this->vettedSecondFactors->get((string) $secondFactorId); |
||
| 427 | |||
| 428 | if (!$unverifiedSecondFactor && !$verifiedSecondFactor && !$vettedSecondFactor) { |
||
| 429 | throw new DomainException('Cannot revoke second factor: no second factor with given id exists.'); |
||
| 430 | } |
||
| 431 | |||
| 432 | if ($unverifiedSecondFactor) { |
||
| 433 | $unverifiedSecondFactor->complyWithRevocation($authorityId); |
||
| 434 | |||
| 435 | return; |
||
| 436 | } |
||
| 437 | |||
| 438 | if ($verifiedSecondFactor) { |
||
| 439 | $verifiedSecondFactor->complyWithRevocation($authorityId); |
||
| 440 | |||
| 441 | return; |
||
| 442 | } |
||
| 443 | |||
| 444 | $vettedSecondFactor->complyWithRevocation($authorityId); |
||
| 445 | } |
||
| 446 | |||
| 447 | /** |
||
| 448 | * @param Institution $institution |
||
| 449 | * @param RegistrationAuthorityRole $role |
||
| 450 | * @param Location $location |
||
| 451 | * @param ContactInformation $contactInformation |
||
| 452 | * @return void |
||
| 453 | */ |
||
| 454 | public function accreditWith( |
||
| 455 | RegistrationAuthorityRole $role, |
||
| 456 | Institution $institution, |
||
| 457 | Location $location, |
||
| 458 | ContactInformation $contactInformation |
||
| 459 | ) { |
||
| 460 | $this->assertNotForgotten(); |
||
| 461 | |||
| 462 | if (!$this->institution->equals($institution)) { |
||
| 463 | throw new DomainException('An Identity may only be accredited within its own institution'); |
||
| 464 | } |
||
| 465 | |||
| 466 | if (!$this->vettedSecondFactors->count()) { |
||
| 467 | throw new DomainException( |
||
| 468 | 'An Identity must have at least one vetted second factor before it can be accredited' |
||
| 469 | ); |
||
| 470 | } |
||
| 471 | |||
| 472 | if ($this->registrationAuthority) { |
||
| 473 | throw new DomainException('Cannot accredit Identity as it has already been accredited'); |
||
| 474 | } |
||
| 475 | |||
| 476 | if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) { |
||
| 477 | $this->apply(new IdentityAccreditedAsRaEvent( |
||
| 478 | $this->id, |
||
| 479 | $this->nameId, |
||
| 480 | $this->institution, |
||
| 481 | $role, |
||
| 482 | $location, |
||
| 483 | $contactInformation |
||
| 484 | )); |
||
| 485 | View Code Duplication | } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) { |
|
| 486 | $this->apply(new IdentityAccreditedAsRaaEvent( |
||
| 487 | $this->id, |
||
| 488 | $this->nameId, |
||
| 489 | $this->institution, |
||
| 490 | $role, |
||
| 491 | $location, |
||
| 492 | $contactInformation |
||
| 493 | )); |
||
| 494 | } else { |
||
| 495 | throw new DomainException('An Identity can only be accredited with either the RA or RAA role'); |
||
| 496 | } |
||
| 497 | } |
||
| 498 | |||
| 499 | public function amendRegistrationAuthorityInformation(Location $location, ContactInformation $contactInformation) |
||
| 500 | { |
||
| 501 | $this->assertNotForgotten(); |
||
| 502 | |||
| 503 | if (!$this->registrationAuthority) { |
||
| 504 | throw new DomainException( |
||
| 505 | 'Cannot amend registration authority information: identity is not a registration authority' |
||
| 506 | ); |
||
| 507 | } |
||
| 508 | |||
| 509 | $this->apply( |
||
| 510 | new RegistrationAuthorityInformationAmendedEvent( |
||
| 511 | $this->id, |
||
| 512 | $this->institution, |
||
| 513 | $this->nameId, |
||
| 514 | $location, |
||
| 515 | $contactInformation |
||
| 516 | ) |
||
| 517 | ); |
||
| 518 | } |
||
| 519 | |||
| 520 | public function appointAs(RegistrationAuthorityRole $role) |
||
| 521 | { |
||
| 522 | $this->assertNotForgotten(); |
||
| 523 | |||
| 524 | if (!$this->registrationAuthority) { |
||
| 525 | throw new DomainException( |
||
| 526 | 'Cannot appoint as different RegistrationAuthorityRole: identity is not a registration authority' |
||
| 527 | ); |
||
| 528 | } |
||
| 529 | |||
| 530 | if ($this->registrationAuthority->isAppointedAs($role)) { |
||
| 531 | return; |
||
| 532 | } |
||
| 533 | |||
| 534 | if ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA))) { |
||
| 535 | $this->apply(new AppointedAsRaEvent($this->id, $this->institution, $this->nameId)); |
||
| 536 | View Code Duplication | } elseif ($role->equals(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA))) { |
|
| 537 | $this->apply(new AppointedAsRaaEvent($this->id, $this->institution, $this->nameId)); |
||
| 538 | } else { |
||
| 539 | throw new DomainException('An Identity can only be appointed as either RA or RAA'); |
||
| 540 | } |
||
| 541 | } |
||
| 542 | |||
| 543 | public function retractRegistrationAuthority() |
||
| 544 | { |
||
| 545 | $this->assertNotForgotten(); |
||
| 546 | |||
| 547 | if (!$this->registrationAuthority) { |
||
| 548 | throw new DomainException( |
||
| 549 | 'Cannot Retract Registration Authority as the Identity is not a registration authority' |
||
| 550 | ); |
||
| 551 | } |
||
| 552 | |||
| 553 | $this->apply(new RegistrationAuthorityRetractedEvent( |
||
| 554 | $this->id, |
||
| 555 | $this->institution, |
||
| 556 | $this->nameId, |
||
| 557 | $this->commonName, |
||
| 558 | $this->email |
||
| 559 | )); |
||
| 560 | } |
||
| 561 | |||
| 562 | public function expressPreferredLocale(Locale $preferredLocale) |
||
| 563 | { |
||
| 564 | $this->assertNotForgotten(); |
||
| 565 | |||
| 566 | if ($this->preferredLocale === $preferredLocale) { |
||
| 567 | return; |
||
| 568 | } |
||
| 569 | |||
| 570 | $this->apply(new LocalePreferenceExpressedEvent($this->id, $this->institution, $preferredLocale)); |
||
| 571 | } |
||
| 572 | |||
| 573 | public function forget() |
||
| 574 | { |
||
| 575 | $this->assertNotForgotten(); |
||
| 576 | |||
| 577 | if ($this->registrationAuthority) { |
||
| 578 | throw new DomainException('Cannot forget an identity that is currently accredited as an RA(A)'); |
||
| 579 | } |
||
| 580 | |||
| 581 | $this->apply(new IdentityForgottenEvent($this->id, $this->institution)); |
||
| 582 | } |
||
| 583 | |||
| 584 | protected function applyIdentityCreatedEvent(IdentityCreatedEvent $event) |
||
| 585 | { |
||
| 586 | $this->id = $event->identityId; |
||
| 587 | $this->institution = $event->identityInstitution; |
||
| 588 | $this->nameId = $event->nameId; |
||
| 589 | $this->commonName = $event->commonName; |
||
| 590 | $this->email = $event->email; |
||
| 591 | $this->preferredLocale = $event->preferredLocale; |
||
| 592 | $this->forgotten = false; |
||
| 593 | |||
| 594 | $this->unverifiedSecondFactors = new SecondFactorCollection(); |
||
| 595 | $this->verifiedSecondFactors = new SecondFactorCollection(); |
||
| 596 | $this->vettedSecondFactors = new SecondFactorCollection(); |
||
| 597 | } |
||
| 598 | |||
| 599 | public function applyIdentityRenamedEvent(IdentityRenamedEvent $event) |
||
| 600 | { |
||
| 601 | $this->commonName = $event->commonName; |
||
| 602 | } |
||
| 603 | |||
| 604 | public function applyIdentityEmailChangedEvent(IdentityEmailChangedEvent $event) |
||
| 605 | { |
||
| 606 | $this->email = $event->email; |
||
| 607 | } |
||
| 608 | |||
| 609 | protected function applyYubikeySecondFactorBootstrappedEvent(YubikeySecondFactorBootstrappedEvent $event) |
||
| 610 | { |
||
| 611 | $secondFactor = VettedSecondFactor::create( |
||
| 612 | $event->secondFactorId, |
||
| 613 | $this, |
||
| 614 | new SecondFactorType('yubikey'), |
||
| 615 | $event->yubikeyPublicId |
||
| 616 | ); |
||
| 617 | |||
| 618 | $this->vettedSecondFactors->set((string) $secondFactor->getId(), $secondFactor); |
||
| 619 | } |
||
| 620 | |||
| 621 | View Code Duplication | protected function applyYubikeyPossessionProvenEvent(YubikeyPossessionProvenEvent $event) |
|
| 622 | { |
||
| 623 | $secondFactor = UnverifiedSecondFactor::create( |
||
| 624 | $event->secondFactorId, |
||
| 625 | $this, |
||
| 626 | new SecondFactorType('yubikey'), |
||
| 627 | $event->yubikeyPublicId, |
||
| 628 | $event->emailVerificationWindow, |
||
| 629 | $event->emailVerificationNonce |
||
| 630 | ); |
||
| 631 | |||
| 632 | $this->unverifiedSecondFactors->set((string) $secondFactor->getId(), $secondFactor); |
||
| 633 | } |
||
| 634 | |||
| 635 | View Code Duplication | protected function applyPhonePossessionProvenEvent(PhonePossessionProvenEvent $event) |
|
| 636 | { |
||
| 637 | $secondFactor = UnverifiedSecondFactor::create( |
||
| 638 | $event->secondFactorId, |
||
| 639 | $this, |
||
| 640 | new SecondFactorType('sms'), |
||
| 641 | $event->phoneNumber, |
||
| 642 | $event->emailVerificationWindow, |
||
| 643 | $event->emailVerificationNonce |
||
| 644 | ); |
||
| 645 | |||
| 646 | $this->unverifiedSecondFactors->set((string) $secondFactor->getId(), $secondFactor); |
||
| 647 | } |
||
| 648 | |||
| 649 | View Code Duplication | protected function applyGssfPossessionProvenEvent(GssfPossessionProvenEvent $event) |
|
| 650 | { |
||
| 651 | $secondFactor = UnverifiedSecondFactor::create( |
||
| 652 | $event->secondFactorId, |
||
| 653 | $this, |
||
| 654 | new SecondFactorType((string) $event->stepupProvider), |
||
| 655 | $event->gssfId, |
||
| 656 | $event->emailVerificationWindow, |
||
| 657 | $event->emailVerificationNonce |
||
| 658 | ); |
||
| 659 | |||
| 660 | $this->unverifiedSecondFactors->set((string) $secondFactor->getId(), $secondFactor); |
||
| 661 | } |
||
| 662 | |||
| 663 | View Code Duplication | protected function applyU2fDevicePossessionProvenEvent(U2fDevicePossessionProvenEvent $event) |
|
| 664 | { |
||
| 665 | $secondFactor = UnverifiedSecondFactor::create( |
||
| 666 | $event->secondFactorId, |
||
| 667 | $this, |
||
| 668 | new SecondFactorType('u2f'), |
||
| 669 | $event->keyHandle, |
||
| 670 | $event->emailVerificationWindow, |
||
| 671 | $event->emailVerificationNonce |
||
| 672 | ); |
||
| 673 | |||
| 674 | $this->unverifiedSecondFactors->set((string) $secondFactor->getId(), $secondFactor); |
||
| 675 | } |
||
| 676 | |||
| 677 | protected function applyEmailVerifiedEvent(EmailVerifiedEvent $event) |
||
| 678 | { |
||
| 679 | $secondFactorId = (string) $event->secondFactorId; |
||
| 680 | |||
| 681 | /** @var UnverifiedSecondFactor $unverified */ |
||
| 682 | $unverified = $this->unverifiedSecondFactors->get($secondFactorId); |
||
| 683 | $verified = $unverified->asVerified($event->registrationRequestedAt, $event->registrationCode); |
||
| 684 | |||
| 685 | $this->unverifiedSecondFactors->remove($secondFactorId); |
||
| 686 | $this->verifiedSecondFactors->set($secondFactorId, $verified); |
||
| 687 | } |
||
| 688 | |||
| 689 | protected function applySecondFactorVettedEvent(SecondFactorVettedEvent $event) |
||
| 690 | { |
||
| 691 | $secondFactorId = (string) $event->secondFactorId; |
||
| 692 | |||
| 693 | /** @var VerifiedSecondFactor $verified */ |
||
| 694 | $verified = $this->verifiedSecondFactors->get($secondFactorId); |
||
| 695 | $vetted = $verified->asVetted(); |
||
| 696 | |||
| 697 | $this->verifiedSecondFactors->remove($secondFactorId); |
||
| 698 | $this->vettedSecondFactors->set($secondFactorId, $vetted); |
||
| 699 | } |
||
| 700 | |||
| 701 | protected function applyUnverifiedSecondFactorRevokedEvent(UnverifiedSecondFactorRevokedEvent $event) |
||
| 702 | { |
||
| 703 | $this->unverifiedSecondFactors->remove((string) $event->secondFactorId); |
||
| 704 | } |
||
| 705 | |||
| 706 | protected function applyCompliedWithUnverifiedSecondFactorRevocationEvent( |
||
| 707 | CompliedWithUnverifiedSecondFactorRevocationEvent $event |
||
| 708 | ) { |
||
| 709 | $this->unverifiedSecondFactors->remove((string) $event->secondFactorId); |
||
| 710 | } |
||
| 711 | |||
| 712 | protected function applyVerifiedSecondFactorRevokedEvent(VerifiedSecondFactorRevokedEvent $event) |
||
| 713 | { |
||
| 714 | $this->verifiedSecondFactors->remove((string) $event->secondFactorId); |
||
| 715 | } |
||
| 716 | |||
| 717 | protected function applyCompliedWithVerifiedSecondFactorRevocationEvent( |
||
| 718 | CompliedWithVerifiedSecondFactorRevocationEvent $event |
||
| 719 | ) { |
||
| 720 | $this->verifiedSecondFactors->remove((string) $event->secondFactorId); |
||
| 721 | } |
||
| 722 | |||
| 723 | protected function applyVettedSecondFactorRevokedEvent(VettedSecondFactorRevokedEvent $event) |
||
| 724 | { |
||
| 725 | $this->vettedSecondFactors->remove((string) $event->secondFactorId); |
||
| 726 | } |
||
| 727 | |||
| 728 | protected function applyCompliedWithVettedSecondFactorRevocationEvent( |
||
| 729 | CompliedWithVettedSecondFactorRevocationEvent $event |
||
| 730 | ) { |
||
| 731 | $this->vettedSecondFactors->remove((string) $event->secondFactorId); |
||
| 732 | } |
||
| 733 | |||
| 734 | protected function applyIdentityAccreditedAsRaEvent(IdentityAccreditedAsRaEvent $event) |
||
| 735 | { |
||
| 736 | $this->registrationAuthority = RegistrationAuthority::accreditWith( |
||
| 737 | $event->registrationAuthorityRole, |
||
| 738 | $event->location, |
||
| 739 | $event->contactInformation |
||
| 740 | ); |
||
| 741 | } |
||
| 742 | |||
| 743 | protected function applyIdentityAccreditedAsRaaEvent(IdentityAccreditedAsRaaEvent $event) |
||
| 744 | { |
||
| 745 | $this->registrationAuthority = RegistrationAuthority::accreditWith( |
||
| 746 | $event->registrationAuthorityRole, |
||
| 747 | $event->location, |
||
| 748 | $event->contactInformation |
||
| 749 | ); |
||
| 750 | } |
||
| 751 | |||
| 752 | protected function applyRegistrationAuthorityInformationAmendedEvent( |
||
| 753 | RegistrationAuthorityInformationAmendedEvent $event |
||
| 754 | ) { |
||
| 755 | $this->registrationAuthority->amendInformation($event->location, $event->contactInformation); |
||
| 756 | } |
||
| 757 | |||
| 758 | protected function applyAppointedAsRaEvent(AppointedAsRaEvent $event) |
||
| 759 | { |
||
| 760 | $this->registrationAuthority->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RA)); |
||
| 761 | } |
||
| 762 | |||
| 763 | protected function applyAppointedAsRaaEvent(AppointedAsRaaEvent $event) |
||
| 764 | { |
||
| 765 | $this->registrationAuthority->appointAs(new RegistrationAuthorityRole(RegistrationAuthorityRole::ROLE_RAA)); |
||
| 766 | } |
||
| 767 | |||
| 768 | protected function applyRegistrationAuthorityRetractedEvent(RegistrationAuthorityRetractedEvent $event) |
||
| 769 | { |
||
| 770 | $this->registrationAuthority = null; |
||
| 771 | } |
||
| 772 | |||
| 773 | protected function applyLocalePreferenceExpressedEvent(LocalePreferenceExpressedEvent $event) |
||
| 774 | { |
||
| 775 | $this->preferredLocale = $event->preferredLocale; |
||
| 776 | } |
||
| 777 | |||
| 778 | protected function applyIdentityForgottenEvent(IdentityForgottenEvent $event) |
||
| 779 | { |
||
| 780 | $this->commonName = CommonName::unknown(); |
||
| 781 | $this->email = Email::unknown(); |
||
| 782 | $this->forgotten = true; |
||
| 783 | } |
||
| 784 | |||
| 785 | public function getAggregateRootId() |
||
| 786 | { |
||
| 787 | return $this->id; |
||
| 788 | } |
||
| 789 | |||
| 790 | protected function getChildEntities() |
||
| 791 | { |
||
| 792 | return array_merge( |
||
| 793 | $this->unverifiedSecondFactors->getValues(), |
||
| 794 | $this->verifiedSecondFactors->getValues(), |
||
| 795 | $this->vettedSecondFactors->getValues() |
||
| 796 | ); |
||
| 797 | } |
||
| 798 | |||
| 799 | /** |
||
| 800 | * @throws DomainException |
||
| 801 | */ |
||
| 802 | private function assertNotForgotten() |
||
| 803 | { |
||
| 804 | if ($this->forgotten) { |
||
| 805 | throw new DomainException('Operation on this Identity is not allowed: it has been forgotten'); |
||
| 806 | } |
||
| 807 | } |
||
| 808 | |||
| 809 | /** |
||
| 810 | * @throws DomainException |
||
| 811 | */ |
||
| 812 | private function assertUserMayAddSecondFactor() |
||
| 813 | { |
||
| 814 | if (count($this->unverifiedSecondFactors) + |
||
| 815 | count($this->verifiedSecondFactors) + |
||
| 816 | count($this->vettedSecondFactors) > 0 |
||
| 817 | ) { |
||
| 818 | throw new DomainException('User may not have more than one token'); |
||
| 819 | } |
||
| 820 | } |
||
| 821 | |||
| 822 | public function getId() |
||
| 823 | { |
||
| 824 | return $this->id; |
||
| 825 | } |
||
| 826 | |||
| 827 | /** |
||
| 828 | * @return NameId |
||
| 829 | */ |
||
| 830 | public function getNameId() |
||
| 831 | { |
||
| 832 | return $this->nameId; |
||
| 833 | } |
||
| 834 | |||
| 835 | /** |
||
| 836 | * @return Institution |
||
| 837 | */ |
||
| 838 | public function getInstitution() |
||
| 839 | { |
||
| 840 | return $this->institution; |
||
| 841 | } |
||
| 842 | |||
| 843 | public function getCommonName() |
||
| 844 | { |
||
| 845 | return $this->commonName; |
||
| 846 | } |
||
| 847 | |||
| 848 | public function getEmail() |
||
| 849 | { |
||
| 850 | return $this->email; |
||
| 851 | } |
||
| 852 | |||
| 853 | public function getPreferredLocale() |
||
| 854 | { |
||
| 855 | return $this->preferredLocale; |
||
| 856 | } |
||
| 857 | |||
| 858 | /** |
||
| 859 | * @param SecondFactorId $secondFactorId |
||
| 860 | * @return VerifiedSecondFactor|null |
||
| 861 | */ |
||
| 862 | public function getVerifiedSecondFactor(SecondFactorId $secondFactorId) |
||
| 863 | { |
||
| 864 | return $this->verifiedSecondFactors->get((string) $secondFactorId); |
||
| 865 | } |
||
| 866 | } |
||
| 867 |
Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.