Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
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 print_error( 'shib_not_all_attributes_error', 'auth_shibboleth' , '', "'".$this->config->user_attribute."' ('".$_SERVER[$this->config->user_attribute]."'), '".$this->config->field_map_firstname."' ('".$_SERVER[$this->config->field_map_firstname]."'), '".$this->config->field_map_lastname."' ('".$_SERVER[$this->config->field_map_lastname]."') and '".$this->config->field_map_email."' ('".$_SERVER[$this->config->field_map_email]."')"); 109 } 110 111 $attrmap = $this->get_attributes(); 112 113 $result = array(); 114 $search_attribs = array(); 115 116 foreach ($attrmap as $key=>$value) { 117 // Check if attribute is present 118 if (!isset($_SERVER[$value])){ 119 $result[$key] = ''; 120 continue; 121 } 122 123 // Make usename lowercase 124 if ($key == 'username'){ 125 $result[$key] = strtolower($this->get_first_string($_SERVER[$value])); 126 } else { 127 $result[$key] = $this->get_first_string($_SERVER[$value]); 128 } 129 } 130 131 // Provide an API to modify the information to fit the Moodle internal 132 // data representation 133 if ( 134 $this->config->convert_data 135 && $this->config->convert_data != '' 136 && is_readable($this->config->convert_data) 137 ) { 138 139 // Include a custom file outside the Moodle dir to 140 // modify the variable $moodleattributes 141 include($this->config->convert_data); 142 } 143 144 return $result; 145 } 146 147 /** 148 * Returns array containg attribute mappings between Moodle and Shibboleth. 149 * 150 * @return array 151 */ 152 function get_attributes() { 153 $configarray = (array) $this->config; 154 155 $moodleattributes = array(); 156 $userfields = array_merge($this->userfields, $this->get_custom_user_profile_fields()); 157 foreach ($userfields as $field) { 158 if (isset($configarray["field_map_$field"])) { 159 $moodleattributes[$field] = $configarray["field_map_$field"]; 160 } 161 } 162 $moodleattributes['username'] = $configarray["user_attribute"]; 163 164 return $moodleattributes; 165 } 166 167 function prevent_local_passwords() { 168 return true; 169 } 170 171 /** 172 * Returns true if this authentication plugin is 'internal'. 173 * 174 * @return bool 175 */ 176 function is_internal() { 177 return false; 178 } 179 180 /** 181 * Whether shibboleth users can change their password or not. 182 * 183 * Shibboleth auth requires password to be changed on shibboleth server directly. 184 * So it is required to have password change url set. 185 * 186 * @return bool true if there's a password url or false otherwise. 187 */ 188 function can_change_password() { 189 if (!empty($this->config->changepasswordurl)) { 190 return true; 191 } else { 192 return false; 193 } 194 } 195 196 /** 197 * Get password change url. 198 * 199 * @return moodle_url|null Returns URL to change password or null otherwise. 200 */ 201 function change_password_url() { 202 if (!empty($this->config->changepasswordurl)) { 203 return new moodle_url($this->config->changepasswordurl); 204 } else { 205 return null; 206 } 207 } 208 209 /** 210 * Hook for login page 211 * 212 */ 213 function loginpage_hook() { 214 global $SESSION, $CFG; 215 216 // Prevent username from being shown on login page after logout 217 $CFG->nolastloggedin = true; 218 219 return; 220 } 221 222 /** 223 * Hook for logout page 224 * 225 */ 226 function logoutpage_hook() { 227 global $SESSION, $redirect; 228 229 // Only do this if logout handler is defined, and if the user is actually logged in via Shibboleth 230 $logouthandlervalid = isset($this->config->logout_handler) && !empty($this->config->logout_handler); 231 if (isset($SESSION->shibboleth_session_id) && $logouthandlervalid ) { 232 // Check if there is an alternative logout return url defined 233 if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) { 234 // Set temp_redirect to alternative return url 235 $temp_redirect = $this->config->logout_return_url; 236 } else { 237 // Backup old redirect url 238 $temp_redirect = $redirect; 239 } 240 241 // Overwrite redirect in order to send user to Shibboleth logout page and let him return back 242 $redirecturl = new moodle_url($this->config->logout_handler, array('return' => $temp_redirect)); 243 $redirect = $redirecturl->out(); 244 } 245 } 246 247 /** 248 * Cleans and returns first of potential many values (multi-valued attributes) 249 * 250 * @param string $string Possibly multi-valued attribute from Shibboleth 251 */ 252 function get_first_string($string) { 253 $list = explode( ';', $string); 254 $clean_string = rtrim($list[0]); 255 256 return $clean_string; 257 } 258 259 /** 260 * Test if settings are correct, print info to output. 261 */ 262 public function test_settings() { 263 global $OUTPUT; 264 265 if (!isset($this->config->user_attribute) || empty($this->config->user_attribute)) { 266 echo $OUTPUT->notification(get_string("shib_not_set_up_error", "auth_shibboleth", 267 (new moodle_url('/auth/shibboleth/README.txt'))->out()), 'notifyproblem'); 268 return; 269 } 270 if ($this->config->convert_data and $this->config->convert_data != '' and !is_readable($this->config->convert_data)) { 271 echo $OUTPUT->notification(get_string("auth_shib_convert_data_warning", "auth_shibboleth"), 'notifyproblem'); 272 return; 273 } 274 if (isset($this->config->organization_selection) && empty($this->config->organization_selection) && 275 isset($this->config->alt_login) && $this->config->alt_login == 'on') { 276 277 echo $OUTPUT->notification(get_string("auth_shib_no_organizations_warning", "auth_shibboleth"), 'notifyproblem'); 278 return; 279 } 280 } 281 282 /** 283 * Return a list of identity providers to display on the login page. 284 * 285 * @param string $wantsurl The requested URL. 286 * @return array List of arrays with keys url, iconurl and name. 287 */ 288 public function loginpage_idp_list($wantsurl) { 289 $config = get_config('auth_shibboleth'); 290 $result = []; 291 292 // Before displaying the button check that Shibboleth is set-up correctly. 293 if (empty($config->user_attribute)) { 294 return $result; 295 } 296 297 $url = new moodle_url('/auth/shibboleth/index.php'); 298 299 if ($config->auth_logo) { 300 $iconurl = moodle_url::make_pluginfile_url( 301 context_system::instance()->id, 302 'auth_shibboleth', 303 'logo', 304 null, 305 null, 306 $config->auth_logo); 307 } else { 308 $iconurl = null; 309 } 310 311 $result[] = ['url' => $url, 'iconurl' => $iconurl, 'name' => $config->login_name]; 312 return $result; 313 } 314 } 315 316 317 /** 318 * Sets the standard SAML domain cookie that is also used to preselect 319 * the right entry on the local way 320 * 321 * @param string $selectedIDP IDP identifier 322 */ 323 function set_saml_cookie($selectedIDP) { 324 if (isset($_COOKIE['_saml_idp'])) 325 { 326 $IDPArray = generate_cookie_array($_COOKIE['_saml_idp']); 327 } 328 else 329 { 330 $IDPArray = array(); 331 } 332 $IDPArray = appendCookieValue($selectedIDP, $IDPArray); 333 setcookie ('_saml_idp', generate_cookie_value($IDPArray), time() + (100*24*3600)); 334 } 335 336 /** 337 * Generate array of IdPs from Moodle Shibboleth settings 338 * 339 * @param string Text containing tuble/triple of IdP entityId, name and (optionally) session initiator 340 * @return array Identifier of IdPs and their name/session initiator 341 */ 342 function get_idp_list($organization_selection) { 343 $idp_list = array(); 344 345 $idp_raw_list = explode("\n", $organization_selection); 346 347 foreach ($idp_raw_list as $idp_line){ 348 $idp_data = explode(',', $idp_line); 349 if (isset($idp_data[2])) 350 { 351 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1]),trim($idp_data[2])); 352 } 353 elseif(isset($idp_data[1])) 354 { 355 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1])); 356 } 357 } 358 359 return $idp_list; 360 } 361 362 /** 363 * Generates an array of IDPs using the cookie value 364 * 365 * @param string Value of SAML domain cookie 366 * @return array Identifiers of IdPs 367 */ 368 function generate_cookie_array($value) { 369 370 // Decodes and splits cookie value 371 $CookieArray = explode(' ', $value); 372 $CookieArray = array_map('base64_decode', $CookieArray); 373 374 return $CookieArray; 375 } 376 377 /** 378 * Generate the value that is stored in the cookie using the list of IDPs 379 * 380 * @param array IdP identifiers 381 * @return string SAML domain cookie value 382 */ 383 function generate_cookie_value($CookieArray) { 384 385 // Merges cookie content and encodes it 386 $CookieArray = array_map('base64_encode', $CookieArray); 387 $value = implode(' ', $CookieArray); 388 return $value; 389 } 390 391 /** 392 * Append a value to the array of IDPs 393 * 394 * @param string IdP identifier 395 * @param array IdP identifiers 396 * @return array IdP identifiers with appended IdP 397 */ 398 function appendCookieValue($value, $CookieArray) { 399 400 array_push($CookieArray, $value); 401 $CookieArray = array_reverse($CookieArray); 402 $CookieArray = array_unique($CookieArray); 403 $CookieArray = array_reverse($CookieArray); 404 405 return $CookieArray; 406 } 407 408 409
title
Description
Body
title
Description
Body
title
Description
Body
title
Body