Completed
Pull Request — develop (#166)
by
unknown
07:21 queued 03:30
created

GatewayController::respondAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 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
 */
18
19
namespace Surfnet\StepupGateway\GatewayBundle\Controller;
20
21
use SAML2\Response as SAMLResponse;
22
use Surfnet\StepupGateway\GatewayBundle\Exception\RequesterFailureException;
23
use Surfnet\StepupGateway\GatewayBundle\Exception\ResponseFailureException;
24
use Surfnet\StepupGateway\GatewayBundle\Service\Gateway\ConsumeAssertionService;
25
use Surfnet\StepupGateway\GatewayBundle\Service\Gateway\FailedResponseService;
26
use Surfnet\StepupGateway\GatewayBundle\Service\Gateway\LoginService;
27
use Surfnet\StepupGateway\GatewayBundle\Service\Gateway\RespondService;
28
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
29
use Symfony\Component\HttpFoundation\Request;
30
use Symfony\Component\HttpFoundation\Response;
31
use Symfony\Component\HttpKernel\Exception\HttpException;
32
33
/**
34
 * Entry point for the Stepup login flow.
35
 *
36
 * See docs/GatewayState.md for a high-level diagram on how this controller
37
 * interacts with outside actors and other parts of Stepup.
38
 */
39
class GatewayController extends Controller
40
{
41
    const RESPONSE_CONTEXT_SERVICE_ID = 'gateway.proxy.response_context';
42
43
    /**
44
     * Receive an AuthnRequest from a service provider.
45
     *
46
     * The service provider is either a Stepup component (SelfService, RA) or
47
     * an external service provider.
48
     *
49
     * This single sign-on action will start a new SAML request to the remote
50
     * IDP configured in Stepup (most likely to be an instance of OpenConext
51
     * EngineBlock).
52
     *
53
     * @param Request $httpRequest
54
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response
55
     */
56
    public function ssoAction(Request $httpRequest)
57
    {
58
        /** @var \Psr\Log\LoggerInterface $logger */
59
        $logger = $this->get('logger');
60
61
        $redirectBinding = $this->get('surfnet_saml.http.redirect_binding');
62
        $gatewayLoginService = $this->getGatewayLoginService();
63
64
        $logger->notice('Received AuthnRequest, started processing');
65
66
        /** @var \Surfnet\SamlBundle\Http\RedirectBinding $redirectBinding */
67
        $originalRequest = $redirectBinding->receiveSignedAuthnRequestFrom($httpRequest);
68
69
        try {
70
            $proxyRequest = $gatewayLoginService->singleSignOn($httpRequest, $originalRequest);
71
        } catch (RequesterFailureException $e) {
72
            $response = $this->getGatewayFailedResponseService()->createRequesterFailureResponse();
73
74
            return $this->renderSamlResponse('consumeAssertion', $response);
75
        }
76
77
        return $redirectBinding->createResponseFor($proxyRequest);
78
    }
79
80
    /**
81
     *
82
     */
83
    public function proxySsoAction()
84
    {
85
        throw new HttpException(418, 'Not Yet Implemented');
86
    }
87
88
    /**
89
     * Receive an AuthnResponse from an identity provider.
90
     *
91
     * The AuthnRequest started in ssoAction() resulted in an AuthnResponse
92
     * from the IDP. This method handles the assertion and forwards the user
93
     * using an internal redirect to the SecondFactorController to start the
94
     * actual second factor verification.
95
     *
96
     * @param Request $request
97
     * @return \Symfony\Component\HttpFoundation\Response
98
     */
99
    public function consumeAssertionAction(Request $request)
100
    {
101
        $responseContext = $this->getResponseContext();
102
        $gatewayLoginService = $this->getGatewayConsumeAssertionService();
103
104
        try {
105
            $gatewayLoginService->consumeAssertion($request, $responseContext);
106
        } catch (ResponseFailureException $e) {
107
            $response = $this->getGatewayFailedResponseService()->createResponseFailureResponse($responseContext);
108
109
            return $this->renderSamlResponse('unprocessableResponse', $response);
110
        }
111
112
        return $this->forward('SurfnetStepupGatewayGatewayBundle:SecondFactor:selectSecondFactorForVerification');
113
    }
114
115
    /**
116
     * Send a SAML response back to the service provider.
117
     *
118
     * Second factor verification handled by SecondFactorController is
119
     * finished. The user was forwarded back to this action with an internal
120
     * redirect. This method sends a AuthnResponse back to the service
121
     * provider in response to the AuthnRequest received in ssoAction().
122
     */
123
    public function respondAction()
124
    {
125
        $responseContext = $this->getResponseContext();
126
        $gatewayLoginService = $this->getGatewayRespondService();
127
128
        $response = $gatewayLoginService->respond($responseContext);
129
        $gatewayLoginService->respondReset($responseContext);
130
131
        return $this->renderSamlResponse('consumeAssertion', $response);
132
    }
133
134
    /**
135
     * @return Response
136
     */
137 View Code Duplication
    public function sendLoaCannotBeGivenAction()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
    {
139
        $responseContext = $this->getResponseContext();
140
        $gatewayLoginService = $this->getGatewayFailedResponseService();
141
142
        $response = $gatewayLoginService->sendLoaCannotBeGiven($responseContext);
143
144
        return $this->renderSamlResponse('consumeAssertion', $response);
145
    }
146
147
    /**
148
     * @return Response
149
     */
150 View Code Duplication
    public function sendAuthenticationCancelledByUserAction()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
    {
152
        $responseContext = $this->getResponseContext();
153
        $gatewayLoginService = $this->getGatewayFailedResponseService();
154
155
        $response = $gatewayLoginService->sendAuthenticationCancelledByUser($responseContext);
156
157
        return $this->renderSamlResponse('consumeAssertion', $response);
158
    }
159
160
    /**
161
     * @param string         $view
162
     * @param SAMLResponse $response
163
     * @return Response
164
     */
165 View Code Duplication
    public function renderSamlResponse($view, SAMLResponse $response)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
    {
167
        $responseContext = $this->getResponseContext();
168
169
        return $this->render($view, [
170
            'acu'        => $responseContext->getDestination(),
171
            'response'   => $this->getResponseAsXML($response),
172
            'relayState' => $responseContext->getRelayState()
173
        ]);
174
    }
175
176
    /**
177
     * @param string   $view
178
     * @param array    $parameters
179
     * @param Response $response
0 ignored issues
show
Documentation introduced by
Should the type for parameter $response not be null|Response?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
180
     * @return Response
181
     */
182
    public function render($view, array $parameters = array(), Response $response = null)
183
    {
184
        return parent::render(
185
            'SurfnetStepupGatewayGatewayBundle:Gateway:' . $view . '.html.twig',
186
            $parameters,
187
            $response
188
        );
189
    }
190
191
    /**
192
     * @return \Surfnet\StepupGateway\GatewayBundle\Saml\ResponseContext
193
     */
194
    public function getResponseContext()
195
    {
196
        $stateHandler = $this->get('gateway.proxy.state_handler');
197
198
        $responseContextServiceId = $stateHandler->getResponseContextServiceId();
199
200
        if (!$responseContextServiceId) {
201
            return $this->get(static::RESPONSE_CONTEXT_SERVICE_ID);
202
        }
203
204
        return $this->get($responseContextServiceId);
205
    }
206
207
    /**
208
     * @param SAMLResponse $response
209
     * @return string
210
     */
211
    private function getResponseAsXML(SAMLResponse $response)
212
    {
213
        return base64_encode($response->toUnsignedXML()->ownerDocument->saveXML());
214
    }
215
216
    /**
217
     * @return LoginService
218
     */
219
    private function getGatewayLoginService()
220
    {
221
        return $this->get('gateway.service.gateway.login');
222
    }
223
224
    /**
225
     * @return ConsumeAssertionService
226
     */
227
    private function getGatewayConsumeAssertionService()
228
    {
229
        return $this->get('gateway.service.gateway.consume_assertion');
230
    }
231
232
    /**
233
     * @return RespondService
234
     */
235
    private function getGatewayRespondService()
236
    {
237
        return $this->get('gateway.service.gateway.respond');
238
    }
239
240
    /**
241
     * @return FailedResponseService
242
     */
243
    private function getGatewayFailedResponseService()
244
    {
245
        return $this->get('gateway.service.gateway.failed_response');
246
    }
247
}
248