@@ -123,14 +123,37 @@ public function send(AbstractMessage $message): ResponseInterface
123
123
*/
124
124
public function receive (ServerRequestInterface $ request ): AbstractMessage
125
125
{
126
- $ query = $ request ->getQueryParams ();
127
-
128
- if (array_key_exists ('SAMLRequest ' , $ query )) {
129
- $ message = $ query ['SAMLRequest ' ];
130
- $ signedQuery = 'SAMLRequest= ' . urlencode ($ query ['SAMLRequest ' ]);
131
- } elseif (array_key_exists ('SAMLResponse ' , $ query )) {
132
- $ message = $ query ['SAMLResponse ' ];
133
- $ signedQuery = 'SAMLResponse= ' . urlencode ($ query ['SAMLResponse ' ]);
126
+ $ query = $ this ->parseQuery ();
127
+ $ signedQuery = $ query ['SignedQuery ' ];
128
+
129
+ /**
130
+ * Get the SAMLRequest/SAMLResponse from the exact same signed data that will be verified later in
131
+ * validateSignature into $res using the actual SignedQuery
132
+ */
133
+ $ res = [];
134
+ foreach (explode ('& ' , $ signedQuery ) as $ e ) {
135
+ $ tmp = explode ('= ' , $ e , 2 );
136
+ $ name = $ tmp [0 ];
137
+ if (count ($ tmp ) === 2 ) {
138
+ $ value = $ tmp [1 ];
139
+ } else {
140
+ /* No value for this parameter. */
141
+ $ value = '' ;
142
+ }
143
+ $ name = urldecode ($ name );
144
+ $ res [$ name ] = urldecode ($ value );
145
+ }
146
+
147
+ /**
148
+ * Put the SAMLRequest/SAMLResponse from the actual query string into $message,
149
+ * and assert that the result from parseQuery() in $query and the parsing of the SignedQuery in $res agree
150
+ */
151
+ if (array_key_exists ('SAMLRequest ' , $ res )) {
152
+ Assert::same ($ res ['SAMLRequest ' ], $ query ['SAMLRequest ' ], 'Parse failure. ' );
153
+ $ message = $ res ['SAMLRequest ' ];
154
+ } elseif (array_key_exists ('SAMLResponse ' , $ res )) {
155
+ Assert::same ($ res ['SAMLResponse ' ], $ query ['SAMLResponse ' ], 'Parse failure. ' );
156
+ $ message = $ res ['SAMLResponse ' ];
134
157
} else {
135
158
throw new Exception ('Missing SAMLRequest or SAMLResponse parameter. ' );
136
159
}
@@ -154,7 +177,6 @@ public function receive(ServerRequestInterface $request): AbstractMessage
154
177
$ message = MessageFactory::fromXML ($ document ->documentElement );
155
178
156
179
if (array_key_exists ('RelayState ' , $ query )) {
157
- $ signedQuery .= '&RelayState= ' . urlencode ($ query ['RelayState ' ]);
158
180
$ this ->setRelayState ($ query ['RelayState ' ]);
159
181
}
160
182
@@ -174,8 +196,6 @@ public function receive(ServerRequestInterface $request): AbstractMessage
174
196
175
197
if (!array_key_exists ('SigAlg ' , $ query )) {
176
198
throw new Exception ('Missing signature algorithm. ' );
177
- } else {
178
- $ signedQuery .= '&SigAlg= ' . urlencode ($ query ['SigAlg ' ]);
179
199
}
180
200
181
201
$ container = ContainerSingleton::getInstance ();
@@ -192,4 +212,67 @@ public function receive(ServerRequestInterface $request): AbstractMessage
192
212
193
213
return $ message ;
194
214
}
215
+
216
+
217
+ /**
218
+ * Helper function to parse query data.
219
+ *
220
+ * This function returns the query string split into key=>value pairs.
221
+ * It also adds a new parameter, SignedQuery, which contains the data that is
222
+ * signed.
223
+ *
224
+ * @return array The query data that is signed.
225
+ * @throws \Exception
226
+ */
227
+ private static function parseQuery () : array
228
+ {
229
+ /*
230
+ * Parse the query string. We need to do this ourself, so that we get access
231
+ * to the raw (urlencoded) values. This is required because different software
232
+ * can urlencode to different values.
233
+ */
234
+ $ data = [];
235
+ $ relayState = '' ;
236
+ $ sigAlg = '' ;
237
+ $ sigQuery = '' ;
238
+
239
+ foreach (explode ('& ' , $ _SERVER ['QUERY_STRING ' ]) as $ e ) {
240
+ $ tmp = explode ('= ' , $ e , 2 );
241
+ $ name = $ tmp [0 ];
242
+ if (count ($ tmp ) === 2 ) {
243
+ $ value = $ tmp [1 ];
244
+ } else {
245
+ /* No value for this parameter. */
246
+ $ value = '' ;
247
+ }
248
+
249
+ $ name = urldecode ($ name );
250
+ // Prevent keys from being set more than once
251
+ if (array_key_exists ($ name , $ data )) {
252
+ throw new Exception ('Duplicate parameter. ' );
253
+ }
254
+ $ data [$ name ] = urldecode ($ value );
255
+
256
+ switch ($ name ) {
257
+ case 'SAMLRequest ' :
258
+ case 'SAMLResponse ' :
259
+ $ sigQuery = $ name . '= ' . $ value ;
260
+ break ;
261
+ case 'RelayState ' :
262
+ $ relayState = '&RelayState= ' . $ value ;
263
+ break ;
264
+ case 'SigAlg ' :
265
+ $ sigAlg = '&SigAlg= ' . $ value ;
266
+ break ;
267
+ }
268
+ }
269
+
270
+ if (array_key_exists ('SAMLRequest ' , $ data ) && array_key_exists ('SAMLResponse ' , $ data )) {
271
+ throw new Exception ('Both SAMLRequest and SAMLResponse provided. ' );
272
+ }
273
+
274
+ $ data ['SignedQuery ' ] = $ sigQuery . $ relayState . $ sigAlg ;
275
+
276
+ return $ data ;
277
+ }
195
278
}
0 commit comments