1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright 2019 SURFnet B.V. |
4
|
|
|
* |
5
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
6
|
|
|
* you may not use this file except in compliance with the License. |
7
|
|
|
* You may obtain a copy of the License at |
8
|
|
|
* |
9
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
10
|
|
|
* |
11
|
|
|
* Unless required by applicable law or agreed to in writing, software |
12
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
13
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14
|
|
|
* See the License for the specific language governing permissions and |
15
|
|
|
* limitations under the License. |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
namespace Surfnet\StepupSelfService\SelfServiceBundle\Mock\RemoteVetting; |
19
|
|
|
|
20
|
|
|
use Exception; |
21
|
|
|
use SAML2\Constants; |
22
|
|
|
use SAML2\Response as SamlResponse; |
23
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
24
|
|
|
use Symfony\Component\HttpFoundation\Request; |
25
|
|
|
use Symfony\Component\HttpFoundation\Response; |
26
|
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; |
27
|
|
|
use Twig\Environment; |
28
|
|
|
|
29
|
|
|
class MockRemoteVetController extends Controller |
|
|
|
|
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* @var MockGateway |
33
|
|
|
*/ |
34
|
|
|
private $mockGateway; |
35
|
|
|
/** |
36
|
|
|
* @var Environment |
37
|
|
|
*/ |
38
|
|
|
private $twig; |
39
|
|
|
|
40
|
|
|
public function __construct(MockGateway $mockGateway, Environment $twig) |
41
|
|
|
{ |
42
|
|
|
$this->mockGateway = $mockGateway; |
43
|
|
|
$this->twig = $twig; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* This is the sso action used to mock a RV IdP callout |
48
|
|
|
* |
49
|
|
|
* @param Request $request |
50
|
|
|
* @return string|Response |
51
|
|
|
*/ |
52
|
|
|
public function ssoAction(Request $request) |
53
|
|
|
{ |
54
|
|
|
if (!in_array($this->getParameter('kernel.environment'), ['test', 'dev'])) { |
55
|
|
|
throw new Exception('Invalid environment encountered.'); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
try { |
59
|
|
|
$status = $request->get('status'); |
60
|
|
|
|
61
|
|
|
// Check binding |
62
|
|
|
if (!$request->isMethod(Request::METHOD_GET) && !$status) { |
63
|
|
|
throw new BadRequestHttpException(sprintf( |
64
|
|
|
'Could not receive AuthnRequest from HTTP Request: expected a GET method, got %s', |
65
|
|
|
$request->getMethod() |
66
|
|
|
)); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
// show possible saml response status to return |
70
|
|
|
if (!$status) { |
71
|
|
|
// Present response |
72
|
|
|
$body = $this->twig->render( |
73
|
|
|
'dev/mock-acs.html.twig', |
74
|
|
|
[ |
75
|
|
|
'action' => $request->getUri(), |
76
|
|
|
'responses' => [ |
77
|
|
|
'success', |
78
|
|
|
'user-cancelled', |
79
|
|
|
'unknown', |
80
|
|
|
], |
81
|
|
|
] |
82
|
|
|
); |
83
|
|
|
return new Response($body); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
// Parse available responses |
87
|
|
|
$response = $this->getSelectedResponse($request, $status); |
88
|
|
|
|
89
|
|
|
// Present response |
90
|
|
|
$body = $this->twig->render( |
91
|
|
|
'dev/mock-acs-post.html.twig', |
92
|
|
|
[ |
93
|
|
|
'response' => $response, |
94
|
|
|
] |
95
|
|
|
); |
96
|
|
|
|
97
|
|
|
return new Response($body); |
98
|
|
|
} catch (BadRequestHttpException $e) { |
99
|
|
|
return new Response($e->getMessage(), $e->getStatusCode()); |
100
|
|
|
} catch (Exception $e) { |
101
|
|
|
return new Response($e->getMessage(), 500); |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @param Request $request |
107
|
|
|
* @param string $status |
108
|
|
|
* @return array |
109
|
|
|
*/ |
110
|
|
|
private function getSelectedResponse(Request $request, $status) |
111
|
|
|
{ |
112
|
|
|
switch (true) { |
113
|
|
|
case ($status == 'success'): |
114
|
|
|
// Parse successful |
115
|
|
|
$rawAttributes = $request->get('attributes'); |
116
|
|
|
$attributes = $this->parseAttributes($rawAttributes); |
117
|
|
|
|
118
|
|
|
$samlResponse = $this->mockGateway->handleSsoSuccess($request, $this->getFullRequestUri($request), $attributes); |
119
|
|
|
return $this->getResponseData($request, $samlResponse); |
120
|
|
|
|
121
|
|
View Code Duplication |
case ($status == 'user-cancelled'): |
|
|
|
|
122
|
|
|
// Parse user cancelled |
123
|
|
|
$samlResponse = $this->mockGateway->handleSsoFailure( |
124
|
|
|
$request, |
125
|
|
|
$this->getFullRequestUri($request), |
126
|
|
|
Constants::STATUS_RESPONDER, |
127
|
|
|
Constants::STATUS_AUTHN_FAILED, |
128
|
|
|
'Authentication cancelled by user' |
129
|
|
|
); |
130
|
|
|
return $this->getResponseData($request, $samlResponse); |
131
|
|
|
|
132
|
|
View Code Duplication |
case ($status == 'unknown'): |
|
|
|
|
133
|
|
|
// Parse unknown |
134
|
|
|
$samlResponse = $this->mockGateway->handleSsoFailure( |
135
|
|
|
$request, |
136
|
|
|
$this->getFullRequestUri($request), |
137
|
|
|
Constants::STATUS_RESPONDER, |
138
|
|
|
Constants::STATUS_AUTHN_FAILED |
139
|
|
|
); |
140
|
|
|
return $this->getResponseData($request, $samlResponse); |
141
|
|
|
default: |
142
|
|
|
throw new BadRequestHttpException(sprintf( |
143
|
|
|
'Could not create a response for status %s', |
144
|
|
|
$status |
145
|
|
|
)); |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param Request $request |
151
|
|
|
* @param SamlResponse $samlResponse |
152
|
|
|
* @return array |
153
|
|
|
*/ |
154
|
|
|
private function getResponseData(Request $request, SamlResponse $samlResponse) |
155
|
|
|
{ |
156
|
|
|
$rawResponse = $this->mockGateway->parsePostResponse($samlResponse); |
157
|
|
|
|
158
|
|
|
return [ |
159
|
|
|
'acu' => $samlResponse->getDestination(), |
160
|
|
|
'rawResponse' => $rawResponse, |
161
|
|
|
'encodedResponse' => base64_encode($rawResponse), |
162
|
|
|
'relayState' => $request->request->get(MockGateway::PARAMETER_RELAY_STATE), |
163
|
|
|
]; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @param Request $request |
168
|
|
|
* @return string |
169
|
|
|
*/ |
170
|
|
|
private function getFullRequestUri(Request $request) |
171
|
|
|
{ |
172
|
|
|
return $request->getSchemeAndHttpHost() . $request->getBasePath() . $request->getPathInfo(); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @param string $data |
177
|
|
|
* @return array |
178
|
|
|
*/ |
179
|
|
|
private function parseAttributes($data) |
180
|
|
|
{ |
181
|
|
|
json_decode($data); |
182
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) { |
183
|
|
|
throw new BadRequestHttpException(sprintf( |
184
|
|
|
'Could not parse the attributes because no valid json was given %s', |
185
|
|
|
$data |
186
|
|
|
)); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
$data = json_decode($data, true); |
190
|
|
|
|
191
|
|
|
$result = []; |
192
|
|
|
foreach ($data as $attr) { |
193
|
|
|
if (!array_key_exists('name', $attr)) { |
194
|
|
|
throw new BadRequestHttpException(sprintf( |
195
|
|
|
'Could not parse the attributes because no valid name was given %s', |
196
|
|
|
json_encode($data) |
197
|
|
|
)); |
198
|
|
|
} |
199
|
|
|
if (!array_key_exists('value', $attr)) { |
200
|
|
|
throw new BadRequestHttpException(sprintf( |
201
|
|
|
'Could not parse the attributes because no valid value was given %s', |
202
|
|
|
json_encode($data) |
203
|
|
|
)); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
if (!is_array($attr['value'])) { |
207
|
|
|
throw new BadRequestHttpException(sprintf( |
208
|
|
|
'Could not parse the attributes because a value should be an array with strings %s', |
209
|
|
|
json_encode($data) |
210
|
|
|
)); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
foreach ($attr['value'] as $value) { |
214
|
|
|
if (!is_string($value)) { |
215
|
|
|
throw new BadRequestHttpException(sprintf( |
216
|
|
|
'Could not parse the attributes because if a value is an array it should consist of strings %s', |
217
|
|
|
json_encode($data) |
218
|
|
|
)); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
$result[$attr['name']] = $attr['value']; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
return $result; |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.