Telepath   F
last analyzed

Complexity

Total Complexity 86

Size/Duplication

Total Lines 542
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 86
eloc 260
c 0
b 0
f 0
dl 0
loc 542
rs 2

10 Methods

Rating   Name   Duplication   Size   Complexity  
D magic() 0 84 18
A determineTestsuiteParameters() 0 21 5
B __construct() 0 26 7
B CATInternalTests() 0 62 10
A checkFlrServerStatus() 0 26 6
B checkFedEtlrUplink() 0 33 7
D genericAPIStatus() 0 80 20
A getOuter() 0 25 6
A checkEtlrStatus() 0 25 6
A checkNROFlow() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Telepath often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Telepath, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
/**
23
 * This file contains code for diagnostics tests
24
 *
25
 * @author Stefan Winter <[email protected]>
26
 * @author Maja Gorecka-Wolniewicz <[email protected]>
27
 *
28
 * @package Developer
29
 * 
30
 */
31
namespace core\diag;
32
33
use \Exception;
0 ignored issues
show
Bug introduced by
The type \Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
468
     */
469
    public function getOuter()
470
    {
471
        if ($this->catProfile > 0) {
472
            $profileObject = \core\ProfileFactory::instantiate($this->catProfile);
473
            $readinessLevel = $profileObject->readinessLevel();
474
            switch ($readinessLevel) {
475
                case \core\AbstractProfile::READINESS_LEVEL_SHOWTIME:
476
                // fall-througuh intended: use the data even if non-public but complete
477
                case \core\AbstractProfile::READINESS_LEVEL_SUFFICIENTCONFIG:
478
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => $profileObject->identifier];
479
                    $outer = $profileObject->getRealmCheckOuterUsername();
480
                    $p = strpos($outer, '@');
481
                    if ($p !== false) {
482
                        $outer = substr($outer, 0, $p);
483
                    }
484
                    break;
485
                case \core\AbstractProfile::READINESS_LEVEL_NOTREADY:
486
                    $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["Profile" => "UNCONCLUSIVE"];
487
                    break;
488
                default:
489
            }
490
        } else {
491
            $outer = "anonymous";
492
        }
493
        return $outer;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $outer does not seem to be defined for all execution paths leading up to this point.
Loading history...
494
    }
495
    /**
496
     * Does the main meditation job
497
     * @return array the findings
498
     */
499
    public function magic()
500
    {
501
        $this->testId = \core\CAT::uuid();
502
        $this->databaseHandle->exec("INSERT INTO diagnosticrun (test_id, realm, suspects, evidence) VALUES ('$this->testId', '$this->realm', NULL, NULL)");
503
        // simple things first: do we know anything about the realm, either
504
        // because it's a CAT participant or because it's in the eduroam DB?
505
        // if so, we can exclude the INFRA_NONEXISTENTREALM cause
506
        $this->additionalFindings[AbstractTest::INFRA_NONEXISTENTREALM]['DATABASE_STATUS'] = ["ID1" => $this->catProfile, "ID2" => $this->dbIdP];
507
        if ($this->catProfile != \core\Federation::UNKNOWN_IDP || $this->dbIdP != \core\Federation::UNKNOWN_IDP) {
508
            unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]);
509
        }
510
        // do we operate on a non-ambiguous, fully configured CAT profile? Then
511
        // we run the more thorough check, otherwise the shallow one.
512
        $this->determineTestSuiteParameters();
513
        // let's do the least amount of testing needed:
514
        // - The CAT reachability test already covers ELTRs, IdP NRO level and the IdP itself.
515
        //   if the realm maps to a CAT IdP, we can run the more thorough tests; otherwise just
516
        //   the normal shallow ones
517
        // these are the normal "realm check" tests covering ETLR, LINK_NRO_IDP, NRO, IDP_RADIUS
518
        $this->CATInternalTests();
519
        // - if the test does NOT go through, we need to find out which of the three is guilty
520
        // - then, the international "via ETLR" check can be used to find out if the IdP alone
521
        //   is guilty. If that one fails, the direct monitoring of servers and ETLRs themselves
522
        //   closes the loop.
523
        // let's see if the ETLRs are up
524
        if (array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons)) {
525
            $this->checkEtlrStatus();
526
        }
527
528
        // then let's check the IdP's FLR, if we know the IdP federation at all
529
        if ($this->idPFederation !== NULL) {
530
            if (array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons)) {
531
                // first the direct connectivity to the server
532
                $this->checkFlrServerStatus(AbstractTest::INFRA_NRO_IDP);
533
            }
534
            // now let's theck the link
535
            if (array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons)) {
536
                $this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_IDP);
537
            }
538
        }
539
        // now, if we know the country the user is currently in, let's see 
540
        // if the NRO SP-side is up
541
        if ($this->visitedFlr !== NULL) {
542
            $this->checkFlrServerStatus(AbstractTest::INFRA_NRO_SP);
543
            // and again its uplink to the ETLR
544
            $this->checkFedEtlrUplink(AbstractTest::INFRA_NRO_SP);
545
        }
546
        // the last thing we can do (but it's a bit redundant): check the country-to-country link
547
        // it's only needed if all three and their links are up, but we want to exclude funny routing blacklists 
548
        // which occur only in the *combination* of source and dest
549
        // if there is an issue at that point, blame the SP: once a request
550
        // would have reached the ETLRs, things would be all good (assuming
551
        // perfection on the ETLRs here!). So the SP has a wrong config.
552
        if ($this->idPFederation !== NULL &&
553
                $this->visitedFlr !== NULL &&
554
                !array_key_exists(AbstractTest::INFRA_ETLR, $this->possibleFailureReasons) &&
555
                !array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons) &&
556
                !array_key_exists(AbstractTest::INFRA_NRO_IDP, $this->possibleFailureReasons) &&
557
                !array_key_exists(AbstractTest::INFRA_LINK_ETLR_NRO_SP, $this->possibleFailureReasons) &&
558
                !array_key_exists(AbstractTest::INFRA_NRO_SP, $this->possibleFailureReasons)
559
        ) {
560
            $countryToCountryStatus = $this->checkNROFlow();
561
            $this->additionalFindings[AbstractTest::INFRA_NRO_SP][] = $countryToCountryStatus;
562
            $this->additionalFindings[AbstractTest::INFRA_ETLR][] = $countryToCountryStatus;
563
            $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = $countryToCountryStatus;
564
            switch ($countryToCountryStatus["STATUS"]) {
565
                case AbstractTest::STATUS_GOOD:
566
                    // all routes work
567
                    break;
568
                case AbstractTest::STATUS_PARTIAL:
569
                // at least one, or even all have a routing problem
570
                case AbstractTest::STATUS_DOWN:
571
                    // that's rather telling.
572
                    $this->possibleFailureReasons[AbstractTest::INFRA_NRO_SP] = 0.95;
573
            }
574
        }
575
        $this->normaliseResultSet();
576
        $jsonSuspects = json_encode($this->possibleFailureReasons, JSON_PRETTY_PRINT);
577
        $jsonEvidence = json_encode($this->additionalFindings, JSON_PRETTY_PRINT);
578
        $this->databaseHandle->exec("UPDATE diagnosticrun SET realm = ?, visited_flr = ?, visited_hotspot = ?, suspects = ?, evidence = ? WHERE test_id = ?", "ssssss", $this->realm, $this->visitedFlr, $this->visitedHotspot, $jsonSuspects, $jsonEvidence, $this->testId);
579
        $_SESSION['TESTID'] = $this->testId;
580
        $_SESSION["SUSPECTS"] = $this->possibleFailureReasons;
581
        $_SESSION["EVIDENCE"] = $this->additionalFindings;
582
        return ["SUSPECTS" => $this->possibleFailureReasons, "EVIDENCE" => $this->additionalFindings];
583
    }
584
}