| Total Complexity | 82 |
| Total Lines | 423 |
| Duplicated Lines | 24.35 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
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 |
||
| 24 | class Telepath extends AbstractTest { |
||
| 25 | |||
| 26 | private $additionalFindings; |
||
| 27 | private $realm; |
||
| 28 | private $visitedFlr; |
||
| 29 | private $visitedHotspot; |
||
| 30 | private $catIdP; |
||
| 31 | private $dbIdP; |
||
| 32 | private $idPFederation; |
||
| 33 | private $testsuite; |
||
| 34 | |||
| 35 | public function __construct(string $realm, $visitedFlr = NULL, $visitedHotspot = NULL) { |
||
| 36 | parent::__construct(); |
||
| 37 | |||
| 38 | $this->additionalFindings = []; |
||
| 39 | $this->realm = $realm; |
||
| 40 | $this->visitedFlr = $visitedFlr; |
||
| 41 | $this->visitedHotspot = $visitedHotspot; |
||
| 42 | $links = \core\Federation::determineIdPIdByRealm($realm); |
||
| 43 | $this->catIdP = $links["CAT"]; |
||
| 44 | $this->dbIdP = $links["EXTERNAL"]; |
||
| 45 | $this->idPFederation = $links["FEDERATION"]; |
||
| 46 | // this is NULL if the realm is not known in either DB |
||
| 47 | // if so, let's try a regex to extract the ccTLD if any |
||
| 48 | $matches = []; |
||
| 49 | if ($this->idPFederation === NULL && preg_match("/\.(..)$/", $realm, $matches)) { |
||
| 50 | $this->idPFederation = strtoupper($matches[1]); |
||
| 51 | } |
||
| 52 | $this->loggerInstance->debug(4, "XYZ: IdP-side NRO is " . $this->idPFederation . "\n"); |
||
| 53 | } |
||
| 54 | |||
| 55 | /* The eduroam OT monitoring has the following return codes: |
||
| 56 | * |
||
| 57 | |||
| 58 | Status codes |
||
| 59 | |||
| 60 | 0 - O.K. |
||
| 61 | -1 - Accept O.K. Reject No |
||
| 62 | -2 - Reject O.K. Accept No |
||
| 63 | -3 - Accept No Reject No |
||
| 64 | -9 - system error |
||
| 65 | -10 - Accept O.K. Reject timeou |
||
| 66 | -11 - Accept O.K. Reject no EAP |
||
| 67 | -20 - Reject O.K. Accept timeou |
||
| 68 | -21 - Reject O.K. Accept no EAP |
||
| 69 | -31 - Accept No Reject timeou |
||
| 70 | -32 - Accept Timeout Reject no |
||
| 71 | -33 - Accept Timeout Reject timeou |
||
| 72 | -35 - Accept No Reject no EAP |
||
| 73 | -36 - Reject No Accept no EAP |
||
| 74 | -37 - Reject No EAP Accept no EAP |
||
| 75 | -40 - UDP test error |
||
| 76 | |||
| 77 | */ |
||
| 78 | |||
| 79 | private function genericAPIStatus($type, $param1 = NULL, $param2 = NULL) { |
||
| 80 | $endpoints = [ |
||
| 81 | 'tlr_test' => "https://monitor.eduroam.org/mapi/index.php?type=tlr_test&tlr=$param1", |
||
| 82 | 'federation_via_tlr' => "https://monitor.eduroam.org/mapi/index.php?type=federation_via_tlr&federation=$param1", |
||
| 83 | 'flrs_test' => "https://monitor.eduroam.org/mapi/index.php?type=flrs_test&federation=$param1", |
||
| 84 | 'flr_by_federation' => "https://monitor.eduroam.org/mapi/index.php?type=flr_by_federation&federation=$param2&with=$param1", |
||
| 85 | ]; |
||
| 86 | $ignore = [ |
||
| 87 | 'tlr_test' => 'tlr', |
||
| 88 | 'federation_via_tlr' => 'fed', |
||
| 89 | 'flrs_test' => 'fed', |
||
| 90 | 'flr_by_federation' => 'fed', |
||
| 91 | ]; |
||
| 92 | $this->loggerInstance->debug(4, "Doing Monitoring API check with $endpoints[$type]\n"); |
||
| 93 | $jsonResult = \core\common\OutsideComm::downloadFile($endpoints[$type]); |
||
| 94 | $this->loggerInstance->debug(4, "Monitoring API Result: $jsonResult\n"); |
||
| 95 | $decoded = json_decode($jsonResult, TRUE); |
||
| 96 | $retval = []; |
||
| 97 | $retval["RAW"] = $decoded; |
||
| 98 | $atLeastOneFunctional = FALSE; |
||
| 99 | $allFunctional = TRUE; |
||
| 100 | if (!isset($decoded[$type]) || isset($decoded['ERROR'])) { |
||
| 101 | $retval["STATUS"] = AbstractTest::STATUS_MONITORINGFAIL; |
||
| 102 | return $retval; |
||
| 103 | } |
||
| 104 | foreach ($decoded[$type] as $instance => $resultset) { |
||
| 105 | if ($instance == $ignore[$type]) { |
||
| 106 | // don't care |
||
| 107 | continue; |
||
| 108 | } |
||
| 109 | // TLR test has statuscode on this level, otherwise need to recurse |
||
| 110 | // one more level |
||
| 111 | switch ($type) { |
||
| 112 | case "tlr_test": |
||
| 113 | View Code Duplication | switch ($resultset['status_code']) { |
|
|
|
|||
| 114 | case 0: |
||
| 115 | $atLeastOneFunctional = TRUE; |
||
| 116 | break; |
||
| 117 | case 9: // monitoring itself has an error, no effect on our verdict |
||
| 118 | case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict |
||
| 119 | case -10: // same as previous |
||
| 120 | case -11: // same as previous |
||
| 121 | break; |
||
| 122 | default: |
||
| 123 | $allFunctional = FALSE; |
||
| 124 | } |
||
| 125 | break; |
||
| 126 | default: |
||
| 127 | foreach ($resultset as $particularInstance => $particularResultset) { |
||
| 128 | View Code Duplication | switch ($particularResultset['status_code']) { |
|
| 129 | case 0: |
||
| 130 | $atLeastOneFunctional = TRUE; |
||
| 131 | break; |
||
| 132 | case 9: // monitoring itself has an error, no effect on our verdict |
||
| 133 | case -1: // Reject test fails, but we diagnose supposed-working connection, so no effect on our verdict |
||
| 134 | case -10: // same as previous |
||
| 135 | case -11: // same as previous |
||
| 136 | break; |
||
| 137 | default: |
||
| 138 | $allFunctional = FALSE; |
||
| 139 | } |
||
| 140 | } |
||
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | if ($allFunctional) { |
||
| 145 | $retval["STATUS"] = AbstractTest::STATUS_GOOD; |
||
| 146 | return $retval; |
||
| 147 | } |
||
| 148 | if ($atLeastOneFunctional) { |
||
| 149 | $retval["STATUS"] = AbstractTest::STATUS_PARTIAL; |
||
| 150 | return $retval; |
||
| 151 | } |
||
| 152 | $retval["STATUS"] = AbstractTest::STATUS_DOWN; |
||
| 153 | return $retval; |
||
| 154 | } |
||
| 155 | |||
| 156 | private function checkEtlrStatus() { |
||
| 157 | // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one |
||
| 158 | // needs a table to determine what goes where :-( |
||
| 159 | return $this->genericAPIStatus("tlr_test", "TLR_EU"); |
||
| 160 | } |
||
| 161 | |||
| 162 | private function checkFedEtlrUplink($fed) { |
||
| 163 | // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one |
||
| 164 | // needs a table to determine what goes where :-( |
||
| 165 | return $this->genericAPIStatus("federation_via_tlr", $fed); |
||
| 166 | } |
||
| 167 | |||
| 168 | private function checkFlrServerStatus($fed) { |
||
| 169 | // TODO: we always check the European TLRs even though the connection in question might go via others and/or this one |
||
| 170 | // needs a table to determine what goes where :-( |
||
| 171 | return $this->genericAPIStatus("flrs_test", $fed); |
||
| 172 | } |
||
| 173 | |||
| 174 | private function checkNROFlow() { |
||
| 175 | return $this->genericAPIStatus("flr_by_federation", $this->idPFederation, $this->visitedFlr); |
||
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * Runs the CAT-internal diagnostics tests. Determines the state of the |
||
| 180 | * realm (and indirectly that of the links and statuses of involved proxies |
||
| 181 | * and returns a judgment whether external Monitoring API tests are warranted |
||
| 182 | * or not |
||
| 183 | * @return boolean TRUE if external tests have to be run |
||
| 184 | */ |
||
| 185 | private function CATInternalTests() { |
||
| 186 | // we are expecting to get a REJECT from all runs, because that means the packet got through to the IdP. |
||
| 187 | // (the ETLR sometimes does a "Reject instead of Ignore" but that is filtered out and changed into a timeout |
||
| 188 | // by the test suite automatically, so it does not disturb the measurement) |
||
| 189 | // If that's true, we can exclude two sources of problems (both proxy levels). Hooray! |
||
| 190 | $allAreConversationReject = TRUE; |
||
| 191 | $atLeastOneConversationReject = FALSE; |
||
| 192 | |||
| 193 | foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['UDP-hosts'] as $probeindex => $probe) { |
||
| 194 | $reachCheck = $this->testsuite->udpReachability($probeindex); |
||
| 195 | if ($reachCheck != RADIUSTests::RETVAL_CONVERSATION_REJECT) { |
||
| 196 | $allAreConversationReject = FALSE; |
||
| 197 | } else { |
||
| 198 | $atLeastOneConversationReject = TRUE; |
||
| 199 | } |
||
| 200 | |||
| 201 | $this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
||
| 202 | $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
||
| 203 | $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["DETAIL" => $this->testsuite->consolidateUdpResult($probeindex)]; |
||
| 204 | } |
||
| 205 | |||
| 206 | if ($allAreConversationReject) { |
||
| 207 | $this->additionalFindings[AbstractTest::INFRA_ETLR][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
||
| 208 | $this->additionalFindings[AbstractTest::INFRA_NRO_IDP][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
||
| 209 | $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["CONNCHECK" => RADIUSTests::RETVAL_CONVERSATION_REJECT]; |
||
| 210 | $this->additionalFindings[AbstractTest::INFRA_LINK_ETLR_NRO_IDP][] = ["LINKCHECK" => RADIUSTests::L_OK]; |
||
| 211 | // we have actually reached an IdP, so all links are good, and the |
||
| 212 | // realm is routable in eduroam. So even if it exists in neither DB |
||
| 213 | // we can exclude the NONEXISTENTREALM case |
||
| 214 | unset($this->possibleFailureReasons[AbstractTest::INFRA_ETLR]); |
||
| 215 | unset($this->possibleFailureReasons[AbstractTest::INFRA_NRO_IDP]); |
||
| 216 | unset($this->possibleFailureReasons[AbstractTest::INFRA_LINK_ETLR_NRO_IDP]); |
||
| 217 | unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]); |
||
| 218 | } |
||
| 219 | |||
| 220 | if ($atLeastOneConversationReject) { |
||
| 221 | // at least we can be sure it exists |
||
| 222 | unset($this->possibleFailureReasons[AbstractTest::INFRA_NONEXISTENTREALM]); |
||
| 223 | // It could still be an IdP RADIUS problem in that some cert oddities |
||
| 224 | // in combination with the device lead to a broken auth |
||
| 225 | // if there is nothing beyond the "REMARK" level, then it's not an IdP problem |
||
| 226 | // otherwise, add the corresponding warnings and errors to $additionalFindings |
||
| 227 | switch ($this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']) { |
||
| 228 | case RADIUSTests::L_OK: |
||
| 229 | case RADIUSTests::L_REMARK: |
||
| 230 | // both are fine - the IdP is working and the user problem |
||
| 231 | // is not on the IdP RADIUS level |
||
| 232 | $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][0]['DETAIL']['level']]; |
||
| 233 | unset($this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS]); |
||
| 234 | break; |
||
| 235 | case RADIUSTests::L_WARN: |
||
| 236 | $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_WARN]; |
||
| 237 | $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. |
||
| 238 | break; |
||
| 239 | case RADIUSTests::L_ERROR: |
||
| 240 | $this->additionalFindings[AbstractTest::INFRA_IDP_RADIUS][] = ["ODDITYLEVEL" => RADIUSTests::L_ERROR]; |
||
| 241 | $this->possibleFailureReasons[AbstractTest::INFRA_IDP_RADIUS] = 0.8; // errors are never good, so we can be reasonably sure we've hit the spot! |
||
| 242 | } |
||
| 243 | } |
||
| 244 | } |
||
| 245 | |||
| 246 | public function magic() { |
||
| 447 | } |
||
| 448 | |||
| 449 | } |
||
| 450 |
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.