SmsRecoveryTokenService   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 56
c 3
b 0
f 0
dl 0
loc 136
rs 10
wmc 20

13 Methods

Rating   Name   Duplication   Size   Complexity  
A forgetTokenCreatedDuringSecondFactorRegistration() 0 3 1
A wasTokenCreatedDuringSecondFactorRegistration() 0 3 1
A tokenCreatedDuringSecondFactorRegistration() 0 3 1
A sendChallenge() 0 15 1
A __construct() 0 6 1
A forgetRecoveryTokenState() 0 3 1
A getMaximumOtpRequestsCount() 0 3 1
A clearSmsVerificationState() 0 3 1
A provePossession() 0 30 5
A hasSmsVerificationState() 0 3 1
A verifyAuthentication() 0 19 4
A authenticate() 0 10 1
A getOtpRequestsRemainingCount() 0 3 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\StepupBundle\Command\SendRecoveryTokenSmsChallengeCommand as StepupSendRecoveryTokenSmsChallengeCommand;
24
use Surfnet\StepupBundle\Command\SendSmsChallengeCommandInterface;
25
use Surfnet\StepupBundle\Command\VerifyPossessionOfPhoneForRecoveryTokenCommand;
26
use Surfnet\StepupBundle\Service\Exception\TooManyChallengesRequestedException;
27
use Surfnet\StepupBundle\Service\SmsRecoveryTokenServiceInterface;
28
use Surfnet\StepupBundle\Value\PhoneNumber\InternationalPhoneNumber;
29
use Surfnet\StepupBundle\Value\PhoneNumber\PhoneNumber;
30
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\ProvePhoneRecoveryTokenPossessionCommand;
31
use Surfnet\StepupMiddlewareClientBundle\Uuid\Uuid;
32
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SendRecoveryTokenSmsAuthenticationChallengeCommand;
33
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SendRecoveryTokenSmsChallengeCommand;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Command\VerifySmsRecoveryTokenChallengeCommand;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\ProofOfPossessionResult;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupSelfServic...ProofOfPossessionResult 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\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\RecoveryTokenState;
37
use Symfony\Contracts\Translation\TranslatorInterface;
38
39
/**
40
 * Directs the different state keeping for SMS based
41
 * Recovery Tokens. This means it communicates with the
42
 * Middleware via the command service to register RT's,
43
 * Keeps state of SMS verifications (RecoveryTokenState).
44
 * And it tracks the overall registration state of
45
 * a RT via SmsRecoveryTokenServiceInterface
46
 *
47
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
48
 */
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...
49
class SmsRecoveryTokenService
50
{
51
    final public const REGISTRATION_RECOVERY_TOKEN_ID = 'registration';
52
53
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
54
        private readonly SmsRecoveryTokenServiceInterface $smsService,
55
        private readonly TranslatorInterface $translator,
56
        private readonly CommandService $commandService,
57
        private readonly RecoveryTokenState $stateHandler,
58
    ) {
59
    }
60
61
    public function getOtpRequestsRemainingCount(string $identifier): int
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getOtpRequestsRemainingCount()
Loading history...
62
    {
63
        return $this->smsService->getOtpRequestsRemainingCount($identifier);
64
    }
65
66
    public function getMaximumOtpRequestsCount(): int
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function getMaximumOtpRequestsCount()
Loading history...
67
    {
68
        return $this->smsService->getMaximumOtpRequestsCount();
69
    }
70
71
    public function hasSmsVerificationState(string $secondFactorId): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function hasSmsVerificationState()
Loading history...
72
    {
73
        return $this->smsService->hasSmsVerificationState($secondFactorId);
74
    }
75
76
    public function clearSmsVerificationState(string $secondFactorId): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function clearSmsVerificationState()
Loading history...
77
    {
78
        $this->smsService->clearSmsVerificationState($secondFactorId);
79
    }
80
81
    public function authenticate(SendRecoveryTokenSmsAuthenticationChallengeCommand $command): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function authenticate()
Loading history...
82
    {
83
        $stepupCommand = new StepupSendRecoveryTokenSmsChallengeCommand();
84
        $stepupCommand->phoneNumber = $command->identifier;
85
        $stepupCommand->body = $this->translator->trans('ss.registration.sms.challenge_body');
86
        $stepupCommand->identity = $command->identity;
87
        $stepupCommand->institution = $command->institution;
88
        $stepupCommand->recoveryTokenId = $command->recoveryTokenId;
89
90
        return $this->smsService->sendChallenge($stepupCommand);
91
    }
92
93
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $command should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
94
     * @return bool Whether SMS sending did not fail.
95
     * @throws TooManyChallengesRequestedException
96
     */
97
    public function sendChallenge(SendRecoveryTokenSmsChallengeCommand|SendSmsChallengeCommandInterface $command): bool
98
    {
99
        $phoneNumber = new InternationalPhoneNumber(
100
            $command->country->getCountryCode(),
101
            new PhoneNumber($command->subscriber)
102
        );
103
104
        $stepupCommand = new StepupSendRecoveryTokenSmsChallengeCommand();
105
        $stepupCommand->phoneNumber = $phoneNumber;
106
        $stepupCommand->body = $this->translator->trans('ss.registration.sms.challenge_body');
107
        $stepupCommand->identity = $command->identity;
108
        $stepupCommand->institution = $command->institution;
109
        $stepupCommand->recoveryTokenId = $command->recoveryTokenId;
110
111
        return $this->smsService->sendChallenge($stepupCommand);
112
    }
113
114
    public function provePossession(VerifySmsRecoveryTokenChallengeCommand $challengeCommand): ProofOfPossessionResult
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function provePossession()
Loading history...
115
    {
116
        $stepupCommand = new VerifyPossessionOfPhoneForRecoveryTokenCommand();
117
        $stepupCommand->challenge = $challengeCommand->challenge;
118
        $stepupCommand->recoveryTokenId = $challengeCommand->recoveryTokenId;
119
120
        $verification = $this->smsService->verifyPossession($stepupCommand);
121
122
        if ($verification->didOtpExpire()) {
123
            return ProofOfPossessionResult::challengeExpired();
124
        }
125
        if ($verification->wasAttemptedTooManyTimes()) {
126
            return ProofOfPossessionResult::tooManyAttempts();
127
        }
128
        if (!$verification->wasSuccessful()) {
129
            return ProofOfPossessionResult::incorrectChallenge();
130
        }
131
132
        $command = new ProvePhoneRecoveryTokenPossessionCommand();
133
        $command->identityId = $challengeCommand->identity;
134
        $command->recoveryTokenId = Uuid::generate();
135
        $command->phoneNumber = $verification->getPhoneNumber();
136
137
        $result = $this->commandService->execute($command);
138
139
        if (!$result->isSuccessful()) {
140
            return ProofOfPossessionResult::proofOfPossessionCommandFailed();
141
        }
142
143
        return ProofOfPossessionResult::recoveryTokenCreated($command->recoveryTokenId);
144
    }
145
146
    public function wasTokenCreatedDuringSecondFactorRegistration(): bool
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function wasTokenCreatedDuringSecondFactorRegistration()
Loading history...
147
    {
148
        return $this->stateHandler->wasRecoveryTokenCreatedDuringSecondFactorRegistration();
149
    }
150
151
    public function forgetTokenCreatedDuringSecondFactorRegistration(): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function forgetTokenCreatedDuringSecondFactorRegistration()
Loading history...
152
    {
153
        $this->stateHandler->forgetTokenCreatedDuringSecondFactorRegistration();
154
    }
155
156
    public function tokenCreatedDuringSecondFactorRegistration(): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function tokenCreatedDuringSecondFactorRegistration()
Loading history...
157
    {
158
        $this->stateHandler->tokenCreatedDuringSecondFactorRegistration();
159
    }
160
161
    public function forgetRecoveryTokenState(): void
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function forgetRecoveryTokenState()
Loading history...
162
    {
163
        $this->stateHandler->forget();
164
    }
165
166
    public function verifyAuthentication(VerifySmsRecoveryTokenChallengeCommand $command): ProofOfPossessionResult
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function verifyAuthentication()
Loading history...
167
    {
168
        $stepupCommand = new VerifyPossessionOfPhoneForRecoveryTokenCommand();
169
        $stepupCommand->challenge = $command->challenge;
170
        $stepupCommand->recoveryTokenId = $command->recoveryTokenId;
171
172
        $verification = $this->smsService->verifyPossession($stepupCommand);
173
174
        if ($verification->didOtpExpire()) {
175
            return ProofOfPossessionResult::challengeExpired();
176
        }
177
        if ($verification->wasAttemptedTooManyTimes()) {
178
            return ProofOfPossessionResult::tooManyAttempts();
179
        }
180
        if (!$verification->wasSuccessful()) {
181
            return ProofOfPossessionResult::incorrectChallenge();
182
        }
183
184
        return ProofOfPossessionResult::recoveryTokenVerified();
185
    }
186
}
187