Passed
Push — master ( c4afc2...9cde23 )
by Pieter van der
27:49 queued 12:42
created

SelfVetController::consumeSelfVetAssertionAction()   B

Complexity

Conditions 6
Paths 15

Size

Total Lines 55
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 34
nc 15
nop 2
dl 0
loc 55
rs 8.7537
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Copyright 2021 SURFnet B.V.
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\StepupSelfService\SelfServiceBundle\Controller;
20
21
use Exception;
22
use Psr\Log\LoggerInterface;
23
use Surfnet\SamlBundle\Entity\IdentityProvider;
24
use Surfnet\SamlBundle\Entity\ServiceProvider;
25
use Surfnet\SamlBundle\Http\PostBinding;
26
use Surfnet\SamlBundle\Http\RedirectBinding;
27
use Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger;
28
use Surfnet\SamlBundle\SAML2\Response\Assertion\InResponseTo;
29
use Surfnet\StepupBundle\Service\LoaResolutionService;
30
use Surfnet\StepupBundle\Service\SecondFactorTypeService;
31
use Surfnet\StepupBundle\Value\Loa;
32
use Surfnet\StepupBundle\Value\SecondFactorType;
33
use Surfnet\StepupBundle\Value\VettingType;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SelfVetCommand;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Service\AuthorizationService;
36
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
37
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfVetMarshaller;
38
use Surfnet\StepupSelfService\SelfServiceBundle\Service\TestSecondFactor\TestAuthenticationRequestFactory;
39
use Surfnet\StepupSelfService\SelfServiceBundle\Value\SelfVetRequestId;
40
use Symfony\Component\HttpFoundation\RedirectResponse;
41
use Symfony\Component\HttpFoundation\Request;
42
use Symfony\Component\HttpFoundation\Session\SessionInterface;
43
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
44
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
45
use Symfony\Component\Security\Core\Exception\AuthenticationException;
46
use function sprintf;
47
48
/**
49
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - Controllers are prone to higher coupling. This one is no exception
50
 */
51
class SelfVetController extends Controller
52
{
53
    public const SELF_VET_SESSION_ID = 'second_factor_self_vet_request_id';
54
55
    /** @var TestAuthenticationRequestFactory */
56
    public $authenticationRequestFactory;
57
58
    /** @var SecondFactorService */
59
    public $secondFactorService;
60
61
    /** @var SecondFactorTypeService */
62
    public $secondFactorTypeService;
63
64
    /** @var ServiceProvider */
65
    private $serviceProvider;
66
67
    /** @var IdentityProvider */
68
    private $identityProvider;
69
70
    /** @var RedirectBinding */
71
    public $redirectBinding;
72
73
    /** @var PostBinding */
74
    public $postBinding;
75
76
    /** @var LoaResolutionService */
77
    public $loaResolutionService;
78
79
    /**
80
     * @var SelfVetMarshaller
81
     */
82
    private $selfVetMarshaller;
83
84
    /** @var SamlAuthenticationLogger */
85
    public $samlLogger;
86
87
    /** @var SessionInterface */
88
    public $session;
89
90
    /** @var LoggerInterface */
91
    public $logger;
92
93
    /** @var AuthorizationService */
94
    private $authorizationService;
95
96
    /**
97
     * @@SuppressWarnings(PHPMD.ExcessiveParameterList)
98
     */
99
    public function __construct(
100
        TestAuthenticationRequestFactory $authenticationRequestFactory,
101
        SecondFactorService $secondFactorService,
102
        SecondFactorTypeService $secondFactorTypeService,
103
        SelfVetMarshaller $marshaller,
104
        AuthorizationService $authorizationService,
105
        ServiceProvider $serviceProvider,
106
        IdentityProvider $identityProvider,
107
        RedirectBinding $redirectBinding,
108
        PostBinding $postBinding,
109
        LoaResolutionService $loaResolutionService,
110
        SamlAuthenticationLogger $samlAuthenticationLogger,
111
        SessionInterface $session,
112
        LoggerInterface $logger
113
    ) {
114
        $this->authenticationRequestFactory = $authenticationRequestFactory;
115
        $this->secondFactorService = $secondFactorService;
116
        $this->secondFactorTypeService = $secondFactorTypeService;
117
        $this->selfVetMarshaller = $marshaller;
118
        $this->authorizationService = $authorizationService;
119
        $this->serviceProvider = $serviceProvider;
120
        $this->identityProvider = $identityProvider;
121
        $this->redirectBinding = $redirectBinding;
122
        $this->postBinding = $postBinding;
123
        $this->loaResolutionService = $loaResolutionService;
124
        $this->samlLogger = $samlAuthenticationLogger;
125
        $this->session = $session;
126
        $this->logger = $logger;
127
    }
128
129
    public function selfVetAction(string $secondFactorId): RedirectResponse
130
    {
131
        $this->logger->notice('Starting self vet proof of possession using higher or equal LoA token');
132
        $identity = $this->getIdentity();
133
134
        if (!$this->selfVetMarshaller->isAllowed($identity, $secondFactorId)) {
135
            throw new NotFoundHttpException();
136
        }
137
138
139
        // Start with some assumptions that are overwritten with the correct values in the code below
140
        $candidateSecondFactorLoa = $this->loaResolutionService->getLoaByLevel(Loa::LOA_SELF_VETTED);
0 ignored issues
show
Bug introduced by
Surfnet\StepupBundle\Value\Loa::LOA_SELF_VETTED of type double is incompatible with the type integer expected by parameter $loaLevel of Surfnet\StepupBundle\Ser...ervice::getLoaByLevel(). ( Ignorable by Annotation )

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

140
        $candidateSecondFactorLoa = $this->loaResolutionService->getLoaByLevel(/** @scrutinizer ignore-type */ Loa::LOA_SELF_VETTED);
Loading history...
141
        $isSelfVetOfSatToken = false;
142
143
        // Determine if we are dealing with a SelfVet action of a SAT token
144
        if ($this->authorizationService->maySelfVetSelfAssertedTokens($identity)) {
145
            $this->logger->notice('Determined we are self vetting a token using a self-asserted token');
146
            $isSelfVetOfSatToken = true;
147
        }
148
149
        // When a regular self-vet action is performed grab the candidate second factor loa from the SF projection
150
        if (!$isSelfVetOfSatToken) {
151
            $this->logger->notice('Determined we are self vetting a token using an identity vetted token');
152
            $candidateSecondFactor = $this->secondFactorService->findOneVerified($secondFactorId);
153
            $candidateSecondFactorLoa = $this->secondFactorTypeService->getLevel(
154
                new SecondFactorType($candidateSecondFactor->type),
155
                new VettingType(VettingType::TYPE_SELF_VET)
156
            );
157
            $candidateSecondFactorLoa = $this->loaResolutionService->getLoaByLevel($candidateSecondFactorLoa);
158
        }
159
        $this->logger->notice(
160
            sprintf(
161
                'Creating AuthNRequest requiring a LoA %s or higher token for self vetting.',
162
                $candidateSecondFactorLoa
163
            )
164
        );
165
        $authenticationRequest = $this->authenticationRequestFactory->createSecondFactorTestRequest(
166
            $identity->nameId,
167
            $candidateSecondFactorLoa
0 ignored issues
show
Bug introduced by
It seems like $candidateSecondFactorLoa can also be of type null; however, parameter $loa of Surfnet\StepupSelfServic...condFactorTestRequest() does only seem to accept Surfnet\StepupBundle\Value\Loa, 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

167
            /** @scrutinizer ignore-type */ $candidateSecondFactorLoa
Loading history...
168
        );
169
170
        $this->session->set(
171
            self::SELF_VET_SESSION_ID,
172
            new SelfVetRequestId($authenticationRequest->getRequestId(), $secondFactorId)
173
        );
174
175
        $samlLogger = $this->samlLogger->forAuthentication($authenticationRequest->getRequestId());
176
        $samlLogger->notice('Sending authentication request to the second factor only IdP');
177
178
        return $this->redirectBinding->createRedirectResponseFor($authenticationRequest);
0 ignored issues
show
Deprecated Code introduced by
The function Surfnet\SamlBundle\Http\...teRedirectResponseFor() has been deprecated: Please use the `createResponseFor` method instead ( Ignorable by Annotation )

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

178
        return /** @scrutinizer ignore-deprecated */ $this->redirectBinding->createRedirectResponseFor($authenticationRequest);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
179
    }
180
181
    public function consumeSelfVetAssertionAction(Request $httpRequest, string $secondFactorId)
182
    {
183
        $identity = $this->getIdentity();
184
        if (!$this->selfVetMarshaller->isAllowed($identity, $secondFactorId)) {
185
            throw new NotFoundHttpException();
186
        }
187
188
        if (!$this->session->has(self::SELF_VET_SESSION_ID)) {
189
            $this->logger->error(
190
                'Received an authentication response for self vetting a second factor, but no response was expected'
191
            );
192
            throw new AccessDeniedHttpException('Did not expect an authentication response');
193
        }
194
195
        $this->logger->notice('Received an authentication response for self vetting a second factor');
196
197
        /** @var SelfVetRequestId $initiatedRequestId */
198
        $initiatedRequestId = $this->session->get(self::SELF_VET_SESSION_ID);
199
200
        $samlLogger = $this->samlLogger->forAuthentication($initiatedRequestId->requestId());
201
202
        $this->session->remove(self::SELF_VET_SESSION_ID);
203
204
        try {
205
            $assertion = $this->postBinding->processResponse(
206
                $httpRequest,
207
                $this->identityProvider,
208
                $this->serviceProvider
209
            );
210
211
            if (!InResponseTo::assertEquals($assertion, $initiatedRequestId->requestId())) {
212
                $samlLogger->error(
213
                    sprintf(
214
                        'Expected a response to the request with ID "%s", but the SAMLResponse was a response to a different request',
215
                        $initiatedRequestId
0 ignored issues
show
Bug introduced by
$initiatedRequestId of type Surfnet\StepupSelfServic...\Value\SelfVetRequestId is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

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

215
                        /** @scrutinizer ignore-type */ $initiatedRequestId
Loading history...
216
                    )
217
                );
218
                throw new AuthenticationException('Unexpected InResponseTo in SAMLResponse');
219
            }
220
            $candidateSecondFactor = $this->secondFactorService->findOneVerified($secondFactorId);
221
            // Proof of possession of higher/equal LoA was successful, now apply the self vet command on Middleware
222
            $command = new SelfVetCommand();
223
            $command->identity = $this->getIdentity();
224
            $command->secondFactor = $candidateSecondFactor;
225
            $command->authoringLoa = $assertion->getAuthnContextClassRef();
226
227
            if ($this->secondFactorService->selfVet($command)) {
228
                $this->session->getFlashBag()->add('success', 'ss.self_vet.second_factor.alert.successful');
0 ignored issues
show
Bug introduced by
The method getFlashBag() does not exist on Symfony\Component\HttpFo...ession\SessionInterface. It seems like you code against a sub-type of Symfony\Component\HttpFo...ession\SessionInterface such as Symfony\Component\HttpFoundation\Session\Session. ( Ignorable by Annotation )

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

228
                $this->session->/** @scrutinizer ignore-call */ 
229
                                getFlashBag()->add('success', 'ss.self_vet.second_factor.alert.successful');
Loading history...
229
            } else {
230
                $this->session->getFlashBag()->add('error', 'ss.self_vet.second_factor.alert.failed');
231
            }
232
        } catch (Exception $exception) {
233
            $this->session->getFlashBag()->add('error', 'ss.self_vet.second_factor.verification_failed');
234
        }
235
        return $this->redirectToRoute('ss_second_factor_list');
236
    }
237
}
238