Passed
Pull Request — main (#308)
by Michiel
16:18 queued 10:22
created

SamlController::consumeAssertionAction()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 63
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 6
eloc 36
nc 9
nop 1
dl 0
loc 63
rs 8.7217
c 5
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
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;
22
23
use Exception;
24
use Psr\Log\LoggerInterface;
25
use Surfnet\SamlBundle\Entity\IdentityProvider;
26
use Surfnet\SamlBundle\Entity\ServiceProvider;
27
use Surfnet\SamlBundle\Http\PostBinding;
28
use Surfnet\SamlBundle\Http\RedirectBinding;
29
use Surfnet\SamlBundle\Http\XMLResponse;
30
use Surfnet\SamlBundle\Metadata\MetadataFactory;
31
use Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger;
32
use Surfnet\SamlBundle\SAML2\Response\Assertion\InResponseTo;
33
use Surfnet\StepupBundle\Service\LoaResolutionService;
34
use Surfnet\StepupBundle\Value\Loa;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Controller\SelfVet\SelfVetController;
36
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
37
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\RecoveryTokenState;
38
use Surfnet\StepupSelfService\SelfServiceBundle\Service\TestSecondFactor\TestAuthenticationRequestFactory;
0 ignored issues
show
Bug introduced by
The type Surfnet\StepupSelfServic...nticationRequestFactory 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...
39
use Surfnet\StepupSelfService\SelfServiceBundle\Value\SelfVetRequestId;
40
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
41
use Symfony\Component\HttpFoundation\RedirectResponse;
42
use Symfony\Component\HttpFoundation\Request;
43
use Symfony\Component\HttpFoundation\RequestStack;
44
use Symfony\Component\HttpFoundation\Response;
45
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
46
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
47
use Symfony\Component\Routing\Attribute\Route;
48
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
49
use Symfony\Component\Security\Core\Exception\AuthenticationException;
50
51
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
52
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) -- Hard to reduce due to different commands and queries used.
53
 * @SuppressWarnings(PHPMD.ExcessiveParameterList) -- Many fields/dependencies are required in controllers. Previously they were yanked from the container via $this->get(), implicit coupling has been made explicit with this change
54
 */
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...
55
class SamlController extends AbstractController
56
{
57
    public function __construct(
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function __construct()
Loading history...
58
        private readonly LoggerInterface                  $logger,
59
        private readonly SecondFactorService              $secondFactorService,
60
        private readonly RequestStack                     $requestStack,
61
        private readonly LoaResolutionService             $loaResolutionService,
62
        private readonly MetadataFactory                  $metadataFactory,
63
        private readonly SamlAuthenticationLogger         $samlAuthenticationLogger,
64
        private readonly TestAuthenticationRequestFactory $testAuthenticationRequestFactory,
65
        private readonly RedirectBinding                  $redirectBinding,
66
        private readonly PostBinding                      $postBinding,
67
        private readonly ServiceProvider $serviceProvider,
68
        private readonly IdentityProvider $testIdentityProvider,
69
    ) {
70
    }
71
72
    /**
73
     * A SelfService user is able to test it's token in this endpoint
74
     *
75
     * @throws NotFoundHttpException
76
     * @throws AccessDeniedException
77
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
78
    #[Route(path: '/second-factor/test', name: 'ss_second_factor_test', methods: ['GET'])]
79
    public function testSecondFactor(): RedirectResponse
80
    {
81
        $this->logger->notice('Starting second factor test');
82
83
        $identity = $this->getUser()->getIdentity();
0 ignored issues
show
Bug introduced by
The method getIdentity() does not exist on Symfony\Component\Security\Core\User\UserInterface. It seems like you code against a sub-type of Symfony\Component\Security\Core\User\UserInterface such as Surfnet\StepupSelfServic...n\AuthenticatedIdentity. ( Ignorable by Annotation )

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

83
        $identity = $this->getUser()->/** @scrutinizer ignore-call */ getIdentity();
Loading history...
84
85
        $vettedSecondFactors = $this->secondFactorService->findVettedByIdentity($identity->id);
86
        if (!$vettedSecondFactors || $vettedSecondFactors->getTotalItems() === 0) {
87
            $this->logger->error(
88
                sprintf(
89
                    'Identity "%s" tried to test a second factor, but does not own a suitable vetted token.',
90
                    $identity->id,
91
                ),
92
            );
93
94
            throw new NotFoundHttpException();
95
        }
96
97
98
        // By requesting LoA 1.5 any relevant token can be tested (LoA 2 and 3)
99
        $authenticationRequest = $this->testAuthenticationRequestFactory->createSecondFactorTestRequest(
100
            $identity->nameId,
101
            $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

101
            $this->loaResolutionService->getLoaByLevel(/** @scrutinizer ignore-type */ Loa::LOA_SELF_VETTED),
Loading history...
102
        );
103
104
        $this->requestStack->getSession()->set('second_factor_test_request_id', $authenticationRequest->getRequestId());
105
106
        $samlLogger = $this->samlAuthenticationLogger->forAuthentication($authenticationRequest->getRequestId());
107
        $samlLogger->notice('Sending authentication request to the second factor test IDP');
108
109
        return $this->redirectBinding->createResponseFor($authenticationRequest);
110
    }
111
112
    #[Route(
113
        path: '/authentication/consume-assertion',
114
        name: 'selfservice_serviceprovider_consume_assertion',
115
        methods: ['POST'],
116
    )]
117
    public function consumeAssertion(Request $httpRequest): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function consumeAssertion()
Loading history...
118
    {
119
        $session = $this->requestStack->getSession();
120
        if ($session->has(SelfVetController::SELF_VET_SESSION_ID)) {
121
            // The test authentication IdP is also used for self vetting, a different session id is
122
            // used to mark a self vet command
123
            /** @var SelfVetRequestId $selfVetRequestId */
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...
124
            $selfVetRequestId = $session->get(SelfVetController::SELF_VET_SESSION_ID);
125
            $secondFactorId = $selfVetRequestId->vettingSecondFactorId();
126
            return $this->forward(
127
                'Surfnet\StepupSelfService\SelfServiceBundle\Controller\SelfVetController::consumeSelfVetAssertion',
128
                ['secondFactorId' => $secondFactorId],
129
            );
130
        }
131
        if ($session->has(RecoveryTokenState::RECOVERY_TOKEN_STEP_UP_REQUEST_ID_IDENTIFIER)) {
132
            // The test authentication IdP is also used for self-asserted recovery token
133
            // verification a different session id is used to mark the authentication.
134
            return $this->forward('Surfnet\StepupSelfService\SelfServiceBundle\Controller\RecoveryTokenController::stepUpConsumeAssertion');
135
        }
136
        if (!$session->has('second_factor_test_request_id')) {
137
            $this->logger->error(
138
                'Received an authentication response for testing a second factor, but no second factor test response was expected',
139
            );
140
141
            throw new AccessDeniedHttpException('Did not expect an authentication response');
142
        }
143
        $this->logger->notice('Received an authentication response for testing a second factor');
144
        $initiatedRequestId = $session->get('second_factor_test_request_id');
145
        $samlLogger = $this->samlAuthenticationLogger->forAuthentication($initiatedRequestId);
146
        $session->remove('second_factor_test_request_id');
147
        try {
148
            $assertion = $this->postBinding->processResponse(
149
                $httpRequest,
150
                $this->testIdentityProvider,
151
                $this->serviceProvider,
152
            );
153
154
            if (!InResponseTo::assertEquals($assertion, $initiatedRequestId)) {
155
                $samlLogger->error(
156
                    sprintf(
157
                        'Expected a response to the request with ID "%s", but the SAMLResponse was a response to a different request',
158
                        $initiatedRequestId,
159
                    ),
160
                );
161
162
                throw new AuthenticationException('Unexpected InResponseTo in SAMLResponse');
163
            }
164
165
            $this->addFlash('success', 'ss.test_second_factor.verification_successful');
166
        } catch (Exception $e) {
167
            $this->logger->info('Receiving the test second factor assertion failed, see context for details', ['context' => $e->getMessage()]);
168
            $this->addFlash('error', 'ss.test_second_factor.verification_failed');
169
        }
170
        return $this->redirectToRoute('ss_second_factor_list');
171
    }
172
173
    #[Route(
174
        path: '/authentication/metadata',
175
        name: 'selfservice_saml_metadata',
176
        methods: ['GET'],
177
    )]
178
    public function metadata(): XMLResponse
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function metadata()
Loading history...
179
    {
180
        return new XMLResponse($this->metadataFactory->generate()->__toString());
181
    }
182
}
183