MDL-18597 Merging from STABLE
This commit is contained in:
@@ -2,8 +2,8 @@ Shibboleth Authentication for Moodle
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
@@ -23,6 +23,8 @@ Changes:
|
||||
- 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
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -42,7 +44,20 @@ 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
|
||||
|
||||
@@ -274,10 +289,13 @@ 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.
|
||||
|
||||
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
|
||||
|
||||
@@ -196,8 +196,17 @@ class auth_plugin_shibboleth extends auth_plugin_base {
|
||||
isset($this->config->logout_handler)
|
||||
&& !empty($this->config->logout_handler)
|
||||
){
|
||||
// Backup old redirect url
|
||||
$temp_redirect = $redirect;
|
||||
// Check if there is an alternative logout return url defined
|
||||
if (
|
||||
isset($this->config->logout_return_url)
|
||||
&& !empty($this->config->logout_return_url)
|
||||
){
|
||||
// Set temp_redirect to alternative return url
|
||||
$temp_redirect = $this->config->logout_return_url;
|
||||
} else {
|
||||
// Backup old redirect url
|
||||
$temp_redirect = $redirect;
|
||||
}
|
||||
|
||||
// Overwrite redirect in order to send user to Shibboleth logout page and let him return back
|
||||
$redirect = $this->config->logout_handler.'?return='.urlencode($temp_redirect);
|
||||
@@ -266,6 +275,7 @@ class auth_plugin_shibboleth extends auth_plugin_base {
|
||||
set_config('organization_selection', $config->organization_selection, 'auth/shibboleth');
|
||||
}
|
||||
set_config('logout_handler', $config->logout_handler, 'auth/shibboleth');
|
||||
set_config('logout_return_url', $config->logout_return_url, 'auth/shibboleth');
|
||||
set_config('login_name', $config->login_name, 'auth/shibboleth');
|
||||
set_config('convert_data', $config->convert_data, 'auth/shibboleth');
|
||||
set_config('auth_instructions', $config->auth_instructions, 'auth/shibboleth');
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
</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' ){
|
||||
@@ -53,17 +53,17 @@
|
||||
}
|
||||
?> />
|
||||
</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;
|
||||
@@ -78,13 +78,35 @@ urn:mace:organization2:providerID, Example Organization 2, /Shibboleth.sso/WAYF/
|
||||
}
|
||||
?>
|
||||
</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)){
|
||||
@@ -94,7 +116,7 @@ As an optional third parameter you can add the location of a Shibboleth session
|
||||
}
|
||||
?>" />
|
||||
</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">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/// Check for timed out sessions
|
||||
if (!empty($SESSION->has_timed_out)) {
|
||||
$session_has_timed_out = true;
|
||||
unset($SESSION->has_timed_out);
|
||||
$SESSION->has_timed_out = false;
|
||||
} else {
|
||||
$session_has_timed_out = false;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ httpsrequired();
|
||||
|
||||
/// Define variables used in page
|
||||
if (!$site = get_site()) {
|
||||
print_error('nosite');
|
||||
error("No site found!");
|
||||
}
|
||||
|
||||
if (empty($CFG->langmenu)) {
|
||||
@@ -37,7 +37,7 @@ httpsrequired();
|
||||
$loginurl = (!empty($CFG->alternateloginurl)) ? $CFG->alternateloginurl : '';
|
||||
|
||||
|
||||
if (get_moodle_cookie() == '') {
|
||||
if (get_moodle_cookie() == '') {
|
||||
set_moodle_cookie('nobody'); // To help search for cookies
|
||||
}
|
||||
|
||||
|
||||
+149
-145
@@ -9,38 +9,47 @@ require_once("../../config.php");
|
||||
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"
|
||||
@@ -61,144 +70,139 @@ For more information see:
|
||||
- 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 );
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user