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:
| 1 | <?php |
||
| 48 | class StepUpAuthenticationService |
||
| 49 | { |
||
| 50 | /** |
||
| 51 | * @var \Surfnet\StepupBundle\Service\LoaResolutionService |
||
| 52 | */ |
||
| 53 | private $loaResolutionService; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * @var \Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactorRepository |
||
| 57 | */ |
||
| 58 | private $secondFactorRepository; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * @var \Surfnet\StepupGateway\ApiBundle\Service\YubikeyService |
||
| 62 | */ |
||
| 63 | private $yubikeyService; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * @var \Surfnet\StepupBundle\Service\SmsSecondFactorService |
||
| 67 | */ |
||
| 68 | private $smsService; |
||
| 69 | |||
| 70 | /** |
||
| 71 | * @var \Symfony\Component\Translation\TranslatorInterface |
||
| 72 | */ |
||
| 73 | private $translator; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * @var \Psr\Log\LoggerInterface |
||
| 77 | */ |
||
| 78 | private $logger; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @param LoaResolutionService $loaResolutionService |
||
| 82 | * @param SecondFactorRepository $secondFactorRepository |
||
| 83 | * @param YubikeyService $yubikeyService |
||
| 84 | * @param SmsSecondFactorService $smsService |
||
| 85 | * @param TranslatorInterface $translator |
||
| 86 | * @param LoggerInterface $logger |
||
| 87 | */ |
||
| 88 | public function __construct( |
||
| 89 | LoaResolutionService $loaResolutionService, |
||
| 90 | SecondFactorRepository $secondFactorRepository, |
||
| 91 | YubikeyService $yubikeyService, |
||
| 92 | SmsSecondFactorService $smsService, |
||
| 93 | TranslatorInterface $translator, |
||
| 94 | LoggerInterface $logger |
||
| 95 | ) { |
||
| 96 | $this->loaResolutionService = $loaResolutionService; |
||
| 97 | $this->secondFactorRepository = $secondFactorRepository; |
||
| 98 | $this->yubikeyService = $yubikeyService; |
||
| 99 | $this->smsService = $smsService; |
||
| 100 | $this->translator = $translator; |
||
| 101 | $this->logger = $logger; |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * @param string $identityNameId |
||
| 106 | * @param Loa $requiredLoa |
||
| 107 | * @return \Doctrine\Common\Collections\ArrayCollection |
||
| 108 | */ |
||
| 109 | public function determineViableSecondFactors( |
||
| 110 | $identityNameId, |
||
| 111 | Loa $requiredLoa |
||
| 112 | ) { |
||
| 113 | $candidateSecondFactors = $this->secondFactorRepository->getAllMatchingFor($requiredLoa, $identityNameId); |
||
| 114 | $this->logger->info( |
||
| 115 | sprintf('Loaded %d matching candidate second factors', count($candidateSecondFactors)) |
||
| 116 | ); |
||
| 117 | |||
| 118 | return $candidateSecondFactors; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * @param string $requestedLoa |
||
| 123 | * @param ServiceProvider $serviceProvider |
||
| 124 | * @param IdentityProvider $authenticatingIdp |
||
|
|
|||
| 125 | * @return null|Loa |
||
| 126 | * |
||
| 127 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) see https://www.pivotaltracker.com/story/show/96065350 |
||
| 128 | * @SuppressWarnings(PHPMD.NPathComplexity) see https://www.pivotaltracker.com/story/show/96065350 |
||
| 129 | */ |
||
| 130 | public function resolveHighestRequiredLoa( |
||
| 131 | $requestedLoa, |
||
| 132 | ServiceProvider $serviceProvider, |
||
| 133 | IdentityProvider $authenticatingIdp = null |
||
| 134 | ) { |
||
| 135 | $loaCandidates = new ArrayCollection(); |
||
| 136 | |||
| 137 | if ($requestedLoa) { |
||
| 138 | $loaCandidates->add($requestedLoa); |
||
| 139 | $this->logger->info(sprintf('Added requested Loa "%s" as candidate', $requestedLoa)); |
||
| 140 | } |
||
| 141 | |||
| 142 | $spConfiguredLoas = $serviceProvider->get('configuredLoas'); |
||
| 143 | $loaCandidates->add($spConfiguredLoas['__default__']); |
||
| 144 | $this->logger->info(sprintf('Added SP\'s default Loa "%s" as candidate', $spConfiguredLoas['__default__'])); |
||
| 145 | |||
| 146 | if ($authenticatingIdp) { |
||
| 147 | View Code Duplication | if (array_key_exists($authenticatingIdp->getEntityId(), $spConfiguredLoas)) { |
|
| 148 | $loaCandidates->add($spConfiguredLoas[$authenticatingIdp->getEntityId()]); |
||
| 149 | $this->logger->info(sprintf( |
||
| 150 | 'Added SP\'s Loa "%s" for this IdP as candidate', |
||
| 151 | $spConfiguredLoas[$authenticatingIdp->getEntityId()] |
||
| 152 | )); |
||
| 153 | } |
||
| 154 | |||
| 155 | $idpConfiguredLoas = $authenticatingIdp->get('configuredLoas'); |
||
| 156 | $loaCandidates->add($idpConfiguredLoas['__default__']); |
||
| 157 | $this->logger->info( |
||
| 158 | sprintf('Added authenticating IdP\'s default Loa "%s" as candidate', $spConfiguredLoas['__default__']) |
||
| 159 | ); |
||
| 160 | |||
| 161 | View Code Duplication | if (array_key_exists($serviceProvider->getEntityId(), $idpConfiguredLoas)) { |
|
| 162 | $loaCandidates->add($idpConfiguredLoas[$serviceProvider->getEntityId()]); |
||
| 163 | $this->logger->info(sprintf( |
||
| 164 | 'Added authenticating IdP\'s Loa "%s" for this SP as candidate', |
||
| 165 | $idpConfiguredLoas[$serviceProvider->getEntityId()] |
||
| 166 | )); |
||
| 167 | } |
||
| 168 | } |
||
| 169 | |||
| 170 | if (!count($loaCandidates)) { |
||
| 171 | throw new RuntimeException('No Loa can be found, at least one Loa (SP default) should be found'); |
||
| 172 | } |
||
| 173 | |||
| 174 | $actualLoas = new ArrayCollection(); |
||
| 175 | foreach ($loaCandidates as $loaDefinition) { |
||
| 176 | $loa = $this->loaResolutionService->getLoa($loaDefinition); |
||
| 177 | if ($loa) { |
||
| 178 | $actualLoas->add($loa); |
||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | if (!count($actualLoas)) { |
||
| 183 | $this->logger->info(sprintf( |
||
| 184 | 'Out of "%d" candidates, no existing Loa could be found, no authentication is possible.', |
||
| 185 | count($loaCandidates) |
||
| 186 | )); |
||
| 187 | |||
| 188 | return null; |
||
| 189 | } |
||
| 190 | |||
| 191 | /** @var \Surfnet\StepupBundle\Value\Loa $highestLoa */ |
||
| 192 | $highestLoa = $actualLoas->first(); |
||
| 193 | foreach ($actualLoas as $loa) { |
||
| 194 | // if the current highest Loa cannot satisfy the next Loa, that must be of a higher level... |
||
| 195 | if (!$highestLoa->canSatisfyLoa($loa)) { |
||
| 196 | $highestLoa = $loa; |
||
| 197 | } |
||
| 198 | } |
||
| 199 | |||
| 200 | $this->logger->info( |
||
| 201 | sprintf('Out of %d candidate Loa\'s, Loa "%s" is the highest', count($loaCandidates), $highestLoa) |
||
| 202 | ); |
||
| 203 | |||
| 204 | return $highestLoa; |
||
| 205 | } |
||
| 206 | |||
| 207 | /** |
||
| 208 | * Returns whether the given Loa identifier identifies the minimum Loa, intrinsic to being authenticated via an IdP. |
||
| 209 | * |
||
| 210 | * @param Loa $loa |
||
| 211 | * @return bool |
||
| 212 | */ |
||
| 213 | public function isIntrinsicLoa(Loa $loa) |
||
| 214 | { |
||
| 215 | return $loa->levelIsLowerOrEqualTo(Loa::LOA_1); |
||
| 216 | } |
||
| 217 | |||
| 218 | /** |
||
| 219 | * @param VerifyYubikeyOtpCommand $command |
||
| 220 | * @return YubikeyOtpVerificationResult |
||
| 221 | */ |
||
| 222 | public function verifyYubikeyOtp(VerifyYubikeyOtpCommand $command) |
||
| 252 | |||
| 253 | /** |
||
| 254 | * @param string $secondFactorId |
||
| 255 | * @return string |
||
| 256 | */ |
||
| 257 | public function getSecondFactorIdentifier($secondFactorId) |
||
| 264 | |||
| 265 | /** |
||
| 266 | * @return int |
||
| 267 | */ |
||
| 268 | public function getSmsOtpRequestsRemainingCount() |
||
| 272 | |||
| 273 | /** |
||
| 274 | * @return int |
||
| 275 | */ |
||
| 276 | public function getSmsMaximumOtpRequestsCount() |
||
| 280 | |||
| 281 | /** |
||
| 282 | * @param SendSmsChallengeCommand $command |
||
| 283 | * @return bool |
||
| 284 | */ |
||
| 285 | public function sendSmsChallenge(SendSmsChallengeCommand $command) |
||
| 300 | |||
| 301 | /** |
||
| 302 | * @param VerifyPossessionOfPhoneCommand $command |
||
| 303 | * @return OtpVerification |
||
| 304 | */ |
||
| 305 | public function verifySmsChallenge(VerifyPossessionOfPhoneCommand $command) |
||
| 309 | |||
| 310 | public function clearSmsVerificationState() |
||
| 314 | } |
||
| 315 |
This check looks for
@paramannotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.