BootstrapCommandService::provePhonePossession()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
nc 1
nop 3
dl 0
loc 12
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright 2020 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\MiddlewareBundle\Service;
20
21
use Ramsey\Uuid\Uuid;
22
use Surfnet\Stepup\Configuration\Value\Institution as ConfigurationInstitution;
23
use Surfnet\Stepup\Identity\Value\Institution;
24
use Surfnet\Stepup\Identity\Value\NameId;
25
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Entity\InstitutionConfigurationOptions;
26
use Surfnet\StepupMiddleware\ApiBundle\Configuration\Repository\InstitutionConfigurationOptionsRepository;
27
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\Identity;
28
use Surfnet\StepupMiddleware\ApiBundle\Identity\Entity\VettedSecondFactor;
29
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\IdentityRepository;
30
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\UnverifiedSecondFactorRepository;
31
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\VerifiedSecondFactorRepository;
32
use Surfnet\StepupMiddleware\ApiBundle\Identity\Repository\VettedSecondFactorRepository;
33
use Surfnet\StepupMiddleware\CommandHandlingBundle\Command\Metadata;
34
use Surfnet\StepupMiddleware\CommandHandlingBundle\EventSourcing\MetadataEnricher;
35
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\CreateIdentityCommand;
36
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\MigrateVettedSecondFactorCommand as CommandHandlingMigrateSecondFactorCommand;
37
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveGssfPossessionCommand;
38
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProvePhonePossessionCommand;
39
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\ProveYubikeyPossessionCommand;
40
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\VerifyEmailCommand;
41
use Surfnet\StepupMiddleware\CommandHandlingBundle\Identity\Command\VetSecondFactorCommand;
42
use Surfnet\StepupMiddleware\CommandHandlingBundle\Pipeline\Pipeline;
43
use Surfnet\StepupMiddleware\MiddlewareBundle\Exception\InvalidArgumentException;
44
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
45
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
46
47
/**
48
 * @SuppressWarnings("PHPMD.CouplingBetweenObjects")
49
 * @SuppressWarnings("PHPMD.TooManyPublicMethods")
50
 */
51
class BootstrapCommandService
52
{
53
    /**
54
     * @var string[]
55
     */
56
    private array $validRegistrationStatuses = ['unverified', 'verified', 'vetted'];
57
58
    public function __construct(
59
        private readonly Pipeline $pipeline,
60
        private readonly MetadataEnricher $enricher,
61
        private readonly TokenStorageInterface $tokenStorage,
62
        private readonly IdentityRepository $identityRepository,
63
        private readonly UnverifiedSecondFactorRepository $unverifiedSecondFactorRepository,
64
        private readonly VerifiedSecondFactorRepository $verifiedSecondFactorRepository,
65
        private readonly VettedSecondFactorRepository $vettedSecondFactorRepository,
66
        private readonly InstitutionConfigurationOptionsRepository $institutionConfigurationRepository,
67
    ) {
68
    }
69
70
    public function setToken(TokenInterface $token): void
71
    {
72
        $this->tokenStorage->setToken($token);
73
    }
74
75
    public function validRegistrationStatus(string $registrationStatus): void
76
    {
77
        if (!in_array($registrationStatus, $this->validRegistrationStatuses)) {
78
            throw new InvalidArgumentException(
79
                sprintf(
80
                    'Invalid argument provided for the "registration-status" argument. One of: %s is expected. Received: "%s"',
81
                    implode(', ', $this->validRegistrationStatuses),
82
                    $registrationStatus,
83
                ),
84
            );
85
        }
86
    }
87
88
    public function requiresMailVerification(string $institution): bool
89
    {
90
        $configuration = $this->institutionConfigurationRepository->findConfigurationOptionsFor(
91
            new ConfigurationInstitution($institution),
92
        );
93
        if ($configuration instanceof InstitutionConfigurationOptions) {
94
            return $configuration->verifyEmailOption->isEnabled();
95
        }
96
        return true;
97
    }
98
99
    public function vetSecondFactor(
100
        string $tokenType,
101
        string $actorId,
102
        Identity $identity,
103
        string $secondFactorId,
104
        string $secondFactorIdentifier,
105
    ): void {
106
        $verifiedSecondFactor = $this->verifiedSecondFactorRepository->findOneBy(
107
            ['identityId' => $identity->id, 'type' => $tokenType],
108
        );
109
110
        $command = new VetSecondFactorCommand();
111
        $command->UUID = (string)Uuid::uuid4();
112
        $command->authorityId = $actorId;
113
        $command->identityId = $identity->id;
114
        $command->secondFactorId = $secondFactorId;
115
        $command->registrationCode = $verifiedSecondFactor->registrationCode;
116
        $command->secondFactorType = $tokenType;
117
        $command->secondFactorIdentifier = $secondFactorIdentifier;
118
        $command->documentNumber = '123987';
119
        $command->identityVerified = true;
120
121
        $this->pipeline->process($command);
122
    }
123
124
    public function createIdentity(
125
        Institution $institution,
126
        NameId $nameId,
127
        string $commonName,
128
        string $email,
129
        string $preferredLocale,
130
    ): CreateIdentityCommand {
131
        $command = new CreateIdentityCommand();
132
        $command->UUID = (string)Uuid::uuid4();
133
        $command->id = (string)Uuid::uuid4();
134
        $command->institution = $institution->getInstitution();
135
        $command->nameId = $nameId->getNameId();
136
        $command->commonName = $commonName;
137
        $command->email = $email;
138
        $command->preferredLocale = $preferredLocale;
139
140
        $this->pipeline->process($command);
141
142
        return $command;
143
    }
144
145
    public function proveGsspPossession(
146
        string $secondFactorId,
147
        Identity $identity,
148
        string $tokenType,
149
        string $tokenIdentifier
150
    ): void {
151
        $command = new ProveGssfPossessionCommand();
152
        $command->UUID = (string)Uuid::uuid4();
153
        $command->secondFactorId = $secondFactorId;
154
        $command->identityId = $identity->id;
155
        $command->stepupProvider = $tokenType;
156
        $command->gssfId = $tokenIdentifier;
157
158
        $this->pipeline->process($command);
159
    }
160
161
    public function provePhonePossession(
162
        string $secondFactorId,
163
        Identity $identity,
164
        string $phoneNumber
165
    ): void {
166
        $command = new ProvePhonePossessionCommand();
167
        $command->UUID = (string)Uuid::uuid4();
168
        $command->secondFactorId = $secondFactorId;
169
        $command->identityId = $identity->id;
170
        $command->phoneNumber = $phoneNumber;
171
172
        $this->pipeline->process($command);
173
    }
174
175
    public function proveYubikeyPossession(
176
        string $secondFactorId,
177
        Identity $identity,
178
        string $yubikeyPublicId
179
    ): void {
180
        $command = new ProveYubikeyPossessionCommand();
181
        $command->UUID = (string)Uuid::uuid4();
182
        $command->secondFactorId = $secondFactorId;
183
        $command->identityId = $identity->id;
184
        $command->yubikeyPublicId = $yubikeyPublicId;
185
186
        $this->pipeline->process($command);
187
    }
188
189
    public function verifyEmail(
190
        Identity $identity,
191
        string $tokenType
192
    ): void {
193
        $unverifiedSecondFactor = $this->unverifiedSecondFactorRepository->findOneBy(
194
            ['identityId' => $identity->id, 'type' => $tokenType],
195
        );
196
197
        $command = new VerifyEmailCommand();
198
        $command->UUID = (string)Uuid::uuid4();
199
        $command->identityId = $identity->id;
200
        $command->verificationNonce = $unverifiedSecondFactor->verificationNonce;
201
202
        $this->pipeline->process($command);
203
    }
204
205
    public function migrateVettedSecondFactor(
206
        Identity $sourceIdentity,
207
        Identity $targetIdentity,
208
        VettedSecondFactor $vettedSecondFactor,
209
    ): void {
210
        $command = new CommandHandlingMigrateSecondFactorCommand();
211
        $command->UUID = (string)Uuid::uuid4();
212
        $command->sourceIdentityId = $sourceIdentity->id;
213
        $command->targetIdentityId = $targetIdentity->id;
214
        $command->sourceSecondFactorId = $vettedSecondFactor->id;
215
        $command->targetSecondFactorId = (string)Uuid::uuid4();
216
217
        $this->pipeline->process($command);
218
    }
219
220
    public function enrichEventMetadata(string $actorId): void
221
    {
222
        $actor = $this->identityRepository->findOneBy(['id' => $actorId]);
223
224
        $metadata = new Metadata();
225
        $metadata->actorId = $actor->id;
226
        $metadata->actorInstitution = $actor->institution;
227
        $this->enricher->setMetadata($metadata);
228
    }
229
230
    public function getIdentity(NameId $nameId, Institution $institution): Identity
231
    {
232
        return $this->identityRepository->findOneByNameIdAndInstitution($nameId, $institution);
233
    }
234
235
    public function getIdentityByNameId(NameId $nameId): ?Identity
236
    {
237
        return $this->identityRepository->findOneByNameId($nameId);
238
    }
239
240
    public function identityExists(NameId $nameId, Institution $institution): bool
241
    {
242
        return $this->identityRepository->hasIdentityWithNameIdAndInstitution($nameId, $institution);
243
    }
244
245
    /**
246
     * @return array|VettedSecondFactor[]
247
     */
248
    public function getVettedSecondFactorsFromIdentity(Identity $identity): array
249
    {
250
        return $this->vettedSecondFactorRepository->findBy(['identityId' => $identity->id]);
251
    }
252
}
253