/** * @param SAML2_AuthnRequest $authnRequest * @param SimpleSAML_Configuration $idpConfig * @param $nameId * @param $issuer * @param array $attributes * @return SAML2_Response */ public function create(SAML2_AuthnRequest $authnRequest, SimpleSAML_Configuration $idpConfig, $nameId, $issuer, array $attributes) { /* $returnAttributes contains the attributes we should return. Send them. */ $assertion = new SAML2_Assertion(); $assertion->setIssuer($issuer); $assertion->setNameId(array('Value' => $nameId, 'Format' => SAML2_Const::NAMEID_UNSPECIFIED)); $assertion->setNotBefore(time()); $assertion->setNotOnOrAfter(time() + 5 * 60); // Valid audiences is not required so disabled for now // $assertion->setValidAudiences(array($authnRequest->getIssuer())); $assertion->setAttributes($attributes); $assertion->setAttributeNameFormat(SAML2_Const::NAMEFORMAT_UNSPECIFIED); $assertion->setAuthnContext(SAML2_Const::AC_PASSWORD); $subjectConfirmation = new SAML2_XML_saml_SubjectConfirmation(); $subjectConfirmation->Method = SAML2_Const::CM_BEARER; $subjectConfirmation->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData(); $subjectConfirmation->SubjectConfirmationData->NotOnOrAfter = time() + 5 * 60; $subjectConfirmation->SubjectConfirmationData->Recipient = $authnRequest->getAssertionConsumerServiceURL(); $subjectConfirmation->SubjectConfirmationData->InResponseTo = $authnRequest->getId(); $assertion->setSubjectConfirmation(array($subjectConfirmation)); $response = new SAML2_Response(); $response->setRelayState($authnRequest->getRelayState()); $response->setDestination($authnRequest->getAssertionConsumerServiceURL()); $response->setIssuer($issuer); $response->setInResponseTo($authnRequest->getId()); $response->setAssertions(array($assertion)); $this->addSigns($response, $idpConfig); return $response; }
continue; } if (count($values) === 0) { /* Return all attributes. */ $returnAttributes[$name] = $attributes[$name]; continue; } /* Filter which attribute values we should return. */ $returnAttributes[$name] = array_intersect($values, $attributes[$name]); } } /* $returnAttributes contains the attributes we should return. Send them. */ $assertion = new SAML2_Assertion(); $assertion->setIssuer($idpEntityId); $assertion->setNameId($query->getNameId()); $assertion->setNotBefore(time()); $assertion->setNotOnOrAfter(time() + 5 * 60); $assertion->setValidAudiences(array($spEntityId)); $assertion->setAttributes($returnAttributes); $assertion->setAttributeNameFormat($attributeNameFormat); $sc = new SAML2_XML_saml_SubjectConfirmation(); $sc->Method = SAML2_Const::CM_BEARER; $sc->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData(); $sc->SubjectConfirmationData->NotOnOrAfter = time() + 5 * 60; $sc->SubjectConfirmationData->Recipient = $endpoint; $sc->SubjectConfirmationData->InResponseTo = $query->getId(); $assertion->setSubjectConfirmation(array($sc)); sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $assertion); $response = new SAML2_Response(); $response->setRelayState($query->getRelayState()); $response->setDestination($endpoint);
private function buildResponse($returnAttributes) { /* SubjectConfirmation */ $sc = new SAML2_XML_saml_SubjectConfirmation(); $sc->Method = SAML2_Const::CM_BEARER; $sc->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData(); $sc->SubjectConfirmationData->NotBefore = time(); $sc->SubjectConfirmationData->NotOnOrAfter = time() + $this->config->getInteger('validFor'); $sc->SubjectConfirmationData->InResponseTo = $this->query->getId(); $assertion = new SAML2_Assertion(); $assertion->setSubjectConfirmation(array($sc)); $assertion->setIssuer($this->aaEntityId); $assertion->setNameId($this->query->getNameId()); $assertion->setNotBefore(time()); $assertion->setNotOnOrAfter(time() + $this->config->getInteger('validFor')); $assertion->setValidAudiences(array($this->spEntityId)); $assertion->setAttributes($returnAttributes); $assertion->setAttributeNameFormat($this->attributeNameFormat); if ($this->signAssertion) { sspmod_saml_Message::addSign($this->aaMetadata, $this->spMetadata, $assertion); } /* The Response */ $response = new SAML2_Response(); $response->setRelayState($this->query->getRelayState()); $response->setIssuer($this->aaEntityId); $response->setInResponseTo($this->query->getId()); $response->setAssertions(array($assertion)); if ($this->signResponse) { sspmod_saml_Message::addSign($this->aaMetadata, $this->spMetadata, $response); } return $response; }
/** * @param SAML2_AuthnRequest|EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request * @param SAML2_Response|EngineBlock_Saml2_ResponseAnnotationDecorator $sourceResponse */ public function createEnhancedResponse(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request, EngineBlock_Saml2_ResponseAnnotationDecorator $sourceResponse) { $newResponse = $this->_createBaseResponse($request); // We don't support multiple assertions, only use the first one. $sourceAssertions = $sourceResponse->getAssertions(); $sourceAssertion = $sourceAssertions[0]; // Store the Origin response and issuer (from the IdP) $newResponse->setOriginalResponse($sourceResponse->getOriginalResponse() ? $sourceResponse->getOriginalResponse() : $sourceResponse); $newResponse->setOriginalIssuer($sourceResponse->getOriginalIssuer() ? $sourceResponse->getOriginalIssuer() : $newResponse->getOriginalResponse()->getIssuer()); // Copy over the Status (which should be success) $newResponse->setStatus($sourceResponse->getStatus()); // Create a new assertion by us. $newAssertion = new SAML2_Assertion(); $newResponse->setAssertions(array($newAssertion)); $newAssertion->setId($this->getNewId(IdFrame::ID_USAGE_SAML2_ASSERTION)); $newAssertion->setIssueInstant(time()); $newAssertion->setIssuer($newResponse->getIssuer()); // Unless of course we are in 'stealth' / transparent mode, in which case, // pretend to be the Identity Provider. $serviceProvider = $this->getRepository()->fetchServiceProviderByEntityId($request->getIssuer()); $mustProxyTransparently = $request->isTransparent() || $serviceProvider->isTransparentIssuer; if (!$this->isInProcessingMode() && $mustProxyTransparently) { $newResponse->setIssuer($newResponse->getOriginalIssuer()); $newAssertion->setIssuer($newResponse->getOriginalIssuer()); } // Copy over the NameID for now... // (further on in the filters we'll have more info and set this to something better) $sourceNameId = $sourceAssertion->getNameId(); if (!empty($sourceNameId) && !empty($sourceNameId['Value']) && !empty($sourceNameId['Format'])) { $newAssertion->setNameId(array('Value' => $sourceNameId['Value'], 'Format' => $sourceNameId['Format'])); } // Set up the Subject Confirmation element. $subjectConfirmation = new SAML2_XML_saml_SubjectConfirmation(); $subjectConfirmation->Method = SAML2_Const::CM_BEARER; $newAssertion->setSubjectConfirmation(array($subjectConfirmation)); $subjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData(); $subjectConfirmation->SubjectConfirmationData = $subjectConfirmationData; // Confirm where we are sending it. $acs = $this->getRequestAssertionConsumer($request); $subjectConfirmationData->Recipient = $acs->location; // Confirm that this is in response to their AuthnRequest (unless, you know, it isn't). if (!$request->isUnsolicited()) { /** @var SAML2_AuthnRequest $request */ $subjectConfirmationData->InResponseTo = $request->getId(); } // Note that it is valid for some 5 minutes. $notOnOrAfter = time() + $this->getConfig('NotOnOrAfter', 300); $newAssertion->setNotBefore(time() - 1); if ($sourceAssertion->getSessionNotOnOrAfter()) { $newAssertion->setSessionNotOnOrAfter($sourceAssertion->getSessionNotOnOrAfter()); } $newAssertion->setNotOnOrAfter($notOnOrAfter); $subjectConfirmationData->NotOnOrAfter = $notOnOrAfter; // And only valid for the SP that requested it. $newAssertion->setValidAudiences(array($request->getIssuer())); // Copy over the Authentication information because the IdP did the authentication, not us. $newAssertion->setAuthnInstant($sourceAssertion->getAuthnInstant()); $newAssertion->setSessionIndex($sourceAssertion->getSessionIndex()); $newAssertion->setAuthnContextClassRef($sourceAssertion->getAuthnContextClassRef()); $newAssertion->setAuthnContextDeclRef($sourceAssertion->getAuthnContextDeclRef()); if ($sourceAssertion->getAuthnContextDecl()) { $newAssertion->setAuthnContextDecl($sourceAssertion->getAuthnContextDecl()); } // Copy over the Authenticating Authorities and add the EntityId of the Source Response Issuer. // Note that because EB generates multiple responses, this will likely result in: // "https://engine/../idp/metadata" !== "https://original-idp/../idpmetadata" => true, gets added // "https://engine/../idp/metadata" !== "https://engine/../idp/metadata" => false, does not get added // "https://engine/../idp/metadata" !== "https://engine/../idp/metadata" => false, does not get added // UNLESS the Response is destined for an SP in VO mode, in which case the flow will be: // "https://engine/../idp/metadata" !== "https://original-idp/../idpmetadata" => true, gets added // "https://engine/../idp/metadata" !== "https://engine/../idp/metadata" => false, does not get added // "https://engine/../idp/metadata/vo:void" !== "https://engine/../idp/metadata" => TRUE, gets added! // This is a 'bug'/'feature' that we're keeping in for BWC reasons. $authenticatingAuthorities = $sourceAssertion->getAuthenticatingAuthority(); if ($this->getUrl('idpMetadataService') !== $sourceResponse->getIssuer()) { $authenticatingAuthorities[] = $sourceResponse->getIssuer(); } $newAssertion->setAuthenticatingAuthority($authenticatingAuthorities); // Copy over the attributes $newAssertion->setAttributes($sourceAssertion->getAttributes()); $newAssertion->setAttributeNameFormat(SAML2_Const::NAMEFORMAT_URI); return $newResponse; }