3 // Implements logout for Shibboleth authenticated users according to:
4 // - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
5 // - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
7 require_once("../../config.php");
9 require_once($CFG->dirroot
."/auth/shibboleth/auth.php");
11 $action = optional_param('action', '', PARAM_ALPHA
);
12 $redirect = optional_param('return', '', PARAM_URL
);
14 // Find out whether host supports https
15 $protocol = 'http://';
17 $protocol = 'https://';
20 // If the shibboleth plugin is not enable, throw an exception.
21 if (!is_enabled_auth('shibboleth')) {
22 throw new moodle_exception(get_string('pluginnotenabled', 'auth', 'shibboleth'));
25 // Front channel logout.
26 $inputstream = file_get_contents("php://input");
27 if ($action == 'logout' && !empty($redirect)) {
29 if ($USER->auth
== 'shibboleth') {
30 // Logout out user from application.
32 // Finally, send user to the return URL.
36 } else if (!empty($inputstream)) {
38 // Back channel logout.
40 $server = new SoapServer($protocol.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
41 $server->addFunction("LogoutNotification");
47 header('Content-Type: text/xml');
50 <?xml version ="1.0" encoding ="UTF-8" ?>
51 <definitions name="LogoutNotification"
52 targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
53 xmlns:notify="urn:mace:shibboleth:2.0:sp:notify"
54 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
55 xmlns="http://schemas.xmlsoap.org/wsdl/">
58 This page either has to be called with the GET arguments 'action' and 'return' via
59 a redirect from the Shibboleth Service Provider logout handler (front-channel
60 logout) or via a SOAP request by a Shibboleth Service Provider (back-channel
62 Because neither of these two variants seems to be the case, the WSDL file
for
63 the web service is returned
.
65 For more information see
:
66 - https
://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
67 - https
://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
71 <schema targetNamespace
="urn:mace:shibboleth:2.0:sp:notify"
72 xmlns
="http://www.w3.org/2000/10/XMLSchema"
73 xmlns
:notify
="urn:mace:shibboleth:2.0:sp:notify">
75 <simpleType name
="string">
76 <restriction base
="string">
77 <minLength value
="1"/>
81 <element name
="OK" type
="notify:OKType"/>
82 <complexType name
="OKType">
89 <message name
="getLogoutNotificationRequest">
90 <part name
="SessionID" type
="notify:string" />
93 <message name
="getLogoutNotificationResponse" >
97 <portType name
="LogoutNotificationPortType">
98 <operation name
="LogoutNotification">
99 <input message
="getLogoutNotificationRequest"/>
100 <output message
="getLogoutNotificationResponse"/>
104 <binding name
="LogoutNotificationBinding" type
="notify:LogoutNotificationPortType">
105 <soap
:binding style
="rpc" transport
="http://schemas.xmlsoap.org/soap/http"/>
106 <operation name
="LogoutNotification">
107 <soap
:operation soapAction
="urn:xmethods-logout-notification#LogoutNotification"/>
111 <service name
="LogoutNotificationService">
112 <port name
="LogoutNotificationPort" binding
="notify:LogoutNotificationBinding">
113 <soap
:address location
="{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}"/>
120 /******************************************************************************/
122 function LogoutNotification($SessionID){
124 global $CFG, $SESSION, $DB;
126 // Delete session of user using $SessionID
127 if(empty($CFG->dbsessions
)) {
130 $dir = $CFG->dataroot
.'/sessions';
132 if ($dh = opendir($dir)) {
133 // Read all session files
134 while (($file = readdir($dh)) !== false) {
135 // Check if it is a file
136 if (is_file($dir.'/'.$file)){
137 $session_key = preg_replace('/sess_/', '', $file);
139 // Read session file data
140 $data = file($dir.'/'.$file);
141 if (isset($data[0])){
142 $user_session = unserializesession($data[0]);
144 // Check if we have found session that shall be deleted
145 if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id
)){
147 // If there is a match, delete file
148 if ($user_session['SESSION']->shibboleth_session_id
== $SessionID){
149 // Delete session file
150 if (!unlink($dir.'/'.$file)){
151 return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
163 //TODO: this needs to be rewritten to use new session stuff
164 if (!empty($CFG->sessiontimeout
)) {
165 $ADODB_SESS_LIFE = $CFG->sessiontimeout
;
168 if ($user_session_data = $DB->get_records_sql('SELECT sesskey, sessdata FROM {sessions2} WHERE expiry > NOW()')) {
169 foreach ($user_session_data as $session_data) {
172 $user_session = adodb_unserialize( urldecode($session_data->sessdata
) );
174 if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id
)){
176 // If there is a match, delete file
177 if ($user_session['SESSION']->shibboleth_session_id
== $SessionID){
178 // Delete this session entry
179 if (ADODB_Session
::destroy($session_data->sesskey
) !== true){
180 return new SoapFault('LogoutError', 'Could not delete Moodle session entry in database.');
188 // If now SoapFault was thrown the function will return OK as the SP assumes
192 /*****************************************************************************/
194 // Same function as in adodb, but cannot be used for file session for some reason...
195 function unserializesession($serialized_string) {
196 $variables = array();
197 $a = preg_split("/(\w+)\|/", $serialized_string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
);
199 for ($i = 0; $i < $counta; $i = $i+
2) {
200 $variables[$a[$i]] = unserialize($a[$i+
1]);