/**
  * Handle an authentication request.
  *
  * @param  array $params
  * @return void
  *
  */
 public function singleSignOnService($params)
 {
     if ($this->_server->callfilters('init')) {
         $request = $this->_server->getBindingsModule()->receiveRequest($params);
         /**
          * We are always a proxy so if the scoped proxycount == 0, respond with a ProxyCountExceeded error
          * @todo register path length in cached responses and use in proxyCount check ??
          */
         if (nvl2($request, 'samlp:Scoping', '_ProxyCount') === 0) {
             $response = $this->_server->createErrorResponse($request, 'ProxyCountExceeded');
             return $this->_server->sendResponseToRequestIssuer($request, $response);
         }
         // Get all registered Single Sign On Services
         $candidateIDPs = $this->_server->getAllowedIdps();
         // No IdPs found! Send an error response back.
         if (empty($candidateIDPs)) {
             $response = $this->_server->createErrorResponse($request, 'NoSupportedIDP');
             return $this->_server->sendResponseToRequestIssuer($request, $response);
         }
         // If we configured an IDPList in metadata this is our primary scoping
         $scopedIDPs = $this->_server->getPresetIDPs();
         /**
          * Add scoping in request to configured scoping - this is NOT according to the spec
          * which says that you MUST append to a received IDPList
          */
         foreach ((array) nvl3($request, 'samlp:Scoping', 'samlp:IDPList', 'samlp:IDPEntry') as $IDPEntry) {
             $scopedIDPs[] = $IDPEntry['_ProviderID'];
         }
         // remove issuer + us from scope for use now ..
         $requesterIDs = array($params['EntityID'], $request['saml:Issuer']['__v']);
         // filter out already visited proxies (RequesterID) to prevent looping ...
         foreach ((array) nvl2($request, 'samlp:Scoping', 'samlp:RequesterID') as $requesterID) {
             $requesterIDs[] = $requesterID['__v'];
         }
         $relevantScopedIDPs = array_diff($scopedIDPs, $requesterIDs);
         // If we have scoping, filter out every non-scoped IdP
         $scopedCandidateIDPs = array_intersect($relevantScopedIDPs, $candidateIDPs);
         $state = array();
         $filters = $this->_server->getCurrentMD('IDP', 'corto:discoverfilter', null, array());
         if (!$filters) {
             $filters = array('demoFilterClass::showWayf');
         }
         $filterparams = array('request' => $request, 'scopedCandidateIDPs' => $scopedCandidateIDPs, 'relevantScopedIDPs' => $relevantScopedIDPs, 'server' => $this->_server);
     }
     /* If we end up her we should show the wayf ... */
     if ($this->_server->callfilters("discovery", $state, $filters, $filterparams)) {
     }
 }
 /**
  *
  * Saves the info needed for later SLO:
  * - Issuer of incoming response or receipient of out going response
  * - Subject
  * - Time when no longer valid @@todo notonorafter - for slo info
  *
  * SLO can be initiated from a back-end request so we need to be able
  * to get to the subjects incoming and outgoing responses using only
  * subject and entityid
  *
  * @param array $message
  */
 public function saveSloInfo(array $message)
 {
     $issuer = $message['saml:Issuer']['__v'];
     $me = $this->getCurrentMD('entityID');
     if ($issuer == $me) {
         // outgoing response
         $type = 'SP';
         $entity = $message['__']['destinationid'];
     } else {
         // incoming response
         $type = 'IDP';
         $entity = $issuer;
     }
     if (!nvl2($this->getRemoteEntity($entity), 'SP', 'saveSLOInfo')) {
         return;
     }
     // @todo support SessionIndex and SubjectConfirmation, EncryptedID
     $id = nvl3($message, 'saml:Assertion', 'saml:Subject', 'saml:NameID');
     if (!$id) {
         throw new Corto_ProxyServer_Exception("No NameID in message (EncryptedID not supported yet!)");
     }
     $sessionnotonorafter = nvl3($message, 'saml:Assertion', 'saml:AuthnStatement', '_SessionNotOnOrAfter');
     $sessionindex = session_id();
     $key = 'ID-' . sha1($me . serialize($id));
     if ($notonorafter = db_del($key, 'notonorafter')) {
         if ($notonorafter > timeStamp()) {
             db_put($key, $notonorafter, 'notonorafter');
             // be prepared for yet another one ...
             throw new Corto_ProxyServer_Exception("A very rare, but yet unsupported situation has happened:\n                An sssertion was received while an earlier LogoutRequest was still active: notonorafter = {$notonorafter}");
         }
     }
     db_add($key, $sessionindex, 'session');
     $info = array('entity' => $entity, 'nameID' => $id, 'nameIDType' => 'saml:NameID', 'sessionnotonorafter' => $sessionnotonorafter, 'session' => $sessionindex);
     db_put($type . '-' . $sessionindex, $info, sha1($entity));
 }
 protected static function optimizeMetaData($type, $md, $optimized = array())
 {
     // @note remember not to set keys for things that might be overidden by merged md
     // ie. set ['saveSLOInfo'] to true, but do not set the ['saveSLOInfo'] at all
     // when false as it WILL overwrite the true !!!
     $meta = array();
     $rawmeta = $md[$type];
     $commonmd = self::merge(nvl($optimized, '_COMMON_'), nvl($rawmeta, '_COMMON_'));
     unset($rawmeta['_COMMON_']);
     if ($entitymd = nvl($rawmeta, 'md:EntityDescriptor')) {
         $rawmeta['md:EntitiesDescriptor'] = array(array('md:EntityDescriptor' => $entitymd));
         unset($rawmeta['md:EntityDescriptor']);
     }
     foreach ((array) nvl($rawmeta, 'md:EntitiesDescriptor') as $entitiesDescriptor) {
         $entitiescommon = array();
         if (isset($entitiesDescriptor['md:Extensions']['mdattr:EntityAttributes']['saml:Attribute'])) {
             foreach ((array) $entitiesDescriptor['md:Extensions']['mdattr:EntityAttributes']['saml:Attribute'] as $attribute) {
                 foreach ((array) $attribute['saml:AttributeValue'] as $attributeValue) {
                     $entitiescommon[$attribute['_Name']][] = $attributeValue;
                 }
             }
         }
         $entitiescommon = self::merge($commonmd, $entitiescommon);
         foreach ((array) $entitiesDescriptor['md:EntityDescriptor'] as $entityDescriptor) {
             if (empty($entityDescriptor['_entityID'])) {
                 $entityDescriptor['_entityID'] = '_COMMON_';
             }
             $cortoEntityDescriptor = array();
             $cortoEntityDescriptor['entityID'] = $entityDescriptor['_entityID'];
             foreach ((array) nvl3($entityDescriptor, 'md:Extensions', 'mdattr:EntityAttributes', 'saml:Attribute') as $attribute) {
                 foreach ((array) $attribute['saml:AttributeValue'] as $attributeValue) {
                     $cortoEntityDescriptor[$attribute['_Name']][] = $attributeValue;
                 }
             }
             foreach ((array) nvl($entityDescriptor, 'md:IDPSSODescriptor') as $idpsso) {
                 foreach (array('SingleSignOnService', 'SingleLogoutService') as $service) {
                     foreach ((array) nvl($idpsso, 'md:' . $service) as $sso) {
                         $cortoEntityDescriptor['IDP'][$service][] = array('Location' => $sso['_Location'], 'Binding' => $sso['_Binding']);
                     }
                 }
                 // metadata overrides auto setting
                 if (empty($cortoEntityDescriptor['IDP']['saveSLOInfo']) && ($saveSLOInfo = (bool) nvl2($cortoEntityDescriptor, 'IDP', 'SingleLogoutService'))) {
                     $cortoEntityDescriptor['IDP']['saveSLOInfo'] = $saveSLOInfo;
                 }
             }
             foreach ((array) nvl($entityDescriptor, 'md:SPSSODescriptor') as $spsso) {
                 foreach (array('AssertionConsumerService', 'SingleLogoutService') as $service) {
                     foreach ((array) nvl($spsso, 'md:' . $service) as $acs) {
                         $cortoEntityDescriptor['SP'][$service][$acs['_index']] = array('Location' => $acs['_Location'], 'Binding' => $acs['_Binding'], 'isDefault' => empty($acs['_isDefault']) ? null : $acs['_isDefault']);
                     }
                 }
                 // metadata overrides auto setting
                 if (empty($cortoEntityDescriptor['SP']['saveSLOInfo']) && ($saveSLOInfo = (bool) nvl2($cortoEntityDescriptor, 'SP', 'SingleLogoutService'))) {
                     $cortoEntityDescriptor['SP']['saveSLOInfo'] = $saveSLOInfo;
                 }
             }
             // this is the default resolution algorithm from Meta 2.2.3
             if (isset($cortoEntityDescriptor['SP']['AssertionConsumerService'])) {
                 $acslist =& $cortoEntityDescriptor['SP']['AssertionConsumerService'];
                 ksort($acslist);
                 $default = null;
                 foreach ((array) $acslist as $index => $acs) {
                     if ($acs['isDefault']) {
                         $default = $index;
                     }
                 }
                 $cortoEntityDescriptor['SP']['AssertionConsumerService']['default'] = $default ? $default : min(array_keys($acslist));
             }
             foreach (self::$descriptors as $descriptor) {
                 foreach ((array) nvl($entityDescriptor, 'md:' . $descriptor . 'SSODescriptor') as $idporsp) {
                     foreach (self::$signings as $signing) {
                         if (isset($idporsp['_' . $signing])) {
                             $cortoEntityDescriptor[$descriptor][$signing] = $idporsp['_' . $signing] == 'true' || $idporsp['_' . $signing] == '1';
                         }
                     }
                     foreach ((array) nvl3($idporsp, 'md:Extensions', 'mdattr:EntityAttributes', 'saml:Attribute') as $attribute) {
                         #print_r($attribute);
                         foreach ((array) $attribute['saml:AttributeValue'] as $attributeValue) {
                             foreach ($attributeValue as $value) {
                                 #print_r($value);
                                 $cortoEntityDescriptor[$descriptor][$attribute['_Name']][] = $value;
                             }
                         }
                     }
                     foreach ((array) nvl($idporsp, 'md:KeyDescriptor') as $keyDescriptor) {
                         $use = nvl($keyDescriptor, '_use', 'signing');
                         if (isset($keyDescriptor['ds:KeyInfo']['ds:X509Data'])) {
                             $cortoEntityDescriptor[$descriptor][$use]['X509Certificate'] = $keyDescriptor['ds:KeyInfo']['ds:X509Data']['ds:X509Certificate']['__v'];
                         } elseif (isset($keyDescriptor['ds:KeyInfo']['ds:KeyName'])) {
                             $cortoEntityDescriptor[$descriptor][$use]['KeyName'] = $keyDescriptor['ds:KeyInfo']['ds:KeyName']['__v'];
                         }
                         /*                               $cortoEntityDescriptor[$descriptor][$keyDescriptor['_use']]['KeyName'] =
                                                     $keyDescriptor['ds:KeyInfo']['ds:X509Data']['ds:KeyName']['__v'];
                                                     */
                     }
                     foreach ((array) nvl($cortoEntityDescriptor[$descriptor], 'corto:privatekey') as $privatekey) {
                         $cortoEntityDescriptor[$descriptor][$privatekey['_use']]['X509Privatekey'] = $privatekey['__v'];
                     }
                     unset($cortoEntityDescriptor[$descriptor]['corto:privatekey']);
                     $cortoEntityDescriptor[$descriptor] = self::merge(nvl($entitiescommon, $descriptor), nvl($cortoEntityDescriptor, $descriptor));
                     #unset($common[$descriptor]);
                 }
             }
             $meta[$entityDescriptor['_entityID']] = self::merge($entitiescommon, $cortoEntityDescriptor);
         }
     }
     return $meta;
 }