/** * Filter the response. * * @param EngineBlock_Saml2_ResponseAnnotationDecorator $response * @param array $responseAttributes * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request * @param ServiceProvider $serviceProvider * @param IdentityProvider $identityProvider * @throws EngineBlock_Exception * @throws Exception */ public function filter(EngineBlock_Saml2_ResponseAnnotationDecorator $response, array &$responseAttributes, EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, ServiceProvider $serviceProvider, IdentityProvider $identityProvider) { /** @var SAML2_AuthnRequest $request */ // Note that IDs are only unique per SP... we hope... $responseNameId = $response->getAssertion()->getNameId(); $sessionKey = $serviceProvider->entityId . '>' . $request->getId(); if (isset($_SESSION[$sessionKey]['collabPersonId'])) { $collabPersonId = $_SESSION[$sessionKey]['collabPersonId']; } else { if ($response->getCollabPersonId()) { $collabPersonId = $response->getCollabPersonId(); } else { if (isset($responseAttributes['urn:oid:1.3.6.1.4.1.1076.20.40.40.1'][0])) { $collabPersonId = $responseAttributes['urn:oid:1.3.6.1.4.1.1076.20.40.40.1'][0]; } else { if (!empty($responseNameId['Value'])) { $collabPersonId = $responseNameId['Value']; } else { $collabPersonId = null; } } } } $commands = $this->_getCommands(); /** @var EngineBlock_Corto_Filter_Command_Abstract $command */ foreach ($commands as $command) { // Inject everything we have into the adapter $command->setProxyServer($this->_server); $command->setIdentityProvider($identityProvider); $command->setServiceProvider($serviceProvider); $command->setRequest($request); $command->setResponse($response); $command->setResponseAttributes($responseAttributes); $command->setCollabPersonId($collabPersonId); // Execute the command try { $command->execute(); } catch (EngineBlock_Exception $e) { $e->idpEntityId = $identityProvider->entityId; $e->spEntityId = $serviceProvider->entityId; $e->userId = $collabPersonId; throw $e; } if (method_exists($command, 'getResponse')) { $response = $command->getResponse(); } if (method_exists($command, 'getResponseAttributes')) { $responseAttributes = $command->getResponseAttributes(); } if (method_exists($command, 'getCollabPersonId')) { $collabPersonId = $command->getCollabPersonId(); } // Give the command a chance to stop filtering if (!$command->mustContinueFiltering()) { break; } } $_SESSION[$sessionKey]['collabPersonId'] = $collabPersonId; }
public function testToString() { $request = new SAML2_AuthnRequest(); $request->setId('TEST123'); $request->setIssueInstant(0); $annotatedRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($request); $annotatedRequest->setDebug(); $this->assertEquals('{"sspMessage":"<?xml version=\\"1.0\\"?>\\n<samlp:AuthnRequest xmlns:samlp=\\"urn:oasis:names:tc:SAML:2.0:protocol\\" xmlns:saml=\\"urn:oasis:names:tc:SAML:2.0:assertion\\" ID=\\"TEST123\\" Version=\\"2.0\\" IssueInstant=\\"1970-01-01T00:00:00Z\\"\\/>\\n","voContext":null,"keyId":null,"explicitVoContext":true,"wasSigned":false,"debug":true,"unsolicited":false,"transparent":false,"deliverByBinding":null}', $annotatedRequest->__toString()); }
public static function cacheResponse(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $receivedRequest, EngineBlock_Saml2_ResponseAnnotationDecorator $receivedResponse, $type) { if ($type !== self::RESPONSE_CACHE_TYPE_IN) { throw new EngineBlock_Exception('Unknown response type'); } if (!isset($_SESSION['CachedResponses'])) { $_SESSION['CachedResponses'] = array(); } $_SESSION['CachedResponses'][] = array('sp' => $receivedRequest->getIssuer(), 'idp' => $receivedResponse->getIssuer(), 'type' => $type, 'response' => $receivedResponse, 'vo' => $receivedRequest->getVoContext(), 'key' => $receivedRequest->getKeyId()); }
public static function createFromRequest(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $originalRequest, IdentityProvider $idpMetadata, EngineBlock_Corto_ProxyServer $server) { $nameIdPolicy = array('AllowCreate' => 'true'); /** * Name policy is not required, so it is only set if configured, SAML 2.0 spec * says only following values are allowed: * - urn:oasis:names:tc:SAML:2.0:nameid-format:transient * - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent. * * Note: Some IDP's like those using ADFS2 do not understand those, for these cases the format can be 'configured as empty * or set to an older version. */ if (!empty($idpMetadata->nameIdFormat)) { $nameIdPolicy['Format'] = $idpMetadata->nameIdFormat; } /** @var SAML2_AuthnRequest $originalRequest */ $sspRequest = new SAML2_AuthnRequest(); $sspRequest->setId($server->getNewId(\OpenConext\Component\EngineBlockFixtures\IdFrame::ID_USAGE_SAML2_REQUEST)); $sspRequest->setIssueInstant(time()); $sspRequest->setDestination($idpMetadata->singleSignOnServices[0]->location); $sspRequest->setForceAuthn($originalRequest->getForceAuthn()); $sspRequest->setIsPassive($originalRequest->getIsPassive()); $sspRequest->setAssertionConsumerServiceURL($server->getUrl('assertionConsumerService')); $sspRequest->setProtocolBinding(SAML2_Const::BINDING_HTTP_POST); $sspRequest->setIssuer($server->getUrl('spMetadataService')); $sspRequest->setNameIdPolicy($nameIdPolicy); if (empty($idpMetadata->disableScoping)) { // Copy over the Idps that are allowed to answer this request. $sspRequest->setIDPList($originalRequest->getIDPList()); // Proxy Count $sspRequest->setProxyCount($originalRequest->getProxyCount() ? $originalRequest->getProxyCount() : $server->getConfig('max_proxies', 10)); // Add the SP to the requesterIds $requesterIds = $originalRequest->getRequesterID(); $requesterIds[] = $originalRequest->getIssuer(); // Add the SP as the requester $sspRequest->setRequesterID($requesterIds); } // Use the default binding even if more exist $request = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($sspRequest); $request->setDeliverByBinding($idpMetadata->singleSignOnServices[0]->binding); return $request; }
/** * Get the metadata for a requester, if allowed by the configuration. * * @param ServiceProvider $serviceProvider * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request * @param MetadataRepositoryInterface $repository * @return null|ServiceProvider */ public static function findRequesterServiceProvider(ServiceProvider $serviceProvider, EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, MetadataRepositoryInterface $repository) { if (!$serviceProvider->isTrustedProxy) { return null; } if (!$request->wasSigned()) { return null; } // Requester IDs are appended to as they pass through a proxy, so we always want the last RequesterID // Note that this is not specified in the spec, but this is what we do and what SSP does. $requesterIds = $request->getRequesterIds(); $lastRequesterEntityId = end($requesterIds); if (!$lastRequesterEntityId) { return null; } $lastRequesterEntity = $repository->findServiceProviderByEntityId($lastRequesterEntityId); if (!$lastRequesterEntity) { throw new EngineBlock_Exception_DissimilarServiceProviderWorkflowStates($serviceProvider, $lastRequesterEntityId); } return $lastRequesterEntity; }
protected function _getNameIdFormat(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, ServiceProvider $spEntityMetadata) { // If a NameIDFormat was explicitly set in the ServiceRegistry, use that... if ($spEntityMetadata->nameIdFormat) { return $spEntityMetadata->nameIdFormat; } // If the SP requests a specific NameIDFormat in their AuthnRequest /** @var SAML2_AuthnRequest $request */ $nameIdPolicy = $request->getNameIdPolicy(); if (!empty($nameIdPolicy['Format'])) { $mayUseRequestedNameIdFormat = true; $requestedNameIdFormat = $nameIdPolicy['Format']; // Do we support the NameID Format that the SP requests? if (!in_array($requestedNameIdFormat, $this->SUPPORTED_NAMEID_FORMATS)) { EngineBlock_ApplicationSingleton::getLog()->notice("Whoa, SP '{$spEntityMetadata->entityId}' requested '{$requestedNameIdFormat}' " . "however we don't support that format, opting to try something else it supports " . "instead of sending an error. SP might not be happy with this violation of the spec " . "but it's probably a lot happier with a valid Response than an Error Response"); $mayUseRequestedNameIdFormat = false; } // Is this SP restricted to specific NameIDFormats? if (!empty($spEntityMetadata->supportedNameIdFormats)) { if (!in_array($requestedNameIdFormat, $spEntityMetadata->supportedNameIdFormats)) { EngineBlock_ApplicationSingleton::getLog()->notice("Whoa, SP '{$spEntityMetadata->entityId}' requested '{$requestedNameIdFormat}' " . "opting to try something else it supports " . "instead of sending an error. SP might not be happy with this violation of the spec " . "but it's probably a lot happier with a valid Response than an Error Response"); $mayUseRequestedNameIdFormat = false; } } if ($mayUseRequestedNameIdFormat) { return $requestedNameIdFormat; } } // So neither a NameIDFormat is explicitly set in the metadata OR a (valid) NameIDPolicy is set in the AuthnRequest // so we check what the SP supports (or what JANUS claims that it supports) and // return the least privacy sensitive one. if (!empty($spEntityMetadata->supportedNameIdFormats)) { foreach ($this->SUPPORTED_NAMEID_FORMATS as $supportedNameIdFormat) { if (in_array($supportedNameIdFormat, $spEntityMetadata->supportedNameIdFormats)) { return $supportedNameIdFormat; } } } throw new EngineBlock_Exception("Whoa, SP '{$spEntityMetadata->entityId}' has no NameIDFormat set, did send a (valid) NameIDPolicy and has no supported NameIDFormats set... I give up...", EngineBlock_Exception::CODE_NOTICE); }
protected function _showWayf(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, array $candidateIdpEntityIds) { // Post to the 'continueToIdp' service $action = $this->_server->getUrl('continueToIdP'); $serviceProvider = $this->_server->getRepository()->fetchServiceProviderByEntityId($request->getIssuer()); $idpList = $this->_transformIdpsForWAYF($candidateIdpEntityIds, $request->isDebugRequest()); $output = $this->_server->renderTemplate('discover', array('preselectedIdp' => $this->_server->getCookie('selectedIdp'), 'action' => $action, 'ID' => $request->getId(), 'idpList' => $idpList, 'metaDataSP' => $serviceProvider)); $this->_server->sendOutput($output); }
/** * @param $ebRequest */ protected function _annotateRequestWithKeyId(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $ebRequest) { $keyId = $this->_server->getKeyId(); if (!$keyId) { return; } $ebRequest->setKeyId($keyId); }
/** * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $fromRequest * @param EngineBlock_Saml2_AuthnRequestAnnotationDecorator $toRequest * @return $this */ public function link(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $fromRequest, EngineBlock_Saml2_AuthnRequestAnnotationDecorator $toRequest) { // Store the mapping from the new request ID to the original request ID $this->linkStorage[$fromRequest->getId()] = $toRequest->getId(); return $this; }
public function sendResponseToRequestIssuer(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, EngineBlock_Saml2_ResponseAnnotationDecorator $response) { /** @var SAML2_AuthnRequest $request */ $requestIssuer = $request->getIssuer(); $serviceProvider = $this->getRepository()->fetchServiceProviderByEntityId($requestIssuer); // Detect error responses and send them off without an assertion. /** @var SAML2_Response $response */ $status = $response->getStatus(); if ($status['Code'] !== 'urn:oasis:names:tc:SAML:2.0:status:Success') { $response->setAssertions(array()); $this->getBindingsModule()->send($response, $serviceProvider); return; } $this->filterOutputAssertionAttributes($response, $request); $this->getBindingsModule()->send($response, $serviceProvider); }