| Total Complexity | 54 | 
| Total Lines | 617 | 
| Duplicated Lines | 0 % | 
| Changes | 9 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like SecondFactorController 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 SecondFactorController, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 54 | class SecondFactorController extends Controller  | 
            ||
| 55 | { | 
            ||
| 56 | const MODE_SFO = 'sfo';  | 
            ||
| 57 | const MODE_SSO = 'sso';  | 
            ||
| 58 | |||
| 59 | public function selectSecondFactorForVerificationSsoAction(Request $request)  | 
            ||
| 60 |     { | 
            ||
| 61 | return $this->selectSecondFactorForVerificationAction(self::MODE_SSO, $request);  | 
            ||
| 62 | }  | 
            ||
| 63 | |||
| 64 | public function selectSecondFactorForVerificationSfoAction(Request $request)  | 
            ||
| 65 |     { | 
            ||
| 66 | return $this->selectSecondFactorForVerificationAction(self::MODE_SFO, $request);  | 
            ||
| 67 | }  | 
            ||
| 68 | |||
| 69 | public function selectSecondFactorForVerificationAction($authenticationMode, Request $request)  | 
            ||
| 70 |     { | 
            ||
| 71 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 72 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 73 | |||
| 74 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 75 | |||
| 76 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 77 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 78 |         $logger->notice('Determining which second factor to use...'); | 
            ||
| 79 | |||
| 80 |         try { | 
            ||
| 81 | // Retrieve all requirements to determine the required LoA  | 
            ||
| 82 | $requestedLoa = $context->getRequiredLoa();  | 
            ||
| 83 |             $spConfiguredLoas = $context->getServiceProvider()->get('configuredLoas'); | 
            ||
| 84 | |||
| 85 | $identityNameId = $context->getIdentityNameId();  | 
            ||
| 86 | $normalizedIdpSho = $context->getNormalizedSchacHomeOrganization();  | 
            ||
| 87 | $normalizedUserSho = $this->getStepupService()->getNormalizedUserShoByIdentityNameId($identityNameId);  | 
            ||
| 88 | |||
| 89 | $requiredLoa = $this  | 
            ||
| 90 | ->getStepupService()  | 
            ||
| 91 | ->resolveHighestRequiredLoa(  | 
            ||
| 92 | $requestedLoa,  | 
            ||
| 93 | $spConfiguredLoas,  | 
            ||
| 94 | $normalizedIdpSho,  | 
            ||
| 95 | $normalizedUserSho  | 
            ||
| 96 | );  | 
            ||
| 97 |         } catch (LoaCannotBeGivenException $e) { | 
            ||
| 98 | // Log the message of the domain exception, this contains a meaningful message.  | 
            ||
| 99 | $logger->notice($e->getMessage());  | 
            ||
| 100 | |||
| 101 | return $this->forward(  | 
            ||
| 102 | 'SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven',  | 
            ||
| 103 | ['authenticationMode' => $authenticationMode]  | 
            ||
| 104 | );  | 
            ||
| 105 | }  | 
            ||
| 106 | |||
| 107 |         $logger->notice(sprintf('Determined that the required Loa is "%s"', $requiredLoa)); | 
            ||
| 108 | |||
| 109 |         if ($this->getStepupService()->isIntrinsicLoa($requiredLoa)) { | 
            ||
| 110 |             $this->get('gateway.authentication_logger')->logIntrinsicLoaAuthentication($originalRequestId); | 
            ||
| 111 | return $this->forward($context->getResponseAction());  | 
            ||
| 112 | }  | 
            ||
| 113 | |||
| 114 | $secondFactorCollection = $this  | 
            ||
| 115 | ->getStepupService()  | 
            ||
| 116 | ->determineViableSecondFactors(  | 
            ||
| 117 | $context->getIdentityNameId(),  | 
            ||
| 118 | $requiredLoa,  | 
            ||
| 119 |                 $this->get('gateway.service.whitelist') | 
            ||
| 120 | );  | 
            ||
| 121 | |||
| 122 | /**  | 
            ||
| 123 | * @var CookieService $ssoCookieService  | 
            ||
| 124 | */  | 
            ||
| 125 |         $ssoCookieService = $this->get('gateway.service.sso_2fa_cookie'); | 
            ||
| 126 | $ssoCookie = $ssoCookieService->read($request);  | 
            ||
| 127 | if ($ssoCookieService->shouldSkip2faAuthentication(  | 
            ||
| 128 | $context,  | 
            ||
| 129 | $requiredLoa->getLevel(),  | 
            ||
| 130 | $identityNameId,  | 
            ||
| 131 | $ssoCookie  | 
            ||
| 132 |         )) { | 
            ||
| 133 |             $logger->notice('Skipping second factor authentication. Required LoA was met by the LoA recorded in the cookie'); | 
            ||
| 134 | // We use the SF from the cookie as the SF that was used for authenticating the second factor authentication  | 
            ||
| 135 | $secondFactor = $this->getSecondFactorService()->findByUuid($ssoCookie->secondFactorId());  | 
            ||
| 136 | $this->getResponseContext($authenticationMode)->saveSelectedSecondFactor($secondFactor);  | 
            ||
| 137 | $this->getResponseContext($authenticationMode)->markSecondFactorVerified();  | 
            ||
| 138 | $this->getResponseContext($authenticationMode)->markVerifiedBySsoOn2faCookie($ssoCookieService->getCookieFingerprint($request));  | 
            ||
| 139 | $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId, $authenticationMode);  | 
            ||
| 140 | return $this->forward($context->getResponseAction());  | 
            ||
| 141 | }  | 
            ||
| 142 |         switch (count($secondFactorCollection)) { | 
            ||
| 143 | case 0:  | 
            ||
| 144 |                 $logger->notice('No second factors can give the determined Loa'); | 
            ||
| 145 | |||
| 146 | return $this->forward(  | 
            ||
| 147 | 'SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven',  | 
            ||
| 148 | ['authenticationMode' => $authenticationMode]  | 
            ||
| 149 | );  | 
            ||
| 150 | break;  | 
            ||
| 151 | |||
| 152 | case 1:  | 
            ||
| 153 | $secondFactor = $secondFactorCollection->first();  | 
            ||
| 154 | $logger->notice(sprintf(  | 
            ||
| 155 | 'Found "%d" second factors, using second factor of type "%s"',  | 
            ||
| 156 | count($secondFactorCollection),  | 
            ||
| 157 | $secondFactor->secondFactorType  | 
            ||
| 158 | ));  | 
            ||
| 159 | |||
| 160 | return $this->selectAndRedirectTo($secondFactor, $context, $authenticationMode);  | 
            ||
| 161 | break;  | 
            ||
| 162 | |||
| 163 | default:  | 
            ||
| 164 | return $this->forward(  | 
            ||
| 165 | 'SurfnetStepupGatewayGatewayBundle:SecondFactor:chooseSecondFactor',  | 
            ||
| 166 | ['authenticationMode' => $authenticationMode, 'secondFactors' => $secondFactorCollection]  | 
            ||
| 167 | );  | 
            ||
| 168 | break;  | 
            ||
| 169 | }  | 
            ||
| 170 | }  | 
            ||
| 171 | |||
| 172 | /**  | 
            ||
| 173 | * The main WAYG screen  | 
            ||
| 174 | * - Shows the token selection screen if you own > 1 token  | 
            ||
| 175 | * - Directly goes to SF auth when identity owns 1 token  | 
            ||
| 176 | *  | 
            ||
| 177 | * @Template  | 
            ||
| 178 | * @param Request $request  | 
            ||
| 179 | * @param string $authenticationMode  | 
            ||
| 180 | * @return array|RedirectResponse|Response  | 
            ||
| 181 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength)  | 
            ||
| 182 | */  | 
            ||
| 183 | public function chooseSecondFactorAction(Request $request, $authenticationMode)  | 
            ||
| 184 |     { | 
            ||
| 185 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 186 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 187 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 188 | |||
| 189 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 190 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 191 |         $logger->notice('Ask the user which one of his suitable second factor tokens to use...'); | 
            ||
| 192 | |||
| 193 |         try { | 
            ||
| 194 | // Retrieve all requirements to determine the required LoA  | 
            ||
| 195 | $requestedLoa = $context->getRequiredLoa();  | 
            ||
| 196 |             $spConfiguredLoas = $context->getServiceProvider()->get('configuredLoas'); | 
            ||
| 197 | |||
| 198 | $normalizedIdpSho = $context->getNormalizedSchacHomeOrganization();  | 
            ||
| 199 | $normalizedUserSho = $this->getStepupService()->getNormalizedUserShoByIdentityNameId($context->getIdentityNameId());  | 
            ||
| 200 | |||
| 201 | $requiredLoa = $this  | 
            ||
| 202 | ->getStepupService()  | 
            ||
| 203 | ->resolveHighestRequiredLoa(  | 
            ||
| 204 | $requestedLoa,  | 
            ||
| 205 | $spConfiguredLoas,  | 
            ||
| 206 | $normalizedIdpSho,  | 
            ||
| 207 | $normalizedUserSho  | 
            ||
| 208 | );  | 
            ||
| 209 |         } catch (LoaCannotBeGivenException $e) { | 
            ||
| 210 | // Log the message of the domain exception, this contains a meaningful message.  | 
            ||
| 211 | $logger->notice($e->getMessage());  | 
            ||
| 212 |             return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendLoaCannotBeGiven'); | 
            ||
| 213 | }  | 
            ||
| 214 | |||
| 215 |         $logger->notice(sprintf('Determined that the required Loa is "%s"', $requiredLoa)); | 
            ||
| 216 | |||
| 217 | $secondFactors = $this  | 
            ||
| 218 | ->getStepupService()  | 
            ||
| 219 | ->determineViableSecondFactors(  | 
            ||
| 220 | $context->getIdentityNameId(),  | 
            ||
| 221 | $requiredLoa,  | 
            ||
| 222 |                 $this->get('gateway.service.whitelist') | 
            ||
| 223 | );  | 
            ||
| 224 | |||
| 225 | $command = new ChooseSecondFactorCommand();  | 
            ||
| 226 | $command->secondFactors = $secondFactors;  | 
            ||
| 227 | |||
| 228 | $form = $this  | 
            ||
| 229 | ->createForm(  | 
            ||
| 230 | ChooseSecondFactorType::class,  | 
            ||
| 231 | $command,  | 
            ||
| 232 |                 ['action' => $this->generateUrl('gateway_verify_second_factor_choose_second_factor', ['authenticationMode' => $authenticationMode])] | 
            ||
| 233 | )  | 
            ||
| 234 | ->handleRequest($request);  | 
            ||
| 235 | $cancelForm = $this->buildCancelAuthenticationForm($authenticationMode)->handleRequest($request);  | 
            ||
| 236 | |||
| 237 |         if ($form->isSubmitted() && $form->isValid()) { | 
            ||
| 238 | $buttonName = $form->getClickedButton()->getName();  | 
            ||
| 239 |             $formResults = $request->request->get('gateway_choose_second_factor', false); | 
            ||
| 240 | |||
| 241 |             if (!isset($formResults[$buttonName])) { | 
            ||
| 242 | throw new InvalidArgumentException(  | 
            ||
| 243 | sprintf(  | 
            ||
| 244 | 'Second factor type "%s" could not be found in the posted form results.',  | 
            ||
| 245 | $buttonName  | 
            ||
| 246 | )  | 
            ||
| 247 | );  | 
            ||
| 248 | }  | 
            ||
| 249 | |||
| 250 | $secondFactorType = $formResults[$buttonName];  | 
            ||
| 251 | |||
| 252 | // Filter the selected second factor from the array collection  | 
            ||
| 253 | $secondFactorFiltered = $secondFactors->filter(  | 
            ||
| 254 |                 function ($secondFactor) use ($secondFactorType) { | 
            ||
| 255 | return $secondFactorType === $secondFactor->secondFactorType;  | 
            ||
| 256 | }  | 
            ||
| 257 | );  | 
            ||
| 258 | |||
| 259 |             if ($secondFactorFiltered->isEmpty()) { | 
            ||
| 260 | throw new InvalidArgumentException(  | 
            ||
| 261 | sprintf(  | 
            ||
| 262 | 'Second factor type "%s" could not be found in the collection of available second factors.',  | 
            ||
| 263 | $secondFactorType  | 
            ||
| 264 | )  | 
            ||
| 265 | );  | 
            ||
| 266 | }  | 
            ||
| 267 | |||
| 268 | $secondFactor = $secondFactorFiltered->first();  | 
            ||
| 269 | |||
| 270 |             $logger->notice(sprintf('User chose "%s" to use as second factor', $secondFactorType)); | 
            ||
| 271 | |||
| 272 | // Forward to action to verify possession of second factor  | 
            ||
| 273 | return $this->selectAndRedirectTo($secondFactor, $context, $authenticationMode);  | 
            ||
| 274 |         } else if ($form->isSubmitted() && !$form->isValid()) { | 
            ||
| 275 | $form->addError(  | 
            ||
| 276 | new FormError(  | 
            ||
| 277 |                     $this->get('translator') | 
            ||
| 278 |                       ->trans('gateway.form.gateway_choose_second_factor.unknown_second_factor_type') | 
            ||
| 279 | )  | 
            ||
| 280 | );  | 
            ||
| 281 | }  | 
            ||
| 282 | |||
| 283 | return [  | 
            ||
| 284 | 'form' => $form->createView(),  | 
            ||
| 285 | 'cancelForm' => $cancelForm->createView(),  | 
            ||
| 286 | 'secondFactors' => $secondFactors,  | 
            ||
| 287 | ];  | 
            ||
| 288 | }  | 
            ||
| 289 | |||
| 290 | public function verifyGssfAction(Request $request)  | 
            ||
| 291 |     { | 
            ||
| 292 |         if (!$request->get('authenticationMode', false)) { | 
            ||
| 293 |             throw new RuntimeException('Unable to determine the authentication mode in the GSSP verification action'); | 
            ||
| 294 | }  | 
            ||
| 295 |         $authenticationMode = $request->get('authenticationMode'); | 
            ||
| 296 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 297 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 298 | |||
| 299 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 300 | |||
| 301 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 302 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 303 |         $logger->info('Received request to verify GSSF'); | 
            ||
| 304 | |||
| 305 | $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);  | 
            ||
| 306 | |||
| 307 | $logger->info(sprintf(  | 
            ||
| 308 | 'Selected GSSF "%s" for verfication, forwarding to Saml handling',  | 
            ||
| 309 | $selectedSecondFactor  | 
            ||
| 310 | ));  | 
            ||
| 311 | |||
| 312 | /** @var \Surfnet\StepupGateway\GatewayBundle\Service\SecondFactorService $secondFactorService */  | 
            ||
| 313 |         $secondFactorService = $this->get('gateway.service.second_factor_service'); | 
            ||
| 314 | /** @var \Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor $secondFactor */  | 
            ||
| 315 | $secondFactor = $secondFactorService->findByUuid($selectedSecondFactor);  | 
            ||
| 316 |         if (!$secondFactor) { | 
            ||
| 317 | throw new RuntimeException(sprintf(  | 
            ||
| 318 | 'Requested verification of GSSF "%s", however that Second Factor no longer exists',  | 
            ||
| 319 | $selectedSecondFactor  | 
            ||
| 320 | ));  | 
            ||
| 321 | }  | 
            ||
| 322 | |||
| 323 | // Also send the response context service id, as later we need to know if this is regular SSO or SFO authn.  | 
            ||
| 324 | $responseContextServiceId = $context->getResponseContextServiceId();  | 
            ||
| 325 | |||
| 326 | return $this->forward(  | 
            ||
| 327 | 'SurfnetStepupGatewaySamlStepupProviderBundle:SamlProxy:sendSecondFactorVerificationAuthnRequest',  | 
            ||
| 328 | [  | 
            ||
| 329 | 'provider' => $secondFactor->secondFactorType,  | 
            ||
| 330 | 'subjectNameId' => $secondFactor->secondFactorIdentifier,  | 
            ||
| 331 | 'responseContextServiceId' => $responseContextServiceId,  | 
            ||
| 332 | ]  | 
            ||
| 333 | );  | 
            ||
| 334 | }  | 
            ||
| 335 | |||
| 336 | public function gssfVerifiedAction(Request $request)  | 
            ||
| 337 |     { | 
            ||
| 338 |         $authenticationMode = $request->get('authenticationMode'); | 
            ||
| 339 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 340 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 341 | |||
| 342 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 343 | |||
| 344 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 345 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 346 |         $logger->info('Attempting to mark GSSF as verified'); | 
            ||
| 347 | |||
| 348 | $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);  | 
            ||
| 349 | |||
| 350 | /** @var \Surfnet\StepupGateway\GatewayBundle\Entity\SecondFactor $secondFactor */  | 
            ||
| 351 |         $secondFactor = $this->get('gateway.service.second_factor_service')->findByUuid($selectedSecondFactor); | 
            ||
| 352 |         if (!$secondFactor) { | 
            ||
| 353 | throw new RuntimeException(  | 
            ||
| 354 | sprintf(  | 
            ||
| 355 | 'Verification of GSSF "%s" succeeded, however that Second Factor no longer exists',  | 
            ||
| 356 | $selectedSecondFactor  | 
            ||
| 357 | )  | 
            ||
| 358 | );  | 
            ||
| 359 | }  | 
            ||
| 360 | |||
| 361 | $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId, $authenticationMode);  | 
            ||
| 362 | $context->markSecondFactorVerified();  | 
            ||
| 363 | |||
| 364 | $logger->info(sprintf(  | 
            ||
| 365 | 'Marked GSSF "%s" as verified, forwarding to Gateway controller to respond',  | 
            ||
| 366 | $selectedSecondFactor  | 
            ||
| 367 | ));  | 
            ||
| 368 | return $this->forward($context->getResponseAction());  | 
            ||
| 369 | }  | 
            ||
| 370 | |||
| 371 | /**  | 
            ||
| 372 | * @Template  | 
            ||
| 373 | * @param Request $request  | 
            ||
| 374 | * @return array|Response  | 
            ||
| 375 | */  | 
            ||
| 376 | public function verifyYubiKeySecondFactorAction(Request $request)  | 
            ||
| 377 |     { | 
            ||
| 378 |         if (!$request->get('authenticationMode', false)) { | 
            ||
| 379 |             throw new RuntimeException('Unable to determine the authentication mode in Yubikey verification action'); | 
            ||
| 380 | }  | 
            ||
| 381 |         $authenticationMode = $request->get('authenticationMode'); | 
            ||
| 382 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 383 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 384 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 385 | |||
| 386 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 387 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 388 | |||
| 389 | $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);  | 
            ||
| 390 | |||
| 391 |         $logger->notice('Verifying possession of Yubikey second factor'); | 
            ||
| 392 | |||
| 393 | $command = new VerifyYubikeyOtpCommand();  | 
            ||
| 394 | $command->secondFactorId = $selectedSecondFactor;  | 
            ||
| 395 | |||
| 396 | $form = $this->createForm(VerifyYubikeyOtpType::class, $command)->handleRequest($request);  | 
            ||
| 397 | $cancelForm = $this->buildCancelAuthenticationForm($authenticationMode)->handleRequest($request);  | 
            ||
| 398 | |||
| 399 |         if ($form->isSubmitted()  && $form->isValid()) { | 
            ||
| 400 | $result = $this->getStepupService()->verifyYubikeyOtp($command);  | 
            ||
| 401 |             if ($result->didOtpVerificationFail()) { | 
            ||
| 402 | $form->addError(  | 
            ||
| 403 |                     new FormError($this->get('translator')->trans('gateway.form.verify_yubikey.otp_verification_failed')) | 
            ||
| 404 | );  | 
            ||
| 405 | |||
| 406 | // OTP field is rendered empty in the template.  | 
            ||
| 407 | return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];  | 
            ||
| 408 |             } elseif (!$result->didPublicIdMatch()) { | 
            ||
| 409 | $form->addError(  | 
            ||
| 410 |                     new FormError($this->get('translator')->trans('gateway.form.verify_yubikey.public_id_mismatch')) | 
            ||
| 411 | );  | 
            ||
| 412 | |||
| 413 | // OTP field is rendered empty in the template.  | 
            ||
| 414 | return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];  | 
            ||
| 415 | }  | 
            ||
| 416 | |||
| 417 | $this->getResponseContext($authenticationMode)->markSecondFactorVerified();  | 
            ||
| 418 | $this->getAuthenticationLogger()->logSecondFactorAuthentication($originalRequestId, $authenticationMode);  | 
            ||
| 419 | |||
| 420 | $logger->info(  | 
            ||
| 421 | sprintf(  | 
            ||
| 422 | 'Marked Yubikey Second Factor "%s" as verified, forwarding to Saml Proxy to respond',  | 
            ||
| 423 | $selectedSecondFactor  | 
            ||
| 424 | )  | 
            ||
| 425 | );  | 
            ||
| 426 | return $this->forward($context->getResponseAction());  | 
            ||
| 427 | }  | 
            ||
| 428 | |||
| 429 | // OTP field is rendered empty in the template.  | 
            ||
| 430 | return ['form' => $form->createView(), 'cancelForm' => $cancelForm->createView()];  | 
            ||
| 431 | }  | 
            ||
| 432 | |||
| 433 | /**  | 
            ||
| 434 | * @Template  | 
            ||
| 435 | * @param Request $request  | 
            ||
| 436 | * @param string $authenticationMode  | 
            ||
| 437 | * @return array|Response  | 
            ||
| 438 | */  | 
            ||
| 439 | public function verifySmsSecondFactorAction(Request $request)  | 
            ||
| 440 |     { | 
            ||
| 441 |         if (!$request->get('authenticationMode', false)) { | 
            ||
| 442 |             throw new RuntimeException('Unable to determine the authentication mode in the SMS verification action'); | 
            ||
| 443 | }  | 
            ||
| 444 |         $authenticationMode = $request->get('authenticationMode'); | 
            ||
| 445 | $this->supportsAuthenticationMode($authenticationMode);  | 
            ||
| 446 | $context = $this->getResponseContext($authenticationMode);  | 
            ||
| 447 | $originalRequestId = $context->getInResponseTo();  | 
            ||
| 448 | |||
| 449 | /** @var \Surfnet\SamlBundle\Monolog\SamlAuthenticationLogger $logger */  | 
            ||
| 450 |         $logger = $this->get('surfnet_saml.logger')->forAuthentication($originalRequestId); | 
            ||
| 451 | |||
| 452 | $selectedSecondFactor = $this->getSelectedSecondFactor($context, $logger);  | 
            ||
| 453 | |||
| 454 |         $logger->notice('Verifying possession of SMS second factor, preparing to send'); | 
            ||
| 455 | |||
| 456 | $command = new SendSmsChallengeCommand();  | 
            ||
| 457 | $command->secondFactorId = $selectedSecondFactor;  | 
            ||
| 458 | |||
| 459 | $form = $this->createForm(SendSmsChallengeType::class, $command)->handleRequest($request);  | 
            ||
| 460 | $cancelForm = $this->buildCancelAuthenticationForm($authenticationMode)->handleRequest($request);  | 
            ||
| 461 | |||
| 462 | $stepupService = $this->getStepupService();  | 
            ||
| 463 | $phoneNumber = InternationalPhoneNumber::fromStringFormat(  | 
            ||
| 464 | $stepupService->getSecondFactorIdentifier($selectedSecondFactor)  | 
            ||
| 465 | );  | 
            ||
| 466 | |||
| 467 | $otpRequestsRemaining = $stepupService->getSmsOtpRequestsRemainingCount($selectedSecondFactor);  | 
            ||
| 468 | $maximumOtpRequests = $stepupService->getSmsMaximumOtpRequestsCount();  | 
            ||
| 469 | $viewVariables = ['otpRequestsRemaining' => $otpRequestsRemaining, 'maximumOtpRequests' => $maximumOtpRequests];  | 
            ||
| 470 | |||
| 471 |         if ($form->isSubmitted() && !$form->isValid()) { | 
            ||
| 472 | return array_merge(  | 
            ||
| 473 | $viewVariables,  | 
            ||
| 474 | [  | 
            ||
| 475 | 'phoneNumber' => $phoneNumber,  | 
            ||
| 476 | 'form' => $form->createView(),  | 
            ||
| 477 | 'cancelForm' => $cancelForm->createView()  | 
            ||
| 478 | ]  | 
            ||
| 479 | );  | 
            ||
| 480 | }  | 
            ||
| 481 | |||
| 482 |         $logger->notice('Verifying possession of SMS second factor, sending challenge per SMS'); | 
            ||
| 483 | |||
| 484 |         if (!$stepupService->sendSmsChallenge($command)) { | 
            ||
| 485 | $form->addError(  | 
            ||
| 486 |                 new FormError($this->get('translator')->trans('gateway.form.send_sms_challenge.sms_sending_failed')) | 
            ||
| 487 | );  | 
            ||
| 488 | |||
| 489 | return array_merge(  | 
            ||
| 490 | $viewVariables,  | 
            ||
| 491 | [  | 
            ||
| 492 | 'phoneNumber' => $phoneNumber,  | 
            ||
| 493 | 'form' => $form->createView(),  | 
            ||
| 494 | 'cancelForm' => $cancelForm->createView()  | 
            ||
| 495 | ]  | 
            ||
| 496 | );  | 
            ||
| 497 | }  | 
            ||
| 498 | return $this->redirect(  | 
            ||
| 499 | $this->generateUrl(  | 
            ||
| 500 | 'gateway_verify_second_factor_sms_verify_challenge',  | 
            ||
| 501 | ['authenticationMode' => $authenticationMode]  | 
            ||
| 502 | )  | 
            ||
| 503 | );  | 
            ||
| 504 | }  | 
            ||
| 505 | |||
| 506 | /**  | 
            ||
| 507 | * @Template  | 
            ||
| 508 | * @param Request $request  | 
            ||
| 509 | * @param string $authenticationMode  | 
            ||
| 510 | * @return array|Response  | 
            ||
| 511 | */  | 
            ||
| 512 | public function verifySmsSecondFactorChallengeAction(Request $request)  | 
            ||
| 569 | }  | 
            ||
| 570 | |||
| 571 | public function cancelAuthenticationAction()  | 
            ||
| 572 |     { | 
            ||
| 573 |         return $this->forward('SurfnetStepupGatewayGatewayBundle:Gateway:sendAuthenticationCancelledByUser'); | 
            ||
| 574 | }  | 
            ||
| 575 | |||
| 576 | /**  | 
            ||
| 577 | * @return \Surfnet\StepupGateway\GatewayBundle\Service\StepupAuthenticationService  | 
            ||
| 578 | */  | 
            ||
| 579 | private function getStepupService()  | 
            ||
| 580 |     { | 
            ||
| 581 |         return $this->get('gateway.service.stepup_authentication'); | 
            ||
| 582 | }  | 
            ||
| 583 | |||
| 584 | /**  | 
            ||
| 585 | * @return ResponseContext  | 
            ||
| 586 | */  | 
            ||
| 587 | private function getResponseContext($authenticationMode)  | 
            ||
| 588 |     { | 
            ||
| 589 |         switch ($authenticationMode) { | 
            ||
| 590 | case self::MODE_SFO:  | 
            ||
| 591 |                 return $this->get($this->get('gateway.proxy.sfo.state_handler')->getResponseContextServiceId()); | 
            ||
| 592 | break;  | 
            ||
| 593 | case self::MODE_SSO:  | 
            ||
| 594 |                 return $this->get($this->get('gateway.proxy.sso.state_handler')->getResponseContextServiceId()); | 
            ||
| 595 | break;  | 
            ||
| 596 | }  | 
            ||
| 597 | }  | 
            ||
| 598 | |||
| 599 | /**  | 
            ||
| 600 | * @return \Surfnet\StepupGateway\GatewayBundle\Monolog\Logger\AuthenticationLogger  | 
            ||
| 601 | */  | 
            ||
| 602 | private function getAuthenticationLogger()  | 
            ||
| 603 |     { | 
            ||
| 604 |         return $this->get('gateway.authentication_logger'); | 
            ||
| 605 | }  | 
            ||
| 606 | |||
| 607 | private function getSecondFactorService(): SecondFactorService  | 
            ||
| 608 |     { | 
            ||
| 609 |         return $this->get('gateway.service.second_factor_service'); | 
            ||
| 610 | }  | 
            ||
| 611 | |||
| 612 | /**  | 
            ||
| 613 | * @param ResponseContext $context  | 
            ||
| 614 | * @param LoggerInterface $logger  | 
            ||
| 615 | * @return string  | 
            ||
| 616 | */  | 
            ||
| 617 | private function getSelectedSecondFactor(ResponseContext $context, LoggerInterface $logger)  | 
            ||
| 618 |     { | 
            ||
| 619 | $selectedSecondFactor = $context->getSelectedSecondFactor();  | 
            ||
| 620 | |||
| 621 |         if (!$selectedSecondFactor) { | 
            ||
| 622 |             $logger->error('Cannot verify possession of an unknown second factor'); | 
            ||
| 623 | |||
| 624 |             throw new BadRequestHttpException('Cannot verify possession of an unknown second factor.'); | 
            ||
| 625 | }  | 
            ||
| 626 | |||
| 627 | return $selectedSecondFactor;  | 
            ||
| 628 | }  | 
            ||
| 629 | |||
| 630 | private function selectAndRedirectTo(SecondFactor $secondFactor, ResponseContext $context, $authenticationMode)  | 
            ||
| 631 |     { | 
            ||
| 632 | $context->saveSelectedSecondFactor($secondFactor);  | 
            ||
| 633 | |||
| 634 | $this->getStepupService()->clearSmsVerificationState($secondFactor->secondFactorId);  | 
            ||
| 635 | |||
| 636 |         $secondFactorTypeService = $this->get('surfnet_stepup.service.second_factor_type'); | 
            ||
| 637 | $secondFactorType = new SecondFactorType($secondFactor->secondFactorType);  | 
            ||
| 638 | |||
| 639 | $route = 'gateway_verify_second_factor_';  | 
            ||
| 640 |         if ($secondFactorTypeService->isGssf($secondFactorType)) { | 
            ||
| 641 | $route .= 'gssf';  | 
            ||
| 642 |         } else { | 
            ||
| 643 | $route .= strtolower($secondFactor->secondFactorType);  | 
            ||
| 644 | }  | 
            ||
| 645 | |||
| 646 | return $this->redirect($this->generateUrl($route, ['authenticationMode' => $authenticationMode]));  | 
            ||
| 647 | }  | 
            ||
| 648 | |||
| 649 | /**  | 
            ||
| 650 | * @param string $authenticationMode  | 
            ||
| 651 | * @return FormInterface  | 
            ||
| 652 | */  | 
            ||
| 653 | private function buildCancelAuthenticationForm($authenticationMode)  | 
            ||
| 664 | );  | 
            ||
| 665 | }  | 
            ||
| 666 | |||
| 667 | private function supportsAuthenticationMode($authenticationMode)  | 
            ||
| 668 |     { | 
            ||
| 669 |         if (!($authenticationMode === self::MODE_SSO || $authenticationMode === self::MODE_SFO)) { | 
            ||
| 670 |             throw new InvalidArgumentException('Invalid authentication mode requested'); | 
            ||
| 671 | }  | 
            ||
| 672 | }  | 
            ||
| 673 | }  | 
            ||
| 674 |