Passed
Push — master ( 8a66a0...ca0319 )
by Stefan
04:21
created

Telepath::checkEtlrStatus()   D

Complexity

Conditions 9
Paths 16

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 28
nc 16
nop 0
dl 0
loc 38
rs 4.909
c 0
b 0
f 0
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 {
25
26
    // list of elements of the infrastructure which could be broken
27
    // along with their occurence probability (guesswork!)
28
    const INFRA_ETLR = "INFRA_ETLR";
29
    const INFRA_LINK_ETLR_NRO_IDP = "INFRA_LINK_ETLR_NRO_IdP";
30
    const INFRA_LINK_ETLR_NRO_SP = "INFRA_LINK_ETLR_NRO_SP";
31
    const INFRA_NRO_SP = "INFRA_NRO_SP";
32
    const INFRA_NRO_IDP = "INFRA_NRO_IdP";
33
    const INFRA_SP_RADIUS = "INFRA_SP_RADIUS";
34
    const INFRA_IDP_RADIUS = "INFRA_IdP_RADIUS";
35
    const INFRA_IDP_AUTHBACKEND = "INFRA_IdP_AUTHBACKEND";
36
    const INFRA_SP_80211 = "INFRA_SP_80211";
37
    const INFRA_SP_LAN = "INFRA_SP_LAN"; 
38
    const INFRA_DEVICE = "INFRA_DEVICE";
39
    const INFRA_NONEXISTENTREALM = "INFRA_NONEXISTENTREALM";
40
41
    private $probabilities;
42
    private $possibleFailureReasons;
43
    private $additionalFindings;
44
    private $realm;
45
    private $visitedFlr;
46
    private $visitedHotspot;
47
    private $catIdP;
48
    private $dbIdP;
49
    private $idPFederation;
50
    private $logHandle;
51
52
    public function __construct(string $realm, $visitedFlr = NULL, $visitedHotspot = NULL) {
53
        // everyone could be guilty
54
        $this->possibleFailureReasons = [
55
            Telepath::INFRA_ETLR,
56
            Telepath::INFRA_LINK_ETLR_NRO_IDP,
57
            Telepath::INFRA_LINK_ETLR_NRO_SP,
58
            Telepath::INFRA_NRO_SP,
59
            Telepath::INFRA_NRO_IDP,
60
            Telepath::INFRA_SP_RADIUS,
61
            Telepath::INFRA_IDP_RADIUS,
62
            Telepath::INFRA_IDP_AUTHBACKEND,
63
            Telepath::INFRA_SP_80211,
64
            Telepath::INFRA_SP_LAN,
65
            Telepath::INFRA_DEVICE,
66
            Telepath::INFRA_NONEXISTENTREALM,
67
        ];
68
69
        // these are NOT constant - in the course of checks, we may find a "smoking gun" and elevate the probability
70
        // in the end, use the numbers of those elements which were not deterministically excluded and normalise to 1
71
        // to get a percentage to report on.
72
        $this->probabilities = [
73
            Telepath::INFRA_ETLR => 0.01,
74
            Telepath::INFRA_LINK_ETLR_NRO_IDP => 0.01,
75
            Telepath::INFRA_LINK_ETLR_NRO_SP => 0.01,
76
            Telepath::INFRA_NRO_SP => 0.02,
77
            Telepath::INFRA_NRO_IDP => 0.02,
78
            Telepath::INFRA_SP_RADIUS => 0.04,
79
            Telepath::INFRA_IDP_RADIUS => 0.04,
80
            Telepath::INFRA_SP_80211 => 0.05,
81
            Telepath::INFRA_SP_LAN => 0.05,
82
            Telepath::INFRA_IDP_AUTHBACKEND => 0.02,
83
            Telepath::INFRA_DEVICE => 0.3,
84
            Telepath::INFRA_NONEXISTENTREALM => 0.7, /* if the eduroam DB were fully and consistently populated, this would have 1.0 - if we don't know anything about the realm, then this is not a valid eduroam realm. But reality says we don't have complete info in the DBs. */
85
        ];
86
        $this->additionalFindings = [];
87
        $this->realm = $realm;
88
        $this->visitedFlr = $visitedFlr;
89
        $this->visitedHotspot = $visitedHotspot;
90
        $links = \core\Federation::determineIdPIdByRealm($realm);
91
        $this->catIdP = $links["CAT"];
92
        $this->dbIdP = $links["EXTERNAL"];
93
        $this->idPFederation = $links["FEDERATION"];
94
        // this is NULL if the realm is not known in either DB
95
        // if so, let's try a regex to extract the ccTLD if any
96
        $matches = [];
97
        if ($this->idPFederation === NULL && preg_match("/\.(..)$/", $realm, $matches)) {
98
            $this->idPFederation = strtoupper($matches[1]);
99
        }
100
        $this->logHandle = new \core\common\Logging();
101
        $this->logHandle->debug(4, "XYZ: IdP-side NRO is " . $this->idPFederation . "\n");
102
    }
103
104
    const STATUS_GOOD = 0;
105
    const STATUS_PARTIAL = -1;
106
    const STATUS_DOWN = -2;
107
    const STATUS_MONITORINGFAIL = -3;
108
109
    /* The eduroam OT monitoring has the following return codes:
110
     * 
111
112
Status codes
113
114
  0 - O.K.
115
 -1 - Accept O.K. Reject No
116
 -2 - Reject O.K. Accept No
117
 -3 - Accept No Reject No
118
 -9 - system error
119
-10 - Accept O.K. Reject timeou
120
-11 - Accept O.K. Reject no EAP 
121
-20 - Reject O.K. Accept timeou
122
-21 - Reject O.K. Accept no EAP 
123
-31 - Accept No  Reject timeou
124
-32 - Accept Timeout Reject no
125
-33 - Accept Timeout Reject timeou
126
-35 - Accept No Reject no EAP
127
-36 - Reject No Accept no EAP 
128
-37 - Reject No EAP Accept no EAP 
129
-40 - UDP test error 
130
131
     */
132
    
133
    private function checkEtlrStatus() {
134
        // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one
135
        // needs a table to determine what goes where :-(
136
        $jsonResult = \core\common\OutsideComm::downloadFile("https://monitor.eduroam.org/mapi/index.php?type=tlr_test&tlr=TLR_EU");
137
        $decoded = json_decode($jsonResult, TRUE);
138
        $retval["RAW"] = $decoded;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$retval was never initialized. Although not strictly required by PHP, it is generally a good practice to add $retval = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
139
        $atLeastOneFunctional = FALSE;
140
        $allFunctional = TRUE;
141
        if (!isset($decoded['tlr_test']) || isset($decoded['ERROR'])) {
142
            $retval["STATUS"] = Telepath::STATUS_MONITORINGFAIL;
143
            return $retval;
144
        }
145
        foreach ($decoded['tlr_test'] as $instance => $resultset) {
146
            if ($instance == "tlr") {
147
                // don't care
148
                continue;
149
            }
150
            switch ($resultset['status_code']) {
151
                case 0:
152
                    $atLeastOneFunctional = TRUE;
153
                    break;
154
                case 9:
155
                    break;
156
                default:
157
                    $allFunctional = FALSE;
158
            }
159
        }
160
        if ($allFunctional) {
161
            $retval["STATUS"] = Telepath::STATUS_GOOD;
162
            return $retval;
163
        }
164
        if ($atLeastOneFunctional) {
165
            $retval["STATUS"] = Telepath::STATUS_PARTIAL;
166
            return $retval;
167
        }
168
        $retval["STATUS"] = Telepath::STATUS_DOWN;
169
        return $retval;
170
    }
171
172
    private function checkFedEtlrUplink($flr) {
0 ignored issues
show
Unused Code introduced by
The parameter $flr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
173
        // TODO: this is a stub, need eduroam OT API to query the current server status
174
        // APIQueryNROviaETLR($flr);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
175
        return Telepath::STATUS_GOOD;
176
    }
177
178
    private function checkFlrServerStatus($flr) {
0 ignored issues
show
Unused Code introduced by
The parameter $flr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
179
        // TODO: this is a stub, need eduroam OT API to query the current server status
180
        // APIQueryNRODirect($flr);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
181
        return Telepath::STATUS_GOOD;
182
    }
183
184
    private function checkNROFlow($visitedFlr, $homeFlr) {
0 ignored issues
show
Unused Code introduced by
The parameter $visitedFlr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $homeFlr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
185
        // TODO: this is a stub, need eduroam OT API to query the current server status
186
        // APIQueryNRODirect($visitedFlr, $homeFlr);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
187
        return Telepath::STATUS_GOOD;
188
    }
189
190
    public function magic() {
191
192
        // simple things first: do we know anything about the realm, either
193
        // because it's a CAT participant or because it's in the eduroam DB?
194
        // if so, we can exclude the INFRA_NONEXISTENTREALM cause
195
196
        $this->additionalFindings[Telepath::INFRA_NONEXISTENTREALM][] = ["ID1" => $this->catIdP, "ID2" => $this->dbIdP];
197
198
        if ($this->catIdP != \core\Federation::UNKNOWN_IDP || $this->dbIdP != \core\Federation::UNKNOWN_IDP) {
199
            $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NONEXISTENTREALM]);
200
        }
201
202
        // let's do the least amount of testing needed:
203
        // - The CAT reachability test already covers ELTRs, IdP NRO level and the IdP itself.
204
        //   if the realm maps to a CAT IdP, we can run the more thorough tests; otherwise just
205
        //   the normal shallow ones
206
207
        if ($this->catIdP > 0) {
208
            $idpObject = new \core\IdP($this->catIdP);
209
            $profileObjects = $idpObject->listProfiles();
210
211
            $bestProfile = FALSE;
212
213
214
            foreach ($profileObjects as $profileObject) {
215
                $mangledRealm = substr($profileObject->realm, strpos($profileObject->realm, "@") + 1);
216
                $readinessLevel = $profileObject->readinessLevel();
217
                if ($readinessLevel == \core\AbstractProfile::READINESS_LEVEL_SHOWTIME && $mangledRealm == $this->realm) {
218
                    $bestProfile = $profileObject;
219
                    break;
220
                }
221
                if ($readinessLevel == \core\AbstractProfile::READINESS_LEVEL_SUFFICIENTCONFIG && $profileObject->realm == $this->realm) {
222
                    $bestProfile = $profileObject;
223
                }
224
            }
225
            if ($bestProfile == FALSE) { // huh? no match on the realm. Then let's take the next-best with SUFFICIENTCONFIG
226
                foreach ($profileObjects as $profileObject) {
227
                    $readinessLevel = $profileObject->readinessLevel();
228
                    if ($readinessLevel == \core\AbstractProfile::READINESS_LEVEL_SUFFICIENTCONFIG) {
229
                        $bestProfile = $profileObject;
230
                        break;
231
                    }
232
                }
233
            }
234
            if ($bestProfile != FALSE) { // still nothing? then there's only a very incomplete profile definition, and we can't work with that. Fall back to shallow
235
                $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["Profile" => $bestProfile->identifier];
236
                $testsuite = new RADIUSTests($this->realm, $bestProfile->getRealmCheckOuterUsername(), $bestProfile->getEapMethodsinOrderOfPreference(1), $bestProfile->getCollapsedAttributes()['eap:server_name'], $bestProfile->getCollapsedAttributes()["eap:ca_file"]);
237
            } else {
238
                $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["Profile" => "UNCONCLUSIVE"];
239
                $testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm);
240
            }
241
        } else {
242
            $testsuite = new RADIUSTests($this->realm, "anonymous@" . $this->realm);
243
        }
244
245
        // we are expecting to get a REJECT from all runs, because that means the packet got through to the IdP.
246
        // (the ETLR sometimes does a "Reject instead of Ignore" but that is filtered out and changed into a timeout
247
        // by the test suite automatically, so it does not disturb the measurement)
248
        // If that's true, we can exclude two sources of problems (both proxy levels). Hooray!
249
        $allAreConversationReject = TRUE;
250
        $atLeastOneConversationReject = FALSE;
251
252
        foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'] as $probeindex => $probe) {
253
            $reachCheck = $testsuite->udpReachability($probeindex);
254
            if ($reachCheck != RADIUSTests::RETVAL_CONVERSATION_REJECT) {
255
                $allAreConversationReject = FALSE;
256
            } else {
257
                $atLeastOneConversationReject = TRUE;
258
            }
259
260
            $this->additionalFindings[Telepath::INFRA_ETLR][] = ["DETAIL" => $testsuite->consolidateUdpResult($probeindex)];
261
            $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["DETAIL" => $testsuite->consolidateUdpResult($probeindex)];
262
            $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["DETAIL" => $testsuite->consolidateUdpResult($probeindex)];
263
        }
264
265
        if ($allAreConversationReject) {
266
            $this->additionalFindings[Telepath::INFRA_ETLR][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
267
            $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
268
            $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT];
269
            $this->additionalFindings[Telepath::INFRA_LINK_ETLR_NRO_IDP][] = ["LINKCHECK" => RADIUSTests::L_OK];
270
            // we have actually reached an IdP, so all links are good, and the
271
            // realm is routable in eduroam. So even if it exists in neither DB
272
            // we can exclude the NONEXISTENTREALM case
273
            $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_ETLR, Telepath::INFRA_NRO_IDP, Telepath::INFRA_LINK_ETLR_NRO_IDP, Telepath::INFRA_NONEXISTENTREALM]);
274
        };
275
276
        if ($atLeastOneConversationReject) {
277
            // at least we can be sure it exists
278
            $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NONEXISTENTREALM]);
279
            // It could still be an IdP RADIUS problem in that some cert oddities 
280
            // in combination with the device lead to a broken auth
281
            // if there is nothing beyond the "REMARK" level, then it's not an IdP problem
282
            // otherwise, add the corresponding warnings and errors to $additionalFindings
283
            switch ($this->additionalFindings[Telepath::INFRA_IDP_RADIUS][0]['DETAIL']['level']) {
284
                case RADIUSTests::L_OK:
285
                case RADIUSTests::L_REMARK:
286
                    // both are fine - the IdP is working and the user problem
287
                    // is not on the IdP RADIUS level
288
                    $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][0]['DETAIL']['level']];
289
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_IDP_RADIUS]);
290
                    break;
291
                case RADIUSTests::L_WARN:
292
                    $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_WARN];
293
                    $this->probabilities[Telepath::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.
294
                    break;
295
                case RADIUSTests::L_ERROR:
296
                    $this->additionalFindings[Telepath::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_ERROR];
297
                    $this->probabilities[Telepath::INFRA_IDP_RADIUS] = 0.8; // errors are never good, so we can be reasonably sure we've hit the spot!
298
            }
299
        }
300
301
        // - if the test does NOT go through, we need to find out which of the three is guilty
302
        // - then, the international "via ETLR" check can be used to find out if the IdP alone
303
        //   is guilty. If that one fails, the direct monitoring of servers and ETLRs themselves
304
        //   closes the loop.
305
        // let's see if the ETLRs are up
306
307
        $etlrStatus = $this->checkEtlrStatus();
308
        $this->additionalFindings[Telepath::INFRA_ETLR][] = $etlrStatus;
309
        switch ($etlrStatus["STATUS"]) {
310
            case Telepath::STATUS_GOOD:
311
                $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_ETLR]);
312
                break;
313
            case Telepath::STATUS_PARTIAL:
314
            case Telepath::STATUS_MONITORINGFAIL:
315
                // one of the ETLRs is down, or there is a failure in the monitoring system? 
316
                // This probably doesn't impact the user unless he's unlucky and has his session fall into failover.
317
                // keep ETLR as a possible problem with original probability
318
                break;
319
            case Telepath::STATUS_DOWN:
320
                // Oh! Well if it is not international roaming, that still doesn't have an effect /in this case/. 
321
                if ($this->idPFederation == $this->visitedFlr) {
322
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_ETLR]);
323
                    break;
324
                }
325
                // But it is about int'l roaming, and we are spot on here.
326
                // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
327
                $this->probabilities[Telepath::INFRA_ETLR] = 0.95;
328
        }
329
        // next up: if the ETLR was okay, check the the FLR and its ETLR 
330
        // uplink (if we know which federation we are talking about)
331
332
        if (!in_array(Telepath::INFRA_ETLR, $this->possibleFailureReasons) && $this->idPFederation != NULL) {
333
            // first the direct connectivity to the server
334
            $flrServerStatus = $this->checkFlrServerStatus($this->idPFederation);
335 View Code Duplication
            switch ($flrServerStatus) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
336
                case Telepath::STATUS_GOOD:
337
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_GOOD];
338
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NRO_IDP]);
339
                    break;
340
                case Telepath::STATUS_PARTIAL:
341
                    // 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.
342
                    // keep FLR as a possible problem with original probability
343
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_PARTIAL];
344
                    break;
345
                case Telepath::STATUS_DOWN:
346
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_DOWN];
347
                    // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
348
                    $this->probabilities[Telepath::INFRA_NRO_IDP] = 0.95;
349
            }
350
            // then its uplink 
351
            $flrUplinkStatus = $this->checkFedEtlrUplink($this->idPFederation);
352 View Code Duplication
            switch ($flrUplinkStatus) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
353
                case Telepath::STATUS_GOOD:
354
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_GOOD];
355
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NRO_IDP, Telepath::INFRA_LINK_ETLR_NRO_IDP]);
356
                    break;
357
                case Telepath::STATUS_PARTIAL:
358
                    // 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.
359
                    // keep FLR as a possible problem with original probability
360
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_PARTIAL];
361
                    break;
362
                case Telepath::STATUS_DOWN:
363
                    $this->additionalFindings[Telepath::INFRA_NRO_IDP][] = ["STATUS" => Telepath::STATUS_DOWN];
364
                    // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
365
                    // if earlier test found the server itself to be the problem, keep it, otherwise put the blame on the link
366
                    if ($this->probabilities[Telepath::INFRA_NRO_IDP] != 0.95) {
367
                        $this->probabilities[Telepath::INFRA_LINK_ETLR_NRO_IDP] = 0.95;
368
                    }
369
            }
370
        }
371
372
        // now, if we know the country the user is currently in, let's see 
373
        // if the NRO SP-side is up
374
        if ($this->visitedFlr !== NULL) {
375
            $visitedFlrStatus = $this->checkFlrServerStatus($this->visitedFlr);
376
            // direct query to server
377 View Code Duplication
            switch ($visitedFlrStatus) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
378
                case Telepath::STATUS_GOOD:
379
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_GOOD];
380
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NRO_SP]);
381
                    break;
382
                case Telepath::STATUS_PARTIAL:
383
                    // 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.
384
                    // keep FLR as a possible problem with original probability
385
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_PARTIAL];
386
                    break;
387
                case Telepath::STATUS_DOWN:
388
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_DOWN];
389
                    // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
390
                    $this->probabilities[Telepath::INFRA_NRO_SP] = 0.95;
391
            }
392
            // and again its uplink to the ETLR
393
            $visitedFlrUplinkStatus = $this->checkFedEtlrUplink($this->visitedFlr);
394 View Code Duplication
            switch ($visitedFlrUplinkStatus) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
395
                case Telepath::STATUS_GOOD:
396
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_GOOD];
397
                    $this->possibleFailureReasons = array_diff($this->possibleFailureReasons, [Telepath::INFRA_NRO_SP, Telepath::INFRA_LINK_ETLR_NRO_SP]);
398
                    break;
399
                case Telepath::STATUS_PARTIAL:
400
                    // 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.
401
                    // keep FLR as a possible problem with original probability
402
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_PARTIAL];
403
                    break;
404
                case Telepath::STATUS_DOWN:
405
                    $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["STATUS" => Telepath::STATUS_DOWN];
406
                    // Raise probability by much (even monitoring is sometimes wrong, or a few minutes behind reality)
407
                    // if earlier test found the server itself to be the problem, keep it, otherwise put the blame on the link
408
                    if ($this->probabilities[Telepath::INFRA_NRO_SP] != 0.95) {
409
                        $this->probabilities[Telepath::INFRA_LINK_ETLR_NRO_SP] = 0.95;
410
                    }
411
            }
412
            // the last thing we can do (but it's a bit redundant): check the country-to-country link
413
            // it's only needed if all three and their links are up, but we want to exclude funny routing blacklists 
414
            // which occur only in the *combination* of source and dest
415
            // if there is an issue at that point, blame the SP: once a request would have reached the ETLRs, things would be all good. So they apparently didn't.
416
            if (!in_array(Telepath::INFRA_ETLR, $this->possibleFailureReasons) &&
417
                    !in_array(Telepath::INFRA_LINK_ETLR_NRO_IDP, $this->possibleFailureReasons) &&
418
                    !in_array(Telepath::INFRA_NRO_IDP, $this->possibleFailureReasons) &&
419
                    !in_array(Telepath::INFRA_LINK_ETLR_NRO_SP, $this->possibleFailureReasons) &&
420
                    !in_array(Telepath::INFRA_NRO_SP, $this->possibleFailureReasons)
421
            ) {
422
                $countryToCountryStatus = $this->checkNROFlow($this->visitedFlr, $this->idPFederation);
423
                switch ($countryToCountryStatus) {
424
                    case Telepath::STATUS_GOOD:
425
                        // all routes work
426
                        break;
427
                    case Telepath::STATUS_PARTIAL:
428
                    // at least one, or even all have a routing problem
429
                    case Telepath::STATUS_DOWN:
430
                        // that's rather telling.
431
                        $this->additionalFindings[Telepath::INFRA_NRO_SP][] = ["C2CLINK" => Telepath::STATUS_DOWN];
432
                        $this->probabilities[Telepath::INFRA_NRO_SP] = 0.95;
433
                }
434
            }
435
        }
436
437
        // done. return both the list of possible problem sources with their probabilities, and the additional findings we collected along the way.
438
        $totalScores = 0.;
439
        foreach ($this->possibleFailureReasons as $oneReason) {
440
            $totalScores += $this->probabilities[$oneReason];
441
        }
442
        $probArray = [];
443
        foreach ($this->possibleFailureReasons as $oneReason) {
444
            $probArray[$oneReason] = $this->probabilities[$oneReason] / $totalScores;
445
        }
446
        array_multisort($probArray, SORT_DESC, SORT_NUMERIC, $this->possibleFailureReasons);
447
448
        return ["SUSPECTS" => $this->possibleFailureReasons, "PROBABILITIES" => $probArray, "EVIDENCE" => $this->additionalFindings];
449
    }
450
451
}
452