Passed
Pull Request — main (#308)
by Paul
11:20 queued 05:30
created

GssfController::consumeAssertion()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 78
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 47
c 1
b 0
f 0
nc 5
nop 2
dl 0
loc 78
rs 8.5341

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
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\Controller\Registration;
22
23
use Exception;
24
use Psr\Log\LoggerInterface;
25
use Surfnet\SamlBundle\Http\XMLResponse;
26
use Surfnet\SamlBundle\SAML2\Attribute\AttributeDictionary;
27
use Surfnet\SamlBundle\SAML2\AuthnRequestFactory;
28
use Surfnet\SamlBundle\SAML2\Response\Assertion\InResponseTo;
29
use Surfnet\StepupBundle\Value\Provider\ViewConfigCollection;
30
use Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\MetadataFactoryCollection;
31
use Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\Provider;
32
use Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\ProviderRepository;
33
use Surfnet\StepupSelfService\SamlStepupProviderBundle\Provider\ViewConfig;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Controller\Controller;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Form\Type\StatusGssfType;
36
use Surfnet\StepupSelfService\SelfServiceBundle\Service\GssfService;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupSelfServic...dle\Service\GssfService 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...
37
use Surfnet\StepupSelfService\SelfServiceBundle\Service\GsspUserAttributeService;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupSelfServic...sspUserAttributeService 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...
38
use Surfnet\StepupSelfService\SelfServiceBundle\Service\InstitutionConfigurationOptionsService;
39
use Symfony\Component\HttpFoundation\RedirectResponse;
40
use Symfony\Component\HttpFoundation\Request;
41
use Symfony\Component\HttpFoundation\Response;
42
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
43
use Symfony\Component\Routing\Annotation\Route;
44
use Surfnet\SamlBundle\Http\RedirectBinding;
45
use \Surfnet\SamlBundle\Http\PostBinding;
0 ignored issues
show
Bug introduced by
The type \Surfnet\SamlBundle\Http\PostBinding 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...
46
47
/**
48
 * Controls registration with Generic SAML Stepup Providers (GSSPs), yielding Generic SAML Second Factors (GSSFs).
49
 */
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...
50
final class GssfController extends Controller
51
{
52
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
53
        private readonly LoggerInterface           $logger,
54
        InstitutionConfigurationOptionsService     $configurationOptionsService,
55
        private readonly ProviderRepository        $providerRepository,
56
        private readonly RedirectBinding           $redirectBinding,
57
        private readonly PostBinding               $postBinding,
58
        private readonly GsspUserAttributeService  $gsspUserAttributeService,
59
        private readonly GssfService               $gssfService,
60
        private readonly AttributeDictionary       $attributeDictionary,
61
        private readonly MetadataFactoryCollection $metadataFactoryCollection,
62
        private readonly ViewConfigCollection      $viewConfigCollection
63
    ) {
64
        parent::__construct($logger, $configurationOptionsService);
65
    }
66
67
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $request should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $provider should have a doc-comment as per coding-style.
Loading history...
68
     * Render the status form.
69
     *
70
     * This action has two parameters:
71
     *
72
     * - authenticationFailed (default false), will trigger an error message
73
     *   and is used when a SAML failure response was received, for example
74
     *   when the users cancelled the registration
75
     *
76
     * - proofOfPossessionFailed (default false), will trigger an error message
77
     *   when possession was not proven, but the SAML response was successful
78
     *
79
     * @return array|Response
80
     */
0 ignored issues
show
Coding Style introduced by
There must be no blank lines after the function comment
Loading history...
81
    #[Route(
82
        path: '/registration/gssf/{provider}/status',
83
        name: 'ss_registration_gssf_status_report',
84
        defaults: ['authenticationFailed' => false, 'proofOfPossessionFailed'=> false ],
85
        methods: ['GET'],
86
    )]
87
88
    public function status(Request $request, string $provider): \Symfony\Component\HttpFoundation\Response
89
    {
90
        $this->assertSecondFactorEnabled($provider);
91
92
        return $this->renderStatusForm(
93
            $provider,
94
            [
95
                'authenticationFailed' => (bool) $request->query->get('authenticationFailed'),
96
                'proofOfPossessionFailed' => (bool) $request->query->get('proofOfPossessionFailed'),
97
            ]
98
        );
99
    }
100
101
    #[Route(
102
        path: '/registration/gssf/{provider}/authenticate',
103
        name: 'ss_registration_gssf_authenticate',
104
        methods: ['POST'],
105
    )]
106
    public function authenticate(string $provider): array|Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function authenticate()
Loading history...
107
    {
108
        $this->assertSecondFactorEnabled($provider);
109
110
        $provider = $this->getProvider($provider);
111
112
        $authnRequest = AuthnRequestFactory::createNewRequest(
113
            $provider->getServiceProvider(),
114
            $provider->getRemoteIdentityProvider()
115
        );
116
117
        $this->gsspUserAttributeService->addGsspUserAttributes(
118
            $authnRequest,
119
            $provider,
120
            $this->getIdentity()
121
        );
122
        $stateHandler = $provider->getStateHandler();
123
        $stateHandler->setRequestId($authnRequest->getRequestId());
124
125
        $this->logger->notice(sprintf(
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
126
            'Sending AuthnRequest with request ID: "%s" to GSSP "%s" at "%s"',
127
            $authnRequest->getRequestId(),
128
            $provider->getName(),
129
            $provider->getRemoteIdentityProvider()->getSsoUrl()
130
        ));
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
131
132
        return $this->redirectBinding->createResponseFor($authnRequest);
133
    }
134
135
    #[Route(
136
        path: '/registration/gssf/{provider}/consume-assertion',
137
        name: 'ss_registration_gssf_consume_assertion',
138
        methods: ['POST'],
139
    )]
140
    public function consumeAssertion(Request $httpRequest, string $provider): array|Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function consumeAssertion()
Loading history...
141
    {
142
        $this->assertSecondFactorEnabled($provider);
143
144
        $provider = $this->getProvider($provider);
145
146
        $this->logger->notice(
147
            sprintf('Received GSSP "%s" SAMLResponse through Gateway, attempting to process', $provider->getName())
148
        );
149
150
        try {
151
            $assertion = $this->postBinding->processResponse(
152
                $httpRequest,
153
                $provider->getRemoteIdentityProvider(),
154
                $provider->getServiceProvider()
155
            );
156
        } catch (Exception $exception) {
157
            $provider->getStateHandler()->clear();
158
159
            $this->logger->error(
160
                sprintf('Could not process received Response, error: "%s"', $exception->getMessage())
161
            );
162
163
            return $this->redirectToStatusReportForm(
164
                $provider,
165
                ['authenticationFailed' => true]
166
            );
167
        }
168
169
        $expectedResponseTo = $provider->getStateHandler()->getRequestId();
170
        $provider->getStateHandler()->clear();
171
172
        if (!InResponseTo::assertEquals($assertion, $expectedResponseTo)) {
173
            $this->logger->critical(sprintf(
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
174
                'Received Response with unexpected InResponseTo, %s',
175
                ($expectedResponseTo ? 'expected "' . $expectedResponseTo . '"' : ' no response expected')
176
            ));
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
177
178
            return $this->redirectToStatusReportForm(
179
                $provider,
180
                ['authenticationFailed' => true]
181
            );
182
        }
183
184
        $this->logger->notice(
185
            sprintf('Processed GSSP "%s" SAMLResponse received through Gateway successfully', $provider->getName())
186
        );
187
188
        $gssfId = $this->attributeDictionary->translate($assertion)->getNameID();
189
190
        $secondFactorId = $this->gssfService->provePossession($this->getIdentity()->id, $provider->getName(), $gssfId);
191
192
        if ($secondFactorId) {
193
            $this->logger->notice('GSSF possession has been proven successfully');
194
195
            if ($this->emailVerificationIsRequired()) {
196
                return $this->redirectToRoute(
197
                    'ss_registration_email_verification_email_sent',
198
                    ['secondFactorId' => $secondFactorId]
199
                );
200
            } else {
201
                return $this->redirectToRoute(
202
                    'ss_second_factor_vetting_types',
203
                    ['secondFactorId' => $secondFactorId]
204
                );
205
            }
206
        }
207
208
        $this->logger->error('Unable to prove GSSF possession');
209
210
        return $this->redirectToStatusReportForm(
211
            $provider,
212
            ['proofOfPossessionFailed' => true]
213
        );
214
    }
215
216
    private function redirectToStatusReportForm(Provider $provider, array $options): RedirectResponse
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function redirectToStatusReportForm()
Loading history...
Coding Style introduced by
Private method name "GssfController::redirectToStatusReportForm" must be prefixed with an underscore
Loading history...
217
    {
218
        return $this->redirectToRoute(
219
            'ss_registration_gssf_status_report',
220
            $options + [
221
                'provider' => $provider->getName(),
222
            ]
223
        );
224
    }
225
226
    #[Route(
227
        path: '/registration/gssf/{provider}/metadata',
228
        name: 'ss_registration_gssf_saml_metadata',
229
        methods: ['GET'],
230
    )]
231
    public function metadata(string $provider): XMLResponse
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function metadata()
Loading history...
232
    {
233
        $this->assertSecondFactorEnabled($provider);
234
235
        $provider = $this->getProvider($provider);
236
        $factory = $this->metadataFactoryCollection->getByIdentifier($provider->getName());
237
238
        return new XMLResponse($factory->generate()->__toString());
239
    }
240
241
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $provider should have a doc-comment as per coding-style.
Loading history...
242
     * @throws NotFoundHttpException
243
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
244
    private function getProvider(string $provider): Provider
0 ignored issues
show
Coding Style introduced by
Private method name "GssfController::getProvider" must be prefixed with an underscore
Loading history...
245
    {
246
        if (!$this->providerRepository->has($provider)) {
247
            $this->logger->info(sprintf('Requested GSSP "%s" does not exist or is not registered', $provider));
248
249
            throw new NotFoundHttpException('Requested provider does not exist');
250
        }
251
252
        return $this->providerRepository->get($provider);
253
    }
254
255
    private function renderStatusForm(string $provider, array $parameters = []): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function renderStatusForm()
Loading history...
Coding Style introduced by
Private method name "GssfController::renderStatusForm" must be prefixed with an underscore
Loading history...
256
    {
257
        /** @var ViewConfig $secondFactorConfig */
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...
258
        $secondFactorConfig = $this->viewConfigCollection->getByIdentifier($provider);
259
260
        $form = $this->createForm(
261
            StatusGssfType::class,
262
            null,
263
            [
264
                'provider' => $provider,
265
                /** @Ignore from translation message extraction */
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...
266
                'label' => $secondFactorConfig->getInitiateButton()
267
            ]
268
        );
269
        /** @var ViewConfig $secondFactorConfig */
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...
270
        $templateParameters = array_merge(
271
            $parameters,
272
            [
273
                'form' => $form->createView(),
274
                'provider' => $provider,
275
                'secondFactorConfig' => $secondFactorConfig,
276
                'verifyEmail' => $this->emailVerificationIsRequired(),
277
            ]
278
        );
279
        return $this->render(
280
            'registration/gssf/status.html.twig',
281
            $templateParameters
282
        );
283
    }
284
}
285