Completed
Push — feature/self-vet ( 9e6747 )
by Michiel
17:14
created

SelfVetController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
c 0
b 0
f 0
rs 9.52
cc 1
nc 1
nop 11

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Copyright 2021 SURF 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\SecondFactorType;
32
use Surfnet\StepupSelfService\SelfServiceBundle\Command\SelfVetCommand;
33
use Surfnet\StepupSelfService\SelfServiceBundle\Service\SecondFactorService;
34
use Surfnet\StepupSelfService\SelfServiceBundle\Service\TestSecondFactor\TestAuthenticationRequestFactory;
35
use Surfnet\StepupSelfService\SelfServiceBundle\Value\SelfVetRequestId;
36
use Symfony\Component\HttpFoundation\RedirectResponse;
37
use Symfony\Component\HttpFoundation\Request;
38
use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
39
use Symfony\Component\HttpFoundation\Session\SessionInterface;
40
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
41
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42
use Symfony\Component\Security\Core\Exception\AuthenticationException;
43
use function sprintf;
44
45
class SelfVetController extends Controller
46
{
47
    public const SELF_VET_SESSION_ID = 'second_factor_self_vet_request_id';
48
49
    /** @var TestAuthenticationRequestFactory */
50
    public $authenticationRequestFactory;
51
52
    /** @var SecondFactorService */
53
    public $secondFactorService;
54
55
    /** @var SecondFactorTypeService */
56
    public $secondFactorTypeService;
57
58
    /** @var ServiceProvider */
59
    private $serviceProvider;
60
61
    /** @var IdentityProvider */
62
    private $identityProvider;
63
64
    /** @var RedirectBinding */
65
    public $redirectBinding;
66
67
    /** @var PostBinding */
68
    public $postBinding;
69
70
    /** @var LoaResolutionService */
71
    public $loaResolutionService;
72
73
    /** @var SamlAuthenticationLogger */
74
    public $samlLogger;
75
76
    /** @var SessionInterface */
77
    public $session;
78
79
    /** @var LoggerInterface */
80
    public $logger;
81
82
    public function __construct(
83
        TestAuthenticationRequestFactory $authenticationRequestFactory,
84
        SecondFactorService $secondFactorService,
85
        SecondFactorTypeService $secondFactorTypeService,
86
        ServiceProvider $serviceProvider,
87
        IdentityProvider $identityProvider,
88
        RedirectBinding $redirectBinding,
89
        PostBinding $postBinding,
90
        LoaResolutionService $loaResolutionService,
91
        SamlAuthenticationLogger $samlAuthenticationLogger,
92
        SessionInterface $session,
93
        LoggerInterface $logger
94
    ) {
95
        $this->authenticationRequestFactory = $authenticationRequestFactory;
96
        $this->secondFactorService = $secondFactorService;
97
        $this->secondFactorTypeService = $secondFactorTypeService;
98
        $this->serviceProvider = $serviceProvider;
99
        $this->identityProvider = $identityProvider;
100
        $this->redirectBinding = $redirectBinding;
101
        $this->postBinding = $postBinding;
102
        $this->loaResolutionService = $loaResolutionService;
103
        $this->samlLogger = $samlAuthenticationLogger;
104
        $this->session = $session;
105
        $this->logger = $logger;
106
    }
107
108
    public function selfVetAction(string $secondFactorId): RedirectResponse
109
    {
110
        $this->logger->notice('Starting self vet proof of possession using higher or equal LoA token');
111
112
        $identity = $this->getIdentity();
113
114
        $vettedSecondFactors = $this->secondFactorService->findVettedByIdentity($identity->id);
115
        if (!$vettedSecondFactors || $vettedSecondFactors->getTotalItems() === 0) {
116
            $this->logger->error(
117
                sprintf(
118
                    'Identity "%s" tried to self vet a second factor, but does not own a suitable vetted token.',
119
                    $identity->id
120
                )
121
            );
122
123
            throw new NotFoundHttpException();
124
        }
125
        $candidateSecondFactor = $this->secondFactorService->findOneVerified($secondFactorId);
126
        $candidateSecondFactorLoa = $this->secondFactorTypeService->getLevel(
127
            new SecondFactorType($candidateSecondFactor->type)
128
        );
129
        $candidateSecondFactorLoa = $this->loaResolutionService->getLoaByLevel($candidateSecondFactorLoa);
130
131
        $this->logger->notice(
132
            sprintf(
133
                'Creating AuthNRequest requiring a LoA %s or higher token for self vetting.',
134
                $candidateSecondFactorLoa
135
            )
136
        );
137
        $authenticationRequest = $this->authenticationRequestFactory->createSecondFactorTestRequest(
138
            $identity->nameId,
139
            $candidateSecondFactorLoa
0 ignored issues
show
Bug introduced by
It seems like $candidateSecondFactorLoa defined by $this->loaResolutionServ...ndidateSecondFactorLoa) on line 129 can be null; however, Surfnet\StepupSelfServic...condFactorTestRequest() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
140
        );
141
142
        $this->session->set(
143
            self::SELF_VET_SESSION_ID,
144
            new SelfVetRequestId($authenticationRequest->getRequestId(), $secondFactorId)
145
        );
146
147
        $samlLogger = $this->samlLogger->forAuthentication($authenticationRequest->getRequestId());
148
        $samlLogger->notice('Sending authentication request to the second factor only IdP');
149
150
        return $this->redirectBinding->createRedirectResponseFor($authenticationRequest);
0 ignored issues
show
Deprecated Code introduced by
The method Surfnet\SamlBundle\Http\...teRedirectResponseFor() has been deprecated with message: Please use the `createResponseFor` method instead

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

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

Loading history...
151
    }
152
153
    public function consumeSelfVetAssertionAction(Request $httpRequest, string $secondFactorId)
154
    {
155
        if (!$this->session->has(self::SELF_VET_SESSION_ID)) {
156
            $this->logger->error(
157
                'Received an authentication response for self vetting a second factor, but no response was expected'
158
            );
159
            throw new AccessDeniedHttpException('Did not expect an authentication response');
160
        }
161
162
        $this->logger->notice('Received an authentication response for self vetting a second factor');
163
164
        /** @var SelfVetRequestId $initiatedRequestId */
165
        $initiatedRequestId = $this->session->get(self::SELF_VET_SESSION_ID);
166
167
        $samlLogger = $this->samlLogger->forAuthentication($initiatedRequestId->requestId());
168
169
        $this->session->remove(self::SELF_VET_SESSION_ID);
170
171
        try {
172
            $assertion = $this->postBinding->processResponse(
173
                $httpRequest,
174
                $this->identityProvider,
175
                $this->serviceProvider
176
            );
177
178
            if (!InResponseTo::assertEquals($assertion, $initiatedRequestId->requestId())) {
179
                $samlLogger->error(
180
                    sprintf(
181
                        'Expected a response to the request with ID "%s", but the SAMLResponse was a response to a different request',
182
                        $initiatedRequestId
183
                    )
184
                );
185
                throw new AuthenticationException('Unexpected InResponseTo in SAMLResponse');
186
            }
187
            $candidateSecondFactor = $this->secondFactorService->findOneVerified($secondFactorId);
188
            // Proof of possession of higher/equal LoA was successful, now apply the self vet command on Middleware
189
            $command = new SelfVetCommand();
190
            $command->identity = $this->getIdentity();
191
            $command->secondFactor = $candidateSecondFactor;
192
            $command->authoringLoa = $assertion->getAuthnContextClassRef();
193
194
            if ($this->secondFactorService->selfVet($command)) {
195
                $this->session->getFlashBag()->add('success', 'ss.second_factor.self_vet.alert.successful');
196
            } else {
197
                $this->session->getFlashBag()->add('error', 'ss.second_factor.self_vet.alert.failed');
198
            }
199
        } catch (Exception $exception) {
200
            $this->session->getFlashBag()->add('error', 'ss.self_vet_second_factor.verification_failed');
201
        }
202
        return $this->redirectToRoute('ss_second_factor_list');
203
    }
204
}
205