/**
  * @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;
 }