/** * 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) { $receivedResponse = $this->_server->getBindingsModule()->receiveResponse(); $receivedRequest = $this->_server->getReceivedRequestFromResponse($receivedResponse); $sp = $this->_server->getRepository()->fetchServiceProviderByEntityId($receivedRequest->getIssuer()); // Verify the SP requester chain. EngineBlock_SamlHelper::getSpRequesterChain($sp, $receivedRequest, $this->_server->getRepository()); // Flush log if SP or IdP has additional logging enabled $idp = $this->_server->getRepository()->fetchIdentityProviderByEntityId($receivedResponse->getIssuer()); 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())); } if ($receivedRequest->isDebugRequest()) { $_SESSION['debugIdpResponse'] = $receivedResponse; $this->_server->redirect($this->_server->getUrl('debugSingleSignOnService'), 'Show original Response from IDP'); return; } if ($receivedRequest->getKeyId()) { $this->_server->setKeyId($receivedRequest->getKeyId()); } // Cache the response EngineBlock_Corto_Model_Response_Cache::cacheResponse($receivedRequest, $receivedResponse, EngineBlock_Corto_Model_Response_Cache::RESPONSE_CACHE_TYPE_IN); $this->_server->filterInputAssertionAttributes($receivedResponse, $receivedRequest); $processingEntities = $this->_server->getConfig('Processing', array()); if (!empty($processingEntities)) { /** @var AbstractRole $firstProcessingEntity */ $firstProcessingEntity = array_shift($processingEntities); $_SESSION['Processing'][$receivedRequest->getId()]['RemainingEntities'] = $processingEntities; $_SESSION['Processing'][$receivedRequest->getId()]['OriginalDestination'] = $receivedResponse->getDestination(); $_SESSION['Processing'][$receivedRequest->getId()]['OriginalIssuer'] = $receivedResponse->getOriginalIssuer(); $_SESSION['Processing'][$receivedRequest->getId()]['OriginalBinding'] = $receivedResponse->getOriginalBinding(); $this->_server->setProcessingMode(); $newResponse = $this->_server->createEnhancedResponse($receivedRequest, $receivedResponse); // Change the destiny of the received response $newResponse->setInResponseTo($receivedResponse->getInResponseTo()); $newResponse->setDestination($firstProcessingEntity->responseProcessingService->location); $newResponse->setDeliverByBinding($firstProcessingEntity->responseProcessingService->binding); $newResponse->setReturn($this->_server->getUrl('processedAssertionConsumerService')); $this->_server->getBindingsModule()->send($newResponse, $firstProcessingEntity); } else { $newResponse = $this->_server->createEnhancedResponse($receivedRequest, $receivedResponse); $this->_server->sendResponseToRequestIssuer($receivedRequest, $newResponse); } }
public function serve($serviceName) { $response = $this->_server->getBindingsModule()->receiveResponse(); $receivedRequest = $this->_server->getReceivedRequestFromResponse($response); if ($receivedRequest->getKeyId()) { $this->_server->setKeyId($receivedRequest->getKeyId()); } $remainingProcessingEntities =& $_SESSION['Processing'][$receivedRequest->getId()]['RemainingEntities']; // @todo check if this is the correct place to flush log // Flush log if SP or IdP has additional logging enabled $sp = $this->_server->getRepository()->fetchServiceProviderByEntityId($receivedRequest->getIssuer()); $idp = $this->_server->getRepository()->fetchIdentityProviderByEntityId($response->getOriginalIssuer()); 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())); } if (!empty($remainingProcessingEntities)) { // Moar processing! /** @var AbstractRole $nextProcessingEntity */ $nextProcessingEntity = array_shift($remainingProcessingEntities); $this->_server->setProcessingMode(); $newResponse = $this->_server->createEnhancedResponse($receivedRequest, $response); // Change the destiny of the received response $newResponse->setId($response->getId()); $newResponse->setDestination($nextProcessingEntity->responseProcessingService->location); $newResponse->setDeliverByBinding($nextProcessingEntity->responseProcessingService->binding); $newResponse->setReturn($this->_server->getUrl('processedAssertionConsumerService')); $this->_server->getBindingsModule()->send($newResponse, $nextProcessingEntity); return; } else { // Done processing! Send off to SP $response->setDestination($_SESSION['Processing'][$receivedRequest->getId()]['OriginalDestination']); $response->setDeliverByBinding($_SESSION['Processing'][$receivedRequest->getId()]['OriginalBinding']); $response->setOriginalIssuer($_SESSION['Processing'][$receivedRequest->getId()]['OriginalIssuer']); $this->_server->unsetProcessingMode(); $sentResponse = $this->_server->createEnhancedResponse($receivedRequest, $response); $this->_server->sendResponseToRequestIssuer($receivedRequest, $sentResponse); return; } }
public function serve($serviceName) { $response = $this->_server->getBindingsModule()->receiveResponse(); $_SESSION['consent'][$response->getId()]['response'] = $response; $request = $this->_server->getReceivedRequestFromResponse($response); $serviceProvider = $this->_server->getRepository()->fetchServiceProviderByEntityId($request->getIssuer()); $spMetadataChain = EngineBlock_SamlHelper::getSpRequesterChain($serviceProvider, $request, $this->_server->getRepository()); $identityProviderEntityId = $response->getOriginalIssuer(); $identityProvider = $this->_server->getRepository()->fetchIdentityProviderByEntityId($identityProviderEntityId); // Flush log if SP or IdP has additional logging enabled $requireAdditionalLogging = EngineBlock_SamlHelper::doRemoteEntitiesRequireAdditionalLogging(array_merge($spMetadataChain, array($identityProvider))); if ($requireAdditionalLogging) { $application = EngineBlock_ApplicationSingleton::getInstance(); $application->flushLog('Activated additional logging for one or more SPs in the SP requester chain, or the IdP'); $log = $application->getLogInstance(); $log->info('Raw HTTP request', array('http_request' => (string) $application->getHttpRequest())); } if ($this->isConsentDisabled($spMetadataChain, $identityProvider)) { $response->setConsent(SAML2_Const::CONSENT_INAPPLICABLE); $response->setDestination($response->getReturn()); $response->setDeliverByBinding('INTERNAL'); $this->_server->getBindingsModule()->send($response, $serviceProvider); return; } $consentDestinationEntityMetadata = $spMetadataChain[0]; $attributes = $response->getAssertion()->getAttributes(); $consent = $this->_consentFactory->create($this->_server, $response, $attributes); $priorConsent = $consent->hasStoredConsent($consentDestinationEntityMetadata); if ($priorConsent) { $response->setConsent(SAML2_Const::CONSENT_PRIOR); $response->setDestination($response->getReturn()); $response->setDeliverByBinding('INTERNAL'); $this->_server->getBindingsModule()->send($response, $serviceProvider); return; } $html = $this->_server->renderTemplate('consent', array('action' => $this->_server->getUrl('processConsentService'), 'ID' => $response->getId(), 'attributes' => $attributes, 'sp' => $consentDestinationEntityMetadata, 'idp' => $identityProvider)); $this->_server->sendOutput($html); }
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); }