See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Authentication Plugin: Shibboleth Authentication 19 * Authentication using Shibboleth. 20 * 21 * Distributed under GPL (c)Markus Hagman 2004-2006 22 * 23 * @package auth_shibboleth 24 * @author Martin Dougiamas 25 * @author Lukas Haemmerle 26 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 27 */ 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 require_once($CFG->libdir.'/authlib.php'); 32 33 /** 34 * Shibboleth authentication plugin. 35 */ 36 class auth_plugin_shibboleth extends auth_plugin_base { 37 38 /** 39 * Constructor. 40 */ 41 public function __construct() { 42 $this->authtype = 'shibboleth'; 43 $this->config = get_config('auth_shibboleth'); 44 } 45 46 /** 47 * Old syntax of class constructor. Deprecated in PHP7. 48 * 49 * @deprecated since Moodle 3.1 50 */ 51 public function auth_plugin_shibboleth() { 52 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); 53 self::__construct(); 54 } 55 56 /** 57 * Returns true if the username and password work and false if they are 58 * wrong or don't exist. 59 * 60 * @param string $username The username (with system magic quotes) 61 * @param string $password The password (with system magic quotes) 62 * @return bool Authentication success or failure. 63 */ 64 function user_login($username, $password) { 65 global $SESSION; 66 67 // If we are in the shibboleth directory then we trust the server var 68 if (!empty($_SERVER[$this->config->user_attribute])) { 69 // Associate Shibboleth session with user for SLO preparation 70 $sessionkey = ''; 71 if (isset($_SERVER['Shib-Session-ID'])){ 72 // This is only available for Shibboleth 2.x SPs 73 $sessionkey = $_SERVER['Shib-Session-ID']; 74 } else { 75 // Try to find out using the user's cookie 76 foreach ($_COOKIE as $name => $value){ 77 if (preg_match('/_shibsession_/i', $name)){ 78 $sessionkey = $value; 79 } 80 } 81 } 82 83 // Set shibboleth session ID for logout 84 $SESSION->shibboleth_session_id = $sessionkey; 85 86 return (strtolower($_SERVER[$this->config->user_attribute]) == strtolower($username)); 87 } else { 88 // If we are not, the user has used the manual login and the login name is 89 // unknown, so we return false. 90 return false; 91 } 92 } 93 94 95 96 /** 97 * Returns the user information for 'external' users. In this case the 98 * attributes provided by Shibboleth 99 * 100 * @return array $result Associative array of user data 101 */ 102 function get_userinfo($username) { 103 // reads user information from shibboleth attributes and return it in array() 104 global $CFG; 105 106 // Check whether we have got all the essential attributes 107 if ( empty($_SERVER[$this->config->user_attribute]) ) { 108 throw new \moodle_exception( 'shib_not_all_attributes_error', 'auth_shibboleth' , '', 109 "'".$this->config->user_attribute."' ('".$_SERVER[$this->config->user_attribute]."'), '". 110 $this->config->field_map_firstname."' ('".$_SERVER[$this->config->field_map_firstname]."'), '". 111 $this->config->field_map_lastname."' ('".$_SERVER[$this->config->field_map_lastname]."') and '". 112 $this->config->field_map_email."' ('".$_SERVER[$this->config->field_map_email]."')"); 113 } 114 115 $attrmap = $this->get_attributes(); 116 117 $result = array(); 118 $search_attribs = array(); 119 120 foreach ($attrmap as $key=>$value) { 121 // Check if attribute is present 122 if (!isset($_SERVER[$value])){ 123 $result[$key] = ''; 124 continue; 125 } 126 127 // Make usename lowercase 128 if ($key == 'username'){ 129 $result[$key] = strtolower($this->get_first_string($_SERVER[$value])); 130 } else { 131 $result[$key] = $this->get_first_string($_SERVER[$value]); 132 } 133 } 134 135 // Provide an API to modify the information to fit the Moodle internal 136 // data representation 137 if ( 138 $this->config->convert_data 139 && $this->config->convert_data != '' 140 && is_readable($this->config->convert_data) 141 ) { 142 143 // Include a custom file outside the Moodle dir to 144 // modify the variable $moodleattributes 145 include($this->config->convert_data); 146 } 147 148 return $result; 149 } 150 151 /** 152 * Returns array containg attribute mappings between Moodle and Shibboleth. 153 * 154 * @return array 155 */ 156 function get_attributes() { 157 $configarray = (array) $this->config; 158 159 $moodleattributes = array(); 160 $userfields = array_merge($this->userfields, $this->get_custom_user_profile_fields()); 161 foreach ($userfields as $field) { 162 if (isset($configarray["field_map_$field"])) { 163 $moodleattributes[$field] = $configarray["field_map_$field"]; 164 } 165 } 166 $moodleattributes['username'] = $configarray["user_attribute"]; 167 168 return $moodleattributes; 169 } 170 171 function prevent_local_passwords() { 172 return true; 173 } 174 175 /** 176 * Returns true if this authentication plugin is 'internal'. 177 * 178 * @return bool 179 */ 180 function is_internal() { 181 return false; 182 } 183 184 /** 185 * Whether shibboleth users can change their password or not. 186 * 187 * Shibboleth auth requires password to be changed on shibboleth server directly. 188 * So it is required to have password change url set. 189 * 190 * @return bool true if there's a password url or false otherwise. 191 */ 192 function can_change_password() { 193 if (!empty($this->config->changepasswordurl)) { 194 return true; 195 } else { 196 return false; 197 } 198 } 199 200 /** 201 * Get password change url. 202 * 203 * @return moodle_url|null Returns URL to change password or null otherwise. 204 */ 205 function change_password_url() { 206 if (!empty($this->config->changepasswordurl)) { 207 return new moodle_url($this->config->changepasswordurl); 208 } else { 209 return null; 210 } 211 } 212 213 /** 214 * Hook for login page 215 * 216 */ 217 function loginpage_hook() { 218 global $SESSION, $CFG; 219 220 // Prevent username from being shown on login page after logout 221 $CFG->nolastloggedin = true; 222 223 return; 224 } 225 226 /** 227 * Hook for logout page 228 * 229 */ 230 function logoutpage_hook() { 231 global $SESSION, $redirect; 232 233 // Only do this if logout handler is defined, and if the user is actually logged in via Shibboleth 234 $logouthandlervalid = isset($this->config->logout_handler) && !empty($this->config->logout_handler); 235 if (isset($SESSION->shibboleth_session_id) && $logouthandlervalid ) { 236 // Check if there is an alternative logout return url defined 237 if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) { 238 // Set temp_redirect to alternative return url 239 $temp_redirect = $this->config->logout_return_url; 240 } else { 241 // Backup old redirect url 242 $temp_redirect = $redirect; 243 } 244 245 // Overwrite redirect in order to send user to Shibboleth logout page and let him return back 246 $redirecturl = new moodle_url($this->config->logout_handler, array('return' => $temp_redirect)); 247 $redirect = $redirecturl->out(); 248 } 249 } 250 251 /** 252 * Cleans and returns first of potential many values (multi-valued attributes) 253 * 254 * @param string $string Possibly multi-valued attribute from Shibboleth 255 */ 256 function get_first_string($string) { 257 $list = explode( ';', $string); 258 $clean_string = rtrim($list[0]); 259 260 return $clean_string; 261 } 262 263 /** 264 * Test if settings are correct, print info to output. 265 */ 266 public function test_settings() { 267 global $OUTPUT; 268 269 if (!isset($this->config->user_attribute) || empty($this->config->user_attribute)) { 270 echo $OUTPUT->notification(get_string("shib_not_set_up_error", "auth_shibboleth", 271 (new moodle_url('/auth/shibboleth/README.txt'))->out()), 'notifyproblem'); 272 return; 273 } 274 if ($this->config->convert_data and $this->config->convert_data != '' and !is_readable($this->config->convert_data)) { 275 echo $OUTPUT->notification(get_string("auth_shib_convert_data_warning", "auth_shibboleth"), 'notifyproblem'); 276 return; 277 } 278 if (isset($this->config->organization_selection) && empty($this->config->organization_selection) && 279 isset($this->config->alt_login) && $this->config->alt_login == 'on') { 280 281 echo $OUTPUT->notification(get_string("auth_shib_no_organizations_warning", "auth_shibboleth"), 'notifyproblem'); 282 return; 283 } 284 } 285 286 /** 287 * Return a list of identity providers to display on the login page. 288 * 289 * @param string $wantsurl The requested URL. 290 * @return array List of arrays with keys url, iconurl and name. 291 */ 292 public function loginpage_idp_list($wantsurl) { 293 $config = get_config('auth_shibboleth'); 294 $result = []; 295 296 // Before displaying the button check that Shibboleth is set-up correctly. 297 if (empty($config->user_attribute)) { 298 return $result; 299 } 300 301 $url = new moodle_url('/auth/shibboleth/index.php'); 302 303 if ($config->auth_logo) { 304 $iconurl = moodle_url::make_pluginfile_url( 305 context_system::instance()->id, 306 'auth_shibboleth', 307 'logo', 308 null, 309 null, 310 $config->auth_logo); 311 } else { 312 $iconurl = null; 313 } 314 315 $result[] = ['url' => $url, 'iconurl' => $iconurl, 'name' => $config->login_name]; 316 return $result; 317 } 318 } 319 320 321 /** 322 * Sets the standard SAML domain cookie that is also used to preselect 323 * the right entry on the local way 324 * 325 * @param string $selectedIDP IDP identifier 326 */ 327 function set_saml_cookie($selectedIDP) { 328 if (isset($_COOKIE['_saml_idp'])) 329 { 330 $IDPArray = generate_cookie_array($_COOKIE['_saml_idp']); 331 } 332 else 333 { 334 $IDPArray = array(); 335 } 336 $IDPArray = appendCookieValue($selectedIDP, $IDPArray); 337 setcookie ('_saml_idp', generate_cookie_value($IDPArray), time() + (100*24*3600)); 338 } 339 340 /** 341 * Generate array of IdPs from Moodle Shibboleth settings 342 * 343 * @param string Text containing tuble/triple of IdP entityId, name and (optionally) session initiator 344 * @return array Identifier of IdPs and their name/session initiator 345 */ 346 function get_idp_list($organization_selection) { 347 $idp_list = array(); 348 349 $idp_raw_list = explode("\n", $organization_selection); 350 351 foreach ($idp_raw_list as $idp_line){ 352 $idp_data = explode(',', $idp_line); 353 if (isset($idp_data[2])) 354 { 355 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1]),trim($idp_data[2])); 356 } 357 elseif(isset($idp_data[1])) 358 { 359 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1])); 360 } 361 } 362 363 return $idp_list; 364 } 365 366 /** 367 * Generates an array of IDPs using the cookie value 368 * 369 * @param string Value of SAML domain cookie 370 * @return array Identifiers of IdPs 371 */ 372 function generate_cookie_array($value) { 373 374 // Decodes and splits cookie value 375 $CookieArray = explode(' ', $value); 376 $CookieArray = array_map('base64_decode', $CookieArray); 377 378 return $CookieArray; 379 } 380 381 /** 382 * Generate the value that is stored in the cookie using the list of IDPs 383 * 384 * @param array IdP identifiers 385 * @return string SAML domain cookie value 386 */ 387 function generate_cookie_value($CookieArray) { 388 389 // Merges cookie content and encodes it 390 $CookieArray = array_map('base64_encode', $CookieArray); 391 $value = implode(' ', $CookieArray); 392 return $value; 393 } 394 395 /** 396 * Append a value to the array of IDPs 397 * 398 * @param string IdP identifier 399 * @param array IdP identifiers 400 * @return array IdP identifiers with appended IdP 401 */ 402 function appendCookieValue($value, $CookieArray) { 403 404 array_push($CookieArray, $value); 405 $CookieArray = array_reverse($CookieArray); 406 $CookieArray = array_unique($CookieArray); 407 $CookieArray = array_reverse($CookieArray); 408 409 return $CookieArray; 410 } 411 412 413
title
Description
Body
title
Description
Body
title
Description
Body
title
Body