/** * @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; }