Skip to content

Commit 6e06120

Browse files
authored
Merge commit from fork
1 parent 3a7ba17 commit 6e06120

File tree

2 files changed

+162
-11
lines changed

2 files changed

+162
-11
lines changed

src/SAML2/HTTPRedirect.php

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,37 @@ public function send(AbstractMessage $message): ResponseInterface
120120
*/
121121
public function receive(ServerRequestInterface $request): AbstractMessage
122122
{
123-
$query = $request->getQueryParams();
124-
125-
if (array_key_exists('SAMLRequest', $query)) {
126-
$message = $query['SAMLRequest'];
127-
$signedQuery = 'SAMLRequest=' . urlencode($query['SAMLRequest']);
128-
} elseif (array_key_exists('SAMLResponse', $query)) {
129-
$message = $query['SAMLResponse'];
130-
$signedQuery = 'SAMLResponse=' . urlencode($query['SAMLResponse']);
123+
$query = $this->parseQuery();
124+
$signedQuery = $query['SignedQuery'];
125+
126+
/**
127+
* Get the SAMLRequest/SAMLResponse from the exact same signed data that will be verified later in
128+
* validateSignature into $res using the actual SignedQuery
129+
*/
130+
$res = [];
131+
foreach (explode('&', $signedQuery) as $e) {
132+
$tmp = explode('=', $e, 2);
133+
$name = $tmp[0];
134+
if (count($tmp) === 2) {
135+
$value = $tmp[1];
136+
} else {
137+
/* No value for this parameter. */
138+
$value = '';
139+
}
140+
$name = urldecode($name);
141+
$res[$name] = urldecode($value);
142+
}
143+
144+
/**
145+
* Put the SAMLRequest/SAMLResponse from the actual query string into $message,
146+
* and assert that the result from parseQuery() in $query and the parsing of the SignedQuery in $res agree
147+
*/
148+
if (array_key_exists('SAMLRequest', $res)) {
149+
Assert::same($res['SAMLRequest'], $query['SAMLRequest'], 'Parse failure.');
150+
$message = $res['SAMLRequest'];
151+
} elseif (array_key_exists('SAMLResponse', $res)) {
152+
Assert::same($res['SAMLResponse'], $query['SAMLResponse'], 'Parse failure.');
153+
$message = $res['SAMLResponse'];
131154
} else {
132155
throw new Exception('Missing SAMLRequest or SAMLResponse parameter.');
133156
}
@@ -151,7 +174,6 @@ public function receive(ServerRequestInterface $request): AbstractMessage
151174
$message = MessageFactory::fromXML($document->documentElement);
152175

153176
if (array_key_exists('RelayState', $query)) {
154-
$signedQuery .= '&RelayState=' . urlencode($query['RelayState']);
155177
$this->setRelayState($query['RelayState']);
156178
}
157179

@@ -170,8 +192,6 @@ public function receive(ServerRequestInterface $request): AbstractMessage
170192

171193
if (!array_key_exists('SigAlg', $query)) {
172194
throw new Exception('Missing signature algorithm.');
173-
} else {
174-
$signedQuery .= '&SigAlg=' . urlencode($query['SigAlg']);
175195
}
176196

177197
$container = ContainerSingleton::getInstance();
@@ -188,4 +208,67 @@ public function receive(ServerRequestInterface $request): AbstractMessage
188208

189209
return $message;
190210
}
211+
212+
213+
/**
214+
* Helper function to parse query data.
215+
*
216+
* This function returns the query string split into key=>value pairs.
217+
* It also adds a new parameter, SignedQuery, which contains the data that is
218+
* signed.
219+
*
220+
* @return array The query data that is signed.
221+
* @throws \Exception
222+
*/
223+
private static function parseQuery() : array
224+
{
225+
/*
226+
* Parse the query string. We need to do this ourself, so that we get access
227+
* to the raw (urlencoded) values. This is required because different software
228+
* can urlencode to different values.
229+
*/
230+
$data = [];
231+
$relayState = '';
232+
$sigAlg = '';
233+
$sigQuery = '';
234+
235+
foreach (explode('&', $_SERVER['QUERY_STRING']) as $e) {
236+
$tmp = explode('=', $e, 2);
237+
$name = $tmp[0];
238+
if (count($tmp) === 2) {
239+
$value = $tmp[1];
240+
} else {
241+
/* No value for this parameter. */
242+
$value = '';
243+
}
244+
245+
$name = urldecode($name);
246+
// Prevent keys from being set more than once
247+
if (array_key_exists($name, $data)) {
248+
throw new Exception('Duplicate parameter.');
249+
}
250+
$data[$name] = urldecode($value);
251+
252+
switch ($name) {
253+
case 'SAMLRequest':
254+
case 'SAMLResponse':
255+
$sigQuery = $name . '=' . $value;
256+
break;
257+
case 'RelayState':
258+
$relayState = '&RelayState=' . $value;
259+
break;
260+
case 'SigAlg':
261+
$sigAlg = '&SigAlg=' . $value;
262+
break;
263+
}
264+
}
265+
266+
if (array_key_exists('SAMLRequest', $data) && array_key_exists('SAMLResponse', $data)) {
267+
throw new Exception('Both SAMLRequest and SAMLResponse provided.');
268+
}
269+
270+
$data['SignedQuery'] = $sigQuery . $relayState . $sigAlg;
271+
272+
return $data;
273+
}
191274
}

0 commit comments

Comments
 (0)