diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php index 93798dcb1e4e48fb72797f72a27e39044e6cc0a4..136c40da7a0848fd37fd3f3386c937d7314fabdf 100644 --- a/lib/SimpleSAML/IdP.php +++ b/lib/SimpleSAML/IdP.php @@ -137,35 +137,28 @@ class SimpleSAML_IdP { /** - * Retrieve list of SP associations. + * Add an SP association. * - * @return array List of SP associations. + * @param array The SP association. */ - public function getAssociations() { + public function addAssociation(array $association) { + assert('isset($association["id"])'); + assert('isset($association["Handler"])'); $session = SimpleSAML_Session::getInstance(); + $session->addAssociation($this->id, $association); + } - $associations = array(); - - foreach ($session->get_sp_list() as $spEntityId) { - - $nameId = $session->getSessionNameId('saml20-sp-remote', $spEntityId); - if($nameId === NULL) { - $nameId = $this->getNameID(); - } - - $id = 'saml:' . $spEntityId; - $associations[$id] = array( - 'id' => $id, - 'Handler' => 'sspmod_saml_IdP_SAML2', - 'saml:entityID' => $spEntityId, - 'saml:NameID' => $nameId, - 'saml:SessionIndex' => $session->getSessionIndex(), - ); - } + /** + * Retrieve list of SP associations. + * + * @return array List of SP associations. + */ + public function getAssociations() { - return $associations; + $session = SimpleSAML_Session::getInstance(); + return $session->getAssociations($this->id); } @@ -178,10 +171,7 @@ class SimpleSAML_IdP { assert('is_string($assocId)'); $session = SimpleSAML_Session::getInstance(); - - if (substr($assocId, 0, 5) === 'saml:') { - $session->set_sp_logout_completed(substr($assocId, 5)); - } + $session->terminateAssociation($this->id, $assocId); } diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php index dc3bd1a2d3964fc2c3e3691e08a90b23f73aa924..2947d32ec133d78c1ebb48a5bdbf40e3edc993a2 100644 --- a/lib/SimpleSAML/Session.php +++ b/lib/SimpleSAML/Session.php @@ -14,10 +14,6 @@ */ class SimpleSAML_Session { - const STATE_ONLINE = 1; - const STATE_LOGOUTINPROGRESS = 2; - const STATE_LOGGEDOUT = 3; - /** * This is a timeout value for setData, which indicates that the data should be deleted * on logout. @@ -89,6 +85,17 @@ class SimpleSAML_Session { private $logoutState; + /** + * The list of IdP-SP associations. + * + * This is an associative array with the IdP id as the key, and the list of + * associations as the value. + * + * @var array + */ + private $associations = array(); + + /** * private constructor restricts instantiaton to getInstance() */ @@ -176,73 +183,8 @@ class SimpleSAML_Session { public function getAuthority() { return $this->authority; } - - - - // *** SP list to be used with SAML 2.0 SLO *** - // *** *** *** *** *** *** *** *** *** *** *** - - public function add_sp_session($entityid) { - SimpleSAML_Logger::debug('Library - Session: Adding SP session: ' . $entityid); - $this->dirty = TRUE; - $this->sp_at_idpsessions[$entityid] = self::STATE_ONLINE; - } - - public function get_next_sp_logout() { - - if (!$this->sp_at_idpsessions) return null; - $this->dirty = TRUE; - - foreach ($this->sp_at_idpsessions AS $entityid => $sp) { - if ($sp == self::STATE_ONLINE) { - $this->sp_at_idpsessions[$entityid] = self::STATE_LOGOUTINPROGRESS; - return $entityid; - } - } - return null; - } - - public function get_sp_list($state = self::STATE_ONLINE) { - - $list = array(); - if (!$this->sp_at_idpsessions) return $list; - - foreach ($this->sp_at_idpsessions AS $entityid => $sp) { - if ($sp == $state) { - $list[] = $entityid; - } - } - return $list; - } - - public function sp_logout_completed() { - if (!$this->sp_at_idpsessions) return TRUE; - - foreach ($this->sp_at_idpsessions AS $entityid => $sp) { - if ($sp != self::STATE_LOGGEDOUT) return FALSE; - } - return TRUE; - } - - - public function set_sp_logout_completed($entityid) { - SimpleSAML_Logger::debug('Library - Session: Setting SP state completed for : ' . $entityid); - $this->dirty = true; - $this->sp_at_idpsessions[$entityid] = self::STATE_LOGGEDOUT; - } - - public function dump_sp_sessions() { - foreach ($this->sp_at_idpsessions AS $entityid => $sp) { - SimpleSAML_Logger::debug('Dump sp sessions: ' . $entityid . ' status: ' . $sp); - } - } - // *** --- *** - - - - /** * This method retrieves from session a cache of a specific Authentication Request * The complete request is not stored, instead the values that will be needed later @@ -335,33 +277,11 @@ class SimpleSAML_Session { } - /** - * Set the NameID of the users session to the specified entity. - * - * @param string $entityType The type of the entity (saml20-sp-remote, shib13-sp-remote, ...). - * @param string $entityId The entity id. - * @param array $nameId The name identifier. - */ - public function setSessionNameId($entityType, $entityId, $nameId) { - assert('is_string($entityType)'); - assert('is_string($entityId)'); - assert('is_array($nameId)'); - - if(!is_array($this->sessionNameId)) { - $this->sessionNameId = array(); - } - - if(!array_key_exists($entityType, $this->sessionNameId)) { - $this->sessionNameId[$entityType] = array(); - } - - $this->sessionNameId[$entityType][$entityId] = $nameId; - } - - /** * Get the NameID of the users session to the specified entity. * + * Deprecated, remove in version 1.7. + * * @param string $entityType The type of the entity (saml20-sp-remote, shib13-sp-remote, ...). * @param string $entityId The entity id. * @return array The name identifier, or NULL if no name identifier is associated with this session. @@ -967,6 +887,150 @@ class SimpleSAML_Session { return $sh->hasSessionCookie(); } + + /** + * Upgrade the association list to the new format. + * + * Should be removed in version 1.7. + * + * @param string $idp The IdP we should add the associations to. + */ + private function upgradeAssociations($idp) { + assert('is_string($idp)'); + + $sp_at_idpsessions = $this->sp_at_idpsessions; + $this->sp_at_idpsessions = NULL; + $this->dirty = TRUE; + + $globalConfig = SimpleSAML_Configuration::getInstance(); + $sessionLifetime = time() + $globalConfig->getInteger('session.duration', 8*60*60); + + foreach ($sp_at_idpsessions as $spEntityId => $state) { + + if ($state !== 1) { /* 1 == STATE_ONLINE */ + continue; + } + + $nameId = $this->getSessionNameId('saml20-sp-remote', $spEntityId); + if($nameId === NULL) { + $nameId = $this->getNameID(); + } + + $id = 'saml:' . $spEntityId; + + $this->addAssociation($idp, array( + 'id' => $id, + 'Handler' => 'sspmod_saml_IdP_SAML2', + 'Expires' => $sessionLifetime, + 'saml:entityID' => $spEntityId, + 'saml:NameID' => $nameId, + 'saml:SessionIndex' => $this->getSessionIndex(), + )); + } + } + + + /** + * Add an SP association for an IdP. + * + * This function is only for use by the SimpleSAML_IdP class. + * + * @param string $idp The IdP id. + * @param array $association The association we should add. + */ + public function addAssociation($idp, array $association) { + assert('is_string($idp)'); + assert('isset($association["id"])'); + assert('isset($association["Handler"])'); + + if (substr($idp, 0, 6) === 'saml2:' && !empty($this->sp_at_idpsessions)) { + /* Remove in 1.7. */ + $this->upgradeAssociations($idp); + } + + if (!isset($this->associations)) { + $this->associations = array(); + } + + if (!isset($this->associations[$idp])) { + $this->associations[$idp] = array(); + } + + $this->associations[$idp][$association['id']] = $association; + + $this->dirty = TRUE; + } + + + /** + * Retrieve the associations for an IdP. + * + * This function is only for use by the SimpleSAML_IdP class. + * + * @param string $idp The IdP id. + * @return array The IdP associations. + */ + public function getAssociations($idp) { + assert('is_string($idp)'); + + if (substr($idp, 0, 6) === 'saml2:' && !empty($this->sp_at_idpsessions)) { + /* Remove in 1.7. */ + $this->upgradeAssociations($idp); + } + + if (!isset($this->associations)) { + $this->associations = array(); + } + + if (!isset($this->associations[$idp])) { + return array(); + } + + foreach ($this->associations[$idp] as $id => $assoc) { + if (!isset($assoc['Expires'])) { + continue; + } + if ($assoc['Expires'] >= time()) { + continue; + } + + unset($this->associations[$idp][$id]); + } + + return $this->associations[$idp]; + } + + + /** + * Remove an SP association for an IdP. + * + * This function is only for use by the SimpleSAML_IdP class. + * + * @param string $idp The IdP id. + * @param string $associationId The id of the association. + */ + public function terminateAssociation($idp, $associationId) { + assert('is_string($idp)'); + assert('is_string($associationId)'); + + if (substr($idp, 0, 6) === 'saml2:' && !empty($this->sp_at_idpsessions)) { + /* Remove in 1.7. */ + $this->upgradeAssociations($idp); + } + + if (!isset($this->associations)) { + return; + } + + if (!isset($this->associations[$idp])) { + return; + } + + unset($this->associations[$idp][$associationId]); + + $this->dirty = TRUE; + } + } ?> \ No newline at end of file diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php index 822bdf3891f9048a1d9a901e92ca3d6cedf076c1..e97cc9e731abf5000c0fe7a37c14e2cb992a910e 100644 --- a/modules/saml/lib/IdP/SAML2.php +++ b/modules/saml/lib/IdP/SAML2.php @@ -49,7 +49,15 @@ class sspmod_saml_IdP_SAML2 { $assertion = sspmod_saml2_Message::buildAssertion($idpMetadata, $spMetadata, $attributes, $consumerURL); $assertion->setInResponseTo($requestId); - $nameId = $assertion->getNameId(); + /* Create the session association (for logout). */ + $association = array( + 'id' => 'saml:' . $spEntityId, + 'Handler' => 'sspmod_saml_IdP_SAML2', + 'Expires' => $assertion->getSessionNotOnOrAfter(), + 'saml:entityID' => $spEntityId, + 'saml:NameID' => $assertion->getNameId(), + 'saml:SessionIndex' => $assertion->getSessionIndex(), + ); /* Maybe encrypt the assertion. */ $assertion = sspmod_saml2_Message::encryptAssertion($idpMetadata, $spMetadata, $assertion); @@ -60,10 +68,8 @@ class sspmod_saml_IdP_SAML2 { $ar->setRelayState($relayState); $ar->setAssertions(array($assertion)); - /* Add the session association (for logout). */ - $session = SimpleSAML_Session::getInstance(); - $session->add_sp_session($spEntityId); - $session->setSessionNameId('saml20-sp-remote', $spEntityId, $nameId); + /* Register the session association with the IdP. */ + $idp->addAssociation($association); /* Send the response. */ $binding = SAML2_Binding::getBinding($protocolBinding);