Revision: 1815
Author: olavmrk
Date: Mon Oct  5 03:53:43 2009
Log: Add support for multiple AssertionConsumerService endpoints.

This patch adds support for multiple AssertionConsumerService endpoints
on the IdP, so that a SP can request that the response is sent to a
specific ACS URL.

Thanks to Georg Gollmann for this patch.

Fixes issue 20
http://code.google.com/p/simplesamlphp/source/detail?r=1815

Modified:
  /trunk/lib/SimpleSAML/Metadata/SAMLParser.php
  /trunk/modules/saml2/lib/Message.php
  /trunk/www/saml2/idp/SSOService.php

=======================================
--- /trunk/lib/SimpleSAML/Metadata/SAMLParser.php       Wed Sep 16 04:11:31 2009
+++ /trunk/lib/SimpleSAML/Metadata/SAMLParser.php       Mon Oct  5 03:53:43 2009
@@ -615,13 +615,24 @@
                        $ret['expire'] = $spd['expire'];
                }

-               /* Find the assertion consumer service endpoint. */
-               $acs = 
$this->getDefaultEndpoint($spd['AssertionConsumerService'],  
array(self::SAML_20_POST_BINDING));
-               if($acs === NULL) {
+               /* Find the assertion consumer service endpoints. */
+               $defaultACS =  
$this->getDefaultEndpoint($spd['AssertionConsumerService'],  
array(self::SAML_20_POST_BINDING));
+               if($defaultACS === NULL) {
                        SimpleSAML_Logger::warning('Could not find a supported 
SAML 2.0  
AssertionConsumerService endpoint for ' .
                                var_export($ret['entityid'], TRUE) . '.');
                } else {
-                       $ret['AssertionConsumerService'] = $acs['Location'];
+                       $defaultACS = $defaultACS['Location'];
+                       $retACS = array($defaultACS);
+
+                       $allACS = 
$this->getEndpoints($spd['AssertionConsumerService'],  
array(self::SAML_20_POST_BINDING));
+                       foreach ($allACS as $acs) {
+                               $acs = $acs['Location'];
+                               if ($acs !== $defaultACS) {
+                                       $retACS[] = $acs;
+                               }
+                       }
+
+                       $ret['AssertionConsumerService'] = $retACS;
                }


@@ -1225,32 +1236,39 @@


        /**
-        * This function attempts to locate the default endpoint which supports 
 
one of the given bindings.
+        * This function attempts to locate all endpoints which supports one of 
 
the given bindings.
         *
-        * @param $endpoints Array with endpoints in the format returned by  
parseGenericEndpoint.
-        * @param $acceptedBindings Array with the accepted bindings. If this 
is  
NULL, then we accept any binding.
-        * @return The default endpoint which supports one of the bindings, or  
NULL if no endpoints supports
-        *         one of the bindings.
         */
-       private function getDefaultEndpoint($endpoints, $acceptedBindings = 
NULL)  
{
+       private function getEndpoints($endpoints, $acceptedBindings = NULL) {

                assert('$acceptedBindings === NULL || 
is_array($acceptedBindings)');

                /* Filter the list of endpoints if $acceptedBindings !== NULL. 
*/
-               if($acceptedBindings !== NULL) {
-                       $newEndpoints = array();
-
-                       foreach($endpoints as $ep) {
-                               /* Add it to the list of valid ACSs if it has 
one of the supported  
bindings. */
-                               if(in_array($ep['Binding'], $acceptedBindings, 
TRUE)) {
-                                       $newEndpoints[] = $ep;
-                               }
-                       }
-
-                       $endpoints = $newEndpoints;
-               }
+               if($acceptedBindings === NULL) return $endpoints;
+
+               $newEndpoints = array();
+               foreach($endpoints as $ep) {
+                       /* Add it to the list of valid ACSs if it has one of 
the supported  
bindings. */
+                       if(in_array($ep['Binding'], $acceptedBindings, TRUE)) {
+                               $newEndpoints[] = $ep;
+                       }
+               }
+               return $newEndpoints;
+       }


+       /**
+        * This function attempts to locate the default endpoint which supports 
 
one of the given bindings.
+        *
+        * @param $endpoints Array with endpoints in the format returned by  
parseGenericEndpoint.
+        * @param $acceptedBindings Array with the accepted bindings. If this 
is  
NULL, then we accept any binding.
+        * @return The default endpoint which supports one of the bindings, or  
NULL if no endpoints supports
+        *         one of the bindings.
+        */
+       private function getDefaultEndpoint($endpoints, $acceptedBindings = 
NULL)  
{
+
+               $endpoints = $this->getEndpoints($endpoints, $acceptedBindings);
+
                /* First we look for the endpoint with isDefault set to true. */
                foreach($endpoints as $ep) {

=======================================
--- /trunk/modules/saml2/lib/Message.php        Thu Oct  1 04:45:27 2009
+++ /trunk/modules/saml2/lib/Message.php        Mon Oct  5 03:53:43 2009
@@ -541,7 +541,7 @@
         * @return SAML2_Assertion  The assertion.
         */
        public static function buildAssertion(SimpleSAML_Configuration  
$srcMetadata,
-               SimpleSAML_Configuration $dstMetadata, array $attributes) {
+               SimpleSAML_Configuration $dstMetadata, array $attributes, 
$consumerURL) {

                $signAssertion = 
$dstMetadata->getBoolean('saml20.sign.assertion', NULL);
                if ($signAssertion === NULL) {
@@ -556,7 +556,7 @@
                }

                $a->setIssuer($srcMetadata->getString('entityid'));
-               
$a->setDestination($dstMetadata->getString('AssertionConsumerService'));
+               $a->setDestination($consumerURL);
                
$a->setValidAudiences(array($dstMetadata->getString('entityid')));

                $a->setNotBefore(time() - 30);
@@ -621,7 +621,7 @@
         * @param SimpleSAML_Configuration $srcMetadata  The metadata of the  
sender (IdP).
         * @param SimpleSAML_Configuration $dstMetadata  The metadata of the  
recipient (SP).
         */
-       public static function buildResponse(SimpleSAML_Configuration  
$srcMetadata, SimpleSAML_Configuration $dstMetadata) {
+       public static function buildResponse(SimpleSAML_Configuration  
$srcMetadata, SimpleSAML_Configuration $dstMetadata, $consumerURL) {

                $signResponse = 
$dstMetadata->getBoolean('saml20.sign.response', NULL);
                if ($signResponse === NULL) {
@@ -631,7 +631,7 @@
                $r = new SAML2_Response();

                $r->setIssuer($srcMetadata->getString('entityid'));
-               
$r->setDestination($dstMetadata->getString('AssertionConsumerService'));
+               $r->setDestination($consumerURL);

                if ($signResponse) {
                        self::addSign($srcMetadata, $dstMetadata, $r);
=======================================
--- /trunk/www/saml2/idp/SSOService.php Fri Sep 25 00:53:59 2009
+++ /trunk/www/saml2/idp/SSOService.php Mon Oct  5 03:53:43 2009
@@ -70,7 +70,14 @@
                $idpMetadata =  
$metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
                $spMetadata = $metadata->getMetaDataConfig($issuer, 
'saml20-sp-remote');

-               $ar = sspmod_saml2_Message::buildResponse($idpMetadata, 
$spMetadata);
+               if (array_key_exists('ConsumerURL', $requestcache)) {
+                       $consumerURL = $requestcache['ConsumerURL'];
+               } else {
+                       $urlArray = 
$spMetadata->getArrayizeString('AssertionConsumerService');
+                       $consumerURL = $urlArray[0];
+               }
+
+               $ar = sspmod_saml2_Message::buildResponse($idpMetadata, 
$spMetadata,  
$consumerURL);
                $ar->setInResponseTo($requestID);
                $ar->setRelayState($relayState);

@@ -141,6 +148,10 @@
                $spentityid = $requestcache['Issuer'];
                $spmetadata = $metadata->getMetaData($spentityid, 
'saml20-sp-remote');

+               $consumerURL = $authnrequest->getAssertionConsumerServiceURL();
+               $consumerArray =  
SimpleSAML_Utilities::arrayize($spmetadata['AssertionConsumerService']);
+               if (($consumerURL != NULL) && (array_search($consumerURL,  
$consumerArray) !== FALSE)) $requestcache['ConsumerURL'] = $consumerURL;
+
                $IDPList = $authnrequest->getIDPList();

                if(array_key_exists('IDPList', $spmetadata)) {
@@ -443,8 +454,14 @@
                /* Begin by creating the assertion. */
                $idpMetadata =  
$metadata->getMetaDataConfig($idpentityid, 'saml20-idp-hosted');
                $spMetadata =  
$metadata->getMetaDataConfig($spentityid, 'saml20-sp-remote');
-
-               $assertion = sspmod_saml2_Message::buildAssertion($idpMetadata, 
 
$spMetadata, $attributes);
+               if (array_key_exists('ConsumerURL', $requestcache)) {
+                       $consumerURL = $requestcache['ConsumerURL'];
+               } else {
+                       $urlArray = 
$spMetadata->getArrayizeString('AssertionConsumerService');
+                       $consumerURL = $urlArray[0];
+               }
+
+               $assertion = sspmod_saml2_Message::buildAssertion($idpMetadata, 
 
$spMetadata, $attributes, $consumerURL);
                $assertion->setInResponseTo($requestID);

                $nameId = $assertion->getNameId();
@@ -454,7 +471,7 @@
                $assertion = 
sspmod_saml2_Message::encryptAssertion($idpMetadata,  
$spMetadata, $assertion);

                /* Create the response. */
-               $ar = sspmod_saml2_Message::buildResponse($idpMetadata, 
$spMetadata);
+               $ar = sspmod_saml2_Message::buildResponse($idpMetadata, 
$spMetadata,  
$consumerURL);
                $ar->setInResponseTo($requestID);
                $ar->setRelayState($relayState);
                $ar->setAssertions(array($assertion));

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"simpleSAMLphp commits" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/simplesamlphp-commits?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to