From f43dc19c503e3c027e45a4b6a94f39ac1bf6ff9c Mon Sep 17 00:00:00 2001 From: Jaime Perez Crespo <jaime.perez@uninett.no> Date: Wed, 5 Aug 2015 15:48:16 +0200 Subject: [PATCH] Reformat SimpleSAML_IdP. --- lib/SimpleSAML/IdP.php | 1094 ++++++++++++++++++++-------------------- 1 file changed, 557 insertions(+), 537 deletions(-) diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php index 57bbf20e4..baaead6b0 100644 --- a/lib/SimpleSAML/IdP.php +++ b/lib/SimpleSAML/IdP.php @@ -1,5 +1,6 @@ <?php + /** * IdP class. * @@ -7,541 +8,560 @@ * * @package SimpleSAMLphp */ -class SimpleSAML_IdP { - - /** - * A cache for resolving IdP id's. - * - * @var array - */ - private static $idpCache = array(); - - - /** - * The identifier for this IdP. - * - * @var string - */ - private $id; - - - /** - * The "association group" for this IdP. - * - * We use this to support cross-protocol logout until - * we implement a cross-protocol IdP. - * - * @var string - */ - private $associationGroup; - - - /** - * The configuration for this IdP. - * - * @var SimpleSAML_Configuration - */ - private $config; - - - /** - * Our authsource. - * - * @var SimpleSAML_Auth_Simple - */ - private $authSource; - - - /** - * Initialize an IdP. - * - * @param string $id The identifier of this IdP. - * - * @throws SimpleSAML_Error_Exception If the IdP is disabled or no such auth source was found. - */ - private function __construct($id) { - assert('is_string($id)'); - - $this->id = $id; - - $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - $globalConfig = SimpleSAML_Configuration::getInstance(); - - if (substr($id, 0, 6) === 'saml2:') { - if (!$globalConfig->getBoolean('enable.saml20-idp', FALSE)) { - throw new SimpleSAML_Error_Exception('enable.saml20-idp disabled in config.php.'); - } - $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'saml20-idp-hosted'); - } elseif (substr($id, 0, 6) === 'saml1:') { - if (!$globalConfig->getBoolean('enable.shib13-idp', FALSE)) { - throw new SimpleSAML_Error_Exception('enable.shib13-idp disabled in config.php.'); - } - $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'shib13-idp-hosted'); - } elseif (substr($id, 0, 5) === 'adfs:') { - if (!$globalConfig->getBoolean('enable.adfs-idp', FALSE)) { - throw new SimpleSAML_Error_Exception('enable.adfs-idp disabled in config.php.'); - } - $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted'); - - try { - /* This makes the ADFS IdP use the same SP associations as the SAML 2.0 IdP. */ - $saml2EntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); - $this->associationGroup = 'saml2:' . $saml2EntityId; - - } catch (Exception $e) { - /* Probably no SAML 2 IdP configured for this host. Ignore the error. */ - } - } else { - assert(FALSE); - } - - if ($this->associationGroup === NULL) { - $this->associationGroup = $this->id; - } - - - $auth = $this->config->getString('auth'); - if (SimpleSAML_Auth_Source::getById($auth) !== NULL) { - $this->authSource = new SimpleSAML_Auth_Simple($auth); - } else { - throw new SimpleSAML_Error_Exception('No such "'.$auth.'" auth source found.'); - } - } - - - /** - * Retrieve the ID of this IdP. - * - * @return string The ID of this IdP. - */ - public function getId() { - return $this->id; - } - - - /** - * Retrieve an IdP by ID. - * - * @param string $id The identifier of the IdP. - * @return SimpleSAML_IdP The IdP. - */ - public static function getById($id) { - assert('is_string($id)'); - - if (isset(self::$idpCache[$id])) { - return self::$idpCache[$id]; - } - - $idp = new self($id); - self::$idpCache[$id] = $idp; - return $idp; - } - - - /** - * Retrieve the IdP "owning" the state. - * - * @param array &$state The state array. - * @return SimpleSAML_IdP The IdP. - */ - public static function getByState(array &$state) { - assert('isset($state["core:IdP"])'); - - return self::getById($state['core:IdP']); - } - - - /** - * Retrieve the configuration for this IdP. - * - * @return SimpleSAML_Configuration The configuration object. - */ - public function getConfig() { - - return $this->config; - } - - - /** - * Get SP name. - * - * @param string $assocId The association identifier. - * @return array|null The name of the SP, as an associative array of language => text, or null if this isn't an SP. - */ - public function getSPName($assocId) { - assert('is_string($assocId)'); - - $prefix = substr($assocId, 0, 4); - $spEntityId = substr($assocId, strlen($prefix) + 1); - $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); - - if ($prefix === 'saml') { - try { - $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote'); - } catch (Exception $e) { - try { - $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote'); - } catch (Exception $e) { - return NULL; - } - } - } else if ($prefix === 'adfs') { - $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'adfs-sp-remote'); - } else { - return NULL; - } - - if ($spMetadata->hasValue('name')) { - return $spMetadata->getLocalizedString('name'); - } elseif ($spMetadata->hasValue('OrganizationDisplayName')) { - return $spMetadata->getLocalizedString('OrganizationDisplayName'); - } else { - return array('en' => $spEntityId); - } - } - - - /** - * Add an SP association. - * - * @param array $association The SP association. - */ - public function addAssociation(array $association) { - assert('isset($association["id"])'); - assert('isset($association["Handler"])'); - - $association['core:IdP'] = $this->id; - - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->addAssociation($this->associationGroup, $association); - } - - - /** - * Retrieve list of SP associations. - * - * @return array List of SP associations. - */ - public function getAssociations() { - - $session = SimpleSAML_Session::getSessionFromRequest(); - return $session->getAssociations($this->associationGroup); - } - - - /** - * Remove an SP association. - * - * @param string $assocId The association id. - */ - public function terminateAssociation($assocId) { - assert('is_string($assocId)'); - - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->terminateAssociation($this->associationGroup, $assocId); - } - - - /** - * Is the current user authenticated? - * - * @return boolean True if the user is authenticated, false otherwise. - */ - public function isAuthenticated() { - return $this->authSource->isAuthenticated(); - } - - - /** - * Called after authproc has run. - * - * @param array $state The authentication request state array. - */ - public static function postAuthProc(array $state) { - assert('is_callable($state["Responder"])'); - - if (isset($state['core:SP'])) { - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->setData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP'], - time(), SimpleSAML_Session::DATA_TIMEOUT_SESSION_END); - } - - call_user_func($state['Responder'], $state); - assert('FALSE'); - } - - - /** - * The user is authenticated. - * - * @param array $state The authentication request state array. - * - * @throws SimpleSAML_Error_Exception If we are not authenticated. - */ - public static function postAuth(array $state) { - - $idp = SimpleSAML_IdP::getByState($state); - - if (!$idp->isAuthenticated()) { - throw new SimpleSAML_Error_Exception('Not authenticated.'); - } - - $state['Attributes'] = $idp->authSource->getAttributes(); - - if (isset($state['SPMetadata'])) { - $spMetadata = $state['SPMetadata']; - } else { - $spMetadata = array(); - } - - if (isset($state['core:SP'])) { - $session = SimpleSAML_Session::getSessionFromRequest(); - $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP']); - if ($previousSSOTime !== NULL) { - $state['PreviousSSOTimestamp'] = $previousSSOTime; - } - } - - $idpMetadata = $idp->getConfig()->toArray(); - - $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadata, $spMetadata, 'idp'); - - $state['ReturnCall'] = array('SimpleSAML_IdP', 'postAuthProc'); - $state['Destination'] = $spMetadata; - $state['Source'] = $idpMetadata; - - $pc->processState($state); - - self::postAuthProc($state); - } - - - /** - * Authenticate the user. - * - * This function authenticates the user. - * - * @param array &$state The authentication request state. - * - * @throws SimpleSAML_Error_NoPassive If we were asked to do passive authentication. - */ - private function authenticate(array &$state) { - - if (isset($state['isPassive']) && (bool)$state['isPassive']) { - throw new SimpleSAML_Error_NoPassive('Passive authentication not supported.'); - } - - $this->authSource->login($state); - } - - - /** - * Re-authenticate the user. - * - * This function re-authenticates an user with an existing session. This gives the authentication source a chance - * to do additional work when re-authenticating for SSO. - * - * Note: This function is not used when ForceAuthn=true. - * - * @param array &$state The authentication request state. - */ - private function reauthenticate(array &$state) { - - $sourceImpl = $this->authSource->getAuthSource(); - if ($sourceImpl === NULL) { - /* Backwards-compatibility with non-authsource IdP. */ - foreach ($this->authSource->getAuthDataArray() as $k => $v) { - $state[$k] = $v; - } - return; - } - - $sourceImpl->reauthenticate($state); - } - - - /** - * Process authentication requests. - * - * @param array &$state The authentication request state. - */ - public function handleAuthenticationRequest(array &$state) { - assert('isset($state["Responder"])'); - - $state['core:IdP'] = $this->id; - - if (isset($state['SPMetadata']['entityid'])) { - $spEntityId = $state['SPMetadata']['entityid']; - } elseif (isset($state['SPMetadata']['entityID'])) { - $spEntityId = $state['SPMetadata']['entityID']; - } else { - $spEntityId = NULL; - } - $state['core:SP'] = $spEntityId; - - /* First, check whether we need to authenticate the user. */ - if (isset($state['ForceAuthn']) && (bool)$state['ForceAuthn']) { - /* Force authentication is in effect. */ - $needAuth = TRUE; - } else { - $needAuth = !$this->isAuthenticated(); - } - - $state['IdPMetadata'] = $this->getConfig()->toArray(); - $state['ReturnCallback'] = array('SimpleSAML_IdP', 'postAuth'); - - try { - if ($needAuth) { - $this->authenticate($state); - assert('FALSE'); - } else { - $this->reauthenticate($state); - } - $this->postAuth($state); - } catch (SimpleSAML_Error_Exception $e) { - SimpleSAML_Auth_State::throwException($state, $e); - } catch (Exception $e) { - $e = new SimpleSAML_Error_UnserializableException($e); - SimpleSAML_Auth_State::throwException($state, $e); - } - } - - - /** - * Find the logout handler of this IdP. - * - * @return string The logout handler class. - * - * @throws SimpleSAML_Error_Exception If we cannot find a logout handler. - */ - public function getLogoutHandler() { - - /* Find the logout handler. */ - $logouttype = $this->getConfig()->getString('logouttype', 'traditional'); - switch ($logouttype) { - case 'traditional': - $handler = 'SimpleSAML_IdP_LogoutTraditional'; - break; - case 'iframe': - $handler = 'SimpleSAML_IdP_LogoutIFrame'; - break; - default: - throw new SimpleSAML_Error_Exception('Unknown logout handler: ' . var_export($logouttype, TRUE)); - } - - return new $handler($this); - - } - - - /** - * Finish the logout operation. - * - * This function will never return. - * - * @param array &$state The logout request state. - */ - public function finishLogout(array &$state) { - assert('isset($state["Responder"])'); - - $idp = SimpleSAML_IdP::getByState($state); - call_user_func($state['Responder'], $idp, $state); - assert('FALSE'); - } - - - /** - * Process a logout request. - * - * This function will never return. - * - * @param array &$state The logout request state. - * @param string|null $assocId The association we received the logout request from, or null if there was no - * association. - */ - public function handleLogoutRequest(array &$state, $assocId) { - assert('isset($state["Responder"])'); - assert('is_string($assocId) || is_null($assocId)'); - - $state['core:IdP'] = $this->id; - $state['core:TerminatedAssocId'] = $assocId; - - if ($assocId !== NULL) { - $this->terminateAssociation($assocId); - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->deleteData('core:idp-ssotime', $this->id . ':' . $state['saml:SPEntityId']); - } - - /* Terminate the local session. */ - $id = SimpleSAML_Auth_State::saveState($state, 'core:Logout:afterbridge'); - $returnTo = SimpleSAML_Module::getModuleURL('core/idp/resumelogout.php', - array('id' => $id) - ); - - $this->authSource->logout($returnTo); - - $handler = $this->getLogoutHandler(); - $handler->startLogout($state, $assocId); - assert('FALSE'); - } - - - /** - * Process a logout response. - * - * This function will never return. - * - * @param string $assocId The association that is terminated. - * @param string|null $relayState The RelayState from the start of the logout. - * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any). - */ - public function handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) { - assert('is_string($assocId)'); - assert('is_string($relayState) || is_null($relayState)'); - - $session = SimpleSAML_Session::getSessionFromRequest(); - $session->deleteData('core:idp-ssotime', $this->id . ';' . substr($assocId, strpos($assocId, ':') +1)); - - $handler = $this->getLogoutHandler(); - $handler->onResponse($assocId, $relayState, $error); - - assert('FALSE'); - } - - - /** - * Log out, then redirect to a URL. - * - * This function never returns. - * - * @param string $url The URL the user should be returned to after logout. - */ - public function doLogoutRedirect($url) { - assert('is_string($url)'); - - $state = array( - 'Responder' => array('SimpleSAML_IdP', 'finishLogoutRedirect'), - 'core:Logout:URL' => $url, - ); - - $this->handleLogoutRequest($state, NULL); - assert('FALSE'); - } - - - /** - * Redirect to a URL after logout. - * - * This function never returns. - * - * @param SimpleSAML_IdP $idp Deprecated. Will be removed. - * @param array &$state The logout state from doLogoutRedirect(). - */ - public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) { - assert('isset($state["core:Logout:URL"])'); - - \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']); - assert('FALSE'); - } - +class SimpleSAML_IdP +{ + + /** + * A cache for resolving IdP id's. + * + * @var array + */ + private static $idpCache = array(); + + + /** + * The identifier for this IdP. + * + * @var string + */ + private $id; + + + /** + * The "association group" for this IdP. + * + * We use this to support cross-protocol logout until + * we implement a cross-protocol IdP. + * + * @var string + */ + private $associationGroup; + + + /** + * The configuration for this IdP. + * + * @var SimpleSAML_Configuration + */ + private $config; + + + /** + * Our authsource. + * + * @var SimpleSAML_Auth_Simple + */ + private $authSource; + + + /** + * Initialize an IdP. + * + * @param string $id The identifier of this IdP. + * + * @throws SimpleSAML_Error_Exception If the IdP is disabled or no such auth source was found. + */ + private function __construct($id) + { + assert('is_string($id)'); + + $this->id = $id; + + $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + $globalConfig = SimpleSAML_Configuration::getInstance(); + + if (substr($id, 0, 6) === 'saml2:') { + if (!$globalConfig->getBoolean('enable.saml20-idp', false)) { + throw new SimpleSAML_Error_Exception('enable.saml20-idp disabled in config.php.'); + } + $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'saml20-idp-hosted'); + } elseif (substr($id, 0, 6) === 'saml1:') { + if (!$globalConfig->getBoolean('enable.shib13-idp', false)) { + throw new SimpleSAML_Error_Exception('enable.shib13-idp disabled in config.php.'); + } + $this->config = $metadata->getMetaDataConfig(substr($id, 6), 'shib13-idp-hosted'); + } elseif (substr($id, 0, 5) === 'adfs:') { + if (!$globalConfig->getBoolean('enable.adfs-idp', false)) { + throw new SimpleSAML_Error_Exception('enable.adfs-idp disabled in config.php.'); + } + $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted'); + + try { + // this makes the ADFS IdP use the same SP associations as the SAML 2.0 IdP + $saml2EntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + $this->associationGroup = 'saml2:'.$saml2EntityId; + } catch (Exception $e) { + // probably no SAML 2 IdP configured for this host. Ignore the error + } + } else { + assert(false); + } + + if ($this->associationGroup === null) { + $this->associationGroup = $this->id; + } + + $auth = $this->config->getString('auth'); + if (SimpleSAML_Auth_Source::getById($auth) !== null) { + $this->authSource = new SimpleSAML_Auth_Simple($auth); + } else { + throw new SimpleSAML_Error_Exception('No such "'.$auth.'" auth source found.'); + } + } + + + /** + * Retrieve the ID of this IdP. + * + * @return string The ID of this IdP. + */ + public function getId() + { + return $this->id; + } + + + /** + * Retrieve an IdP by ID. + * + * @param string $id The identifier of the IdP. + * + * @return SimpleSAML_IdP The IdP. + */ + public static function getById($id) + { + assert('is_string($id)'); + + if (isset(self::$idpCache[$id])) { + return self::$idpCache[$id]; + } + + $idp = new self($id); + self::$idpCache[$id] = $idp; + return $idp; + } + + + /** + * Retrieve the IdP "owning" the state. + * + * @param array &$state The state array. + * + * @return SimpleSAML_IdP The IdP. + */ + public static function getByState(array &$state) + { + assert('isset($state["core:IdP"])'); + + return self::getById($state['core:IdP']); + } + + + /** + * Retrieve the configuration for this IdP. + * + * @return SimpleSAML_Configuration The configuration object. + */ + public function getConfig() + { + return $this->config; + } + + + /** + * Get SP name. + * + * @param string $assocId The association identifier. + * + * @return array|null The name of the SP, as an associative array of language => text, or null if this isn't an SP. + */ + public function getSPName($assocId) + { + assert('is_string($assocId)'); + + $prefix = substr($assocId, 0, 4); + $spEntityId = substr($assocId, strlen($prefix) + 1); + $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + + if ($prefix === 'saml') { + try { + $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote'); + } catch (Exception $e) { + try { + $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote'); + } catch (Exception $e) { + return null; + } + } + } else { + if ($prefix === 'adfs') { + $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'adfs-sp-remote'); + } else { + return null; + } + } + + if ($spMetadata->hasValue('name')) { + return $spMetadata->getLocalizedString('name'); + } elseif ($spMetadata->hasValue('OrganizationDisplayName')) { + return $spMetadata->getLocalizedString('OrganizationDisplayName'); + } else { + return array('en' => $spEntityId); + } + } + + + /** + * Add an SP association. + * + * @param array $association The SP association. + */ + public function addAssociation(array $association) + { + assert('isset($association["id"])'); + assert('isset($association["Handler"])'); + + $association['core:IdP'] = $this->id; + + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->addAssociation($this->associationGroup, $association); + } + + + /** + * Retrieve list of SP associations. + * + * @return array List of SP associations. + */ + public function getAssociations() + { + $session = SimpleSAML_Session::getSessionFromRequest(); + return $session->getAssociations($this->associationGroup); + } + + + /** + * Remove an SP association. + * + * @param string $assocId The association id. + */ + public function terminateAssociation($assocId) + { + assert('is_string($assocId)'); + + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->terminateAssociation($this->associationGroup, $assocId); + } + + + /** + * Is the current user authenticated? + * + * @return boolean True if the user is authenticated, false otherwise. + */ + public function isAuthenticated() + { + return $this->authSource->isAuthenticated(); + } + + + /** + * Called after authproc has run. + * + * @param array $state The authentication request state array. + */ + public static function postAuthProc(array $state) + { + assert('is_callable($state["Responder"])'); + + if (isset($state['core:SP'])) { + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->setData( + 'core:idp-ssotime', + $state['core:IdP'].';'.$state['core:SP'], + time(), + SimpleSAML_Session::DATA_TIMEOUT_SESSION_END + ); + } + + call_user_func($state['Responder'], $state); + assert('FALSE'); + } + + + /** + * The user is authenticated. + * + * @param array $state The authentication request state array. + * + * @throws SimpleSAML_Error_Exception If we are not authenticated. + */ + public static function postAuth(array $state) + { + $idp = SimpleSAML_IdP::getByState($state); + + if (!$idp->isAuthenticated()) { + throw new SimpleSAML_Error_Exception('Not authenticated.'); + } + + $state['Attributes'] = $idp->authSource->getAttributes(); + + if (isset($state['SPMetadata'])) { + $spMetadata = $state['SPMetadata']; + } else { + $spMetadata = array(); + } + + if (isset($state['core:SP'])) { + $session = SimpleSAML_Session::getSessionFromRequest(); + $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'].';'.$state['core:SP']); + if ($previousSSOTime !== null) { + $state['PreviousSSOTimestamp'] = $previousSSOTime; + } + } + + $idpMetadata = $idp->getConfig()->toArray(); + + $pc = new SimpleSAML_Auth_ProcessingChain($idpMetadata, $spMetadata, 'idp'); + + $state['ReturnCall'] = array('SimpleSAML_IdP', 'postAuthProc'); + $state['Destination'] = $spMetadata; + $state['Source'] = $idpMetadata; + + $pc->processState($state); + + self::postAuthProc($state); + } + + + /** + * Authenticate the user. + * + * This function authenticates the user. + * + * @param array &$state The authentication request state. + * + * @throws SimpleSAML_Error_NoPassive If we were asked to do passive authentication. + */ + private function authenticate(array &$state) + { + if (isset($state['isPassive']) && (bool) $state['isPassive']) { + throw new SimpleSAML_Error_NoPassive('Passive authentication not supported.'); + } + + $this->authSource->login($state); + } + + + /** + * Re-authenticate the user. + * + * This function re-authenticates an user with an existing session. This gives the authentication source a chance + * to do additional work when re-authenticating for SSO. + * + * Note: This function is not used when ForceAuthn=true. + * + * @param array &$state The authentication request state. + */ + private function reauthenticate(array &$state) + { + $sourceImpl = $this->authSource->getAuthSource(); + if ($sourceImpl === null) { + // backwards-compatibility with non-authsource IdP + foreach ($this->authSource->getAuthDataArray() as $k => $v) { + $state[$k] = $v; + } + return; + } + + $sourceImpl->reauthenticate($state); + } + + + /** + * Process authentication requests. + * + * @param array &$state The authentication request state. + */ + public function handleAuthenticationRequest(array &$state) + { + assert('isset($state["Responder"])'); + + $state['core:IdP'] = $this->id; + + if (isset($state['SPMetadata']['entityid'])) { + $spEntityId = $state['SPMetadata']['entityid']; + } elseif (isset($state['SPMetadata']['entityID'])) { + $spEntityId = $state['SPMetadata']['entityID']; + } else { + $spEntityId = null; + } + $state['core:SP'] = $spEntityId; + + // first, check whether we need to authenticate the user + if (isset($state['ForceAuthn']) && (bool) $state['ForceAuthn']) { + // force authentication is in effect + $needAuth = true; + } else { + $needAuth = !$this->isAuthenticated(); + } + + $state['IdPMetadata'] = $this->getConfig()->toArray(); + $state['ReturnCallback'] = array('SimpleSAML_IdP', 'postAuth'); + + try { + if ($needAuth) { + $this->authenticate($state); + assert('FALSE'); + } else { + $this->reauthenticate($state); + } + $this->postAuth($state); + } catch (SimpleSAML_Error_Exception $e) { + SimpleSAML_Auth_State::throwException($state, $e); + } catch (Exception $e) { + $e = new SimpleSAML_Error_UnserializableException($e); + SimpleSAML_Auth_State::throwException($state, $e); + } + } + + + /** + * Find the logout handler of this IdP. + * + * @return SimpleSAML_IdP_LogoutHandler The logout handler class. + * + * @throws SimpleSAML_Error_Exception If we cannot find a logout handler. + */ + public function getLogoutHandler() + { + // find the logout handler + $logouttype = $this->getConfig()->getString('logouttype', 'traditional'); + switch ($logouttype) { + case 'traditional': + $handler = 'SimpleSAML_IdP_LogoutTraditional'; + break; + case 'iframe': + $handler = 'SimpleSAML_IdP_LogoutIFrame'; + break; + default: + throw new SimpleSAML_Error_Exception('Unknown logout handler: '.var_export($logouttype, true)); + } + + return new $handler($this); + } + + + /** + * Finish the logout operation. + * + * This function will never return. + * + * @param array &$state The logout request state. + */ + public function finishLogout(array &$state) + { + assert('isset($state["Responder"])'); + + $idp = SimpleSAML_IdP::getByState($state); + call_user_func($state['Responder'], $idp, $state); + assert('false'); + } + + + /** + * Process a logout request. + * + * This function will never return. + * + * @param array &$state The logout request state. + * @param string|null $assocId The association we received the logout request from, or null if there was no + * association. + */ + public function handleLogoutRequest(array &$state, $assocId) + { + assert('isset($state["Responder"])'); + assert('is_string($assocId) || is_null($assocId)'); + + $state['core:IdP'] = $this->id; + $state['core:TerminatedAssocId'] = $assocId; + + if ($assocId !== null) { + $this->terminateAssociation($assocId); + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->deleteData('core:idp-ssotime', $this->id.':'.$state['saml:SPEntityId']); + } + + // terminate the local session + $id = SimpleSAML_Auth_State::saveState($state, 'core:Logout:afterbridge'); + $returnTo = SimpleSAML_Module::getModuleURL('core/idp/resumelogout.php', array('id' => $id)); + + $this->authSource->logout($returnTo); + + $handler = $this->getLogoutHandler(); + $handler->startLogout($state, $assocId); + assert('false'); + } + + + /** + * Process a logout response. + * + * This function will never return. + * + * @param string $assocId The association that is terminated. + * @param string|null $relayState The RelayState from the start of the logout. + * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any). + */ + public function handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null) + { + assert('is_string($assocId)'); + assert('is_string($relayState) || is_null($relayState)'); + + $session = SimpleSAML_Session::getSessionFromRequest(); + $session->deleteData('core:idp-ssotime', $this->id.';'.substr($assocId, strpos($assocId, ':') + 1)); + + $handler = $this->getLogoutHandler(); + $handler->onResponse($assocId, $relayState, $error); + + assert('false'); + } + + + /** + * Log out, then redirect to a URL. + * + * This function never returns. + * + * @param string $url The URL the user should be returned to after logout. + */ + public function doLogoutRedirect($url) + { + assert('is_string($url)'); + + $state = array( + 'Responder' => array('SimpleSAML_IdP', 'finishLogoutRedirect'), + 'core:Logout:URL' => $url, + ); + + $this->handleLogoutRequest($state, null); + assert('false'); + } + + + /** + * Redirect to a URL after logout. + * + * This function never returns. + * + * @param SimpleSAML_IdP $idp Deprecated. Will be removed. + * @param array &$state The logout state from doLogoutRedirect(). + */ + public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) + { + assert('isset($state["core:Logout:URL"])'); + + \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']); + assert('false'); + } } -- GitLab