[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * Returns true if this authentication plugin can change the user's 182 * password. 183 * 184 * @return bool 185 */ 186 function can_change_password() { 187 return false; 188 } 189 190 /** 191 * Hook for login page 192 * 193 */ 194 function loginpage_hook() { 195 global $SESSION, $CFG; 196 197 // Prevent username from being shown on login page after logout 198 $CFG->nolastloggedin = true; 199 200 return; 201 } 202 203 /** 204 * Hook for logout page 205 * 206 */ 207 function logoutpage_hook() { 208 global $SESSION, $redirect; 209 210 // Only do this if logout handler is defined, and if the user is actually logged in via Shibboleth 211 $logouthandlervalid = isset($this->config->logout_handler) && !empty($this->config->logout_handler); 212 if (isset($SESSION->shibboleth_session_id) && $logouthandlervalid ) { 213 // Check if there is an alternative logout return url defined 214 if (isset($this->config->logout_return_url) && !empty($this->config->logout_return_url)) { 215 // Set temp_redirect to alternative return url 216 $temp_redirect = $this->config->logout_return_url; 217 } else { 218 // Backup old redirect url 219 $temp_redirect = $redirect; 220 } 221 222 // Overwrite redirect in order to send user to Shibboleth logout page and let him return back 223 $redirecturl = new moodle_url($this->config->logout_handler, array('return' => $temp_redirect)); 224 $redirect = $redirecturl->out(); 225 } 226 } 227 228 229 230 /** 231 * Prints a form for configuring this authentication plugin. 232 * 233 * This function is called from admin/auth.php, and outputs a full page with 234 * a form for configuring this plugin. 235 * 236 * @param array $page An object containing all the data for this page. 237 */ 238 function config_form($config, $err, $user_fields) { 239 include "config.html"; 240 } 241 242 /** 243 * Processes and stores configuration data for this authentication plugin. 244 * 245 * 246 * @param object $config Configuration object 247 */ 248 function process_config($config) { 249 global $CFG; 250 251 // set to defaults if undefined 252 if (!isset($config->auth_instructions) or empty($config->user_attribute)) { 253 $config->auth_instructions = get_string('auth_shib_instructions', 'auth_shibboleth', $CFG->wwwroot.'/auth/shibboleth/index.php'); 254 } 255 if (!isset ($config->user_attribute)) { 256 $config->user_attribute = ''; 257 } 258 if (!isset ($config->convert_data)) { 259 $config->convert_data = ''; 260 } 261 262 if (!isset($config->changepasswordurl)) { 263 $config->changepasswordurl = ''; 264 } 265 266 if (!isset($config->login_name)) { 267 $config->login_name = 'Shibboleth Login'; 268 } 269 270 // Clean idp list 271 if (isset($config->organization_selection) && !empty($config->organization_selection) && isset($config->alt_login) && $config->alt_login == 'on') { 272 $idp_list = get_idp_list($config->organization_selection); 273 if (count($idp_list) < 1){ 274 return false; 275 } 276 $config->organization_selection = ''; 277 foreach ($idp_list as $idp => $value){ 278 $config->organization_selection .= $idp.', '.$value[0].', '.$value[1]."\n"; 279 } 280 } 281 282 283 // save settings 284 set_config('user_attribute', $config->user_attribute, 'auth/shibboleth'); 285 286 if (isset($config->organization_selection) && !empty($config->organization_selection)) { 287 set_config('organization_selection', $config->organization_selection, 'auth/shibboleth'); 288 } 289 set_config('logout_handler', $config->logout_handler, 'auth/shibboleth'); 290 set_config('logout_return_url', $config->logout_return_url, 'auth/shibboleth'); 291 set_config('login_name', $config->login_name, 'auth/shibboleth'); 292 set_config('convert_data', $config->convert_data, 'auth/shibboleth'); 293 set_config('auth_instructions', $config->auth_instructions, 'auth/shibboleth'); 294 set_config('changepasswordurl', $config->changepasswordurl, 'auth/shibboleth'); 295 296 // Overwrite alternative login URL if integrated WAYF is used 297 if (isset($config->alt_login) && $config->alt_login == 'on'){ 298 set_config('alt_login', $config->alt_login, 'auth/shibboleth'); 299 set_config('alternateloginurl', $CFG->wwwroot.'/auth/shibboleth/login.php'); 300 } else { 301 // Check if integrated WAYF was enabled and is now turned off 302 // If it was and only then, reset the Moodle alternate URL 303 if (isset($this->config->alt_login) and $this->config->alt_login == 'on'){ 304 set_config('alt_login', 'off', 'auth/shibboleth'); 305 set_config('alternateloginurl', ''); 306 } 307 $config->alt_login = 'off'; 308 } 309 310 // Check values and return false if something is wrong 311 // Patch Anyware Technologies (14/05/07) 312 if (($config->convert_data != '')&&(!file_exists($config->convert_data) || !is_readable($config->convert_data))){ 313 return false; 314 } 315 316 // Check if there is at least one entry in the IdP list 317 if (isset($config->organization_selection) && empty($config->organization_selection) && isset($config->alt_login) && $config->alt_login == 'on'){ 318 return false; 319 } 320 321 return true; 322 } 323 324 /** 325 * Cleans and returns first of potential many values (multi-valued attributes) 326 * 327 * @param string $string Possibly multi-valued attribute from Shibboleth 328 */ 329 function get_first_string($string) { 330 $list = explode( ';', $string); 331 $clean_string = rtrim($list[0]); 332 333 return $clean_string; 334 } 335 } 336 337 338 /** 339 * Sets the standard SAML domain cookie that is also used to preselect 340 * the right entry on the local wayf 341 * 342 * @param IdP identifiere 343 */ 344 function set_saml_cookie($selectedIDP) { 345 if (isset($_COOKIE['_saml_idp'])) 346 { 347 $IDPArray = generate_cookie_array($_COOKIE['_saml_idp']); 348 } 349 else 350 { 351 $IDPArray = array(); 352 } 353 $IDPArray = appendCookieValue($selectedIDP, $IDPArray); 354 setcookie ('_saml_idp', generate_cookie_value($IDPArray), time() + (100*24*3600)); 355 } 356 357 /** 358 * Prints the option elements for the select element of the drop down list 359 * 360 */ 361 function print_idp_list(){ 362 $config = get_config('auth/shibboleth'); 363 364 $IdPs = get_idp_list($config->organization_selection); 365 if (isset($_COOKIE['_saml_idp'])){ 366 $idp_cookie = generate_cookie_array($_COOKIE['_saml_idp']); 367 do { 368 $selectedIdP = array_pop($idp_cookie); 369 } while (!isset($IdPs[$selectedIdP]) && count($idp_cookie) > 0); 370 371 } else { 372 $selectedIdP = '-'; 373 } 374 375 foreach($IdPs as $IdP => $data){ 376 if ($IdP == $selectedIdP){ 377 echo '<option value="'.$IdP.'" selected="selected">'.$data[0].'</option>'; 378 } else { 379 echo '<option value="'.$IdP.'">'.$data[0].'</option>'; 380 } 381 } 382 } 383 384 385 /** 386 * Generate array of IdPs from Moodle Shibboleth settings 387 * 388 * @param string Text containing tuble/triple of IdP entityId, name and (optionally) session initiator 389 * @return array Identifier of IdPs and their name/session initiator 390 */ 391 392 function get_idp_list($organization_selection) { 393 $idp_list = array(); 394 395 $idp_raw_list = explode("\n", $organization_selection); 396 397 foreach ($idp_raw_list as $idp_line){ 398 $idp_data = explode(',', $idp_line); 399 if (isset($idp_data[2])) 400 { 401 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1]),trim($idp_data[2])); 402 } 403 elseif(isset($idp_data[1])) 404 { 405 $idp_list[trim($idp_data[0])] = array(trim($idp_data[1])); 406 } 407 } 408 409 return $idp_list; 410 } 411 412 /** 413 * Generates an array of IDPs using the cookie value 414 * 415 * @param string Value of SAML domain cookie 416 * @return array Identifiers of IdPs 417 */ 418 function generate_cookie_array($value) { 419 420 // Decodes and splits cookie value 421 $CookieArray = explode(' ', $value); 422 $CookieArray = array_map('base64_decode', $CookieArray); 423 424 return $CookieArray; 425 } 426 427 /** 428 * Generate the value that is stored in the cookie using the list of IDPs 429 * 430 * @param array IdP identifiers 431 * @return string SAML domain cookie value 432 */ 433 function generate_cookie_value($CookieArray) { 434 435 // Merges cookie content and encodes it 436 $CookieArray = array_map('base64_encode', $CookieArray); 437 $value = implode(' ', $CookieArray); 438 return $value; 439 } 440 441 /** 442 * Append a value to the array of IDPs 443 * 444 * @param string IdP identifier 445 * @param array IdP identifiers 446 * @return array IdP identifiers with appended IdP 447 */ 448 function appendCookieValue($value, $CookieArray) { 449 450 array_push($CookieArray, $value); 451 $CookieArray = array_reverse($CookieArray); 452 $CookieArray = array_unique($CookieArray); 453 $CookieArray = array_reverse($CookieArray); 454 455 return $CookieArray; 456 } 457 458 459
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |