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 execute() { $logger = EngineBlock_ApplicationSingleton::getLog(); $enforcer = new EngineBlock_Arp_AttributeReleasePolicyEnforcer(); $attributes = $this->_responseAttributes; // Get the Requester chain, which starts at the oldest (farthest away from us SP) and ends with our next hop. $requesterChain = EngineBlock_SamlHelper::getSpRequesterChain($this->_serviceProvider, $this->_request, $this->_server->getRepository()); // Note that though we should traverse in reverse ordering, it doesn't make a difference. // A then B filter or B then A filter are equivalent. foreach ($requesterChain as $spMetadata) { $spEntityId = $spMetadata->entityId; $arp = $this->getMetadataRepository()->fetchServiceProviderArp($spMetadata); if (!$arp) { continue; } $logger->info("Applying attribute release policy for {$spEntityId}"); $attributes = $enforcer->enforceArp($arp, $attributes); } $this->_responseAttributes = $attributes; }
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); }