|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* ****************************************************************************** |
|
5
|
|
|
* Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 |
|
6
|
|
|
* and GN4-2 consortia |
|
7
|
|
|
* |
|
8
|
|
|
* License: see the web/copyright.php file in the file structure |
|
9
|
|
|
* ****************************************************************************** |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace core\diag; |
|
13
|
|
|
|
|
14
|
|
|
use \Exception; |
|
15
|
|
|
|
|
16
|
|
|
require_once(dirname(dirname(__DIR__)) . "/config/_config.php"); |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* The overall coordination class that runs all kinds of tests to find out where |
|
20
|
|
|
* and what is wrong. Operates on the realm of a user. Can do more magic if it |
|
21
|
|
|
* also knows which federation the user is currently positioned in, or even |
|
22
|
|
|
* which exact hotspot to analyse. |
|
23
|
|
|
*/ |
|
24
|
|
|
class Telepath extends AbstractTest { |
|
25
|
|
|
|
|
26
|
|
|
private $realm; |
|
27
|
|
|
private $visitedFlr; |
|
28
|
|
|
private $visitedHotspot; |
|
29
|
|
|
private $catProfile; |
|
30
|
|
|
private $dbIdP; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* |
|
34
|
|
|
* @var string|null |
|
35
|
|
|
*/ |
|
36
|
|
|
private $idPFederation; |
|
37
|
|
|
private $testsuite; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* prime the Telepath with info it needs to know to successfully meditate over the problem |
|
41
|
|
|
* @param string $realm the realm of the user |
|
42
|
|
|
* @param string|null $visitedFlr which NRO is the user visiting |
|
43
|
|
|
* @param string|null $visitedHotspot external DB ID of the hotspot he visited |
|
44
|
|
|
*/ |
|
45
|
|
|
public function __construct(string $realm, $visitedFlr = NULL, $visitedHotspot = NULL) { |
|
46
|
|
|
// Telepath is the first one in a chain, no previous inputs allowed |
|
47
|
|
|
if (isset($_SESSION) && isset($_SESSION["SUSPECTS"])) { |
|
48
|
|
|
unset($_SESSION["SUSPECTS"]); |
|
49
|
|
|
} |
|
50
|
|
|
if (isset($_SESSION) && isset($_SESSION["EVIDENCE"])) { |
|
51
|
|
|
unset($_SESSION["EVIDENCE"]); |
|
52
|
|
|
} |
|
53
|
|
|
// now fill with default values |
|
54
|
|
|
parent::__construct(); |
|
55
|
|
|
$this->realm = $realm; |
|
56
|
|
|
$this->visitedFlr = $visitedFlr; |
|
57
|
|
|
$this->visitedHotspot = $visitedHotspot; |
|
58
|
|
|
$links = \core\Federation::determineIdPIdByRealm($realm); |
|
59
|
|
|
$this->catProfile = $links["CAT"]; |
|
60
|
|
|
$this->dbIdP = $links["EXTERNAL"]; |
|
61
|
|
|
$this->idPFederation = $links["FEDERATION"]; |
|
62
|
|
|
// this is NULL if the realm is not known in either DB |
|
63
|
|
|
// if so, let's try a regex to extract the ccTLD if any |
|
64
|
|
|
$matches = []; |
|
65
|
|
|
if ($this->idPFederation === NULL && preg_match("/\.(..)$/", $realm, $matches)) { |
|
66
|
|
|
$this->idPFederation = strtoupper($matches[1]); |
|
67
|
|
|
} |
|
68
|
|
|
$this->loggerInstance->debug(4, "XYZ: IdP-side NRO is " . $this->idPFederation . "\n"); |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
/* The eduroam OT monitoring has the following return codes: |
|
72
|
|
|
* |
|
73
|
|
|
|
|
74
|
|
|
Status codes |
|
75
|
|
|
|
|
76
|
|
|
0 - O.K. |
|
77
|
|
|
-1 - Accept O.K. Reject No |
|
78
|
|
|
-2 - Reject O.K. Accept No |
|
79
|
|
|
-3 - Accept No Reject No |
|
80
|
|
|
-9 - system error |
|
81
|
|
|
-10 - Accept O.K. Reject timeou |
|
82
|
|
|
-11 - Accept O.K. Reject no EAP |
|
83
|
|
|
-20 - Reject O.K. Accept timeou |
|
84
|
|
|
-21 - Reject O.K. Accept no EAP |
|
85
|
|
|
-31 - Accept No Reject timeou |
|
86
|
|
|
-32 - Accept Timeout Reject no |
|
87
|
|
|
-33 - Accept Timeout Reject timeou |
|
88
|
|
|
-35 - Accept No Reject no EAP |
|
89
|
|
|
-36 - Reject No Accept no EAP |
|
90
|
|
|
-37 - Reject No EAP Accept no EAP |
|
91
|
|
|
-40 - UDP test error |
|
92
|
|
|
|
|
93
|
|
|
*/ |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* ask the monitoring API about the things it knows |
|
97
|
|
|
* @param string $type which type of test to execute |
|
98
|
|
|
* @param string $param1 test-specific parameter number 1, if any |
|
99
|
|
|
* @param string $param2 test-specific parameter number 2, if any |
|
100
|
|
|
* @return array |
|
101
|
|
|
*/ |
|
102
|
|
|
private function genericAPIStatus($type, $param1 = NULL, $param2 = NULL) { |
|
103
|
|
|
$endpoints = [ |
|
104
|
|
|
'tlr_test' => "https://monitor.eduroam.org/mapi/index.php?type=tlr_test&tlr=$param1", |
|
105
|
|
|
'federation_via_tlr' => "https://monitor.eduroam.org/mapi/index.php?type=federation_via_tlr&federation=$param1", |
|
106
|
|
|
'flrs_test' => "https://monitor.eduroam.org/mapi/index.php?type=flrs_test&federation=$param1", |
|
107
|
|
|
'flr_by_federation' => "https://monitor.eduroam.org/mapi/index.php?type=flr_by_federation&federation=$param2&with=$param1", |
|
108
|
|
|
]; |
|
109
|
|
|
$ignore = [ |
|
110
|
|
|
'tlr_test' => 'tlr', |
|
111
|
|
|
'federation_via_tlr' => 'fed', |
|
112
|
|
|
'flrs_test' => 'fed', |
|
113
|
|
|
'flr_by_federation' => 'fed', |
|
114
|
|
|
]; |
|
115
|
|
|
$this->loggerInstance->debug(4, "Doing Monitoring API check with $endpoints[$type]\n"); |
|
116
|
|
|
$jsonResult = \core\common\OutsideComm::downloadFile($endpoints[$type]); |
|
117
|
|
|
$this->loggerInstance->debug(4, "Monitoring API Result: $jsonResult\n"); |
|
118
|
|
|
$decoded = json_decode($jsonResult, TRUE); |
|
119
|
|
|
$retval = []; |
|
120
|
|
|
$retval["RAW"] = $decoded; |
|
121
|
|
|
$atLeastOneFunctional = FALSE; |
|
122
|
|
|
$allFunctional = TRUE; |
|
123
|
|
|
if (!isset($decoded[$type]) || isset($decoded['ERROR'])) { |
|
124
|
|
|
$retval["STATUS"] = AbstractTest::STATUS_MONITORINGFAIL; |
|
125
|
|
|
return $retval; |
|
126
|
|
|
} |
|
127
|
|
|
foreach ($decoded[$type] as $instance => $resultset) { |
|
128
|
|
|
if ($instance == $ignore[$type]) { |
|
129
|
|
|
// don't care |
|
130
|
|
|
continue; |
|
131
|
|
|
} |
|
132
|
|
|
// TLR test has statuscode on this level, otherwise need to recurse |
|
133
|
|
|
// one more level |
|
134
|
|
|
switch ($type) { |
|
135
|
|
|
case "tlr_test": |
|
136
|
|
View Code Duplication |
switch ($resultset['status_code']) { |
|
|
|
|
|
|
137
|
|
|
case 0: |
|
138
|
|
|
$atLeastOneFunctional = TRUE; |
|
139
|
|
|
break; |
|
140
|
|
|
case 9: // monitoring itself has an error, no effect on our verdict |
|
141
|
|
|
case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict |
|
142
|
|
|
case -10: // same as previous |
|
143
|
|
|
case -11: // same as previous |
|
144
|
|
|
break; |
|
145
|
|
|
default: |
|
146
|
|
|
$allFunctional = FALSE; |
|
147
|
|
|
} |
|
148
|
|
|
break; |
|
149
|
|
|
default: |
|
150
|
|
|
foreach ($resultset as $particularInstance => $particularResultset) { |
|
151
|
|
View Code Duplication |
switch ($particularResultset['status_code']) { |
|
|
|
|
|
|
152
|
|
|
case 0: |
|
153
|
|
|
$atLeastOneFunctional = TRUE; |
|
154
|
|
|
break; |
|
155
|
|
|
case 9: // monitoring itself has an error, no effect on our verdict |
|
156
|
|
|
case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict |
|
157
|
|
|
case -10: // same as previous |
|
158
|
|
|
case -11: // same as previous |
|
159
|
|
|
break; |
|
160
|
|
|
default: |
|
161
|
|
|
$allFunctional = FALSE; |
|
162
|
|
|
} |
|
163
|
|
|
} |
|
164
|
|
|
} |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
if ($allFunctional) { |
|
168
|
|
|
$retval["STATUS"] = AbstractTest::STATUS_GOOD; |
|
169
|
|
|
return $retval; |
|
170
|
|
|
} |
|
171
|
|
|
if ($atLeastOneFunctional) { |
|
172
|
|
|
$retval["STATUS"] = AbstractTest::STATUS_PARTIAL; |
|
173
|
|
|
return $retval; |
|
174
|
|
|
} |
|
175
|
|
|
$retval["STATUS"] = AbstractTest::STATUS_DOWN; |
|
176
|
|
|
return $retval; |
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
|
|
/** |
|
180
|
|
|
* Are the ETLR servers in order? |
|
181
|
|
|
* @return array |
|
182
|
|
|
*/ |
|
183
|
|
|
private function checkEtlrStatus() { |
|
184
|
|
|
// TODO: we always check the European TLRs even though the connection in question might go via others and/or this one |
|
185
|
|
|
// needs a table to determine what goes where :-( |
|
186
|
|
|
$ret = $this->genericAPIStatus("tlr_test", "TLR_EU"); |
|
187
|
|
|
$this->additionalFindings[AbstractTest::INFRA_ETLR][] = $ret; |
|
188
|
|
|
switch ($ret["STATUS"]) { |
|
189
|
|
|
case AbstractTest::STATUS_GOOD: |
|
190
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]); |
|
191
|
|
|
break; |
|
192
|
|
|
case AbstractTest::STATUS_PARTIAL: |
|
193
|
|
|
case AbstractTest::STATUS_MONITORINGFAIL: |
|
194
|
|
|
// one of the ETLRs is down, or there is a failure in the monitoring system? |
|
195
|
|
|
// This probably doesn't impact the user unless he's unlucky and has his session fall into failover. |
|
196
|
|
|
// keep ETLR as a possible problem with original probability |
|
197
|
|
|
break; |
|
198
|
|
|
case AbstractTest::STATUS_DOWN: |
|
199
|
|
|
// Oh! Well if it is not international roaming, that still doesn't have an effect /in this case/. |
|
200
|
|
|
if ($this->idPFederation == $this->visitedFlr) { |
|
201
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]); |
|
202
|
|
|
break; |
|
203
|
|
|
} |
|
204
|
|
|
// But it is about int'l roaming, and we are spot on here. |
|
205
|
|
|
// Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality) |
|
206
|
|
|
$this->possibleFailureReasons[AbstractTest::INFRA_ETLR] = 0.95; |
|
207
|
|
|
} |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Is the uplink between an NRO server and the ETLRs in order? |
|
212
|
|
|
* @param int $whichSide |
|
213
|
|
|
* @return array |
|
214
|
|
|
*/ |
|
215
|
|
|
private function checkFedEtlrUplink($whichSide) { |
|
216
|
|
|
// TODO: we always check the European TLRs even though the connection in question might go via others and/or this one |
|
217
|
|
|
// needs a table to determine what goes where :-( |
|
218
|
|
|
switch ($whichSide) { |
|
219
|
|
|
case AbstractTest::INFRA_NRO_IDP: |
|
220
|
|
|
$fed = $this->idPFederation; |
|
221
|
|
|
$linkVariant = AbstractTest::INFRA_LINK_ETLR_NRO_IDP; |
|
222
|
|
|
break; |
|
223
|
|
|
case AbstractTest::INFRA_NRO_SP: |
|
224
|
|
|
$fed = $this->visitedFlr; |
|
|
|
|
|
|
225
|
|
|
$linkVariant = AbstractTest::INFRA_LINK_ETLR_NRO_SP; |
|
|
|
|
|
|
226
|
|
|
default: |
|
227
|
|
|
throw new Exception("This function operates on the IdP- or SP-side FLR, nothing else!"); |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
$ret = $this->genericAPIStatus("federation_via_tlr", $fed); |
|
231
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = $ret; |
|
232
|
|
|
switch ($ret["STATUS"]) { |
|
233
|
|
|
case AbstractTest::STATUS_GOOD: |
|
234
|
|
|
unset($this->possibleFailureReasons[$whichSide]); |
|
235
|
|
|
unset($this->possibleFailureReasons[$linkVariant]); |
|
236
|
|
|
break; |
|
237
|
|
|
case AbstractTest::STATUS_PARTIAL: |
|
238
|
|
|
// a subset of the FLRs is down? This probably doesn't impact the user unless he's unlucky and has his session fall into failover. |
|
239
|
|
|
// keep FLR as a possible problem with original probability |
|
240
|
|
|
break; |
|
241
|
|
|
case AbstractTest::STATUS_DOWN: |
|
242
|
|
|
// Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality) |
|
243
|
|
|
// if earlier test found the server itself to be the problem, keep it, otherwise put the blame on the link |
|
244
|
|
|
if ($this->possibleFailureReasons[$whichSide] != 0.95) { |
|
245
|
|
|
$this->possibleFailureReasons[$linkVariant] = 0.95; |
|
246
|
|
|
} |
|
247
|
|
|
} |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
/** |
|
251
|
|
|
* Is the NRO server itself in order? |
|
252
|
|
|
* @param int $whichSide |
|
253
|
|
|
* @return array |
|
254
|
|
|
*/ |
|
255
|
|
|
private function checkFlrServerStatus($whichSide) { |
|
256
|
|
|
switch ($whichSide) { |
|
257
|
|
|
case AbstractTest::INFRA_NRO_IDP: |
|
258
|
|
|
$fed = $this->idPFederation; |
|
259
|
|
|
break; |
|
260
|
|
|
case AbstractTest::INFRA_NRO_SP: |
|
261
|
|
|
$fed = $this->visitedFlr; |
|
|
|
|
|
|
262
|
|
|
default: |
|
263
|
|
|
throw new Exception("This function operates on the IdP- or SP-side FLR, nothing else!"); |
|
264
|
|
|
} |
|
265
|
|
|
|
|
266
|
|
|
$ret = $this->genericAPIStatus("flrs_test", $fed); |
|
267
|
|
|
$this->additionalFindings[$whichSide][] = $ret; |
|
268
|
|
|
switch ($ret["STATUS"]) { |
|
269
|
|
|
case AbstractTest::STATUS_GOOD: |
|
270
|
|
|
unset($this->possibleFailureReasons[$whichSide]); |
|
271
|
|
|
break; |
|
272
|
|
|
case AbstractTest::STATUS_PARTIAL: |
|
273
|
|
|
// a subset of the FLRs is down? This probably doesn't impact the user unless he's unlucky and has his session fall into failover. |
|
274
|
|
|
// keep FLR as a possible problem with original probability |
|
275
|
|
|
break; |
|
276
|
|
|
case AbstractTest::STATUS_DOWN: |
|
277
|
|
|
// Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality) |
|
278
|
|
|
$this->possibleFailureReasons[$whichSide] = 0.95; |
|
279
|
|
|
} |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
/** |
|
283
|
|
|
* Does authentication traffic flow between a given source and destination NRO? |
|
284
|
|
|
* @return array |
|
285
|
|
|
*/ |
|
286
|
|
|
private function checkNROFlow() { |
|
287
|
|
|
return $this->genericAPIStatus("flr_by_federation", $this->idPFederation, $this->visitedFlr); |
|
288
|
|
|
} |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Runs the CAT-internal diagnostics tests. Determines the state of the |
|
292
|
|
|
* realm (and indirectly that of the links and statuses of involved proxies |
|
293
|
|
|
* and returns a judgment whether external Monitoring API tests are warranted |
|
294
|
|
|
* or not |
|
295
|
|
|
* @return boolean TRUE if external tests have to be run |
|
296
|
|
|
*/ |
|
297
|
|
|
private function CATInternalTests() { |
|
298
|
|
|
// we are expecting to get a REJECT from all runs, because that means the packet got through to the IdP. |
|
299
|
|
|
// (the ETLR sometimes does a "Reject instead of Ignore" but that is filtered out and changed into a timeout |
|
300
|
|
|
// by the test suite automatically, so it does not disturb the measurement) |
|
301
|
|
|
// If that's true, we can exclude two sources of problems (both proxy levels). Hooray! |
|
302
|
|
|
$allAreConversationReject = TRUE; |
|
303
|
|
|
$atLeastOneConversationReject = FALSE; |
|
304
|
|
|
|
|
305
|
|
|
foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'] as $probeindex => $probe) { |
|
306
|
|
|
$reachCheck = $this->testsuite->udpReachability($probeindex); |
|
307
|
|
|
if ($reachCheck != RADIUSTests::RETVAL_CONVERSATION_REJECT) { |
|
308
|
|
|
$allAreConversationReject = FALSE; |
|
309
|
|
|
} else { |
|
310
|
|
|
$atLeastOneConversationReject = TRUE; |
|
311
|
|
|
} |
|
312
|
|
|
|
|
313
|
|
|
$this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
|
314
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
|
315
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
|
316
|
|
|
} |
|
317
|
|
|
|
|
318
|
|
|
if ($allAreConversationReject) { |
|
319
|
|
|
$this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
|
320
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
|
321
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
|
322
|
|
|
$this->additionalFindings[AbstractTest::INFRA_LINK_ETLR_NRO_IDP][] = ["LINKCHECK" => RADIUSTests::L_OK]; |
|
323
|
|
|
// we have actually reached an IdP, so all links are good, and the |
|
324
|
|
|
// realm is routable in eduroam. So even if it exists in neither DB |
|
325
|
|
|
// we can exclude the NONEXISTENTREALM case |
|
326
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]); |
|
327
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_NRO_IDP]); |
|
328
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_LINK_ETLR_NRO_IDP]); |
|
329
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]); |
|
330
|
|
|
} |
|
331
|
|
|
|
|
332
|
|
|
if ($atLeastOneConversationReject) { |
|
333
|
|
|
// at least we can be sure it exists |
|
334
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]); |
|
335
|
|
|
// It could still be an IdP RADIUS problem in that some cert oddities |
|
336
|
|
|
// in combination with the device lead to a broken auth |
|
337
|
|
|
// if there is nothing beyond the "REMARK" level, then it's not an IdP problem |
|
338
|
|
|
// otherwise, add the corresponding warnings and errors to $additionalFindings |
|
339
|
|
|
switch ($this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']) { |
|
340
|
|
|
case RADIUSTests::L_OK: |
|
341
|
|
|
case RADIUSTests::L_REMARK: |
|
342
|
|
|
// both are fine - the IdP is working and the user problem |
|
343
|
|
|
// is not on the IdP RADIUS level |
|
344
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']]; |
|
345
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS]); |
|
346
|
|
|
break; |
|
347
|
|
|
case RADIUSTests::L_WARN: |
|
348
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_WARN]; |
|
349
|
|
|
$this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS] = 0.3; // possibly we found the culprit - if RADIUS server is misconfigured AND user is on a device which reacts picky about exactly this oddity. |
|
350
|
|
|
break; |
|
351
|
|
|
case RADIUSTests::L_ERROR: |
|
352
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_ERROR]; |
|
353
|
|
|
$this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS] = 0.8; // errors are never good, so we can be reasonably sure we've hit the spot! |
|
354
|
|
|
} |
|
355
|
|
|
} |
|
356
|
|
|
} |
|
357
|
|
|
|
|
358
|
|
|
private function determineTestsuiteParameters() { |
|
359
|
|
|
if ($this->catProfile > 0) { |
|
360
|
|
|
$profileObject = \core\ProfileFactory::instantiate($this->catProfile); |
|
361
|
|
|
$readinessLevel = $profileObject->readinessLevel(); |
|
362
|
|
|
|
|
363
|
|
|
switch ($readinessLevel) { |
|
364
|
|
|
case \core\AbstractProfile::READINESS_LEVEL_SHOWTIME: |
|
365
|
|
|
case \core\AbstractProfile::READINESS_LEVEL_SUFFICIENTCONFIG: |
|
366
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => $profileObject->identifier]; |
|
367
|
|
|
$this->testsuite = new RADIUSTests($this->realm, $profileObject->getRealmCheckOuterUsername(), $profileObject->getEapMethodsinOrderOfPreference(1), $profileObject->getCollapsedAttributes()['eap:server_name'], $profileObject->getCollapsedAttributes()["eap:ca_file"]); |
|
368
|
|
|
|
|
369
|
|
|
case \core\AbstractProfile::READINESS_LEVEL_NOTREADY: |
|
370
|
|
|
$this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => "UNCONCLUSIVE"]; |
|
371
|
|
|
$this->testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm); |
|
372
|
|
|
break; |
|
373
|
|
|
default: |
|
374
|
|
|
} |
|
375
|
|
|
} else { |
|
376
|
|
|
$this->testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm); |
|
377
|
|
|
} |
|
378
|
|
|
} |
|
379
|
|
|
|
|
380
|
|
|
/** |
|
381
|
|
|
* Does the main meditation job |
|
382
|
|
|
* @return array the findings |
|
383
|
|
|
*/ |
|
384
|
|
|
public function magic() { |
|
385
|
|
|
|
|
386
|
|
|
// simple things first: do we know anything about the realm, either |
|
387
|
|
|
// because it's a CAT participant or because it's in the eduroam DB? |
|
388
|
|
|
// if so, we can exclude the INFRA_NONEXISTENTREALM cause |
|
389
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NONEXISTENTREALM]['DATABASE_STATUS'] = ["ID1" => $this->catProfile, "ID2" => $this->dbIdP]; |
|
390
|
|
|
if ($this->catProfile != \core\Federation::UNKNOWN_IDP || $this->dbIdP != \core\Federation::UNKNOWN_IDP) { |
|
391
|
|
|
unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]); |
|
392
|
|
|
} |
|
393
|
|
|
// do we operate on a non-ambiguous, fully configured CAT profile? Then |
|
394
|
|
|
// we run the more thorough check, otherwise the shallow one. |
|
395
|
|
|
$this->determineTestSuiteParameters(); |
|
396
|
|
|
// let's do the least amount of testing needed: |
|
397
|
|
|
// - The CAT reachability test already covers ELTRs, IdP NRO level and the IdP itself. |
|
398
|
|
|
// if the realm maps to a CAT IdP, we can run the more thorough tests; otherwise just |
|
399
|
|
|
// the normal shallow ones |
|
400
|
|
|
// these are the normal "realm check" tests covering ETLR, LINK_NRO_IDP, NRO, IDP_RADIUS |
|
401
|
|
|
$this->CATInternalTests(); |
|
402
|
|
|
// - if the test does NOT go through, we need to find out which of the three is guilty |
|
403
|
|
|
// - then, the international "via ETLR" check can be used to find out if the IdP alone |
|
404
|
|
|
// is guilty. If that one fails, the direct monitoring of servers and ETLRs themselves |
|
405
|
|
|
// closes the loop. |
|
406
|
|
|
// let's see if the ETLRs are up |
|
407
|
|
|
if (array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons)) { |
|
408
|
|
|
$this->checkEtlrStatus(); |
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
// then let's check the IdP's FLR, if we know the IdP federation at all |
|
412
|
|
|
if ($this->idPFederation !== NULL) { |
|
413
|
|
|
if (array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons)) { |
|
414
|
|
|
// first the direct connectivity to the server |
|
415
|
|
|
$this->checkFlrServerStatus(AbstractTest::INFRA_NRO_IDP); |
|
416
|
|
|
} |
|
417
|
|
|
// now let's theck the link |
|
418
|
|
|
if (array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons)) { |
|
419
|
|
|
$this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_IDP); |
|
420
|
|
|
} |
|
421
|
|
|
} |
|
422
|
|
|
// now, if we know the country the user is currently in, let's see |
|
423
|
|
|
// if the NRO SP-side is up |
|
424
|
|
|
if ($this->visitedFlr !== NULL) { |
|
425
|
|
|
$this->checkFlrServerStatus(AbstractTest::INFRA_NRO_SP); |
|
426
|
|
|
// and again its uplink to the ETLR |
|
427
|
|
|
$this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_SP); |
|
428
|
|
|
} |
|
429
|
|
|
// the last thing we can do (but it's a bit redundant): check the country-to-country link |
|
430
|
|
|
// it's only needed if all three and their links are up, but we want to exclude funny routing blacklists |
|
431
|
|
|
// which occur only in the *combination* of source and dest |
|
432
|
|
|
// if there is an issue at that point, blame the SP: once a request |
|
433
|
|
|
// would have reached the ETLRs, things would be all good (assuming |
|
434
|
|
|
// perfection on the ETLRs here!). So the SP has a wrong config. |
|
435
|
|
|
if ($this->idPFederation !== NULL && |
|
436
|
|
|
$this->visitedFlr !== NULL && |
|
437
|
|
|
!array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons) && |
|
438
|
|
|
!array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons) && |
|
439
|
|
|
!array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons) && |
|
440
|
|
|
!array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_SP, $this->possibleFailureReasons) && |
|
441
|
|
|
!array_key_exists(AbstractTest::INFRA_NRO_SP, $this->possibleFailureReasons) |
|
442
|
|
|
) { |
|
443
|
|
|
$countryToCountryStatus = $this->checkNROFlow(); |
|
444
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NRO_SP][] = $countryToCountryStatus; |
|
445
|
|
|
$this->additionalFindings[AbstractTest::INFRA_ETLR][] = $countryToCountryStatus; |
|
446
|
|
|
$this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = $countryToCountryStatus; |
|
447
|
|
|
switch ($countryToCountryStatus["STATUS"]) { |
|
448
|
|
|
case AbstractTest::STATUS_GOOD: |
|
449
|
|
|
// all routes work |
|
450
|
|
|
break; |
|
451
|
|
|
case AbstractTest::STATUS_PARTIAL: |
|
452
|
|
|
// at least one, or even all have a routing problem |
|
453
|
|
|
case AbstractTest::STATUS_DOWN: |
|
454
|
|
|
// that's rather telling. |
|
455
|
|
|
$this->possibleFailureReasons[AbstractTest::INFRA_NRO_SP] = 0.95; |
|
456
|
|
|
} |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
$this->normaliseResultSet(); |
|
460
|
|
|
|
|
461
|
|
|
$_SESSION["SUSPECTS"] = $this->possibleFailureReasons; |
|
462
|
|
|
$_SESSION["EVIDENCE"] = $this->additionalFindings; |
|
463
|
|
|
return ["SUSPECTS" => $this->possibleFailureReasons, "EVIDENCE" => $this->additionalFindings]; |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
|
|
} |
|
467
|
|
|
|
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.