Completed
Push — master ( 8dc349...5f2d53 )
by A.
02:44
created

VettingController::getTranslator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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\Controller;
20
21
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
22
use Surfnet\StepupBundle\Value\SecondFactorType;
23
use Surfnet\StepupRa\RaBundle\Command\StartVettingProcedureCommand;
24
use Surfnet\StepupRa\RaBundle\Command\VerifyIdentityCommand;
25
use Surfnet\StepupRa\RaBundle\Exception\DomainException;
26
use Surfnet\StepupRa\RaBundle\Exception\RuntimeException;
27
use Surfnet\StepupRa\RaBundle\Security\Authentication\Token\SamlToken;
28
use Surfnet\StepupRa\RaBundle\Service\SecondFactorService;
29
use Surfnet\StepupRa\RaBundle\Service\VettingService;
30
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
31
use Symfony\Component\Form\FormError;
32
use Symfony\Component\Form\SubmitButton;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\HttpFoundation\Response;
35
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
36
use Symfony\Component\Translation\TranslatorInterface;
37
38
/**
39
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
40
 */
41
class VettingController extends Controller
42
{
43
    /**
44
     * @Template
45
     * @param Request $request
46
     * @return array|Response
47
     */
48
    public function startProcedureAction(Request $request)
49
    {
50
        $this->denyAccessUnlessGranted(['ROLE_RA']);
51
        $logger = $this->get('logger');
52
53
        $logger->notice('Vetting Procedure Search started');
54
55
        $command = new StartVettingProcedureCommand();
56
57
        $form = $this->createForm('ra_start_vetting_procedure', $command)->handleRequest($request);
58
59
        if (!$form->isValid()) {
60
            $logger->notice('No search submitted, displaying search by registration code form');
61
62
            return ['form' => $form->createView()];
63
        }
64
65
        $secondFactor = $this->getSecondFactorService()
66
            ->findVerifiedSecondFactorByRegistrationCode($command->registrationCode);
67
68
        if ($secondFactor === null) {
69
            $form->addError(new FormError('ra.form.start_vetting_procedure.unknown_registration_code'));
70
            $logger->notice('Cannot start new vetting procedure, no second factor found');
71
72
            return ['form' => $form->createView()];
73
        }
74
75
        $enabledSecondFactors = $this->container->getParameter('surfnet_stepup_ra.enabled_second_factors');
76
        if (!in_array($secondFactor->type, $enabledSecondFactors, true)) {
77
            $logger->warning(
78
                sprintf(
79
                    'An RA attempted vetting of disabled second factor "%s" of type "%s"',
80
                    $secondFactor->id,
81
                    $secondFactor->type
82
                )
83
            );
84
85
            return $this
86
                ->render(
87
                    'SurfnetStepupRaRaBundle:Vetting:secondFactorTypeDisabled.html.twig',
88
                    ['secondFactorType' => $secondFactor->type]
89
                )
90
                ->setStatusCode(Response::HTTP_BAD_REQUEST);
91
        }
92
93
        /** @var SamlToken $token */
94
        $token = $this->get('security.token_storage')->getToken();
95
        $command->authorityId = $this->getIdentity()->id;
96
        $command->authorityLoa = $token->getLoa();
97
        $command->secondFactor = $secondFactor;
0 ignored issues
show
Documentation Bug introduced by
It seems like $secondFactor can also be of type false. However, the property $secondFactor is declared as type object<Surfnet\StepupMid...o\VerifiedSecondFactor>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
98
99
        if (!$this->getVettingService()->isLoaSufficientToStartProcedure($command)) {
100
            $form->addError(new FormError('ra.form.start_vetting_procedure.loa_insufficient'));
101
102
            $logger->notice('Cannot start new vetting procedure, Authority LoA is insufficient');
103
104
            return ['form' => $form->createView()];
105
        }
106
107
        $procedureId = $this->getVettingService()->startProcedure($command);
108
109
        $this->get('ra.procedure_logger')
110
            ->forProcedure($procedureId)
111
            ->notice(sprintf('Starting new Vetting Procedure for second factor of type "%s"', $secondFactor->type));
112
113
        $secondFactorType = new SecondFactorType($secondFactor->type);
114
        if ($secondFactorType->isYubikey()) {
115
            return $this->redirectToRoute('ra_vetting_yubikey_verify', ['procedureId' => $procedureId]);
116
        } elseif ($secondFactorType->isSms()) {
117
            return $this->redirectToRoute('ra_vetting_sms_send_challenge', ['procedureId' => $procedureId]);
118
        } elseif ($secondFactorType->isGssf()) {
119
            return $this->redirectToRoute(
120
                'ra_vetting_gssf_initiate',
121
                [
122
                    'procedureId' => $procedureId,
123
                    'provider'    => $secondFactor->type
124
                ]
125
            );
126
        } elseif ($secondFactorType->isU2f()) {
127
            return $this->redirectToRoute('ra_vetting_u2f_start_authentication', ['procedureId' => $procedureId]);
128
        } else {
129
            throw new RuntimeException(
130
                sprintf('RA does not support vetting procedure for second factor type "%s"', $secondFactor->type)
131
            );
132
        }
133
    }
134
135
    public function cancelProcedureAction($procedureId)
136
    {
137
        $logger = $this->get('ra.procedure_logger')->forProcedure($procedureId);
138
139 View Code Duplication
        if (!$this->getVettingService()->hasProcedure($procedureId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
140
            $logger->notice(sprintf('Vetting procedure "%s" not found', $procedureId));
141
            throw new NotFoundHttpException(sprintf('Vetting procedure "%s" not found', $procedureId));
142
        }
143
144
        $this->getVettingService()->cancelProcedure($procedureId);
145
        $this->addFlash('info', $this->get('translator')->trans('ra.vetting.flash.cancelled'));
146
147
        return $this->redirectToRoute('ra_vetting_search');
148
    }
149
150
    /**
151
     * @Template
152
     * @param Request $request
153
     * @param string $procedureId
154
     * @return array|Response
155
     */
156
    public function verifyIdentityAction(Request $request, $procedureId)
157
    {
158
        $this->denyAccessUnlessGranted(['ROLE_RA']);
159
160
        $logger = $this->get('ra.procedure_logger')->forProcedure($procedureId);
161
        $logger->notice('Verify Identity Form requested');
162
163 View Code Duplication
        if (!$this->getVettingService()->hasProcedure($procedureId)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
164
            $logger->notice(sprintf('Vetting procedure "%s" not found', $procedureId));
165
            throw new NotFoundHttpException(sprintf('Vetting procedure "%s" not found', $procedureId));
166
        }
167
168
        $command = new VerifyIdentityCommand();
169
        $form = $this->createForm('ra_verify_identity', $command)->handleRequest($request);
170
171
        /** @var SubmitButton $cancelButton */
172
        $cancelButton = $form->get('cancel');
173 View Code Duplication
        if ($cancelButton->isClicked()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
174
            $this->getVettingService()->cancelProcedure($procedureId);
175
            $this->addFlash('info', $this->get('translator')->trans('ra.vetting.flash.cancelled'));
176
177
            return $this->redirectToRoute('ra_vetting_search');
178
        }
179
180
        $vettingService = $this->getVettingService();
181
        $commonName = $vettingService->getIdentityCommonName($procedureId);
182
183
        $showForm = function ($error = null) use ($form, $commonName) {
184
            if ($error) {
185
                $form->addError(new FormError($error));
186
            }
187
188
            return ['commonName' => $commonName, 'form' => $form->createView()];
189
        };
190
191
        if (!$form->isValid()) {
192
            $logger->notice('Verify Identity Form not submitted, displaying form');
193
194
            return $showForm();
195
        }
196
197
        try {
198
            $vettingService->verifyIdentity($procedureId, $command);
199
        } catch (DomainException $e) {
200
            $this->get('logger')->error(
201
                "RA attempted to verify identity, but the vetting procedure does not allow it",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal RA attempted to verify i...edure does not allow it does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
202
                ['exception' => $e, 'procedure' => $procedureId]
203
            );
204
205
            return $showForm('ra.verify_identity.identity_verification_failed');
206
        }
207
208
        try {
209
            $vetting = $vettingService->vet($procedureId);
210
            if ($vetting->isSuccessful()) {
211
                $logger->notice('Identity Verified, vetting completed');
212
213
                return $this->redirectToRoute('ra_vetting_completed', ['procedureId' => $procedureId]);
214
            }
215
216
            $logger->error('RA attempted to vet second factor, but the command failed');
217
218
            if (in_array(VettingService::REGISTRATION_CODE_EXPIRED_ERROR, $vetting->getErrors())) {
219
                $registrationCodeExpiredError = $this->getTranslator()
220
                    ->trans(
221
                        'ra.verify_identity.registration_code_expired',
222
                        [
223
                            '%self_service_url%' => $this->getParameter('surfnet_stepup_ra.self_service_url'),
224
                        ]
225
                    );
226
227
                return $showForm($registrationCodeExpiredError);
228
            }
229
230
            return $showForm('ra.verify_identity.second_factor_vetting_failed');
231
        } catch (DomainException $e) {
232
            $logger->error(
233
                "RA attempted to vet second factor, but the vetting procedure didn't allow it",
234
                ['exception' => $e]
235
            );
236
237
            return $showForm('ra.verify_identity.second_factor_vetting_failed');
238
        }
239
    }
240
241
    /**
242
     * @Template
243
     */
244
    public function vettingCompletedAction()
245
    {
246
        return [];
247
    }
248
249
    /**
250
     * @return SecondFactorService
251
     */
252
    private function getSecondFactorService()
253
    {
254
        return $this->get('ra.service.second_factor');
255
    }
256
257
    /**
258
     * @return VettingService
259
     */
260
    private function getVettingService()
261
    {
262
        return $this->get('ra.service.vetting');
263
    }
264
265
    /**
266
     * @return \Surfnet\StepupMiddlewareClientBundle\Identity\Dto\Identity
267
     */
268
    private function getIdentity()
269
    {
270
        return $this->get('security.token_storage')->getToken()->getUser();
271
    }
272
273
    /**
274
     * @return TranslatorInterface
275
     */
276
    private function getTranslator()
277
    {
278
        return $this->get('translator');
279
    }
280
}
281