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