Passed
Push — feature/symfony6-upgrade ( 166417...86e2b9 )
by Paul
06:45
created

SamlController::testSecondFactor()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 21
nc 2
nop 0
dl 0
loc 36
rs 9.584
c 0
b 0
f 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
 */
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...
18
19
namespace Surfnet\StepupSelfService\SelfServiceBundle\Controller;
20
21
use Exception;
22
use Surfnet\SamlBundle\Http\XMLResponse;
23
use Surfnet\SamlBundle\SAML2\Response\Assertion\InResponseTo;
24
use Surfnet\StepupBundle\Value\Loa;
25
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SelfAssertedTokens\RecoveryTokenState;
26
use Surfnet\StepupSelfService\SelfServiceBundle\Value\SelfVetRequestId;
27
use Symfony\Component\HttpFoundation\RedirectResponse;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpFoundation\Response;
30
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
31
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
32
use Symfony\Component\Routing\Annotation\Route;
33
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
34
use Symfony\Component\Security\Core\Exception\AuthenticationException;
35
36
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
37
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects) -- Hard to reduce due to different commands and queries used.
38
 */
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...
39
class SamlController extends Controller
40
{
41
    /**
42
     * A SelfService user is able to test it's token in this endpoint
43
     *
44
     * @return RedirectResponse
45
     * @throws NotFoundHttpException
46
     * @throws AccessDeniedException
47
     */
48
#[Route(path:'/second-factor/test', name:'ss_second_factor_test', methods:  ['GET'])]
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected at least 4 spaces, found 0
Loading history...
49
public function testSecondFactor(): RedirectResponse
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 4 spaces, found 0
Loading history...
50
    {
0 ignored issues
show
Coding Style introduced by
Opening brace indented incorrectly; expected 0 spaces, found 4
Loading history...
51
        $logger = $this->get('logger');
0 ignored issues
show
Bug introduced by
The method get() does not exist on Surfnet\StepupSelfServic...ntroller\SamlController. ( Ignorable by Annotation )

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

51
        /** @scrutinizer ignore-call */ 
52
        $logger = $this->get('logger');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
52
        $logger->notice('Starting second factor test');
53
54
        $secondFactorService = $this->get('surfnet_stepup_self_service_self_service.service.second_factor');
55
        $loaResolutionService = $this->get('surfnet_stepup.service.loa_resolution');
56
        $identity = $this->getIdentity();
57
58
        $vettedSecondFactors = $secondFactorService->findVettedByIdentity($identity->id);
59
        if (!$vettedSecondFactors || $vettedSecondFactors->getTotalItems() === 0) {
60
            $logger->error(
61
                sprintf(
62
                    'Identity "%s" tried to test a second factor, but does not own a suitable vetted token.',
63
                    $identity->id
64
                )
65
            );
66
67
            throw new NotFoundHttpException();
68
        }
69
70
        $authenticationRequestFactory = $this->get('self_service.test_second_factor_authentication_request_factory');
71
72
        // By requesting LoA 1.5 any relevant token can be tested (LoA 2 and 3)
73
        $authenticationRequest = $authenticationRequestFactory->createSecondFactorTestRequest(
74
            $identity->nameId,
75
            $loaResolutionService->getLoaByLevel(Loa::LOA_SELF_VETTED)
76
        );
77
78
        $this->get('session')->set('second_factor_test_request_id', $authenticationRequest->getRequestId());
79
80
        $samlLogger = $this->get('surfnet_saml.logger')->forAuthentication($authenticationRequest->getRequestId());
81
        $samlLogger->notice('Sending authentication request to the second factor test IDP');
82
83
        return $this->get('surfnet_saml.http.redirect_binding')->createRedirectResponseFor($authenticationRequest);
84
    }
0 ignored issues
show
Coding Style introduced by
Closing brace indented incorrectly; expected 0 spaces, found 4
Loading history...
85
86
    #[Route(
87
        path: '/authentication/consume-assertion',
88
        name: 'selfservice_serviceprovider_consume_assertion',
89
        methods: ['POST'],
90
    )]
91
    public function consumeAssertion(Request $httpRequest): Response
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function consumeAssertion()
Loading history...
92
    {
93
        $logger = $this->get('logger');
94
95
        $session = $this->get('session');
96
        if ($session->has(SelfVetController::SELF_VET_SESSION_ID)) {
97
            // The test authentication IdP is also used for self vetting, a different session id is
98
            // used to mark a self vet command
99
            /** @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...
100
            $selfVetRequestId = $session->get(SelfVetController::SELF_VET_SESSION_ID);
101
            $secondFactorId = $selfVetRequestId->vettingSecondFactorId();
102
            return $this->forward(
103
                'SurfnetStepupSelfServiceSelfServiceBundle:SelfVet:consumeSelfVetAssertion',
104
                ['secondFactorId' => $secondFactorId]
105
            );
106
        }
107
        if ($session->has(RecoveryTokenState::RECOVERY_TOKEN_STEP_UP_REQUEST_ID_IDENTIFIER)) {
108
            // The test authentication IdP is also used for self-asserted recovery token
109
            // verification a different session id is used to mark the authentication.
110
            return $this->forward('SurfnetStepupSelfServiceSelfServiceBundle:RecoveryToken:stepUpConsumeAssertion');
111
        }
112
        if (!$session->has('second_factor_test_request_id')) {
113
            $logger->error(
114
                'Received an authentication response for testing a second factor, but no second factor test response was expected'
115
            );
116
117
            throw new AccessDeniedHttpException('Did not expect an authentication response');
118
        }
119
        $logger->notice('Received an authentication response for testing a second factor');
120
        $initiatedRequestId = $session->get('second_factor_test_request_id');
121
        $samlLogger = $this->get('surfnet_saml.logger')->forAuthentication($initiatedRequestId);
122
        $session->remove('second_factor_test_request_id');
123
        $postBinding = $this->get('surfnet_saml.http.post_binding');
124
        try {
125
            $assertion = $postBinding->processResponse(
126
                $httpRequest,
127
                $this->get('self_service.second_factor_test_idp'),
128
                $this->get('surfnet_saml.hosted.service_provider')
129
            );
130
131
            if (!InResponseTo::assertEquals($assertion, $initiatedRequestId)) {
132
                $samlLogger->error(
133
                    sprintf(
134
                        'Expected a response to the request with ID "%s", but the SAMLResponse was a response to a different request',
135
                        $initiatedRequestId
136
                    )
137
                );
138
139
                throw new AuthenticationException('Unexpected InResponseTo in SAMLResponse');
140
            }
141
142
            $session->getFlashBag()->add('success', 'ss.test_second_factor.verification_successful');
143
        } catch (Exception) {
144
            $session->getFlashBag()->add('error', 'ss.test_second_factor.verification_failed');
145
        }
146
        return $this->redirectToRoute('ss_second_factor_list');
147
    }
148
149
    #[Route(
150
        path: '/authentication/metadata',
151
        name: 'selfservice_saml_metadata',
152
        methods: ['GET'],
153
    )]
154
    public function metadata(): XMLResponse
0 ignored issues
show
Coding Style introduced by
Missing doc comment for function metadata()
Loading history...
155
    {
156
        /** @var \Surfnet\SamlBundle\Metadata\MetadataFactory $metadataFactory */
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...
157
        $metadataFactory = $this->get('surfnet_saml.metadata_factory');
158
159
        return new XMLResponse($metadataFactory->generate());
160
    }
161
}
162