-------------------------------------------------------------------------------
Requirements:
-- Shibboleth target 1.1 or later. See documentation for your Shibboleth
- federation on how to set up Shibboleth.
+- Shibboleth Service Provider 1.3 or newer. Recommended is 2.1 or newer.
+ See documentation for your Shibboleth federation on how to set up Shibboleth.
Changes:
- 11. 2004: Created by Markus Hagman
- 12. 2008: Shibboleth 2.x and Single Logout support added
- 1. 2008: Added logout hook and moved Shibboleth config strings to utf8 auth
language files.
+- 3. 2009: Added various improvements and bug fixes reported by Ina Müller from
+ university Tuebingen and Peter Ellis of University of Washington
Moodle Configuration with Dual login
-------------------------------------------------------------------------------
with something that fits your needs, e.g. 'require affiliation student'.
For IIS you have protect the auth/shibboleth directory directly in the
- RequestMap of the Shibboleth configuration file (shibboleth.xml). See
+ RequestMap of the Shibboleth configuration file (shibboleth.xml or
+ shibboleth2.xml).
+
+--
+<Path name="moodle" requireSession="false" >
+ <Path name="auth/shibboleth/index.php" requireSession="true" >
+ <AccessControl>
+ ...
+ </AccessControl>
+ </Path>
+</Path>
+--
+
+ Also see:
https://spaces.internet2.edu/display/SHIB2/NativeSPRequestMapper and
https://spaces.internet2.edu/display/SHIB2/NativeSPAccessControl
page saying that you were successfully logged out and if you go back to Moodle
you also should be logged out from Moodle.
+Requirements:
+- PHP needs the Soap Extension, which maybe must installed manually:
+ More information is available here http://ch.php.net/soap
+- Logout only works with Shibboleth Service Provider 2.1 or higher
Limitations:
-Single Logout is only supported with SAML2 and so far only with the Shibboleth
-Service Provider 2.x.
+Single Logout is only supported when SAML2 is used at the SP and the IdP.
As of December 2008, the Shibboleth Identity Provider 2.1.1 does not yet support
Single Logout (SLO). Therefore, the single logout feature cannot be used yet.
One of the reasons why SLO isn't supported yet is because there aren't many
</tr>
<tr valign="top">
- <td align="right">Moodle WAYF Service:</td>
+ <td align="right"><?php print_string("auth_shib_integrated_wayf", "auth") ?>:</td>
<td>
<input name="alt_login" type="checkbox" <?php
if ( isset($config->alt_login) and $config->alt_login == 'on' ){
}
?> />
</td>
- <td>If you check this, Moodle will use its own WAYF service instead of the one configured for Shibboleth. Moodle will display a drop-down list on this alternative login page where the user has to select his Identity Provider.</td>
+ <td><?php print_string("auth_shib_integrated_wayf_description", "auth") ?></td>
</tr>
<tr valign="top">
- <td align="right">Identity Providers:</td>
+ <td align="right"><?php print_string("auth_shib_idp_list", "auth") ?>:</td>
<td>
<textarea name="organization_selection" rows="10" cols="30" style="overflow: auto; white-space: nowrap;"
><?php
if (!isset($config->organization_selection)){
echo 'urn:mace:organization1:providerID, Example Organization 1
-https://another.idp-id.com/shibboleth, Other Example Organization
+https://another.idp-id.com/shibboleth, Other Example Organization, /Shibboleth.sso/DS/SWITCHaai
urn:mace:organization2:providerID, Example Organization 2, /Shibboleth.sso/WAYF/SWITCHaai';
} else {
echo $config->organization_selection;
}
?>
</td>
- <td>Provide a list of Identity Provider entityIDs to let the user choose from on the login page.
-On each line there must be a comma-separated tuple for entityID of the IdP (see the Shibboleth metadata file) and Name of IdP as it shall be displayed in the drow-down list.
-As an optional third parameter you can add the location of a Shibboleth session initiator that shall be used in case your Moodle installation is part of a multi federation setup.</td>
+ <td><?php print_string("auth_shib_idp_list_description", "auth") ?></td>
</tr>
<tr valign="top">
- <td align="right">Authentication Method Name:</td>
+ <td align="right"><?php print_string("auth_shib_logout_url", "auth") ?>:</td>
+ <td>
+ <input name="logout_handler" type="text" size="30" value="<?php
+ if ( isset($config->logout_handler) and !empty($config->logout_handler)){
+ echo $config->logout_handler;
+ }
+ ?>" />
+ </td>
+ <td><?php print_string("auth_shib_logout_url_description", "auth") ?></td>
+</tr>
+
+<tr valign="top">
+ <td align="right"><?php print_string("auth_shib_logout_return_url", "auth") ?>:</td>
+ <td>
+ <input name="logout_return_url" type="text" size="30" value="<?php
+ if ( isset($config->logout_return_url) and !empty($config->logout_return_url)){
+ echo $config->logout_return_url;
+ }
+ ?>" />
+ </td>
+ <td><?php print_string("auth_shib_logout_return_url_description", "auth") ?></td>
+</tr>
+
+<tr valign="top">
+ <td align="right"><?php print_string("auth_shib_auth_method", "auth") ?>:</td>
<td>
<input name="login_name" type="text" size="30" value="<?php
if ( isset($config->login_name) and !empty($config->login_name)){
}
?>" />
</td>
- <td>Provide a name for the Shibboleth authentication method that is familiar to your users. This could be the name of your Shibboleth federation, e.g. "SWITCHaai Login" or "InCommon Login" and so on.</td>
+ <td><?php print_string("auth_shib_auth_method_description", "auth") ?></td>
</tr>
<tr valign="top">
require_once($CFG->dirroot."/auth/shibboleth/auth.php");
+// Find out whether host supports https
+$protocol = 'http://';
+if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'){
+ $protocol = 'https://';
+}
+
// Front channel logout
if (
- isset($_GET['return'])
- && isset($_GET['action'])
- && $_GET['action'] == 'logout'
+ 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']);
+
+ // 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();
+
+ // Requires PHP 5
+
+
+ // Set SOAP header
+ $server = new SoapServer($protocol.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
+
+
+ $server->addFunction("LogoutNotification");
+ $server->handle();
}
// Return WSDL
else {
-
- header('Content-Type: text/xml');
-
- echo <<<WSDL
+
+ 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"
- 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>
+ <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="{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}"/>
+ </port>
+ </service>
</definitions>
WSDL;
- exit;
+ 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 = $DB->get_records_sql('SELECT sesskey, sessdata FROM {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
-
+
+ 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)) {
+ // Read all session files
+ while (($file = readdir($dh)) !== false) {
+ // Check if it is a file
+ if (is_file($dir.'/'.$file)){
+ $session_key = ereg_replace('sess_', '', $file);
+
+ // Read session file data
+ $data = file($dir.'/'.$file);
+ if (isset($data[0])){
+ $user_session = unserializesession($data[0]);
+
+ // Check if we have found session that shall be deleted
+ if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
+
+ // If there is a match, delete file
+ if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
+ // Delete session file
+ if (!unlink($dir.'/'.$file)){
+ return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
+ }
+ }
+ }
+ }
+ }
+ }
+ 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) {
+
+ // Get user session
+ $user_session = adodb_unserialize( urldecode($session_data->sessdata) );
+
+ if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
+
+ // 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.');
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // 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 );
+ $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 );
}
-
?>