IdentityCommandHandler::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 10
dl 0
loc 12
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\StepupMiddleware\CommandHandlingBundle\Identity\CommandHandler;
20
21
use Broadway\CommandHandling\SimpleCommandHandler;
22
use Broadway\Repository\Repository as RepositoryInterface;
23
use Surfnet\Stepup\Configuration\Value\Institution as ConfigurationInstitution;
24
use Surfnet\Stepup\Helper\RecoveryTokenSecretHelper;
25
use Surfnet\Stepup\Helper\SecondFactorProvePossessionHelper;
26
use Surfnet\Stepup\Identity\Api\Identity as IdentityApi;
27
use Surfnet\Stepup\Identity\Entity\ConfigurableSettings;
28
use Surfnet\Stepup\Identity\Identity;
29
use Surfnet\Stepup\Identity\Value\CommonName;
30
use Surfnet\Stepup\Identity\Value\DocumentNumber;
31
use Surfnet\Stepup\Identity\Value\Email;
32
use Surfnet\Stepup\Identity\Value\GssfId;
33
use Surfnet\Stepup\Identity\Value\IdentityId;
34
use Surfnet\Stepup\Identity\Value\Institution;
35
use Surfnet\Stepup\Identity\Value\Locale;
0 ignored issues
show
Bug introduced by
The type Surfnet\Stepup\Identity\Value\Locale was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
use Surfnet\Stepup\Identity\Value\NameId;
37
use Surfnet\Stepup\Identity\Value\PhoneNumber;
38
use Surfnet\Stepup\Identity\Value\RecoveryTokenId;
39
use Surfnet\Stepup\Identity\Value\SafeStore;
40
use Surfnet\Stepup\Identity\Value\SecondFactorId;
41
use Surfnet\Stepup\Identity\Value\SecondFactorIdentifierFactory;
42
use Surfnet\Stepup\Identity\Value\StepupProvider;
43
use Surfnet\Stepup\Identity\Value\U2fKeyHandle;
44
use Surfnet\Stepup\Identity\Value\UnhashedSecret;
45
use Surfnet\Stepup\Identity\Value\YubikeyPublicId;
46
use Surfnet\StepupBundle\Service\LoaResolutionService;
47
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
48
use Surfnet\StepupBundle\Value\SecondFactorType;
49
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Entity\InstitutionConfigurationOptions;
50
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\AllowedSecondFactorListService;
51
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Service\InstitutionConfigurationOptionsService;
52
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\IdentityRepository;
53
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\RuntimeException;
54
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\SecondFactorNotAllowedException;
55
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\UnknownLoaException;
56
use Surfnet\StepupMiddleware\CommandHandlingBundle\Exception\UnsupportedLocaleException;
57
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\BootstrapIdentityWithYubikeySecondFactorCommand;
58
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\CreateIdentityCommand;
59
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ExpressLocalePreferenceCommand;
60
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\MigrateVettedSecondFactorCommand;
61
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\PromiseSafeStoreSecretTokenPossessionCommand;
62
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveGssfPossessionCommand;
63
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProvePhonePossessionCommand;
64
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProvePhoneRecoveryTokenPossessionCommand;
65
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveU2fDevicePossessionCommand;
66
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveYubikeyPossessionCommand;
67
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\RegisterSelfAssertedSecondFactorCommand;
68
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\RevokeOwnRecoveryTokenCommand;
69
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\RevokeOwnSecondFactorCommand;
70
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\RevokeRegistrantsRecoveryTokenCommand;
71
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\RevokeRegistrantsSecondFactorCommand;
72
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\SelfVetSecondFactorCommand;
73
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\SendSecondFactorRegistrationEmailCommand;
74
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\UpdateIdentityCommand;
75
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\VerifyEmailCommand;
76
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\VetSecondFactorCommand;
77
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\CommandHandler\Exception\DuplicateIdentityException;
78
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Service\RegistrationMailService;
79
80
/**
81
 * @SuppressWarnings("PHPMD.CouplingBetweenObjects")
82
 * @SuppressWarnings("PHPMD.TooManyMethods")
83
 * @SuppressWarnings("PHPMD.TooManyPublicMethods")
84
 */
85
class IdentityCommandHandler extends SimpleCommandHandler
86
{
87
    /**
88
     * @SuppressWarnings("PHPMD.ExcessiveParameterList")
89
     */
90
    public function __construct(
91
        private readonly RepositoryInterface $eventSourcedRepository,
92
        private readonly IdentityRepository $identityProjectionRepository,
93
        private readonly ConfigurableSettings $configurableSettings,
94
        private readonly AllowedSecondFactorListService $allowedSecondFactorListService,
95
        private readonly SecondFactorTypeService $secondFactorTypeService,
96
        private readonly SecondFactorProvePossessionHelper $provePossessionHelper,
97
        private readonly InstitutionConfigurationOptionsService $institutionConfigurationOptionsService,
98
        private readonly LoaResolutionService $loaResolutionService,
99
        private readonly RecoveryTokenSecretHelper $recoveryTokenSecretHelper,
100
        private readonly RegistrationMailService $registrationMailService,
101
    ) {
102
    }
103
104
    public function handleCreateIdentityCommand(CreateIdentityCommand $command): void
105
    {
106
        $preferredLocale = new Locale($command->preferredLocale);
107
        $this->assertIsValidLocale($preferredLocale);
108
109
        $identity = Identity::create(
110
            new IdentityId($command->id),
111
            new Institution($command->institution),
112
            new NameId($command->nameId),
113
            new CommonName($command->commonName),
114
            new Email($command->email),
115
            $preferredLocale,
116
        );
117
118
        $this->eventSourcedRepository->save($identity);
119
    }
120
121
    public function handleUpdateIdentityCommand(UpdateIdentityCommand $command): void
122
    {
123
        /** @var IdentityApi $identity */
124
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->id));
125
126
        $identity->restore(new CommonName($command->commonName), new Email($command->email));
127
        $identity->rename(new CommonName($command->commonName));
128
        $identity->changeEmail(new Email($command->email));
129
130
        $this->eventSourcedRepository->save($identity);
131
    }
132
133
    public function handleBootstrapIdentityWithYubikeySecondFactorCommand(
134
        BootstrapIdentityWithYubikeySecondFactorCommand $command,
135
    ): void {
136
        $preferredLocale = new Locale($command->preferredLocale);
137
        $this->assertIsValidLocale($preferredLocale);
138
139
        $institution = new Institution($command->institution);
140
        $nameId = new NameId($command->nameId);
141
142
        if ($this->identityProjectionRepository->hasIdentityWithNameIdAndInstitution($nameId, $institution)) {
143
            throw DuplicateIdentityException::forBootstrappingWithYubikeySecondFactor($nameId, $institution);
144
        }
145
146
        $identity = Identity::create(
147
            new IdentityId($command->identityId),
148
            $institution,
149
            $nameId,
150
            new CommonName($command->commonName),
151
            new Email($command->email),
152
            $preferredLocale,
153
        );
154
155
        $configurationInstitution = new ConfigurationInstitution(
156
            (string)$identity->getInstitution(),
157
        );
158
159
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
160
161
        $identity->bootstrapYubikeySecondFactor(
162
            new SecondFactorId($command->secondFactorId),
163
            new YubikeyPublicId($command->yubikeyPublicId),
164
            $tokenCount,
165
        );
166
167
        $this->eventSourcedRepository->save($identity);
168
    }
169
170
    public function handleProveYubikeyPossessionCommand(ProveYubikeyPossessionCommand $command): void
171
    {
172
        /** @var IdentityApi $identity */
173
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
174
175
        $this->assertSecondFactorIsAllowedFor(new SecondFactorType('yubikey'), $identity->getInstitution());
176
177
        $configurationInstitution = new ConfigurationInstitution(
178
            (string)$identity->getInstitution(),
179
        );
180
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
181
182
        $identity->provePossessionOfYubikey(
183
            new SecondFactorId($command->secondFactorId),
184
            new YubikeyPublicId($command->yubikeyPublicId),
185
            $this->emailVerificationIsRequired($identity),
186
            $this->configurableSettings->createNewEmailVerificationWindow(),
187
            $tokenCount,
188
        );
189
190
        $this->eventSourcedRepository->save($identity);
191
    }
192
193
    public function handleProvePhonePossessionCommand(ProvePhonePossessionCommand $command): void
194
    {
195
        /** @var IdentityApi $identity */
196
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
197
198
        $this->assertSecondFactorIsAllowedFor(new SecondFactorType('sms'), $identity->getInstitution());
199
200
        $configurationInstitution = new ConfigurationInstitution(
201
            (string)$identity->getInstitution(),
202
        );
203
204
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
205
206
        $identity->provePossessionOfPhone(
207
            new SecondFactorId($command->secondFactorId),
208
            new PhoneNumber($command->phoneNumber),
209
            $this->emailVerificationIsRequired($identity),
210
            $this->configurableSettings->createNewEmailVerificationWindow(),
211
            $tokenCount,
212
        );
213
214
        $this->eventSourcedRepository->save($identity);
215
    }
216
217
    public function handleProveGssfPossessionCommand(ProveGssfPossessionCommand $command): void
218
    {
219
        /** @var IdentityApi $identity */
220
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
221
        $secondFactorType = $command->stepupProvider;
222
223
        // Validate that the chosen second factor type (stepupProvider) is allowed for the users instituti
224
        $this->assertSecondFactorIsAllowedFor(new SecondFactorType($secondFactorType), $identity->getInstitution());
225
226
        $configurationInstitution = new ConfigurationInstitution(
227
            (string)$identity->getInstitution(),
228
        );
229
230
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
231
232
        $identity->provePossessionOfGssf(
233
            new SecondFactorId($command->secondFactorId),
234
            new StepupProvider($secondFactorType),
235
            new GssfId($command->gssfId),
236
            $this->emailVerificationIsRequired($identity),
237
            $this->configurableSettings->createNewEmailVerificationWindow(),
238
            $tokenCount,
239
        );
240
241
        $this->eventSourcedRepository->save($identity);
242
    }
243
244
    public function handleProveU2fDevicePossessionCommand(ProveU2fDevicePossessionCommand $command): void
245
    {
246
        /** @var IdentityApi $identity */
247
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
248
249
        $this->assertSecondFactorIsAllowedFor(new SecondFactorType('u2f'), $identity->getInstitution());
250
251
        $configurationInstitution = new ConfigurationInstitution(
252
            (string)$identity->getInstitution(),
253
        );
254
255
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
256
257
        $identity->provePossessionOfU2fDevice(
0 ignored issues
show
Deprecated Code introduced by
The function Surfnet\Stepup\Identity\...PossessionOfU2fDevice() has been deprecated: Built in U2F support is dropped from StepUp, this was not removed to support event replay ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

257
        /** @scrutinizer ignore-deprecated */ $identity->provePossessionOfU2fDevice(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
258
            new SecondFactorId($command->secondFactorId),
259
            new U2fKeyHandle($command->keyHandle),
0 ignored issues
show
Deprecated Code introduced by
The class Surfnet\Stepup\Identity\Value\U2fKeyHandle has been deprecated: Built in U2F support is dropped from StepUp, this was not removed to support event replay ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

259
            /** @scrutinizer ignore-deprecated */ new U2fKeyHandle($command->keyHandle),
Loading history...
260
            $this->emailVerificationIsRequired($identity),
261
            $this->configurableSettings->createNewEmailVerificationWindow(),
262
            $tokenCount,
263
        );
264
265
        $this->eventSourcedRepository->save($identity);
266
    }
267
268
    public function handleVerifyEmailCommand(VerifyEmailCommand $command): void
269
    {
270
        /** @var IdentityApi $identity */
271
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
272
273
        $identity->verifyEmail($command->verificationNonce);
274
275
        $this->eventSourcedRepository->save($identity);
276
    }
277
278
279
    public function handleProvePhoneRecoveryTokenPossessionCommand(ProvePhoneRecoveryTokenPossessionCommand $command,): void
280
    {
281
        /** @var IdentityApi $identity */
282
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
283
284
        $this->assertSelfAssertedTokensEnabled($identity->getInstitution());
285
        $identity->provePossessionOfPhoneRecoveryToken(
286
            new RecoveryTokenId($command->recoveryTokenId),
287
            new PhoneNumber($command->phoneNumber),
288
        );
289
290
        $this->eventSourcedRepository->save($identity);
291
    }
292
293
    public function handlePromiseSafeStoreSecretTokenPossessionCommand(
294
        PromiseSafeStoreSecretTokenPossessionCommand $command,
295
    ): void {
296
        /** @var IdentityApi $identity */
297
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
298
299
        $this->assertSelfAssertedTokensEnabled($identity->getInstitution());
300
        $secret = $this->recoveryTokenSecretHelper->hash(new UnhashedSecret($command->secret));
301
        $identity->promisePossessionOfSafeStoreSecretRecoveryToken(
302
            new RecoveryTokenId($command->recoveryTokenId),
303
            new SafeStore($secret),
304
        );
305
306
        $this->eventSourcedRepository->save($identity);
307
    }
308
309
    public function handleVetSecondFactorCommand(VetSecondFactorCommand $command): void
310
    {
311
        /** @var IdentityApi $authority */
312
        $authority = $this->eventSourcedRepository->load(new IdentityId($command->authorityId));
313
        /** @var IdentityApi $registrant */
314
        $registrant = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
315
316
        $secondFactorType = new SecondFactorType($command->secondFactorType);
317
        $secondFactorIdentifier = SecondFactorIdentifierFactory::forType(
318
            $secondFactorType,
319
            $command->secondFactorIdentifier,
320
        );
321
322
        $authority->vetSecondFactor(
323
            $registrant,
324
            new SecondFactorId($command->secondFactorId),
325
            $secondFactorType,
326
            $secondFactorIdentifier,
327
            $command->registrationCode,
328
            new DocumentNumber($command->documentNumber),
329
            $command->identityVerified,
330
            $this->secondFactorTypeService,
331
            $this->provePossessionHelper,
332
            $command->provePossessionSkipped,
333
        );
334
335
        $this->eventSourcedRepository->save($authority);
336
        $this->eventSourcedRepository->save($registrant);
337
    }
338
339
    public function handleRegisterSelfAssertedSecondFactorCommand(RegisterSelfAssertedSecondFactorCommand $command,): void
340
    {
341
        /** @var IdentityApi $identity */
342
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
343
        $secondFactorIdentifier = SecondFactorIdentifierFactory::forType(
344
            new SecondFactorType($command->secondFactorType),
345
            $command->secondFactorIdentifier,
346
        );
347
348
        $identity->registerSelfAssertedSecondFactor(
349
            $secondFactorIdentifier,
350
            $this->secondFactorTypeService,
351
            new RecoveryTokenId($command->authoringRecoveryTokenId),
352
        );
353
354
        $this->eventSourcedRepository->save($identity);
355
    }
356
357
    public function handleSelfVetSecondFactorCommand(SelfVetSecondFactorCommand $command): void
358
    {
359
        /** @var IdentityApi $identity */
360
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
361
        $secondFactorIdentifier = SecondFactorIdentifierFactory::forType(
362
            new SecondFactorType($command->secondFactorType),
363
            $command->secondFactorId,
364
        );
365
366
        $loaIdentifier = null;
367
        // Be backwards compatible for SelfService 3.5, there we misused the `authoringSecondFactorIdentifier` field
368
        // on the SelfVetSecondFactorCommand to pass along the LoA of the authoring second factor token.
369
        // This was repaired in the SAT release of SelfService (4.0 and upwards)
370
        // the field was renamed to `authoringSecondFactorLoa` then.
371
        //
372
        // @todo remove this BC construct once we drop BC support for SelfService 3.5
373
        if ($command->authoringSecondFactorIdentifier) {
374
            $loaIdentifier = $command->authoringSecondFactorIdentifier;
375
        } elseif ($command->authoringSecondFactorLoa !== '' && $command->authoringSecondFactorLoa !== '0') {
376
            $loaIdentifier = $command->authoringSecondFactorLoa;
377
        }
378
        if (!$loaIdentifier) {
379
            throw new UnknownLoaException('The authoring LoA was not configured on the command');
380
        }
381
382
        $loa = $this->loaResolutionService->getLoa($loaIdentifier);
383
        if ($loa === null) {
384
            throw new UnknownLoaException(
385
                sprintf(
386
                    'Authorizing second factor with LoA %s can not be resolved',
387
                    $command->authoringSecondFactorLoa,
388
                ),
389
            );
390
        }
391
392
        $identity->selfVetSecondFactor(
393
            $loa,
394
            $command->registrationCode,
395
            $secondFactorIdentifier,
396
            $this->secondFactorTypeService,
397
        );
398
        $this->eventSourcedRepository->save($identity);
399
    }
400
401
    public function handleMigrateVettedSecondFactorCommand(MigrateVettedSecondFactorCommand $command): void
402
    {
403
        /** @var IdentityApi $sourceIdentity */
404
        $sourceIdentity = $this->eventSourcedRepository->load(new IdentityId($command->sourceIdentityId));
405
        /** @var IdentityApi $targetIdentity */
406
        $targetIdentity = $this->eventSourcedRepository->load(new IdentityId($command->targetIdentityId));
407
408
        // Check if second factor type is allowed by destination institution
409
        $secondFactor = $sourceIdentity->getVettedSecondFactorById(new SecondFactorId($command->sourceSecondFactorId));
410
        $this->assertSecondFactorIsAllowedFor($secondFactor->getType(), $targetIdentity->getInstitution());
411
412
        // Determine the maximum number of allowed tokens for the institution
413
        $configurationInstitution = new ConfigurationInstitution(
414
            (string)$targetIdentity->getInstitution(),
415
        );
416
        $tokenCount = $this->institutionConfigurationOptionsService->getMaxNumberOfTokensFor($configurationInstitution);
417
418
        // move second factor
419
        $targetIdentity->migrateVettedSecondFactor(
420
            $sourceIdentity,
421
            new SecondFactorId($command->sourceSecondFactorId),
422
            $command->targetSecondFactorId,
423
            $tokenCount,
424
        );
425
        $this->eventSourcedRepository->save($targetIdentity);
426
    }
427
428
    public function handleRevokeOwnSecondFactorCommand(RevokeOwnSecondFactorCommand $command): void
429
    {
430
        /** @var IdentityApi $identity */
431
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
432
        $identity->revokeSecondFactor(new SecondFactorId($command->secondFactorId));
433
434
        $this->eventSourcedRepository->save($identity);
435
    }
436
437
    public function handleRevokeRegistrantsSecondFactorCommand(RevokeRegistrantsSecondFactorCommand $command): void
438
    {
439
        /** @var IdentityApi $identity */
440
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
441
        $identity->complyWithSecondFactorRevocation(
442
            new SecondFactorId($command->secondFactorId),
443
            new IdentityId($command->authorityId),
444
        );
445
446
        $this->eventSourcedRepository->save($identity);
447
    }
448
449
    public function handleRevokeOwnRecoveryTokenCommand(RevokeOwnRecoveryTokenCommand $command): void
450
    {
451
        /** @var IdentityApi $identity */
452
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
453
        $identity->revokeRecoveryToken(new RecoveryTokenId($command->recoveryTokenId));
454
455
        $this->eventSourcedRepository->save($identity);
456
    }
457
458
    public function handleRevokeRegistrantsRecoveryTokenCommand(RevokeRegistrantsRecoveryTokenCommand $command): void
459
    {
460
        /** @var IdentityApi $identity */
461
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
462
        $identity->complyWithRecoveryTokenRevocation(
463
            new RecoveryTokenId($command->recoveryTokenId),
464
            new IdentityId($command->authorityId),
465
        );
466
467
        $this->eventSourcedRepository->save($identity);
468
    }
469
470
    public function handleSendSecondFactorRegistrationEmailCommand(SendSecondFactorRegistrationEmailCommand $command,): void
471
    {
472
        $this->registrationMailService->send($command->identityId, $command->secondFactorId);
473
    }
474
475
    public function handleExpressLocalePreferenceCommand(ExpressLocalePreferenceCommand $command): void
476
    {
477
        $preferredLocale = new Locale($command->preferredLocale);
478
        $this->assertIsValidLocale($preferredLocale);
479
480
        /** @var IdentityApi $identity */
481
        $identity = $this->eventSourcedRepository->load(new IdentityId($command->identityId));
482
        $identity->expressPreferredLocale($preferredLocale);
483
484
        $this->eventSourcedRepository->save($identity);
485
    }
486
487
    private function assertIsValidLocale(Locale $locale): void
488
    {
489
        if (!$this->configurableSettings->isSupportedLocale($locale)) {
490
            throw new UnsupportedLocaleException(
491
                sprintf('Given locale "%s" is not a supported locale', (string)$locale),
492
            );
493
        }
494
    }
495
496
    private function assertSecondFactorIsAllowedFor(SecondFactorType $secondFactor, Institution $institution): void
497
    {
498
        $allowedSecondFactorList = $this->allowedSecondFactorListService->getAllowedSecondFactorListFor(
499
            new ConfigurationInstitution($institution->getInstitution()),
500
        );
501
502
        if (!$allowedSecondFactorList->allows($secondFactor)) {
503
            throw new SecondFactorNotAllowedException(
504
                sprintf(
505
                    'Institution "%s" does not support second factor "%s"',
506
                    $institution->getInstitution(),
507
                    $secondFactor->getSecondFactorType(),
508
                ),
509
            );
510
        }
511
    }
512
513
    public function assertSelfAssertedTokensEnabled(Institution $institution): void
514
    {
515
        $configurationInstitution = new ConfigurationInstitution(
516
            (string)$institution,
517
        );
518
519
        $institutionConfiguration = $this->institutionConfigurationOptionsService
520
            ->findInstitutionConfigurationOptionsFor($configurationInstitution);
521
        if (!$institutionConfiguration || !$institutionConfiguration->selfAssertedTokensOption->isEnabled()) {
522
            throw new RuntimeException(
523
                sprintf(
524
                    'Registration of self-asserted tokens is not allowed for this institution "%s".',
525
                    (string)$institution,
526
                ),
527
            );
528
        }
529
    }
530
531
    /**
532
     * @return bool
533
     */
534
    private function emailVerificationIsRequired(IdentityApi $identity): bool
535
    {
536
        $institution = new ConfigurationInstitution(
537
            (string)$identity->getInstitution(),
538
        );
539
540
        $configuration = $this->institutionConfigurationOptionsService
541
            ->findInstitutionConfigurationOptionsFor($institution);
542
543
        if (!$configuration instanceof InstitutionConfigurationOptions) {
544
            return true;
545
        }
546
547
        return $configuration->verifyEmailOption->isEnabled();
548
    }
549
}
550