Complete bugfix for issue #561. HTTP-Post supported for SLO not initiated by the SP. Working for both traditional logout and iframe version.

git-svn-id: http://simplesamlphp.googlecode.com/svn/trunk@3262 44740490-163a-0410-bde0-09ae8108e29a
This commit is contained in:
jaimepc@gmail.com 2013-08-13 10:28:38 +00:00
parent 4b88d23d59
commit 76d9c5e50d
7 changed files with 157 additions and 33 deletions

View File

@ -29,8 +29,7 @@ class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler {
try {
$idp = SimpleSAML_IdP::getByState($association);
$url = call_user_func(array($association['Handler'], 'getLogoutURL'), $idp, $association, $relayState);
SimpleSAML_Utilities::redirect($url);
call_user_func(array($association['Handler'], 'sendLogoutRequest'), $idp, $association, $relayState);
} catch (Exception $e) {
SimpleSAML_Logger::warning('Unable to initialize logout to ' . var_export($id, TRUE) . '.');
$this->idp->terminateAssociation($id);

View File

@ -74,7 +74,10 @@ if ($type === 'embed') {
} else {
$this->includeAtTemplateBase('includes/header.php');
}
?>
<div id="wrap">
<div id="content">
<?php
if ($from !== NULL) {
echo('<div><img style="float: left; margin-right: 12px" src="/' . $this->data['baseurlpath'] . 'resources/icons/checkmark.48x48.png" alt="Successful logout" />');
@ -167,7 +170,6 @@ echo('<form method="post" action="logout-iframe-done.php" id="failed-form" targe
echo('<input type="hidden" name="id" value="' . $id . '" />');
echo('<input type="submit" name="continue" value="' . $this->t('{logout:return}'). '" />');
echo('</form>');
echo('</div>');
if ($nProgress == 0 && $nFailed == 0) {
@ -203,10 +205,14 @@ if ($type === 'js') {
<?php
}
?>
</div>
<!-- #content -->
<?php
if ($type === 'embed') {
$this->includeAtTemplateBase('includes/footer-embed.php');
} else {
$this->includeAtTemplateBase('includes/footer.php');
}
?>
</div>
<!-- #wrap -->

View File

@ -83,7 +83,8 @@ if ($type === 'js' || $type === 'nojs') {
try {
$assocIdP = SimpleSAML_IdP::getByState($sp);
$url = call_user_func(array($sp['Handler'], 'getLogoutURL'), $assocIdP, $sp, NULL);
$params = array('id' => $id, 'sp' => $sp['id'], 'type' => $type);
$url = SimpleSAML_Module::getModuleURL('core/idp/performlogout.php', $params);
$sp['core:Logout-IFrame:URL'] = $url;
} catch (Exception $e) {
$sp['core:Logout-IFrame:State'] = 'failed';

View File

@ -0,0 +1,59 @@
<?php
if (!isset($_REQUEST['id'])) {
throw new SimpleSAML_Error_BadRequest('Missing id-parameter.');
}
$id = (string)$_REQUEST['id'];
if (!isset($_REQUEST['sp'])) {
throw new SimpleSAML_Error_BadRequest('Missing sp-parameter.');
}
$sp = urldecode($_REQUEST['sp']);
$type = @(string)$_REQUEST['type'];
$state = SimpleSAML_Auth_State::loadState($id, 'core:Logout-IFrame');
$idp = SimpleSAML_IdP::getByState($state);
$associations = $state['core:Logout-IFrame:Associations'];
if (!isset($associations[$sp])) {
exit;
}
$association = $associations[$sp];
$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
$idpMetadata = $idp->getConfig();
$spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
$lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata);
$lr->setSessionIndex($association['saml:SessionIndex']);
$lr->setNameId($association['saml:NameID']);
$assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
if ($assertionLifetime === NULL) {
$assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
}
$lr->setNotOnOrAfter(time() + $assertionLifetime);
$encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL);
if ($encryptNameId === NULL) {
$encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE);
}
if ($encryptNameId) {
$lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
}
SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array(
'spEntityID' => $association['saml:entityID'],
'idpEntityID' => $idpMetadata->getString('entityid'),
));
$bindings = array(SAML2_Const::BINDING_HTTP_REDIRECT);
if ($type === 'js') {
array_push($bindings, SAML2_Const::BINDING_HTTP_POST);
}
$dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', $bindings);
$binding = SAML2_Binding::getBinding($dst['Binding']);
$lr->setDestination($dst['Location']);
$binding->send($lr);

View File

@ -382,9 +382,43 @@ class sspmod_saml_IdP_SAML2 {
}
/**
* Send a logout request to a given association.
*
* @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
* @param array $association The association that should be terminated.
* @param string|NULL $relayState An id that should be carried across the logout.
*/
public static function sendLogoutRequest(SimpleSAML_IdP $idp, array $association, $relayState) {
assert('is_string($relayState) || is_null($relayState)');
SimpleSAML_Logger::info('Sending SAML 2.0 LogoutRequest to: '. var_export($association['saml:entityID'], TRUE));
$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
$idpMetadata = $idp->getConfig();
$spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array(
'spEntityID' => $association['saml:entityID'],
'idpEntityID' => $idpMetadata->getString('entityid'),
));
$dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', array(
SAML2_Const::BINDING_HTTP_REDIRECT,
SAML2_Const::BINDING_HTTP_POST)
);
$binding = SAML2_Binding::getBinding($dst['Binding']);
$lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
$lr->setDestination($dst['Location']);
$binding->send($lr);
}
/**
* Send a logout response.
*
* @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
* @param array &$state The logout state array.
*/
public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state) {
@ -512,7 +546,8 @@ class sspmod_saml_IdP_SAML2 {
/**
* Retrieve a logout URL for a given logout association.
* Retrieve a logout URL for a given logout association. Only for logout endpoints that support
* HTTP-Redirect binding.
*
* @param SimpleSAML_IdP $idp The IdP we are sending a logout request from.
* @param array $association The association that should be terminated.
@ -527,29 +562,11 @@ class sspmod_saml_IdP_SAML2 {
$idpMetadata = $idp->getConfig();
$spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
$lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata);
$lr->setRelayState($relayState);
$lr->setSessionIndex($association['saml:SessionIndex']);
$lr->setNameId($association['saml:NameID']);
$assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
if ($assertionLifetime === NULL) {
$assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
}
$lr->setNotOnOrAfter(time() + $assertionLifetime);
$encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL);
if ($encryptNameId === NULL) {
$encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE);
}
if ($encryptNameId) {
$lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
}
SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array(
'spEntityID' => $association['saml:entityID'],
'idpEntityID' => $idpMetadata->getString('entityid'),
));
// It doesn't make sense to use HTTP-Post when asking for a logout URL, therefore we allow only
// HTTP-Redirect.
$dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT));
$lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState);
$lr->setDestination($dst['Location']);
$binding = new SAML2_HTTPRedirect();
return $binding->getRedirectURL($lr);
@ -693,7 +710,8 @@ class sspmod_saml_IdP_SAML2 {
* @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
* @return string The NameFormat.
*/
private static function getAttributeNameFormat(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata) {
private static function getAttributeNameFormat(SimpleSAML_Configuration $idpMetadata,
SimpleSAML_Configuration $spMetadata) {
/* Try SP metadata first. */
$attributeNameFormat = $spMetadata->getString('attributes.NameFormat', NULL);
@ -940,6 +958,40 @@ class sspmod_saml_IdP_SAML2 {
}
/**
* Build a logout request based on information in the metadata.
*
* @param SimpleSAML_Configuration idpMetadata The metadata of the IdP.
* @param SimpleSAML_Configuration spMetadata The metadata of the SP.
* @param array $association The SP association.
* @param string|NULL $relayState An id that should be carried across the logout.
*/
private static function buildLogoutRequest(SimpleSAML_Configuration $idpMetadata,
SimpleSAML_Configuration $spMetadata, array $association, $relayState) {
$lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata);
$lr->setRelayState($relayState);
$lr->setSessionIndex($association['saml:SessionIndex']);
$lr->setNameId($association['saml:NameID']);
$assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
if ($assertionLifetime === NULL) {
$assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
}
$lr->setNotOnOrAfter(time() + $assertionLifetime);
$encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL);
if ($encryptNameId === NULL) {
$encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE);
}
if ($encryptNameId) {
$lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata));
}
return $lr;
}
/**
* Build a authentication response based on information in the metadata.
*
@ -947,7 +999,8 @@ class sspmod_saml_IdP_SAML2 {
* @param SimpleSAML_Configuration $spMetadata The metadata of the SP.
* @param string $consumerURL The Destination URL of the response.
*/
private static function buildResponse(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, $consumerURL) {
private static function buildResponse(SimpleSAML_Configuration $idpMetadata,
SimpleSAML_Configuration $spMetadata, $consumerURL) {
$signResponse = $spMetadata->getBoolean('saml20.sign.response', NULL);
if ($signResponse === NULL) {

View File

@ -19,5 +19,5 @@ if(array_key_exists('head', $this->data)) {
}
?>
</head>
<body>
<body class="body-embed">

View File

@ -16,6 +16,12 @@ body {
font: 83%/1.5 arial,tahoma,verdana,sans-serif;
}
.body-embed {
padding: 0;
background: #ffffff;
font: 83%/1.5 arial,tahoma,verdana,sans-serif;
}
img {
border: none;
display: block;