/**
  * @return EngineBlock_Corto_Module_Bindings
  */
 private function mockBindingsModule()
 {
     $spRequest = new SAML2_AuthnRequest();
     $spRequest->setId('SPREQUEST');
     $spRequest->setIssuer('testSp');
     $spRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($spRequest);
     $ebRequest = new SAML2_AuthnRequest();
     $ebRequest->setId('EBREQUEST');
     $ebRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($ebRequest);
     $dummyLog = new Psr\Log\NullLogger();
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($dummyLog);
     $authnRequestRepository->store($spRequest);
     $authnRequestRepository->store($ebRequest);
     $authnRequestRepository->link($ebRequest, $spRequest);
     $assertion = new SAML2_Assertion();
     $assertion->setAttributes(array('urn:org:openconext:corto:internal:sp-entity-id' => array('testSp'), 'urn:mace:dir:attribute-def:cn' => array(null)));
     $responseFixture = new SAML2_Response();
     $responseFixture->setInResponseTo('EBREQUEST');
     $responseFixture->setAssertions(array($assertion));
     $responseFixture = new EngineBlock_Saml2_ResponseAnnotationDecorator($responseFixture);
     $responseFixture->setOriginalIssuer('testIdP');
     // Mock bindings module
     /** @var EngineBlock_Corto_Module_Bindings $bindingsModuleMock */
     $bindingsModuleMock = Phake::mock('EngineBlock_Corto_Module_Bindings');
     Phake::when($bindingsModuleMock)->receiveResponse()->thenReturn($responseFixture);
     return $bindingsModuleMock;
 }
 /**
  * Handle the forwarding of the user to the proper IdP0 after the WAYF screen.
  *
  * @param string $serviceName
  * @throws EngineBlock_Corto_Module_Services_Exception
  * @throws EngineBlock_Exception
  * @throws EngineBlock_Corto_Module_Services_SessionLostException
  */
 public function serve($serviceName)
 {
     $selectedIdp = urldecode($_REQUEST['idp']);
     if (!$selectedIdp) {
         throw new EngineBlock_Corto_Module_Services_Exception('No IdP selected after WAYF');
     }
     // Retrieve the request from the session.
     $id = $_POST['ID'];
     if (!$id) {
         throw new EngineBlock_Exception('Missing ID for AuthnRequest after WAYF', EngineBlock_Exception::CODE_NOTICE);
     }
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($this->_server->getSessionLog());
     $request = $authnRequestRepository->findRequestById($id);
     if (!$request) {
         throw new EngineBlock_Corto_Module_Services_SessionLostException('Session lost after WAYF');
     }
     // Flush log if SP or IdP has additional logging enabled
     $sp = $this->_server->getRepository()->fetchServiceProviderByEntityId($request->getIssuer());
     $idp = $this->_server->getRepository()->fetchIdentityProviderByEntityId($selectedIdp);
     if (EngineBlock_SamlHelper::doRemoteEntitiesRequireAdditionalLogging(array($sp, $idp))) {
         $application = EngineBlock_ApplicationSingleton::getInstance();
         $application->flushLog('Activated additional logging for the SP or IdP');
         $log = $application->getLogInstance();
         $log->info('Raw HTTP request', array('http_request' => (string) $application->getHttpRequest()));
     }
     $this->_server->sendAuthenticationRequest($request, $selectedIdp);
 }
 public function serve($serviceName)
 {
     $log = $this->_server->getSessionLog();
     $response = $this->_displayDebugResponse($serviceName);
     if ($response) {
         return;
     }
     /** @var EngineBlock_Saml2_AuthnRequestAnnotationDecorator|SAML2_AuthnRequest $request */
     $request = $this->_getRequest($serviceName);
     $log->info(sprintf("Fetching service provider matching request issuer '%s'", $request->getIssuer()));
     $sp = $this->_server->getRepository()->fetchServiceProviderByEntityId($request->getIssuer());
     // Flush log if an SP in the requester chain has additional logging enabled
     $log->info("Determining whether service provider in chain requires additional logging");
     $isAdditionalLoggingRequired = EngineBlock_SamlHelper::doRemoteEntitiesRequireAdditionalLogging(EngineBlock_SamlHelper::getSpRequesterChain($sp, $request, $this->_server->getRepository()));
     if ($isAdditionalLoggingRequired) {
         $application = EngineBlock_ApplicationSingleton::getInstance();
         $application->flushLog('Activated additional logging for one or more SPs in the SP requester chain');
         $logger = $application->getLogInstance();
         $logger->info('Raw HTTP request', array('http_request' => (string) $application->getHttpRequest()));
     } else {
         $log->info("No additional logging required");
     }
     // validate custom acs-location (only for unsolicited, normal logins
     //  fall back to default ACS location instead of showing error page)
     if ($serviceName === 'unsolicitedSingleSignOnService') {
         if (!$this->_verifyAcsLocation($request, $sp)) {
             throw new EngineBlock_Corto_Exception_InvalidAcsLocation('Unsolicited sign-on service called, but unknown or invalid ACS location requested');
         }
         $log->info('Unsolicited sign-on ACS location verified.');
     }
     // The request may specify it ONLY wants a response from specific IdPs
     // or we could have it configured that the SP may only be serviced by specific IdPs
     $scopedIdps = $this->_getScopedIdPs($request);
     $cacheResponseSent = $this->_sendCachedResponse($request, $scopedIdps);
     if ($cacheResponseSent) {
         return;
     }
     // If the scoped proxycount = 0, respond with a ProxyCountExceeded error
     if ($request->getProxyCount() === 0) {
         $log->info("Request does not allow any further proxying, responding with 'ProxyCountExceeded' status");
         $response = $this->_server->createErrorResponse($request, 'ProxyCountExceeded');
         $this->_server->sendResponseToRequestIssuer($request, $response);
         return;
     }
     // Get all registered Single Sign On Services
     // Note that we could also only get the ones that are allowed for this SP, but we may also want to show
     // those that are not allowed.
     $candidateIDPs = $this->_server->getRepository()->findAllIdentityProviderEntityIds();
     $posOfOwnIdp = array_search($this->_server->getUrl('idpMetadataService'), $candidateIDPs);
     if ($posOfOwnIdp !== false) {
         $log->info("Removed ourselves from the candidate IdP list");
         unset($candidateIDPs[$posOfOwnIdp]);
     }
     // If we have scoping, filter out every non-scoped IdP
     if (count($scopedIdps) > 0) {
         $log->info(sprintf('%d candidate IdPs before scoping', count($candidateIDPs)), array('idps' => array_values($candidateIDPs)));
         $candidateIDPs = array_intersect($scopedIdps, $candidateIDPs);
         $log->info(sprintf('%d candidate IdPs after scoping', count($candidateIDPs)), array('idps' => array_values($candidateIDPs)));
     } else {
         $log->info(sprintf('No IdP scoping required, %d candidate IdPs', count($candidateIDPs)), array('idps' => array_values($candidateIDPs)));
     }
     // 0 IdPs found! Throw an exception.
     if (count($candidateIDPs) === 0) {
         throw new EngineBlock_Corto_Module_Service_SingleSignOn_NoIdpsException('No candidate IdPs found');
     }
     // Exactly 1 candidate found, send authentication request to the first one.
     if (count($candidateIDPs) === 1) {
         $idp = array_shift($candidateIDPs);
         $log->info("Only 1 candidate IdP ('{$idp}'): omitting WAYF, sending authentication request");
         $this->_server->sendAuthenticationRequest($request, $idp);
         return;
     }
     // Multiple IdPs found...
     // > 1 IdPs found, but isPassive attribute given, unable to show WAYF.
     if ($request->getIsPassive()) {
         $log->info('Request is passive, but can be handled by more than one IdP: responding with NoPassive status');
         $response = $this->_server->createErrorResponse($request, 'NoPassive');
         $this->_server->sendResponseToRequestIssuer($request, $response);
         return;
     }
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($log);
     $authnRequestRepository->store($request);
     // Show WAYF
     $log->info("Multiple candidate IdPs: redirecting to WAYF");
     $this->_showWayf($request, $candidateIDPs);
 }
 private function mockGlobals()
 {
     $_POST['ID'] = 'test';
     $_POST['consent'] = 'yes';
     $assertion = new SAML2_Assertion();
     $assertion->setAttributes(array('urn:mace:dir:attribute-def:mail' => '*****@*****.**'));
     $spRequest = new SAML2_AuthnRequest();
     $spRequest->setId('SPREQUEST');
     $spRequest->setIssuer('https://sp.example.edu');
     $spRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($spRequest);
     $ebRequest = new SAML2_AuthnRequest();
     $ebRequest->setId('EBREQUEST');
     $ebRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($ebRequest);
     $dummySessionLog = new Psr\Log\NullLogger();
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($dummySessionLog);
     $authnRequestRepository->store($spRequest);
     $authnRequestRepository->store($ebRequest);
     $authnRequestRepository->link($ebRequest, $spRequest);
     $sspResponse = new SAML2_Response();
     $sspResponse->setInResponseTo('EBREQUEST');
     $sspResponse->setAssertions(array($assertion));
     $_SESSION['consent']['test']['response'] = new EngineBlock_Saml2_ResponseAnnotationDecorator($sspResponse);
 }
 /**
  * @param EngineBlock_Saml2_ResponseAnnotationDecorator $response
  * @return EngineBlock_Saml2_AuthnRequestAnnotationDecorator
  * @throws EngineBlock_Corto_ProxyServer_Exception
  * @throws EngineBlock_Exception
  * @throws EngineBlock_Corto_Module_Services_SessionLostException
  */
 public function getReceivedRequestFromResponse(EngineBlock_Saml2_ResponseAnnotationDecorator $response)
 {
     /** @var SAML2_Response $response */
     $requestId = $response->getInResponseTo();
     if (!$requestId) {
         throw new EngineBlock_Corto_ProxyServer_Exception('Response without InResponseTo, e.g. unsolicited. We don\'t support this.', EngineBlock_Exception::CODE_NOTICE);
     }
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($this->getSessionLog());
     $spRequestId = $authnRequestRepository->findLinkedRequestId($requestId);
     if (!$spRequestId) {
         throw new EngineBlock_Corto_Module_Services_SessionLostException("Trying to find a AuthnRequest (we made and sent) with id '{$requestId}' but it is not known in this session? " . "This could be an unsolicited Response (which we do not support) but more likely the user lost their session", EngineBlock_Corto_ProxyServer_Exception::CODE_NOTICE);
     }
     $spRequest = $authnRequestRepository->findRequestById($spRequestId);
     if (!$spRequest) {
         throw new EngineBlock_Corto_ProxyServer_Exception('Response has no known Request', EngineBlock_Corto_ProxyServer_Exception::CODE_NOTICE);
     }
     return $spRequest;
 }