]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-18597 Merging from STABLE
authorexe-cutor <exe-cutor>
Wed, 18 Mar 2009 13:28:57 +0000 (13:28 +0000)
committerexe-cutor <exe-cutor>
Wed, 18 Mar 2009 13:28:57 +0000 (13:28 +0000)
auth/shibboleth/README.txt
auth/shibboleth/auth.php
auth/shibboleth/config.html
auth/shibboleth/login.php
auth/shibboleth/logout.php

index 1a71fb64128b58bd73079eadbc70f4e637a9a95b..850b59d26e8f7b856a51860ba9329284a8fc10ee 100644 (file)
@@ -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 
index 764435849f7f6c9b6fbe0c2ee3c8f9ebbc7bc6b8..9fcfdbf78076c9f3b6f425e17d104a58eded5c5e 100644 (file)
@@ -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');
index 1322c0487ba51655ed1a6a116d57b2a44698adb7..5bad2145334caf1fce725595078a8a696259238f 100755 (executable)
@@ -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' ){
             }
         ?> />
     </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">
index 88f3fc043d3fd35a2775f7142427a894cc8e8dd4..caa2fd917d812d2e8bc3bb53be7f73486ae2c718 100644 (file)
@@ -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
     }
 
index 55608580dbbed07207fb9513238bc4f46c6837b9..24ab73a7b796be743ca5edca9a021f2beb3408cf 100644 (file)
@@ -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 );
 }
 
-
 ?>