/** * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request * @param ServiceProvider $remoteEntity * @return bool */ protected function _verifyAcsLocation(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, ServiceProvider $remoteEntity) { /** @var SAML2_AuthnRequest $request */ // show error when acl is given without binding or vice versa $acsUrl = $request->getAssertionConsumerServiceURL(); $acsIndex = $request->getAssertionConsumerServiceIndex(); $protocolBinding = $request->getProtocolBinding(); if ($acsUrl xor $protocolBinding) { $this->_server->getSessionLog()->error("Incomplete ACS location found in request (missing URL or binding)"); return false; } // if none specified, all is ok if (!$acsUrl && !$acsIndex) { return true; } $acs = $this->_server->getCustomAssertionConsumer($request, $remoteEntity); // acs is only returned on valid and known ACS return is_array($acs); }
/** * Returns a custom ACS location from request or false when * none is specified * * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request * @param ServiceProvider $serviceProvider * @return null|\OpenConext\Component\EngineBlockMetadata\IndexedService */ public function getCustomAssertionConsumer(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, ServiceProvider $serviceProvider) { $requestWasSigned = $request->wasSigned(); /** @var SAML2_AuthnRequest $request */ // Ignore requests for bindings we don't support for responses. if ($request->getProtocolBinding() !== SAML2_Const::BINDING_HTTP_POST) { $this->_server->getSessionLog()->notice("ProtocolBinding '{$request->getProtocolBinding()}' requested is not supported, ignoring..."); return false; } // Custom ACS Location & ProtocolBinding goes first if ($request->getAssertionConsumerServiceURL() && $request->getProtocolBinding()) { if ($requestWasSigned) { $this->_server->getSessionLog()->info("Using AssertionConsumerServiceLocation '{$request->getAssertionConsumerServiceURL()}' " . "and ProtocolBinding '{$request->getProtocolBinding()}' from signed request. "); return new Service($request->getAssertionConsumerServiceURL(), $request->getProtocolBinding()); } else { $requestAcsIsRegisteredInMetadata = false; foreach ($serviceProvider->assertionConsumerServices as $entityAcs) { $requestAcsIsRegisteredInMetadata = $entityAcs->location === $request->getAssertionConsumerServiceURL() && $entityAcs->binding === $request->getProtocolBinding(); if ($requestAcsIsRegisteredInMetadata) { break; } } if ($requestAcsIsRegisteredInMetadata) { $this->_server->getSessionLog()->info("Using AssertionConsumerServiceLocation '{$request->getAssertionConsumerServiceURL()}' " . "and ProtocolBinding '{$request->getProtocolBinding()}' from unsigned request, " . "it's okay though, the ACSLocation and Binding were registered in the metadata"); return new Service($request->getAssertionConsumerServiceURL(), $request->getProtocolBinding()); } else { $this->_server->getSessionLog()->notice("AssertionConsumerServiceLocation '{$request->getAssertionConsumerServiceURL()}' " . "and ProtocolBinding '{$request->getProtocolBinding()}' were mentioned in request, " . "but the AuthnRequest was not signed, and the ACSLocation and Binding were not found in " . "the metadata for the SP, so I am disallowed from acting upon it." . "Trying the default endpoint.."); } } return false; } else { if ($request->getAssertionConsumerServiceURL() || $request->getProtocolBinding()) { // Note that an SP is not actually required to supply both a URL and a Binding. // But what should we do if we don't have both? Pick out a random counterpart from the metadata? // Seems a little hard to predict for the SP, so we go with the default endpoint. $this->_server->getSessionLog()->notice("AssertionConsumerServiceLocation '{$request->getAssertionConsumerServiceURL()}' " . "or ProtocolBinding '{$request->getProtocolBinding()}' were mentioned in request, " . "but not both! Ignoring... "); } } if ($request->getAssertionConsumerServiceIndex()) { $index = (int) $request->getAssertionConsumerServiceIndex(); // Find the indexed ACS in the metadata. $indexedAssertionConsumerService = null; foreach ($serviceProvider->assertionConsumerServices as $assertionConsumerService) { if ((int) $assertionConsumerService->serviceIndex === $index) { $indexedAssertionConsumerService = $assertionConsumerService; break; } } if ($indexedAssertionConsumerService) { $this->_server->getSessionLog()->info("Using AssertionConsumerServiceIndex '{$index}' from request"); return $indexedAssertionConsumerService; } else { $this->_server->getSessionLog()->notice("AssertionConsumerServiceIndex was mentioned in request, but we don't know any ACS by " . "index '{$index}'? Maybe the metadata was updated and we don't have that endpoint yet? " . "Trying the default endpoint.."); } } }