SecondFactorService   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 90
c 1
b 0
f 0
dl 0
loc 215
rs 10
wmc 23

17 Methods

Rating   Name   Duplication   Size   Complexity  
A filterAvailableSecondFactors() 0 9 3
A __construct() 0 2 1
A findVettedByIdentity() 0 4 1
A revoke() 0 12 1
A verifyEmail() 0 9 1
A doSecondFactorsExistForIdentity() 0 9 1
A findUnverifiedByVerificationNonce() 0 14 1
A findUnverifiedByIdentity() 0 4 1
A registerSelfAssertedToken() 0 12 1
A selfVet() 0 13 1
A findOneUnverified() 0 3 1
A findOneVetted() 0 3 1
A findOneVerified() 0 3 1
A determineAvailable() 0 9 1
A getSecondFactorsForIdentity() 0 27 2
A identityHasSecondFactorOfStateWithId() 0 20 4
A findVerifiedByIdentity() 0 5 1
1
<?php
2
3
declare(strict_types = 1);
4
5
/**
6
 * Copyright 2014 SURFnet bv
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
Coding Style introduced by
Missing @link tag in file comment
Loading history...
20
21
namespace Surfnet\StepupSelfService\SelfServiceBundle\Service;
22
23
use Surfnet\StepupMiddlewareClient\Identity\Dto\UnverifiedSecondFactorSearchQuery;
24
use Surfnet\StepupMiddlewareClient\Identity\Dto\VerifiedSecondFactorOfIdentitySearchQuery;
25
use Surfnet\StepupMiddlewareClient\Identity\Dto\VettedSecondFactorSearchQuery;
26
use Surfnet\StepupMiddlewareClientBundle\Dto\CollectionDto;
27
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\RegisterSelfAssertedSecondFactorCommand;
28
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\SelfVetSecondFactorCommand;
29
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\RevokeOwnSecondFactorCommand;
30
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\VerifyEmailCommand;
31
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\UnverifiedSecondFactor;
32
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\UnverifiedSecondFactorCollection;
33
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VerifiedSecondFactor;
34
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VerifiedSecondFactorCollection;
35
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VettedSecondFactor;
36
use Surfnet\StepupMiddlewareClientBundle\Identity\Dto\VettedSecondFactorCollection;
37
use Surfnet\StepupMiddlewareClientBundle\Identity\Service\SecondFactorService as MiddlewareSecondFactorService;
38
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SelfAssertedTokenRegistrationCommand;
39
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SelfVetCommand;
40
use Surfnet\StepupSelfService\SelfServiceBundle\Command\RevokeCommand;
41
use Surfnet\StepupSelfService\SelfServiceBundle\Exception\LogicException;
42
43
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
44
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
45
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
46
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @package tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
47
class SecondFactorService
48
{
49
    public function __construct(private readonly MiddlewareSecondFactorService $secondFactors, private readonly CommandService $commandService)
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
50
    {
51
    }
52
53
    public function verifyEmail(string $identityId, string $nonce): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function verifyEmail()
Loading history...
54
    {
55
        $command = new VerifyEmailCommand();
56
        $command->identityId = $identityId;
57
        $command->verificationNonce = $nonce;
58
59
        $result = $this->commandService->execute($command);
60
61
        return $result->isSuccessful();
62
    }
63
64
    public function revoke(RevokeCommand $command): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function revoke()
Loading history...
65
    {
66
        /** @var UnverifiedSecondFactor|VerifiedSecondFactor|VettedSecondFactor $secondFactor */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
67
        $secondFactor = $command->secondFactor;
68
69
        $apiCommand = new RevokeOwnSecondFactorCommand();
70
        $apiCommand->identityId = $command->identity->id;
71
        $apiCommand->secondFactorId = $secondFactor->id;
72
73
        $result = $this->commandService->execute($apiCommand);
74
75
        return $result->isSuccessful();
76
    }
77
78
    public function selfVet(SelfVetCommand $command): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function selfVet()
Loading history...
79
    {
80
        $apiCommand = new SelfVetSecondFactorCommand();
81
        $apiCommand->identityId = $command->identity->id;
82
        $apiCommand->registrationCode = $command->secondFactor->registrationCode;
83
        $apiCommand->secondFactorIdentifier = $command->secondFactor->id;
84
        $apiCommand->secondFactorId = $command->secondFactor->secondFactorIdentifier;
85
        $apiCommand->secondFactorType = $command->secondFactor->type;
86
        $apiCommand->authorityId = $command->identity->id;
87
        $apiCommand->authoringSecondFactorIdentifier = $command->authoringLoa;
88
89
        $result = $this->commandService->execute($apiCommand);
90
        return $result->isSuccessful();
91
    }
92
93
    public function registerSelfAssertedToken(SelfAssertedTokenRegistrationCommand $command): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function registerSelfAssertedToken()
Loading history...
94
    {
95
        $apiCommand = new RegisterSelfAssertedSecondFactorCommand();
96
        $apiCommand->identityId = $command->identity->id;
97
        $apiCommand->registrationCode = $command->secondFactor->registrationCode;
98
        $apiCommand->secondFactorIdentifier = $command->secondFactor->secondFactorIdentifier;
99
        $apiCommand->secondFactorId = $command->secondFactor->id;
100
        $apiCommand->secondFactorType = $command->secondFactor->type;
101
        $apiCommand->authorityId = $command->identity->id;
102
        $apiCommand->authoringRecoveryTokenId = $command->recoveryTokenId;
103
104
        return $this->commandService->execute($apiCommand)->isSuccessful();
105
    }
106
107
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $identityId should have a doc-comment as per coding-style.
Loading history...
108
     * Returns whether the given registrant has registered second factors with Step-up. The state of the second factor
109
     * is irrelevant.
110
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
111
    public function doSecondFactorsExistForIdentity(string $identityId): bool
112
    {
113
        $unverifiedSecondFactors = $this->findUnverifiedByIdentity($identityId);
114
        $verifiedSecondFactors = $this->findVerifiedByIdentity($identityId);
115
        $vettedSecondFactors = $this->findVettedByIdentity($identityId);
116
117
        return $unverifiedSecondFactors->getTotalItems() +
118
            $verifiedSecondFactors->getTotalItems() +
119
            $vettedSecondFactors->getTotalItems() > 0;
120
    }
121
122
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $identityId should have a doc-comment as per coding-style.
Loading history...
123
     * Returns the given registrant's unverified second factors.
124
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
125
    public function findUnverifiedByIdentity(string $identityId): ?UnverifiedSecondFactorCollection
126
    {
127
        return $this->secondFactors->searchUnverified(
128
            (new UnverifiedSecondFactorSearchQuery())->setIdentityId($identityId),
129
        );
130
    }
131
132
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $identityId should have a doc-comment as per coding-style.
Loading history...
133
     * Returns the given registrant's verified second factors.
134
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
135
    public function findVerifiedByIdentity(string $identityId): ?VerifiedSecondFactorCollection
136
    {
137
        $query = new VerifiedSecondFactorOfIdentitySearchQuery();
138
        $query->setIdentityId($identityId);
139
        return $this->secondFactors->searchOwnVerified($query);
140
    }
141
142
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $identityId should have a doc-comment as per coding-style.
Loading history...
143
     * Returns the given registrant's verified second factors.
144
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
145
    public function findVettedByIdentity(string $identityId): ?VettedSecondFactorCollection
146
    {
147
        return $this->secondFactors->searchVetted(
148
            (new VettedSecondFactorSearchQuery())->setIdentityId($identityId),
149
        );
150
    }
151
152
    public function identityHasSecondFactorOfStateWithId(string $identityId, $state, $secondFactorId): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function identityHasSecondFactorOfStateWithId()
Loading history...
153
    {
154
        $secondFactors = match ($state) {
155
            'unverified' => $this->findUnverifiedByIdentity($identityId),
156
            'verified' => $this->findVerifiedByIdentity($identityId),
157
            'vetted' => $this->findVettedByIdentity($identityId),
158
            default => throw new LogicException(sprintf('Invalid second factor state "%s" given.', $state)),
159
        };
160
161
        if ($secondFactors->getElements() === []) {
162
            return false;
163
        }
164
165
        foreach ($secondFactors->getElements() as $secondFactor) {
166
            if ($secondFactor->id === $secondFactorId) {
167
                return true;
168
            }
169
        }
170
171
        return false;
172
    }
173
174
    public function findOneUnverified(string $secondFactorId): ?UnverifiedSecondFactor
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function findOneUnverified()
Loading history...
175
    {
176
        return $this->secondFactors->getUnverified($secondFactorId);
177
    }
178
179
    public function findOneVerified(string $secondFactorId): ?VerifiedSecondFactor
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function findOneVerified()
Loading history...
180
    {
181
        return $this->secondFactors->getVerified($secondFactorId);
182
    }
183
184
    public function findOneVetted(string $secondFactorId): ?VettedSecondFactor
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function findOneVetted()
Loading history...
185
    {
186
        return $this->secondFactors->getVetted($secondFactorId);
187
    }
188
189
    public function findUnverifiedByVerificationNonce(string $identityId, string $verificationNonce): ?UnverifiedSecondFactor
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function findUnverifiedByVerificationNonce()
Loading history...
190
    {
191
        $secondFactors = $this->secondFactors->searchUnverified(
192
            (new UnverifiedSecondFactorSearchQuery())
193
                ->setIdentityId($identityId)
194
                ->setVerificationNonce($verificationNonce),
195
        );
196
197
        $elements = $secondFactors->getElements();
198
199
        return match (count($elements)) {
200
            0 => null,
201
            1 => reset($elements),
202
            default => throw new LogicException('There cannot be more than one unverified second factor with the same nonce'),
203
        };
204
    }
205
206
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
207
     * @param $identity
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
208
     * @param array $allSecondFactors
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
209
     * @param $allowedSecondFactors
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
210
     * @param $maximumNumberOfRegistrations
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
211
     * @return SecondFactorTypeCollection
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
212
     */
213
    public function getSecondFactorsForIdentity(
214
        $identity,
215
        array $allSecondFactors,
216
        $allowedSecondFactors,
217
        $maximumNumberOfRegistrations,
218
    ): SecondFactorTypeCollection {
219
        $unverified = $this->findUnverifiedByIdentity($identity->id);
220
        $verified = $this->findVerifiedByIdentity($identity->id);
221
        $vetted = $this->findVettedByIdentity($identity->id);
222
        // Determine which Second Factors are still available for registration.
223
        $available = $this->determineAvailable($allSecondFactors, $unverified, $verified, $vetted);
0 ignored issues
show
Bug introduced by
It seems like $vetted can also be of type null; however, parameter $vettedCollection of Surfnet\StepupSelfServic...e::determineAvailable() does only seem to accept Surfnet\StepupMiddleware...dSecondFactorCollection, maybe add an additional type check? ( Ignorable by Annotation )

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

223
        $available = $this->determineAvailable($allSecondFactors, $unverified, $verified, /** @scrutinizer ignore-type */ $vetted);
Loading history...
Bug introduced by
It seems like $verified can also be of type null; however, parameter $verifiedCollection of Surfnet\StepupSelfServic...e::determineAvailable() does only seem to accept Surfnet\StepupMiddleware...dSecondFactorCollection, maybe add an additional type check? ( Ignorable by Annotation )

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

223
        $available = $this->determineAvailable($allSecondFactors, $unverified, /** @scrutinizer ignore-type */ $verified, $vetted);
Loading history...
Bug introduced by
It seems like $unverified can also be of type null; however, parameter $unverifiedCollection of Surfnet\StepupSelfServic...e::determineAvailable() does only seem to accept Surfnet\StepupMiddleware...dSecondFactorCollection, maybe add an additional type check? ( Ignorable by Annotation )

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

223
        $available = $this->determineAvailable($allSecondFactors, /** @scrutinizer ignore-type */ $unverified, $verified, $vetted);
Loading history...
224
225
        if (!empty($allowedSecondFactors)) {
226
            $available = array_intersect(
227
                $available,
228
                $allowedSecondFactors,
229
            );
230
        }
231
232
        $collection = new SecondFactorTypeCollection();
233
        $collection->unverified = $unverified;
234
        $collection->verified = $verified;
235
        $collection->vetted = $vetted;
236
        $collection->available = array_combine($available, $available);
237
        $collection->maxNumberOfRegistrations = $maximumNumberOfRegistrations;
238
239
        return $collection;
240
    }
241
242
    private function determineAvailable(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function determineAvailable()
Loading history...
Coding Style introduced by
Private method name "SecondFactorService::determineAvailable" must be prefixed with an underscore
Loading history...
243
        array                            $allSecondFactors,
244
        UnverifiedSecondFactorCollection $unverifiedCollection,
245
        VerifiedSecondFactorCollection   $verifiedCollection,
246
        VettedSecondFactorCollection     $vettedCollection,
247
    ): array {
248
        $allSecondFactors = $this->filterAvailableSecondFactors($allSecondFactors, $unverifiedCollection);
249
        $allSecondFactors = $this->filterAvailableSecondFactors($allSecondFactors, $verifiedCollection);
250
        return $this->filterAvailableSecondFactors($allSecondFactors, $vettedCollection);
251
    }
252
253
    private function filterAvailableSecondFactors(array $allSecondFactors, CollectionDto $collection): array
0 ignored issues
show
Coding Style introduced by
Private method name "SecondFactorService::filterAvailableSecondFactors" must be prefixed with an underscore
Loading history...
Coding Style introduced by
Missing doc comment for function filterAvailableSecondFactors()
Loading history...
254
    {
255
        foreach ($collection->getElements() as $secondFactor) {
256
            $keyFound = array_search($secondFactor->type, $allSecondFactors);
257
            if (is_numeric($keyFound)) {
258
                unset($allSecondFactors[$keyFound]);
259
            }
260
        }
261
        return $allSecondFactors;
262
    }
263
}
264