/** * @param Request $httpRequest * @param string $provider * @return array|Response */ public function consumeAssertionAction(Request $httpRequest, $provider) { $provider = $this->getProvider($provider); $this->get('logger')->notice(sprintf('Received GSSP "%s" SAMLResponse through Gateway, attempting to process', $provider->getName())); try { /** @var \Surfnet\SamlBundle\Http\PostBinding $postBinding */ $postBinding = $this->get('surfnet_saml.http.post_binding'); $assertion = $postBinding->processResponse($httpRequest, $provider->getRemoteIdentityProvider(), $provider->getServiceProvider()); } catch (Exception $exception) { $provider->getStateHandler()->clear(); $this->getLogger()->error(sprintf('Could not process received Response, error: "%s"', $exception->getMessage())); return $this->renderInitiateForm($provider->getName(), ['authenticationFailed' => true]); } $expectedResponseTo = $provider->getStateHandler()->getRequestId(); $provider->getStateHandler()->clear(); if (!InResponseTo::assertEquals($assertion, $expectedResponseTo)) { $this->getLogger()->critical(sprintf('Received Response with unexpected InResponseTo, %s', $expectedResponseTo ? 'expected "' . $expectedResponseTo . '"' : ' no response expected')); return $this->renderInitiateForm($provider->getName(), ['authenticationFailed' => true]); } $this->get('logger')->notice(sprintf('Processed GSSP "%s" SAMLResponse received through Gateway successfully', $provider->getName())); /** @var \Surfnet\StepupSelfService\SelfServiceBundle\Service\GssfService $service */ $service = $this->get('surfnet_stepup_self_service_self_service.service.gssf'); /** @var \Surfnet\SamlBundle\SAML2\Attribute\AttributeDictionary $attributeDictionary */ $attributeDictionary = $this->get('surfnet_saml.saml.attribute_dictionary'); $gssfId = $attributeDictionary->translate($assertion)->getNameID(); $secondFactorId = $service->provePossession($this->getIdentity()->id, $provider->getName(), $gssfId); if ($secondFactorId) { $this->getLogger()->notice('GSSF possession has been proven successfully'); return $this->redirectToRoute('ss_registration_email_verification_email_sent', ['secondFactorId' => $secondFactorId]); } $this->getLogger()->error('Unable to prove GSSF possession'); return $this->renderInitiateForm($provider->getName(), ['proofOfPossessionFailed' => true]); }
/** * @test * @group saml2-response * @group saml2 */ public function in_reponse_to_equality_is_strictly_checked() { $assertion = new SAML2_Assertion(); $subjectConfirmationWithData = new SAML2_XML_saml_SubjectConfirmation(); $subjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData(); $subjectConfirmationData->InResponseTo = '1'; $subjectConfirmationWithData->SubjectConfirmationData = $subjectConfirmationData; $assertion->setSubjectConfirmation([$subjectConfirmationWithData]); $this->assertTrue(InResponseTo::assertEquals($assertion, '1')); $this->assertFalse(InResponseTo::assertEquals($assertion, 1)); }
private function handleEvent(GetResponseEvent $event) { /** @var SessionHandler $sessionHandler */ $sessionHandler = $this->container->get('ra.security.authentication.session_handler'); // reinstate the token from the session. Could be expanded with logout check if needed if ($this->getTokenStorage()->getToken()) { return; } /** @var SamlInteractionProvider $samlInteractionProvider */ $samlInteractionProvider = $this->container->get('ra.security.authentication.saml'); if (!$samlInteractionProvider->isSamlAuthenticationInitiated()) { $sessionHandler->setCurrentRequestUri($event->getRequest()->getUri()); $event->setResponse($samlInteractionProvider->initiateSamlRequest()); /** @var SamlAuthenticationLogger $logger */ $logger = $this->container->get('surfnet_saml.logger')->forAuthentication($sessionHandler->getRequestId()); $logger->notice('Sending AuthnRequest'); return; } /** @var SamlAuthenticationLogger $logger */ $logger = $this->container->get('surfnet_saml.logger')->forAuthentication($sessionHandler->getRequestId()); $expectedInResponseTo = $sessionHandler->getRequestId(); try { $assertion = $samlInteractionProvider->processSamlResponse($event->getRequest()); } catch (PreconditionNotMetException $e) { $logger->notice(sprintf('SAML response precondition not met: "%s"', $e->getMessage())); $event->setResponse($this->renderPreconditionExceptionResponse($e)); return; } catch (Exception $e) { $logger->error(sprintf('Failed SAMLResponse Parsing: "%s"', $e->getMessage())); throw new AuthenticationException('Failed SAMLResponse parsing', 0, $e); } if (!InResponseTo::assertEquals($assertion, $expectedInResponseTo)) { $logger->error('Unknown or unexpected InResponseTo in SAMLResponse'); throw new AuthenticationException('Unknown or unexpected InResponseTo in SAMLResponse'); } $logger->notice('Successfully processed SAMLResponse, attempting to authenticate'); $loaResolutionService = $this->container->get('surfnet_stepup.service.loa_resolution'); $loa = $loaResolutionService->getLoa($assertion->getAuthnContextClassRef()); $token = new SamlToken($loa); $token->assertion = $assertion; /** @var AuthenticationProviderManager $authenticationManager */ $authenticationManager = $this->container->get('security.authentication.manager'); try { $authToken = $authenticationManager->authenticate($token); } catch (BadCredentialsException $exception) { $logger->error(sprintf('Bad credentials, reason: "%s"', $exception->getMessage()), ['exception' => $exception]); $event->setResponse($this->renderBadCredentialsResponse($exception)); return; } catch (AuthenticationException $failed) { $logger->error(sprintf('Authentication Failed, reason: "%s"', $failed->getMessage()), ['exception' => $failed]); $event->setResponse($this->renderAuthenticationExceptionResponse($failed)); return; } // for the current request $this->getTokenStorage()->setToken($authToken); // migrate the session to prevent session hijacking $sessionHandler->migrate(); $event->setResponse(new RedirectResponse($sessionHandler->getCurrentRequestUri())); $logger->notice('Authentication succeeded, redirecting to original location'); }
/** * @param Request $httpRequest * @param string $provider * @return array|Response */ public function verifyAction(Request $httpRequest, $provider) { $provider = $this->getProvider($provider); $this->get('logger')->notice(sprintf('Received GSSP "%s" SAMLResponse through Gateway, attempting to process', $provider->getName())); try { /** @var \Surfnet\SamlBundle\Http\PostBinding $postBinding */ $postBinding = $this->get('surfnet_saml.http.post_binding'); $assertion = $postBinding->processResponse($httpRequest, $provider->getRemoteIdentityProvider(), $provider->getServiceProvider()); } catch (Exception $exception) { $provider->getStateHandler()->clear(); $this->getLogger()->error(sprintf('Could not process received Response, error: "%s"', $exception->getMessage())); throw new BadRequestHttpException('Could not process received SAML response, cannot return to vetting procedure'); } $expectedResponseTo = $provider->getStateHandler()->getRequestId(); $provider->getStateHandler()->clear(); if (!InResponseTo::assertEquals($assertion, $expectedResponseTo)) { $this->getLogger()->critical(sprintf('Received Response with unexpected InResponseTo: %s', $expectedResponseTo ? 'expected "' . $expectedResponseTo . '"' : ' no response expected')); throw new BadRequestHttpException('Received unexpected SAML response, cannot return to vetting procedure'); } $this->get('logger')->notice(sprintf('Processed GSSP "%s" SAMLResponse received through Gateway successfully', $provider->getName())); /** @var \Surfnet\SamlBundle\SAML2\Attribute\AttributeDictionary $attributeDictionary */ $attributeDictionary = $this->get('surfnet_saml.saml.attribute_dictionary'); $gssfId = $attributeDictionary->translate($assertion)->getNameID(); /** @var \Surfnet\StepupRa\RaBundle\Service\VettingService $vettingService */ $vettingService = $this->get('ra.service.vetting'); $result = $vettingService->verifyGssfId($gssfId); if ($result->isSuccess()) { $this->getLogger()->notice('GSSP possession proven successfully'); return $this->redirectToRoute('ra_vetting_verify_identity', ['procedureId' => $result->getProcedureId()]); } if (!$result->getProcedureId()) { // Should be unreachable statement, because the request ID is compared to the response ID a few lines before // this. throw new RuntimeException('Procedure ID for GSSF verification procedure could not be recovered.'); } $this->getLogger()->notice('Unable to prove possession of correct GSSF: ' . 'GSSF ID registered in Self-Service does not match current GSSF ID'); return $this->render('SurfnetStepupRaRaBundle:Vetting/Gssf:initiate.html.twig', ['provider' => $provider->getName(), 'procedureId' => $result->getProcedureId(), 'gssfIdMismatch' => true]); }
/** * @param GetResponseEvent $event */ private function handleEvent(GetResponseEvent $event) { if ($this->tokenStorage->getToken()) { return; } if (!$this->samlInteractionProvider->isSamlAuthenticationInitiated()) { $this->sendAuthnRequest($event); return; } $expectedInResponseTo = $this->stateHandler->getRequestId(); $logger = $this->logger; try { $assertion = $this->samlInteractionProvider->processSamlResponse($event->getRequest()); } catch (PreconditionNotMetException $e) { $logger->notice(sprintf('SAML response precondition not met: "%s"', $e->getMessage())); $this->setPreconditionExceptionResponse($e, $event); return; } catch (Exception $e) { $logger->error(sprintf('Failed SAMLResponse Parsing: "%s"', $e->getMessage())); throw new AuthenticationException('Failed SAMLResponse parsing', 0, $e); } if (!InResponseTo::assertEquals($assertion, $expectedInResponseTo)) { $logger->error('Unknown or unexpected InResponseTo in SAMLResponse'); throw new AuthenticationException('Unknown or unexpected InResponseTo in SAMLResponse'); } $logger->notice('Successfully processed SAMLResponse, attempting to authenticate'); $token = new SamlToken(); $token->assertion = $assertion; try { $authToken = $this->authenticationManager->authenticate($token); } catch (AuthenticationException $failed) { $logger->error(sprintf('Authentication Failed, reason: "%s"', $failed->getMessage())); $this->setAuthenticationFailedResponse($event); return; } $this->tokenStorage->setToken($authToken); // migrate the session to prevent session hijacking $this->session->migrate(); $event->setResponse(new RedirectResponse($this->stateHandler->getCurrentRequestUri())); $logger->notice('Authentication succeeded, redirecting to original location'); }