[ 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: External Database Authentication 19 * 20 * Checks against an external database. 21 * 22 * @package auth_db 23 * @author Martin Dougiamas 24 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 require_once($CFG->libdir.'/authlib.php'); 30 31 /** 32 * External database authentication plugin. 33 */ 34 class auth_plugin_db extends auth_plugin_base { 35 36 /** 37 * Constructor. 38 */ 39 function __construct() { 40 global $CFG; 41 require_once($CFG->libdir.'/adodb/adodb.inc.php'); 42 43 $this->authtype = 'db'; 44 $this->config = get_config('auth/db'); 45 if (empty($this->config->extencoding)) { 46 $this->config->extencoding = 'utf-8'; 47 } 48 } 49 50 /** 51 * Returns true if the username and password work and false if they are 52 * wrong or don't exist. 53 * 54 * @param string $username The username 55 * @param string $password The password 56 * @return bool Authentication success or failure. 57 */ 58 function user_login($username, $password) { 59 global $CFG, $DB; 60 61 if ($this->is_configured() === false) { 62 debugging(get_string('auth_notconfigured', 'auth', $this->authtype)); 63 return false; 64 } 65 66 $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); 67 $extpassword = core_text::convert($password, 'utf-8', $this->config->extencoding); 68 69 if ($this->is_internal()) { 70 // Lookup username externally, but resolve 71 // password locally -- to support backend that 72 // don't track passwords. 73 74 if (isset($this->config->removeuser) and $this->config->removeuser == AUTH_REMOVEUSER_KEEP) { 75 // No need to connect to external database in this case because users are never removed and we verify password locally. 76 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) { 77 return validate_internal_user_password($user, $password); 78 } else { 79 return false; 80 } 81 } 82 83 $authdb = $this->db_init(); 84 85 $rs = $authdb->Execute("SELECT * 86 FROM {$this->config->table} 87 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"); 88 if (!$rs) { 89 $authdb->Close(); 90 debugging(get_string('auth_dbcantconnect','auth_db')); 91 return false; 92 } 93 94 if (!$rs->EOF) { 95 $rs->Close(); 96 $authdb->Close(); 97 // User exists externally - check username/password internally. 98 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) { 99 return validate_internal_user_password($user, $password); 100 } 101 } else { 102 $rs->Close(); 103 $authdb->Close(); 104 // User does not exist externally. 105 return false; 106 } 107 108 } else { 109 // Normal case: use external db for both usernames and passwords. 110 111 $authdb = $this->db_init(); 112 113 $rs = $authdb->Execute("SELECT {$this->config->fieldpass} 114 FROM {$this->config->table} 115 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"); 116 if (!$rs) { 117 $authdb->Close(); 118 debugging(get_string('auth_dbcantconnect','auth_db')); 119 return false; 120 } 121 122 if ($rs->EOF) { 123 $authdb->Close(); 124 return false; 125 } 126 127 $fields = array_change_key_case($rs->fields, CASE_LOWER); 128 $fromdb = $fields[strtolower($this->config->fieldpass)]; 129 $rs->Close(); 130 $authdb->Close(); 131 132 if ($this->config->passtype === 'plaintext') { 133 return ($fromdb == $extpassword); 134 } else if ($this->config->passtype === 'md5') { 135 return (strtolower($fromdb) == md5($extpassword)); 136 } else if ($this->config->passtype === 'sha1') { 137 return (strtolower($fromdb) == sha1($extpassword)); 138 } else if ($this->config->passtype === 'saltedcrypt') { 139 return password_verify($extpassword, $fromdb); 140 } else { 141 return false; 142 } 143 144 } 145 } 146 147 /** 148 * Connect to external database. 149 * 150 * @return ADOConnection 151 * @throws moodle_exception 152 */ 153 function db_init() { 154 if ($this->is_configured() === false) { 155 throw new moodle_exception('auth_dbcantconnect', 'auth_db'); 156 } 157 158 // Connect to the external database (forcing new connection). 159 $authdb = ADONewConnection($this->config->type); 160 if (!empty($this->config->debugauthdb)) { 161 $authdb->debug = true; 162 ob_start(); //Start output buffer to allow later use of the page headers. 163 } 164 $authdb->Connect($this->config->host, $this->config->user, $this->config->pass, $this->config->name, true); 165 $authdb->SetFetchMode(ADODB_FETCH_ASSOC); 166 if (!empty($this->config->setupsql)) { 167 $authdb->Execute($this->config->setupsql); 168 } 169 170 return $authdb; 171 } 172 173 /** 174 * Returns user attribute mappings between moodle and ldap. 175 * 176 * @return array 177 */ 178 function db_attributes() { 179 $moodleattributes = array(); 180 // If we have custom fields then merge them with user fields. 181 $customfields = $this->get_custom_user_profile_fields(); 182 if (!empty($customfields) && !empty($this->userfields)) { 183 $userfields = array_merge($this->userfields, $customfields); 184 } else { 185 $userfields = $this->userfields; 186 } 187 188 foreach ($userfields as $field) { 189 if (!empty($this->config->{"field_map_$field"})) { 190 $moodleattributes[$field] = $this->config->{"field_map_$field"}; 191 } 192 } 193 $moodleattributes['username'] = $this->config->fielduser; 194 return $moodleattributes; 195 } 196 197 /** 198 * Reads any other information for a user from external database, 199 * then returns it in an array. 200 * 201 * @param string $username 202 * @return array 203 */ 204 function get_userinfo($username) { 205 global $CFG; 206 207 $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); 208 209 $authdb = $this->db_init(); 210 211 // Array to map local fieldnames we want, to external fieldnames. 212 $selectfields = $this->db_attributes(); 213 214 $result = array(); 215 // If at least one field is mapped from external db, get that mapped data. 216 if ($selectfields) { 217 $select = array(); 218 foreach ($selectfields as $localname=>$externalname) { 219 $select[] = "$externalname"; 220 } 221 $select = implode(', ', $select); 222 $sql = "SELECT $select 223 FROM {$this->config->table} 224 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"; 225 226 if ($rs = $authdb->Execute($sql)) { 227 if (!$rs->EOF) { 228 $fields = $rs->FetchRow(); 229 // Convert the associative array to an array of its values so we don't have to worry about the case of its keys. 230 $fields = array_values($fields); 231 foreach (array_keys($selectfields) as $index => $localname) { 232 $value = $fields[$index]; 233 $result[$localname] = core_text::convert($value, $this->config->extencoding, 'utf-8'); 234 } 235 } 236 $rs->Close(); 237 } 238 } 239 $authdb->Close(); 240 return $result; 241 } 242 243 /** 244 * Change a user's password. 245 * 246 * @param stdClass $user User table object 247 * @param string $newpassword Plaintext password 248 * @return bool True on success 249 */ 250 function user_update_password($user, $newpassword) { 251 global $DB; 252 253 if ($this->is_internal()) { 254 $puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST); 255 // This will also update the stored hash to the latest algorithm 256 // if the existing hash is using an out-of-date algorithm (or the 257 // legacy md5 algorithm). 258 if (update_internal_user_password($puser, $newpassword)) { 259 $user->password = $puser->password; 260 return true; 261 } else { 262 return false; 263 } 264 } else { 265 // We should have never been called! 266 return false; 267 } 268 } 269 270 /** 271 * Synchronizes user from external db to moodle user table. 272 * 273 * Sync should be done by using idnumber attribute, not username. 274 * You need to pass firstsync parameter to function to fill in 275 * idnumbers if they don't exists in moodle user table. 276 * 277 * Syncing users removes (disables) users that don't exists anymore in external db. 278 * Creates new users and updates coursecreator status of users. 279 * 280 * This implementation is simpler but less scalable than the one found in the LDAP module. 281 * 282 * @param progress_trace $trace 283 * @param bool $do_updates Optional: set to true to force an update of existing accounts 284 * @return int 0 means success, 1 means failure 285 */ 286 function sync_users(progress_trace $trace, $do_updates=false) { 287 global $CFG, $DB; 288 289 require_once($CFG->dirroot . '/user/lib.php'); 290 291 // List external users. 292 $userlist = $this->get_userlist(); 293 294 // Delete obsolete internal users. 295 if (!empty($this->config->removeuser)) { 296 297 $suspendselect = ""; 298 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { 299 $suspendselect = "AND u.suspended = 0"; 300 } 301 302 // Find obsolete users. 303 if (count($userlist)) { 304 $remove_users = array(); 305 // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php 306 $userlistchunks = array_chunk($userlist , 10000); 307 foreach($userlistchunks as $userlistchunk) { 308 list($notin_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', false); 309 $params['authtype'] = $this->authtype; 310 $sql = "SELECT u.id, u.username 311 FROM {user} u 312 WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql"; 313 $params['mnethostid'] = $CFG->mnet_localhost_id; 314 $remove_users = $remove_users + $DB->get_records_sql($sql, $params); 315 } 316 } else { 317 $sql = "SELECT u.id, u.username 318 FROM {user} u 319 WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect"; 320 $params = array(); 321 $params['authtype'] = $this->authtype; 322 $params['mnethostid'] = $CFG->mnet_localhost_id; 323 $remove_users = $DB->get_records_sql($sql, $params); 324 } 325 326 if (!empty($remove_users)) { 327 $trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users))); 328 329 foreach ($remove_users as $user) { 330 if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) { 331 delete_user($user); 332 $trace->output(get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); 333 } else if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { 334 $updateuser = new stdClass(); 335 $updateuser->id = $user->id; 336 $updateuser->suspended = 1; 337 user_update_user($updateuser, false); 338 $trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); 339 } 340 } 341 } 342 unset($remove_users); 343 } 344 345 if (!count($userlist)) { 346 // Exit right here, nothing else to do. 347 $trace->finished(); 348 return 0; 349 } 350 351 // Update existing accounts. 352 if ($do_updates) { 353 // Narrow down what fields we need to update. 354 $all_keys = array_keys(get_object_vars($this->config)); 355 $updatekeys = array(); 356 foreach ($all_keys as $key) { 357 if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) { 358 if ($this->config->{$key} === 'onlogin') { 359 array_push($updatekeys, $match[1]); // The actual key name. 360 } 361 } 362 } 363 unset($all_keys); unset($key); 364 365 // Only go ahead if we actually have fields to update locally. 366 if (!empty($updatekeys)) { 367 $update_users = array(); 368 // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php 369 $userlistchunks = array_chunk($userlist , 10000); 370 foreach($userlistchunks as $userlistchunk) { 371 list($in_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', true); 372 $params['authtype'] = $this->authtype; 373 $params['mnethostid'] = $CFG->mnet_localhost_id; 374 $sql = "SELECT u.id, u.username 375 FROM {user} u 376 WHERE u.auth = :authtype AND u.deleted = 0 AND u.mnethostid = :mnethostid AND u.username {$in_sql}"; 377 $update_users = $update_users + $DB->get_records_sql($sql, $params); 378 } 379 380 if ($update_users) { 381 $trace->output("User entries to update: ".count($update_users)); 382 383 foreach ($update_users as $user) { 384 if ($this->update_user_record($user->username, $updatekeys)) { 385 $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); 386 } else { 387 $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'), 1); 388 } 389 } 390 unset($update_users); 391 } 392 } 393 } 394 395 396 // Create missing accounts. 397 // NOTE: this is very memory intensive and generally inefficient. 398 $suspendselect = ""; 399 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { 400 $suspendselect = "AND u.suspended = 0"; 401 } 402 $sql = "SELECT u.id, u.username 403 FROM {user} u 404 WHERE u.auth=:authtype AND u.deleted='0' AND mnethostid=:mnethostid $suspendselect"; 405 406 $users = $DB->get_records_sql($sql, array('authtype'=>$this->authtype, 'mnethostid'=>$CFG->mnet_localhost_id)); 407 408 // Simplify down to usernames. 409 $usernames = array(); 410 if (!empty($users)) { 411 foreach ($users as $user) { 412 array_push($usernames, $user->username); 413 } 414 unset($users); 415 } 416 417 $add_users = array_diff($userlist, $usernames); 418 unset($usernames); 419 420 if (!empty($add_users)) { 421 $trace->output(get_string('auth_dbuserstoadd','auth_db',count($add_users))); 422 // Do not use transactions around this foreach, we want to skip problematic users, not revert everything. 423 foreach($add_users as $user) { 424 $username = $user; 425 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { 426 if ($olduser = $DB->get_record('user', array('username' => $username, 'deleted' => 0, 'suspended' => 1, 427 'mnethostid' => $CFG->mnet_localhost_id, 'auth' => $this->authtype))) { 428 $updateuser = new stdClass(); 429 $updateuser->id = $olduser->id; 430 $updateuser->suspended = 0; 431 user_update_user($updateuser); 432 $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name' => $username, 433 'id' => $olduser->id)), 1); 434 continue; 435 } 436 } 437 438 // Do not try to undelete users here, instead select suspending if you ever expect users will reappear. 439 440 // Prep a few params. 441 $user = $this->get_userinfo_asobj($user); 442 $user->username = $username; 443 $user->confirmed = 1; 444 $user->auth = $this->authtype; 445 $user->mnethostid = $CFG->mnet_localhost_id; 446 if (empty($user->lang)) { 447 $user->lang = $CFG->lang; 448 } 449 if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) { 450 $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1); 451 continue; 452 } 453 try { 454 $id = user_create_user($user, false); // It is truly a new user. 455 $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1); 456 } catch (moodle_exception $e) { 457 $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1); 458 continue; 459 } 460 // If relevant, tag for password generation. 461 if ($this->is_internal()) { 462 set_user_preference('auth_forcepasswordchange', 1, $id); 463 set_user_preference('create_password', 1, $id); 464 } 465 // Make sure user context is present. 466 context_user::instance($id); 467 } 468 unset($add_users); 469 } 470 $trace->finished(); 471 return 0; 472 } 473 474 function user_exists($username) { 475 476 // Init result value. 477 $result = false; 478 479 $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); 480 481 $authdb = $this->db_init(); 482 483 $rs = $authdb->Execute("SELECT * 484 FROM {$this->config->table} 485 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' "); 486 487 if (!$rs) { 488 print_error('auth_dbcantconnect','auth_db'); 489 } else if (!$rs->EOF) { 490 // User exists externally. 491 $result = true; 492 } 493 494 $authdb->Close(); 495 return $result; 496 } 497 498 499 function get_userlist() { 500 501 // Init result value. 502 $result = array(); 503 504 $authdb = $this->db_init(); 505 506 // Fetch userlist. 507 $rs = $authdb->Execute("SELECT {$this->config->fielduser} 508 FROM {$this->config->table} "); 509 510 if (!$rs) { 511 print_error('auth_dbcantconnect','auth_db'); 512 } else if (!$rs->EOF) { 513 while ($rec = $rs->FetchRow()) { 514 $rec = array_change_key_case((array)$rec, CASE_LOWER); 515 array_push($result, $rec[strtolower($this->config->fielduser)]); 516 } 517 } 518 519 $authdb->Close(); 520 return $result; 521 } 522 523 /** 524 * Reads user information from DB and return it in an object. 525 * 526 * @param string $username username 527 * @return array 528 */ 529 function get_userinfo_asobj($username) { 530 $user_array = truncate_userinfo($this->get_userinfo($username)); 531 $user = new stdClass(); 532 foreach($user_array as $key=>$value) { 533 $user->{$key} = $value; 534 } 535 return $user; 536 } 537 538 /** 539 * will update a local user record from an external source. 540 * is a lighter version of the one in moodlelib -- won't do 541 * expensive ops such as enrolment. 542 * 543 * If you don't pass $updatekeys, there is a performance hit and 544 * values removed from DB won't be removed from moodle. 545 * 546 * @param string $username username 547 * @param bool $updatekeys 548 * @return stdClass 549 */ 550 function update_user_record($username, $updatekeys=false) { 551 global $CFG, $DB; 552 553 //just in case check text case 554 $username = trim(core_text::strtolower($username)); 555 556 // get the current user record 557 $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id)); 558 if (empty($user)) { // trouble 559 error_log("Cannot update non-existent user: $username"); 560 print_error('auth_dbusernotexist','auth_db',$username); 561 die; 562 } 563 564 // Ensure userid is not overwritten. 565 $userid = $user->id; 566 $needsupdate = false; 567 568 $updateuser = new stdClass(); 569 $updateuser->id = $userid; 570 if ($newinfo = $this->get_userinfo($username)) { 571 $newinfo = truncate_userinfo($newinfo); 572 573 if (empty($updatekeys)) { // All keys? This does not support removing values. 574 $updatekeys = array_keys($newinfo); 575 } 576 577 foreach ($updatekeys as $key) { 578 if (isset($newinfo[$key])) { 579 $value = $newinfo[$key]; 580 } else { 581 $value = ''; 582 } 583 584 if (!empty($this->config->{'field_updatelocal_' . $key})) { 585 if (isset($user->{$key}) and $user->{$key} != $value) { // Only update if it's changed. 586 $needsupdate = true; 587 $updateuser->$key = $value; 588 } 589 } 590 } 591 } 592 if ($needsupdate) { 593 require_once($CFG->dirroot . '/user/lib.php'); 594 user_update_user($updateuser); 595 } 596 return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0)); 597 } 598 599 /** 600 * Called when the user record is updated. 601 * Modifies user in external database. It takes olduser (before changes) and newuser (after changes) 602 * compares information saved modified information to external db. 603 * 604 * @param stdClass $olduser Userobject before modifications 605 * @param stdClass $newuser Userobject new modified userobject 606 * @return boolean result 607 * 608 */ 609 function user_update($olduser, $newuser) { 610 if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) { 611 error_log("ERROR:User renaming not allowed in ext db"); 612 return false; 613 } 614 615 if (isset($olduser->auth) and $olduser->auth != $this->authtype) { 616 return true; // Just change auth and skip update. 617 } 618 619 $curruser = $this->get_userinfo($olduser->username); 620 if (empty($curruser)) { 621 error_log("ERROR:User $olduser->username found in ext db"); 622 return false; 623 } 624 625 $extusername = core_text::convert($olduser->username, 'utf-8', $this->config->extencoding); 626 627 $authdb = $this->db_init(); 628 629 $update = array(); 630 foreach($curruser as $key=>$value) { 631 if ($key == 'username') { 632 continue; // Skip this. 633 } 634 if (empty($this->config->{"field_updateremote_$key"})) { 635 continue; // Remote update not requested. 636 } 637 if (!isset($newuser->$key)) { 638 continue; 639 } 640 $nuvalue = $newuser->$key; 641 // Support for textarea fields. 642 if (isset($nuvalue['text'])) { 643 $nuvalue = $nuvalue['text']; 644 } 645 if ($nuvalue != $value) { 646 $update[] = $this->config->{"field_map_$key"}."='".$this->ext_addslashes(core_text::convert($nuvalue, 'utf-8', $this->config->extencoding))."'"; 647 } 648 } 649 if (!empty($update)) { 650 $authdb->Execute("UPDATE {$this->config->table} 651 SET ".implode(',', $update)." 652 WHERE {$this->config->fielduser}='".$this->ext_addslashes($extusername)."'"); 653 } 654 $authdb->Close(); 655 return true; 656 } 657 658 /** 659 * A chance to validate form data, and last chance to 660 * do stuff before it is inserted in config_plugin 661 * 662 * @param stfdClass $form 663 * @param array $err errors 664 * @return void 665 */ 666 function validate_form($form, &$err) { 667 if ($form->passtype === 'internal') { 668 $this->config->changepasswordurl = ''; 669 set_config('changepasswordurl', '', 'auth/db'); 670 } 671 } 672 673 function prevent_local_passwords() { 674 return !$this->is_internal(); 675 } 676 677 /** 678 * Returns true if this authentication plugin is "internal". 679 * 680 * Internal plugins use password hashes from Moodle user table for authentication. 681 * 682 * @return bool 683 */ 684 function is_internal() { 685 if (!isset($this->config->passtype)) { 686 return true; 687 } 688 return ($this->config->passtype === 'internal'); 689 } 690 691 /** 692 * Returns false if this plugin is enabled but not configured. 693 * 694 * @return bool 695 */ 696 public function is_configured() { 697 if (!empty($this->config->type)) { 698 return true; 699 } 700 return false; 701 } 702 703 /** 704 * Indicates if moodle should automatically update internal user 705 * records with data from external sources using the information 706 * from auth_plugin_base::get_userinfo(). 707 * 708 * @return bool true means automatically copy data from ext to user table 709 */ 710 function is_synchronised_with_external() { 711 return true; 712 } 713 714 /** 715 * Returns true if this authentication plugin can change the user's 716 * password. 717 * 718 * @return bool 719 */ 720 function can_change_password() { 721 return ($this->is_internal() or !empty($this->config->changepasswordurl)); 722 } 723 724 /** 725 * Returns the URL for changing the user's pw, or empty if the default can 726 * be used. 727 * 728 * @return moodle_url 729 */ 730 function change_password_url() { 731 if ($this->is_internal() || empty($this->config->changepasswordurl)) { 732 // Standard form. 733 return null; 734 } else { 735 // Use admin defined custom url. 736 return new moodle_url($this->config->changepasswordurl); 737 } 738 } 739 740 /** 741 * Returns true if plugin allows resetting of internal password. 742 * 743 * @return bool 744 */ 745 function can_reset_password() { 746 return $this->is_internal(); 747 } 748 749 /** 750 * Prints a form for configuring this authentication plugin. 751 * 752 * This function is called from admin/auth.php, and outputs a full page with 753 * a form for configuring this plugin. 754 * 755 * @param stdClass $config 756 * @param array $err errors 757 * @param array $user_fields 758 * @return void 759 */ 760 function config_form($config, $err, $user_fields) { 761 include 'config.html'; 762 } 763 764 /** 765 * Processes and stores configuration data for this authentication plugin. 766 * 767 * @param srdClass $config 768 * @return bool always true or exception 769 */ 770 function process_config($config) { 771 // set to defaults if undefined 772 if (!isset($config->host)) { 773 $config->host = 'localhost'; 774 } 775 if (!isset($config->type)) { 776 $config->type = 'mysql'; 777 } 778 if (!isset($config->sybasequoting)) { 779 $config->sybasequoting = 0; 780 } 781 if (!isset($config->name)) { 782 $config->name = ''; 783 } 784 if (!isset($config->user)) { 785 $config->user = ''; 786 } 787 if (!isset($config->pass)) { 788 $config->pass = ''; 789 } 790 if (!isset($config->table)) { 791 $config->table = ''; 792 } 793 if (!isset($config->fielduser)) { 794 $config->fielduser = ''; 795 } 796 if (!isset($config->fieldpass)) { 797 $config->fieldpass = ''; 798 } 799 if (!isset($config->passtype)) { 800 $config->passtype = 'plaintext'; 801 } 802 if (!isset($config->extencoding)) { 803 $config->extencoding = 'utf-8'; 804 } 805 if (!isset($config->setupsql)) { 806 $config->setupsql = ''; 807 } 808 if (!isset($config->debugauthdb)) { 809 $config->debugauthdb = 0; 810 } 811 if (!isset($config->removeuser)) { 812 $config->removeuser = AUTH_REMOVEUSER_KEEP; 813 } 814 if (!isset($config->changepasswordurl)) { 815 $config->changepasswordurl = ''; 816 } 817 818 // Save settings. 819 set_config('host', $config->host, 'auth/db'); 820 set_config('type', $config->type, 'auth/db'); 821 set_config('sybasequoting', $config->sybasequoting, 'auth/db'); 822 set_config('name', $config->name, 'auth/db'); 823 set_config('user', $config->user, 'auth/db'); 824 set_config('pass', $config->pass, 'auth/db'); 825 set_config('table', $config->table, 'auth/db'); 826 set_config('fielduser', $config->fielduser, 'auth/db'); 827 set_config('fieldpass', $config->fieldpass, 'auth/db'); 828 set_config('passtype', $config->passtype, 'auth/db'); 829 set_config('extencoding', trim($config->extencoding), 'auth/db'); 830 set_config('setupsql', trim($config->setupsql),'auth/db'); 831 set_config('debugauthdb', $config->debugauthdb, 'auth/db'); 832 set_config('removeuser', $config->removeuser, 'auth/db'); 833 set_config('changepasswordurl', trim($config->changepasswordurl), 'auth/db'); 834 835 return true; 836 } 837 838 /** 839 * Add slashes, we can not use placeholders or system functions. 840 * 841 * @param string $text 842 * @return string 843 */ 844 function ext_addslashes($text) { 845 if (empty($this->config->sybasequoting)) { 846 $text = str_replace('\\', '\\\\', $text); 847 $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text); 848 } else { 849 $text = str_replace("'", "''", $text); 850 } 851 return $text; 852 } 853 854 /** 855 * Test if settings are ok, print info to output. 856 * @private 857 */ 858 public function test_settings() { 859 global $CFG, $OUTPUT; 860 861 // NOTE: this is not localised intentionally, admins are supposed to understand English at least a bit... 862 863 raise_memory_limit(MEMORY_HUGE); 864 865 if (empty($this->config->table)) { 866 echo $OUTPUT->notification('External table not specified.', 'notifyproblem'); 867 return; 868 } 869 870 if (empty($this->config->fielduser)) { 871 echo $OUTPUT->notification('External user field not specified.', 'notifyproblem'); 872 return; 873 } 874 875 $olddebug = $CFG->debug; 876 $olddisplay = ini_get('display_errors'); 877 ini_set('display_errors', '1'); 878 $CFG->debug = DEBUG_DEVELOPER; 879 $olddebugauthdb = $this->config->debugauthdb; 880 $this->config->debugauthdb = 1; 881 error_reporting($CFG->debug); 882 883 $adodb = $this->db_init(); 884 885 if (!$adodb or !$adodb->IsConnected()) { 886 $this->config->debugauthdb = $olddebugauthdb; 887 $CFG->debug = $olddebug; 888 ini_set('display_errors', $olddisplay); 889 error_reporting($CFG->debug); 890 ob_end_flush(); 891 892 echo $OUTPUT->notification('Cannot connect the database.', 'notifyproblem'); 893 return; 894 } 895 896 $rs = $adodb->Execute("SELECT * 897 FROM {$this->config->table} 898 WHERE {$this->config->fielduser} <> 'random_unlikely_username'"); // Any unlikely name is ok here. 899 900 if (!$rs) { 901 echo $OUTPUT->notification('Can not read external table.', 'notifyproblem'); 902 903 } else if ($rs->EOF) { 904 echo $OUTPUT->notification('External table is empty.', 'notifyproblem'); 905 $rs->close(); 906 907 } else { 908 $fields_obj = $rs->FetchObj(); 909 $columns = array_keys((array)$fields_obj); 910 911 echo $OUTPUT->notification('External table contains following columns:<br />'.implode(', ', $columns), 'notifysuccess'); 912 $rs->close(); 913 } 914 915 $adodb->Close(); 916 917 $this->config->debugauthdb = $olddebugauthdb; 918 $CFG->debug = $olddebug; 919 ini_set('display_errors', $olddisplay); 920 error_reporting($CFG->debug); 921 ob_end_flush(); 922 } 923 924 /** 925 * Clean the user data that comes from an external database. 926 * @deprecated since 3.1, please use core_user::clean_data() instead. 927 * @param array $user the user data to be validated against properties definition. 928 * @return stdClass $user the cleaned user data. 929 */ 930 public function clean_data($user) { 931 debugging('The method clean_data() has been deprecated, please use core_user::clean_data() instead.', 932 DEBUG_DEVELOPER); 933 return core_user::clean_data($user); 934 } 935 } 936 937
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 |