diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php index b80831289f8a6acae6e6b92b6b7fdac2fa0c8f4c..19a18b3622606f7afdce4193c6918b37d3908355 100644 --- a/lib/SimpleSAML/Session.php +++ b/lib/SimpleSAML/Session.php @@ -203,13 +203,23 @@ class SimpleSAML_Session { 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); diff --git a/templates/default/logout-iframe.php b/templates/default/logout-iframe.php index c6d8f44ec5d72bf39aa463bf0d155d3c20d4c738..35cdda2ac96978c923a278f9ab0fbc25ae6c3a82 100644 --- a/templates/default/logout-iframe.php +++ b/templates/default/logout-iframe.php @@ -20,25 +20,37 @@ for (j=1; j<=10; j++) { ?> <div id="content"> - - <p style="margin: .2em">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</p> - <?php + <p>You have initiated a <strong>global logout</strong> from the service <strong><?php echo $this->data['requesterName']; ?></strong>. Global logout means you will be logged out from all services connected to this identity provider. This page will show the status of the logout proccess for all of the services you are logged into.</p> + + - foreach ($this->data['sparray'] AS $sp) { - echo '<iframe class="hiddeniframe" style="width: 200px; height: 100px" src="' . $sp['url'] . '" ></iframe>'; - } + - foreach ($this->data['sparray'] AS $spentityid => $sp) { - echo '<div class="inprogress" style="border: 1px solid #888; background: #eee; color: #444; padding: .2em; margin: .7em" id="' . $spentityid . '"> - <img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from ' . $spentityid . '</div>'; - } + <?php - - ?> + foreach ($this->data['sparray'] AS $sp) { + echo '<iframe class="hiddeniframe" style="border: 1px solid #888; width: 80%; height: 100px" src="' . $sp['url'] . '" ></iframe>'; + } + + foreach ($this->data['sparray'] AS $spentityid => $sp) { + echo '<div class="inprogress" id="' . $spentityid . '"> + <img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from <strong>' . $sp['name'] . '</strong></div>'; + } + + + ?> + + <div id="interrupt">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</div> + <div id="iscompleted">You have successfully logged out from all services listed above. + <!-- form method="get" action="<?php echo $this->data['logoutresponse']; ?>"> + <input type="submit" name="s" value="OK, continue back to <?php echo $this->data['requesterName']; ?> to complete the logout process." /> + </form --> + <br />[ <a href="<?php echo $this->data['logoutresponse']; ?>">OK, continue back to <?php echo $this->data['requesterName']; ?> to complete the logout process.</a> ] + </div> </div> - + -<?php $this->includeAtTemplateBase('includes/footer.php'); ?> +<?php $this->includeAtTemplateBase('includes/footer.php'); ?> \ No newline at end of file diff --git a/www/resources/default.css b/www/resources/default.css index 64cd821140573f5e7d2bbdac3c573944e0b3cd0e..88dcf6f3658dfc86b3c60082fa3d5451b73562b0 100644 --- a/www/resources/default.css +++ b/www/resources/default.css @@ -256,10 +256,47 @@ table.attributes tr td { vertical-align: top; } + +div.allcompleted#interrupt { + display: none; +} +div#interrupt a:link { + color: #036; + border-bottom: 1px dotted #036; + text-decoration: none; +} +div#interrupt a:hover { + border-bottom: 1px solid #036; +} +div#interrupt { + display: block; + border: 3px solid #036; + background: #39F; + padding: 1em; + margin: 1em; +} +div#iscompleted { + display: none; + border: 3px solid #993; + background: #FF9; + padding: 1em; + margin: 1em; +} +div.allcompleted#iscompleted { + display: block ! important; +} +div.inprogress, div.loggedout { + + background: #eee; + color: #444; + padding: 1em; + margin: 1em; +} div.inprogress { - background: #eee; + border: 1px dotted #888; } div.loggedout { + border: 1px solid #888; background: #9f9 ! important ; } iframe.hiddeniframe { diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php index 95ae16c0268deaa6d77d04aaf60f43052fbfb6e0..f38e74e709c7014942310abb6d8eb9162b39fb32 100644 --- a/www/saml2/idp/SingleLogoutService.php +++ b/www/saml2/idp/SingleLogoutService.php @@ -31,6 +31,16 @@ try { SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got IdP entity id: ' . $idpentityid); +$logouttype = 'traditional'; +$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted'); +if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype']; + +if ($logouttype !== 'traditional') + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [traditional]')); + + + + /** * The $logoutInfo contains information about the current logout operation. * It can have the following attributes: diff --git a/www/saml2/idp/SingleLogoutServiceiFrame.php b/www/saml2/idp/SingleLogoutServiceiFrame.php index 5daf095451912059a856501a874f0e179371ef07..03a2002669da472d93876b728cf736f628d91c66 100644 --- a/www/saml2/idp/SingleLogoutServiceiFrame.php +++ b/www/saml2/idp/SingleLogoutServiceiFrame.php @@ -29,6 +29,16 @@ try { SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid); + +$logouttype = 'traditional'; +$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted'); +if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype']; + +if ($logouttype !== 'iframe') + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [iframe]')); + + + /** * The $logoutInfo contains information about the current logout operation. * It can have the following attributes: @@ -80,6 +90,8 @@ require_once(SimpleSAML_Utilities::resolvePath('libextinc') . '/xajax/xajax.inc. */ function updateslostatus() { + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutServiceiFrame: Accessing SAML 2.0 IdP endpoint SingleLogoutService (iFrame version) within updateslostatus() '); + $config = SimpleSAML_Configuration::getInstance(); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $session = SimpleSAML_Session::getInstance(); @@ -96,10 +108,36 @@ function updateslostatus() { error_log('Completed ' . $spentityid); // add a command to the response to assign the innerHTML attribute of // the element with id="SomeElementId" to whatever the new content is + + $spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote'); + $name = array_key_exists('name', $spmetadata) ? $spmetadata['name'] : $spentityid; + + $objResponse->addAssign($spentityid, "className", 'loggedout'); - $objResponse->addAssign($spentityid, "innerHTML", 'Logging out from ' . $spentityid . ' successfully completed'); + $objResponse->addAssign($spentityid, "innerHTML", 'Logging out from <strong>' . $name . '</strong> successfully completed'); } + + if ($session->sp_logout_completed() === TRUE) { + $objResponse->addAssign('iscompleted', "className", 'allcompleted'); + $objResponse->addAssign('interrupt', "className", 'allcompleted'); + + /** + * Clean up session object to save storage. + */ + if ($config->getValue('debug', false)) + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize()); + + $session->clean(); + + if ($config->getValue('debug', false)) + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize()); + + + + } else { + SimpleSAML_Logger::debug('SAML2.0 - sp_logout_completed FALSE'); + } //return the xajaxResponse object return $objResponse; @@ -244,12 +282,15 @@ foreach ($listofsps AS $spentityid) { // $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP' $url = $httpredirect->getRedirectURL($req, $idpentityid, $spentityid, NULL, 'SingleLogoutService', 'SAMLRequest', 'IdP'); - $sparray[$spentityid] = array('url' => $url); + $spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote'); + $name = array_key_exists('name', $spmetadata) ? $spmetadata['name'] : $spentityid; -} + $sparray[$spentityid] = array('url' => $url, 'name' => $name); +} +#print_r($sparray); @@ -271,16 +312,7 @@ try { SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Found logout info with these keys: ' . join(',', array_keys($logoutInfo))); - /** - * Clean up session object to save storage. - */ - if ($config->getValue('debug', false)) - SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize()); - - $session->clean(); - - if ($config->getValue('debug', false)) - SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize()); + /* @@ -327,7 +359,9 @@ try { - +$spmeta = $metadata->getMetaData($requester, 'saml20-sp-remote'); +$spname = $requester; +if (array_key_exists('name', $spmeta)) $spname = $spmeta['name']; @@ -362,6 +396,7 @@ $et->data['header'] = 'SAML 2.0 IdP Ajax Logout'; $et->data['sparray'] = $sparray; $et->data['logoutresponse'] = $logoutresponse; $et->data['xajax'] = $xajax; +$et->data['requesterName'] = $spname; $et->data['head'] = $xajax->printJavascript(); @@ -372,124 +407,4 @@ exit(0); - - -/* - * If we get an LogoutRequest then we initiate the logout process. - */ -if (isset($_GET['SAMLRequest'])) { - - $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - $logoutrequest = $binding->decodeLogoutRequest($_GET); - - $session->setAuthenticated(false); - - //$requestid = $authnrequest->getRequestID(); - //$session->setAuthnRequest($requestid, $authnrequest); - - //echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>'; - - error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . ' '); - - $session->set_sp_logout_completed($logoutrequest->getIssuer() ); - $session->setLogoutRequest($logoutrequest); - -/* - * We receive a Logout Response to a Logout Request that we have issued earlier. - */ -} elseif (isset($_GET['SAMLResponse'])) { - - $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - $loginresponse = $binding->decodeLogoutResponse($_GET); - - $session->set_sp_logout_completed($loginresponse->getIssuer()); - - error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . ' '); -} - -/* - * We proceed to send logout requests to all remaining SPs. - */ -$spentityid = $session->get_next_sp_logout(); -if ($spentityid) { - - error_log('IdP LogoutService: next SP ' . $spentityid); - - try { - $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); - - // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { - $req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP'); - - $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - - $relayState = SimpleSAML_Utilities::selfURL(); - if (isset($_GET['RelayState'])) { - $relayState = $_GET['RelayState']; - } - - //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' - $httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP'); - - exit(); - - } catch(Exception $exception) { - - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error sending logout request to service'; - $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; - $et->data['e'] = $exception; - - $et->show(); - exit(0); - } - - -} - -/* - * Logout procedure is done and we send a Logout Response back to the SP - */ -error_log('IdP LogoutService: SPs done '); -try { - - $logoutrequest = $session->getLogoutRequest(); - if (!$logoutrequest) { - throw new Exception('Could not get reference to the logout request.'); - } - - $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); - - // generate($issuer, $receiver, $inresponseto, $mode ) - - $logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP'); - - // echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>'; - // exit(); - - $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - - $relayState = SimpleSAML_Utilities::selfURL(); - if (isset($_GET['RelayState'])) { - $relayState = $_GET['RelayState']; - } - - //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' - $httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); - -} catch(Exception $exception) { - - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error sending response to service'; - $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; - $et->data['e'] = $exception; - - $et->show(); - -} - - - ?> \ No newline at end of file diff --git a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php index cd7a8f57c7c2b964b123a6fc211a0e6bd0159759..ec9b18038485945bb618270570ca8ba8df38f50c 100644 --- a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php +++ b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php @@ -34,6 +34,12 @@ try { SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid); +$logouttype = 'traditional'; +$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted'); +if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype']; + +if ($logouttype !== 'iframe') + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [iframe]')); @@ -60,8 +66,9 @@ if (isset($_GET['SAMLResponse'])) { } else { - SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS' new Exception('No valid SAMLResponse found? Probably some error in remote partys metadata that sends something to this endpoint that is not SAML LogoutResponses') ); - echo 'Not set: SAMLResponse'; + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS', + new Exception('No valid SAMLResponse found? Probably some error in remote partys metadata that sends something to this endpoint that is not SAML LogoutResponses') ); +# echo 'Not set: SAMLResponse'; } ?> \ No newline at end of file