Completed
Pull Request — develop (#108)
by Boy
07:37 queued 03:56
created

VettingService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 19
rs 9.4285
cc 1
eloc 17
nc 1
nop 8

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\StepupRa\RaBundle\Service;
20
21
use Surfnet\StepupBundle\Command\SendSmsChallengeCommand;
22
use Surfnet\StepupBundle\Command\VerifyPossessionOfPhoneCommand;
23
use Surfnet\StepupBundle\Service\SmsSecondFactor\OtpVerification;
24
use Surfnet\StepupBundle\Service\SmsSecondFactorService;
25
use Surfnet\StepupBundle\Value\PhoneNumber\InternationalPhoneNumber;
26
use Surfnet\StepupBundle\Value\SecondFactorType;
27
use Surfnet\StepupMiddlewareClientBundle\Identity\Command\VetSecondFactorCommand;
28
use Surfnet\StepupRa\RaBundle\Command\CreateU2fSignRequestCommand;
29
use Surfnet\StepupRa\RaBundle\Command\StartVettingProcedureCommand;
30
use Surfnet\StepupRa\RaBundle\Command\VerifyIdentityCommand;
31
use Surfnet\StepupRa\RaBundle\Command\VerifyU2fAuthenticationCommand;
32
use Surfnet\StepupRa\RaBundle\Command\VerifyYubikeyPublicIdCommand;
33
use Surfnet\StepupRa\RaBundle\Exception\DomainException;
34
use Surfnet\StepupRa\RaBundle\Exception\InvalidArgumentException;
35
use Surfnet\StepupRa\RaBundle\Exception\LoaTooLowException;
36
use Surfnet\StepupRa\RaBundle\Exception\UnknownVettingProcedureException;
37
use Surfnet\StepupRa\RaBundle\Repository\VettingProcedureRepository;
38
use Surfnet\StepupRa\RaBundle\Service\Gssf\VerificationResult as GssfVerificationResult;
39
use Surfnet\StepupRa\RaBundle\Service\U2f\AuthenticationVerificationResult;
40
use Surfnet\StepupRa\RaBundle\Service\U2f\SignRequestCreationResult;
41
use Surfnet\StepupRa\RaBundle\VettingProcedure;
42
use Surfnet\StepupU2fBundle\Dto\SignRequest;
43
use Surfnet\StepupU2fBundle\Dto\SignResponse;
44
use Symfony\Component\Translation\TranslatorInterface;
45
46
/**
47
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
48
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
49
 */
50
class VettingService
51
{
52
    /**
53
     * @var \Surfnet\StepupBundle\Service\SmsSecondFactorService
54
     */
55
    private $smsSecondFactorService;
56
57
    /**
58
     * @var \Surfnet\StepupRa\RaBundle\Service\YubikeySecondFactorService
59
     */
60
    private $yubikeySecondFactorService;
61
62
    /**
63
     * @var \Surfnet\StepupRa\RaBundle\Service\GssfService
64
     */
65
    private $gssfService;
66
67
    /**
68
     * @var \Surfnet\StepupRa\RaBundle\Service\U2fService
69
     */
70
    private $u2fService;
71
72
    /**
73
     * @var \Surfnet\StepupRa\RaBundle\Service\CommandService
74
     */
75
    private $commandService;
76
77
    /**
78
     * @var \Surfnet\StepupRa\RaBundle\Repository\VettingProcedureRepository
79
     */
80
    private $vettingProcedureRepository;
81
82
    /**
83
     * @var \Symfony\Component\Translation\TranslatorInterface
84
     */
85
    private $translator;
86
87
    /**
88
     * @var \Surfnet\StepupRa\RaBundle\Service\IdentityService
89
     */
90
    private $identityService;
91
92
    public function __construct(
93
        SmsSecondFactorService $smsSecondFactorService,
94
        YubikeySecondFactorService $yubikeySecondFactorService,
95
        GssfService $gssfService,
96
        U2fService $u2fService,
97
        CommandService $commandService,
98
        VettingProcedureRepository $vettingProcedureRepository,
99
        TranslatorInterface $translator,
100
        IdentityService $identityService
101
    ) {
102
        $this->smsSecondFactorService = $smsSecondFactorService;
103
        $this->yubikeySecondFactorService = $yubikeySecondFactorService;
104
        $this->gssfService = $gssfService;
105
        $this->u2fService = $u2fService;
106
        $this->commandService = $commandService;
107
        $this->vettingProcedureRepository = $vettingProcedureRepository;
108
        $this->translator = $translator;
109
        $this->identityService = $identityService;
110
    }
111
112
    /**
113
     * @param StartVettingProcedureCommand $command
114
     * @return bool
115
     */
116
    public function isLoaSufficientToStartProcedure(StartVettingProcedureCommand $command)
117
    {
118
        $secondFactorType = new SecondFactorType($command->secondFactor->type);
119
120
        return $secondFactorType->isSatisfiedBy($command->authorityLoa);
121
    }
122
123
    /**
124
     * @param StartVettingProcedureCommand $command
125
     * @return string The procedure ID.
126
     */
127
    public function startProcedure(StartVettingProcedureCommand $command)
128
    {
129
        $this->smsSecondFactorService->clearSmsVerificationState();
130
131
        if (!$this->isLoaSufficientToStartProcedure($command)) {
132
            throw new LoaTooLowException(
133
                sprintf(
134
                    "Registration authority has LoA '%s', which is not enough to allow vetting of a '%s' second factor",
135
                    $command->authorityLoa,
136
                    $command->secondFactor->type
137
                )
138
            );
139
        }
140
141
        $procedure = VettingProcedure::start(
142
            $command->secondFactor->id,
143
            $command->authorityId,
144
            $command->registrationCode,
145
            $command->secondFactor
146
        );
147
148
        $this->vettingProcedureRepository->store($procedure);
149
150
        return $procedure->getId();
151
    }
152
153
    /**
154
     * @param string $procedureId
155
     * @throws UnknownVettingProcedureException
156
     */
157 View Code Duplication
    public function cancelProcedure($procedureId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
    {
159
        if (!is_string($procedureId)) {
160
            throw InvalidArgumentException::invalidType('string', 'procedureId', $procedureId);
161
        }
162
163
        $procedure = $this->vettingProcedureRepository->retrieve($procedureId);
164
165
        if (!$procedure) {
166
            throw new UnknownVettingProcedureException(
167
                sprintf("No vetting procedure with id '%s' is known.", $procedureId)
168
            );
169
        }
170
171
        $this->vettingProcedureRepository->remove($procedureId);
172
    }
173
174
    /**
175
     * @return int
176
     */
177
    public function getSmsOtpRequestsRemainingCount()
178
    {
179
        return $this->smsSecondFactorService->getOtpRequestsRemainingCount();
180
    }
181
182
    /**
183
     * @return int
184
     */
185
    public function getSmsMaximumOtpRequestsCount()
186
    {
187
        return $this->smsSecondFactorService->getMaximumOtpRequestsCount();
188
    }
189
190
    /**
191
     * @param string $procedureId
192
     * @param SendSmsChallengeCommand $command
193
     * @return bool
194
     * @throws UnknownVettingProcedureException
195
     * @throws DomainException
196
     */
197
    public function sendSmsChallenge($procedureId, SendSmsChallengeCommand $command)
198
    {
199
        $procedure = $this->getProcedure($procedureId);
200
201
        $phoneNumber = InternationalPhoneNumber::fromStringFormat(
202
            $procedure->getSecondFactor()->secondFactorIdentifier
203
        );
204
205
        $identity = $this->identityService->findById($procedure->getSecondFactor()->identityId);
206
207
        if (!$identity) {
208
            throw new DomainException("Second factor is coupled to an identity that doesn't exist");
209
        }
210
211
        $command->phoneNumber = $phoneNumber;
212
        $command->body        = $this->translator->trans('ra.vetting.sms.challenge_body', [], 'messages', $identity->preferredLocale);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
213
        $command->identity    = $procedure->getSecondFactor()->identityId;
214
        $command->institution = $procedure->getSecondFactor()->institution;
215
216
        return $this->smsSecondFactorService->sendChallenge($command);
217
    }
218
219
    /**
220
     * @param string                   $procedureId
221
     * @param VerifyPossessionOfPhoneCommand $command
222
     * @return OtpVerification
223
     * @throws UnknownVettingProcedureException
224
     * @throws DomainException
225
     */
226
    public function verifyPhoneNumber($procedureId, VerifyPossessionOfPhoneCommand $command)
227
    {
228
        $procedure = $this->getProcedure($procedureId);
229
230
        $verification = $this->smsSecondFactorService->verifyPossession($command);
231
232
        if (!$verification->wasSuccessful()) {
233
            return $verification;
234
        }
235
236
        $procedure->verifySecondFactorIdentifier($verification->getPhoneNumber());
237
        $this->vettingProcedureRepository->store($procedure);
238
239
        return $verification;
240
    }
241
242
    /**
243
     * @param string                       $procedureId
244
     * @param VerifyYubikeyPublicIdCommand $command
245
     * @return YubikeySecondFactor\VerificationResult
246
     */
247
    public function verifyYubikeyPublicId($procedureId, VerifyYubikeyPublicIdCommand $command)
248
    {
249
        $procedure = $this->getProcedure($procedureId);
250
251
        $command->expectedPublicId = $procedure->getSecondFactor()->secondFactorIdentifier;
252
        $command->identityId = $procedure->getSecondFactor()->identityId;
253
        $command->institution = $procedure->getSecondFactor()->institution;
254
255
        $result = $this->yubikeySecondFactorService->verifyYubikeyPublicId($command);
256
257
        if ($result->didPublicIdMatch()) {
258
            $procedure->verifySecondFactorIdentifier($result->getPublicId()->getYubikeyPublicId());
259
260
            $this->vettingProcedureRepository->store($procedure);
261
        }
262
263
        return $result;
264
    }
265
266
    /**
267
     * @param string $procedureId
268
     */
269
    public function startGssfVerification($procedureId)
270
    {
271
        $procedure = $this->getProcedure($procedureId);
272
273
        $this->gssfService->startVerification($procedure->getSecondFactor()->secondFactorIdentifier, $procedureId);
274
    }
275
276
    /**
277
     * @param string $gssfId
278
     * @return GssfVerificationResult
279
     */
280
    public function verifyGssfId($gssfId)
281
    {
282
        $result = $this->gssfService->verify($gssfId);
283
284
        if (!$result->isSuccess()) {
285
            return $result;
286
        }
287
288
        $procedure = $this->getProcedure($result->getProcedureId());
289
        $procedure->verifySecondFactorIdentifier($gssfId);
290
291
        $this->vettingProcedureRepository->store($procedure);
292
293
        return $result;
294
    }
295
296
    /**
297
     * @param string $procedureId
298
     * @return SignRequestCreationResult
299
     */
300
    public function createU2fSignRequest($procedureId)
301
    {
302
        $procedure = $this->getProcedure($procedureId);
303
304
        $command = new CreateU2fSignRequestCommand();
305
        $command->keyHandle = $procedure->getSecondFactor()->secondFactorIdentifier;
306
        $command->identityId = $procedure->getSecondFactor()->identityId;
307
        $command->institution = $procedure->getSecondFactor()->institution;
308
309
        return $this->u2fService->createSignRequest($command);
310
    }
311
312
    /**
313
     * @param string       $procedureId
314
     * @param SignRequest  $signRequest
315
     * @param SignResponse $signResponse
316
     * @return AuthenticationVerificationResult
317
     */
318
    public function verifyU2fAuthentication($procedureId, SignRequest $signRequest, SignResponse $signResponse)
319
    {
320
        $procedure = $this->getProcedure($procedureId);
321
322
        $command = new VerifyU2fAuthenticationCommand();
323
        $command->identityId = $procedure->getSecondFactor()->identityId;
324
        $command->institution = $procedure->getSecondFactor()->institution;
325
        $command->signRequest = $signRequest;
326
        $command->signResponse = $signResponse;
327
328
        $result = $this->u2fService->verifyAuthentication($command);
329
330
        if ($result->wasSuccessful()) {
331
            $procedure->verifySecondFactorIdentifier($signResponse->keyHandle);
332
            $this->vettingProcedureRepository->store($procedure);
333
        }
334
335
        return $result;
336
    }
337
338
    /**
339
     * @param string $procedureId
340
     * @param VerifyIdentityCommand $command
341
     * @return void
342
     * @throws UnknownVettingProcedureException
343
     * @throws DomainException
344
     */
345
    public function verifyIdentity($procedureId, VerifyIdentityCommand $command)
346
    {
347
        $procedure = $this->getProcedure($procedureId);
348
        $procedure->verifyIdentity($command->documentNumber, $command->identityVerified);
349
350
        $this->vettingProcedureRepository->store($procedure);
351
    }
352
353
    /**
354
     * @param string $procedureId
355
     * @return bool
356
     * @throws UnknownVettingProcedureException
357
     * @throws DomainException
358
     */
359
    public function vet($procedureId)
360
    {
361
        $procedure = $this->getProcedure($procedureId);
362
        $procedure->vet();
363
364
        $command = new VetSecondFactorCommand();
365
        $command->authorityId = $procedure->getAuthorityId();
366
        $command->identityId = $procedure->getSecondFactor()->identityId;
367
        $command->secondFactorId = $procedure->getSecondFactor()->id;
368
        $command->registrationCode = $procedure->getRegistrationCode();
369
        $command->secondFactorType = $procedure->getSecondFactor()->type;
370
        $command->secondFactorIdentifier = $procedure->getInputSecondFactorIdentifier();
371
        $command->documentNumber = $procedure->getDocumentNumber();
372
        $command->identityVerified = $procedure->isIdentityVerified();
373
374
        $result = $this->commandService->execute($command);
375
376
        if (!$result->isSuccessful()) {
377
            return false;
378
        }
379
380
        $this->vettingProcedureRepository->remove($procedureId);
381
382
        return true;
383
    }
384
385
    /**
386
     * @param string $procedureId
387
     * @return string
388
     * @throws UnknownVettingProcedureException
389
     */
390
    public function getIdentityCommonName($procedureId)
391
    {
392
        return $this->getProcedure($procedureId)->getSecondFactor()->commonName;
393
    }
394
395
    /**
396
     * @param $procedureId
397
     * @return string
398
     * @throws UnknownVettingProcedureException
399
     */
400
    public function getSecondFactorIdentifier($procedureId)
401
    {
402
        return $this->getProcedure($procedureId)->getSecondFactor()->secondFactorIdentifier;
403
    }
404
405
    /**
406
     * @param string $procedureId
407
     * @return null|VettingProcedure
408
     * @throws UnknownVettingProcedureException
409
     */
410 View Code Duplication
    private function getProcedure($procedureId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
411
    {
412
        if (!is_string($procedureId)) {
413
            throw InvalidArgumentException::invalidType('string', 'procedureId', $procedureId);
414
        }
415
416
        $procedure = $this->vettingProcedureRepository->retrieve($procedureId);
417
418
        if (!$procedure) {
419
            throw new UnknownVettingProcedureException(
420
                sprintf("No vetting procedure with id '%s' is known.", $procedureId)
421
            );
422
        }
423
424
        return $procedure;
425
    }
426
427
    /**
428
     * @param string $procedureId
429
     * @return bool
430
     */
431
    public function hasProcedure($procedureId)
432
    {
433
        if (!is_string($procedureId)) {
434
            throw InvalidArgumentException::invalidType('string', 'procedureId', $procedureId);
435
        }
436
437
        return $this->vettingProcedureRepository->retrieve($procedureId) !== null;
438
    }
439
}
440