PageRenderTime 84ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/auth/shibboleth/logout.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 204 lines | 136 code | 40 blank | 28 comment | 31 complexity | 8bdb4782adeaa0682e62b38e516331f1 MD5 | raw file
  1. <?php
  2. // Implements logout for Shibboleth authenticated users according to:
  3. // - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
  4. // - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
  5. require_once("../../config.php");
  6. require_once($CFG->dirroot."/auth/shibboleth/auth.php");
  7. $action = optional_param('action', '', PARAM_ALPHA);
  8. $redirect = optional_param('return', '', PARAM_URL);
  9. // Find out whether host supports https
  10. $protocol = 'http://';
  11. if (is_https()) {
  12. $protocol = 'https://';
  13. }
  14. // If the shibboleth plugin is not enable, throw an exception.
  15. if (!is_enabled_auth('shibboleth')) {
  16. throw new moodle_exception(get_string('pluginnotenabled', 'auth', 'shibboleth'));
  17. }
  18. // Front channel logout.
  19. $inputstream = file_get_contents("php://input");
  20. if ($action == 'logout' && !empty($redirect)) {
  21. if (isloggedin($USER) && $USER->auth == 'shibboleth') {
  22. // Logout user from application.
  23. require_logout();
  24. }
  25. // Finally, send user to the return URL.
  26. redirect($redirect);
  27. } else if (!empty($inputstream)) {
  28. // Back channel logout.
  29. // Set SOAP header.
  30. $server = new SoapServer($protocol.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'/LogoutNotification.wsdl');
  31. $server->addFunction("LogoutNotification");
  32. $server->handle();
  33. } else {
  34. // Return WSDL.
  35. header('Content-Type: text/xml');
  36. echo <<<WSDL
  37. <?xml version ="1.0" encoding ="UTF-8" ?>
  38. <definitions name="LogoutNotification"
  39. targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
  40. xmlns:notify="urn:mace:shibboleth:2.0:sp:notify"
  41. xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  42. xmlns="http://schemas.xmlsoap.org/wsdl/">
  43. <!--
  44. This page either has to be called with the GET arguments 'action' and 'return' via
  45. a redirect from the Shibboleth Service Provider logout handler (front-channel
  46. logout) or via a SOAP request by a Shibboleth Service Provider (back-channel
  47. logout).
  48. Because neither of these two variants seems to be the case, the WSDL file for
  49. the web service is returned.
  50. For more information see:
  51. - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLogoutInitiator
  52. - https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPNotify
  53. -->
  54. <types>
  55. <schema targetNamespace="urn:mace:shibboleth:2.0:sp:notify"
  56. xmlns="http://www.w3.org/2000/10/XMLSchema"
  57. xmlns:notify="urn:mace:shibboleth:2.0:sp:notify">
  58. <simpleType name="string">
  59. <restriction base="string">
  60. <minLength value="1"/>
  61. </restriction>
  62. </simpleType>
  63. <element name="OK" type="notify:OKType"/>
  64. <complexType name="OKType">
  65. <sequence/>
  66. </complexType>
  67. </schema>
  68. </types>
  69. <message name="getLogoutNotificationRequest">
  70. <part name="SessionID" type="notify:string" />
  71. </message>
  72. <message name="getLogoutNotificationResponse" >
  73. <part name="OK"/>
  74. </message>
  75. <portType name="LogoutNotificationPortType">
  76. <operation name="LogoutNotification">
  77. <input message="getLogoutNotificationRequest"/>
  78. <output message="getLogoutNotificationResponse"/>
  79. </operation>
  80. </portType>
  81. <binding name="LogoutNotificationBinding" type="notify:LogoutNotificationPortType">
  82. <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  83. <operation name="LogoutNotification">
  84. <soap:operation soapAction="urn:xmethods-logout-notification#LogoutNotification"/>
  85. </operation>
  86. </binding>
  87. <service name="LogoutNotificationService">
  88. <port name="LogoutNotificationPort" binding="notify:LogoutNotificationBinding">
  89. <soap:address location="{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['PHP_SELF']}"/>
  90. </port>
  91. </service>
  92. </definitions>
  93. WSDL;
  94. exit;
  95. }
  96. /******************************************************************************/
  97. function LogoutNotification($SessionID){
  98. global $CFG, $SESSION, $DB;
  99. // Delete session of user using $SessionID
  100. if(empty($CFG->dbsessions)) {
  101. // File session
  102. $dir = $CFG->dataroot .'/sessions';
  103. if (is_dir($dir)) {
  104. if ($dh = opendir($dir)) {
  105. // Read all session files
  106. while (($file = readdir($dh)) !== false) {
  107. // Check if it is a file
  108. if (is_file($dir.'/'.$file)){
  109. $session_key = preg_replace('/sess_/', '', $file);
  110. // Read session file data
  111. $data = file($dir.'/'.$file);
  112. if (isset($data[0])){
  113. $user_session = unserializesession($data[0]);
  114. // Check if we have found session that shall be deleted
  115. if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
  116. // If there is a match, delete file
  117. if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
  118. // Delete session file
  119. if (!unlink($dir.'/'.$file)){
  120. return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. closedir($dh);
  128. }
  129. }
  130. } else {
  131. // DB Session
  132. //TODO: this needs to be rewritten to use new session stuff
  133. if (!empty($CFG->sessiontimeout)) {
  134. $ADODB_SESS_LIFE = $CFG->sessiontimeout;
  135. }
  136. if ($user_session_data = $DB->get_records_sql('SELECT sesskey, sessdata FROM {sessions2} WHERE expiry > NOW()')) {
  137. foreach ($user_session_data as $session_data) {
  138. // Get user session
  139. $user_session = adodb_unserialize( urldecode($session_data->sessdata) );
  140. if (isset($user_session['SESSION']) && isset($user_session['SESSION']->shibboleth_session_id)){
  141. // If there is a match, delete file
  142. if ($user_session['SESSION']->shibboleth_session_id == $SessionID){
  143. // Delete this session entry
  144. if (ADODB_Session::destroy($session_data->sesskey) !== true){
  145. return new SoapFault('LogoutError', 'Could not delete Moodle session entry in database.');
  146. }
  147. }
  148. }
  149. }
  150. }
  151. }
  152. // If now SoapFault was thrown the function will return OK as the SP assumes
  153. }
  154. /*****************************************************************************/
  155. // Same function as in adodb, but cannot be used for file session for some reason...
  156. function unserializesession($serialized_string) {
  157. $variables = array();
  158. $a = preg_split("/(\w+)\|/", $serialized_string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  159. $counta = count($a);
  160. for ($i = 0; $i < $counta; $i = $i+2) {
  161. $variables[$a[$i]] = unserialize($a[$i+1]);
  162. }
  163. return $variables;
  164. }