mirror of
https://github.com/moodle/moodle.git
synced 2025-04-15 21:45:37 +02:00
Merged Shibboleth logout support from 1.9 stable
This commit is contained in:
parent
636bbc8728
commit
2db6ec1946
@ -20,6 +20,7 @@ Changes:
|
||||
- 10. 2007: Removed the requirement for email address, surname and given name
|
||||
attributes on request of Markus Hagman
|
||||
- 11. 2007: Integrated WAYF Service in Moodle
|
||||
- 12. 2008: Single Logout support added
|
||||
|
||||
Moodle Configuration with Dual login
|
||||
-------------------------------------------------------------------------------
|
||||
@ -87,7 +88,7 @@ Moodle Configuration with Dual login
|
||||
moodle/auth/shibboleth/ is protected but *not* the other
|
||||
scripts and especially not the login.php script.
|
||||
|
||||
5. Save the changes for the 'Shibboleth settings'. T
|
||||
5. Save the changes for the 'Shibboleth settings'.
|
||||
|
||||
Important Note: If you went for 4.b (integrated WAYF service), saving the
|
||||
settings will overwrite the Moodle Alternate Login URL
|
||||
@ -199,6 +200,51 @@ Example file:
|
||||
?>
|
||||
--
|
||||
|
||||
|
||||
How to add logout support
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
In order make Moodle support Shibboleth logout, one has to make the Shibboleth
|
||||
Service Provider (SP) aware of the Moodle logout capability. Only then the SP
|
||||
can trigger Moodle's front or back channel logout handler.
|
||||
|
||||
To make the SP aware of the Moodle logout, you have to add the following to the
|
||||
Shibboleth main configuration file shibboleth2.xml (usually in /etc/shibboleth/)
|
||||
just before the <MetadataProvider> element.
|
||||
|
||||
--
|
||||
<Notify
|
||||
Channel="back"
|
||||
Location="https://#YOUR_MOODLE_HOSTNAME#/moodle/auth/shibboleth/logout.php" />
|
||||
|
||||
<Notify
|
||||
Channel="front"
|
||||
Location="https://#YOUR_MOODLE_HOSTNAME#/moodle/auth/shibboleth/logout.php" />
|
||||
|
||||
--
|
||||
|
||||
The restart the Shibboleth daemon and check the log file for errors. If there
|
||||
were no errors, you cat test the logout feature by accessing Moodle,
|
||||
authenticating via Shibboleth and the access the URL:
|
||||
#YOUR_MOODLE_HOSTNAME#/Shibboleth.sso/Logout (assuming you have a standard
|
||||
Shibboleth installation). If everything worked well, you should see a Shibboleth
|
||||
page saying that you were successfully logged out and if you go back to Moodle
|
||||
you also should be logged out from Moodle.
|
||||
|
||||
|
||||
Limitations:
|
||||
Single Logout is only supported with SAML2 and so far only with the Shibboleth
|
||||
Service Provider 2.x.
|
||||
As of December 2008, the Shibboleth Identity Provider 2.1.1 does not yet support
|
||||
Single Logout (SLO). Therefore, the logout feature doesn't make that much
|
||||
sense yet. One of the reasons why SLO isn't supported yet is because there aren't
|
||||
many applications yet that were adapted to support front and back channel
|
||||
logout. Hopefully, the Moodle logout helps to motivate the developers to
|
||||
implement SLO :)
|
||||
|
||||
Also see https://spaces.internet2.edu/display/SHIB2/SLOIssues for some
|
||||
background information.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
In case of problems and questions with Shibboleth authentication, contact
|
||||
Lukas Haemmerle <lukas.haemmerle@switch.ch> or Markus Hagman <hagman@hytti.uku.fi>
|
||||
|
@ -51,9 +51,27 @@ class auth_plugin_shibboleth extends auth_plugin_base {
|
||||
* @return bool Authentication success or failure.
|
||||
*/
|
||||
function user_login($username, $password) {
|
||||
|
||||
global $SESSION;
|
||||
|
||||
// If we are in the shibboleth directory then we trust the server var
|
||||
if (!empty($_SERVER[$this->config->user_attribute])) {
|
||||
// Associate Shibboleth session with user for SLO preparation
|
||||
$sessionkey = '';
|
||||
if (isset($_SERVER['Shib-Session-ID'])){
|
||||
// This is only available for Shibboleth 2.x SPs
|
||||
$sessionkey = $_SERVER['Shib-Session-ID'];
|
||||
} else {
|
||||
// Try to find out using the user's cookie
|
||||
foreach ($_COOKIE as $name => $value){
|
||||
if (eregi('_shibsession_', $name)){
|
||||
$sessionkey = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set shibboleth session ID for logout
|
||||
$SESSION->shibboleth_session_id = $sessionkey;
|
||||
|
||||
return (strtolower($_SERVER[$this->config->user_attribute]) == strtolower($username));
|
||||
} else {
|
||||
// If we are not, the user has used the manual login and the login name is
|
||||
|
204
auth/shibboleth/logout.php
Normal file
204
auth/shibboleth/logout.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php // $Id$
|
||||
|
||||
// Implements logout for Shibboleth authenticated users according to:
|
||||
// - https://spaces.internet2.edu/display/SHIB2/NativeSPLogoutInitiator
|
||||
// - https://spaces.internet2.edu/display/SHIB2/NativeSPNotify
|
||||
|
||||
require_once("../../config.php");
|
||||
|
||||
require_once($CFG->dirroot."/auth/shibboleth/auth.php");
|
||||
|
||||
|
||||
// Front channel logout
|
||||
if (
|
||||
isset($_GET['return'])
|
||||
&& isset($_GET['action'])
|
||||
&& $_GET['action'] == 'logout'
|
||||
){
|
||||
|
||||
// Logout out user from application
|
||||
// E.g. destroy application session/cookie etc
|
||||
require_logout();
|
||||
|
||||
// Finally, send user to the return URL
|
||||
redirect($_GET['return']);
|
||||
}
|
||||
|
||||
// Back channel logout
|
||||
elseif (!empty($HTTP_RAW_POST_DATA)) {
|
||||
|
||||
// Requires PHP 5
|
||||
|
||||
// Set SOAP header
|
||||
$server = new SoapServer('https://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
|
||||
$server->addFunction("LogoutNotification");
|
||||
$server->handle();
|
||||
}
|
||||
|
||||
// Return WSDL
|
||||
else {
|
||||
|
||||
header('Content-Type: text/xml');
|
||||
|
||||
echo <<<WSDL
|
||||
<?xml version ="1.0" encoding ="UTF-8" ?>
|
||||
<definitions name="LogoutNotification"
|
||||
targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
|
||||
xmlns:notify="urn:mace:shibboleth:2.0:sp:notify"
|
||||
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||
xmlns="http://schemas.xmlsoap.org/wsdl/">
|
||||
|
||||
<!--
|
||||
This page either has to be called with the GET arguments 'action' and 'return' via
|
||||
a redirect from the Shibboleth Service Provider logout handler (front-channel
|
||||
logout) or via a SOAP request by a Shibboleth Service Provider (back-channel
|
||||
logout).
|
||||
Because neither of these two variants seems to be the case, the WSDL file for
|
||||
the web service is returned.
|
||||
|
||||
For more information see:
|
||||
- https://spaces.internet2.edu/display/SHIB2/NativeSPLogoutInitiator
|
||||
- https://spaces.internet2.edu/display/SHIB2/NativeSPNotify
|
||||
-->
|
||||
|
||||
<types>
|
||||
<schema targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
|
||||
xmlns="http://www.w3.org/2000/10/XMLSchema"
|
||||
xmlns:notify="urn:mace:shibboleth:2.0:sp:notify">
|
||||
|
||||
<simpleType name="string">
|
||||
<restriction base="string">
|
||||
<minLength value="1"/>
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<element name="OK" type="notify:OKType"/>
|
||||
<complexType name="OKType">
|
||||
<sequence/>
|
||||
</complexType>
|
||||
|
||||
</schema>
|
||||
</types>
|
||||
|
||||
<message name="getLogoutNotificationRequest">
|
||||
<part name="SessionID" type="notify:string" />
|
||||
</message>
|
||||
|
||||
<message name="getLogoutNotificationResponse" >
|
||||
<part name="OK"/>
|
||||
</message>
|
||||
|
||||
<portType name="LogoutNotificationPortType">
|
||||
<operation name="LogoutNotification">
|
||||
<input message="getLogoutNotificationRequest"/>
|
||||
<output message="getLogoutNotificationResponse"/>
|
||||
</operation>
|
||||
</portType>
|
||||
|
||||
<binding name="LogoutNotificationBinding" type="notify:LogoutNotificationPortType">
|
||||
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
|
||||
<operation name="LogoutNotification">
|
||||
<soap:operation soapAction="urn:xmethods-logout-notification#LogoutNotification"/>
|
||||
</operation>
|
||||
</binding>
|
||||
|
||||
<service name="LogoutNotificationService">
|
||||
<port name="LogoutNotificationPort" binding="notify:LogoutNotificationBinding">
|
||||
<soap:address location="https://{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}"/>
|
||||
</port>
|
||||
</service>
|
||||
</definitions>
|
||||
WSDL;
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
function LogoutNotification($SessionID){
|
||||
|
||||
global $CFG, $SESSION;
|
||||
|
||||
// Delete session of user using $SessionID
|
||||
if(empty($CFG->dbsessions)) {
|
||||
|
||||
// File session
|
||||
$dir = $CFG->dataroot .'/sessions';
|
||||
if (is_dir($dir)) {
|
||||
if ($dh = opendir($dir)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
//echo $dir.'/'.$file."\n";exit;
|
||||
if (is_file($dir.'/'.$file)){
|
||||
$session_key = ereg_replace('sess_', '', $file);
|
||||
|
||||
$data = file($dir.'/'.$file);
|
||||
if (isset($data[0])){
|
||||
$user_session = unserializesession($data[0]);
|
||||
|
||||
if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
|
||||
//echo '2. Shibboleth Session (from filesystem session) of '.$user_session['USER']->username.':' .$user_session['SESSION']->shibboleth_session_id."\n";
|
||||
// If there is a match, delete file
|
||||
if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
|
||||
// Delete this file
|
||||
if (!unlink($dir.'/'.$file)){
|
||||
return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
|
||||
}
|
||||
}
|
||||
}
|
||||
//print_r($user_session);
|
||||
}
|
||||
|
||||
//echo "Moodle session: $session_key \n";
|
||||
//echo "filename: $file \n";
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// DB Session
|
||||
if (!empty($CFG->sessiontimeout)) {
|
||||
$ADODB_SESS_LIFE = $CFG->sessiontimeout;
|
||||
}
|
||||
|
||||
if ($user_session_data = get_records_sql('SELECT sesskey, sessdata FROM '. $CFG->prefix .'sessions2 WHERE expiry > NOW()')) {
|
||||
foreach ($user_session_data as $session_data) {
|
||||
|
||||
//print_r($session_data);
|
||||
$user_session = adodb_unserialize( urldecode($session_data->sessdata) );
|
||||
|
||||
if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
|
||||
//echo '3. Shibboleth Session (from ADODB session) of '.$user_session['USER']->username.':' .$user_session['SESSION']->shibboleth_session_id."\n";
|
||||
|
||||
// If there is a match, delete file
|
||||
if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
|
||||
// Delete this session entry
|
||||
if (ADODB_Session::destroy($session_data->sesskey) !== true){
|
||||
return new SoapFault('LogoutError', 'Could not delete Moodle session entry in database.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//print_r($user_session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If now SoapFault was thrown the function will return OK as the SP assumes
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
// Same function as in adodb, but cannot be used for file session for some reason...
|
||||
function unserializesession( $serialized_string ){
|
||||
$variables = array( );
|
||||
$a = preg_split( "/(\w+)\|/", $serialized_string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE );
|
||||
for( $i = 0; $i < count( $a ); $i = $i+2 ) {
|
||||
$variables[$a[$i]] = unserialize( $a[$i+1] );
|
||||
}
|
||||
return( $variables );
|
||||
}
|
||||
|
||||
|
||||
?>
|
Loading…
x
Reference in New Issue
Block a user