weekly on-sync release 2.5dev
[moodle.git] / auth / db / auth.php
CommitLineData
b9ddb2d5 1<?php
b9ddb2d5 2/**
b9ddb2d5 3 * Authentication Plugin: External Database Authentication
4 *
5 * Checks against an external database.
6 *
7415aed1
PS
7 * @package auth
8 * @subpackage db
9 * @author Martin Dougiamas
10 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
b9ddb2d5 11 */
12
7415aed1 13defined('MOODLE_INTERNAL') || die();
b9ddb2d5 14
6bc1e5d5 15require_once($CFG->libdir.'/authlib.php');
e7721485 16require_once($CFG->libdir.'/adodb/adodb.inc.php');
6bc1e5d5 17
b9ddb2d5 18/**
19 * External database authentication plugin.
20 */
6bc1e5d5 21class auth_plugin_db extends auth_plugin_base {
b9ddb2d5 22
23 /**
24 * Constructor.
25 */
26 function auth_plugin_db() {
6bc1e5d5 27 $this->authtype = 'db';
b9ddb2d5 28 $this->config = get_config('auth/db');
8ae42b8d 29 if (empty($this->config->extencoding)) {
30 $this->config->extencoding = 'utf-8';
31 }
b9ddb2d5 32 }
33
34 /**
35 * Returns true if the username and password work and false if they are
36 * wrong or don't exist.
37 *
576c063b 38 * @param string $username The username
39 * @param string $password The password
8ae42b8d 40 *
41 * @return bool Authentication success or failure.
b9ddb2d5 42 */
139ebfdb 43 function user_login($username, $password) {
576c063b 44 global $CFG, $DB;
b9ddb2d5 45
ab6e0848
PS
46 $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
47 $extpassword = textlib::convert($password, 'utf-8', $this->config->extencoding);
8ae42b8d 48
7415aed1 49 if ($this->is_internal()) {
b9ddb2d5 50 // lookup username externally, but resolve
51 // password locally -- to support backend that
52 // don't track passwords
ba87b41b
PS
53
54 if (isset($this->config->removeuser) and $this->config->removeuser == AUTH_REMOVEUSER_KEEP) {
55 // No need to connect to external database in this case because users are never removed and we verify password locally.
56 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
57 return validate_internal_user_password($user, $password);
58 } else {
59 return false;
60 }
61 }
62
63 $authdb = $this->db_init();
64
8ae42b8d 65 $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
66 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
b9ddb2d5 67 if (!$rs) {
03cedd62 68 $authdb->Close();
03ea0b32 69 debugging(get_string('auth_dbcantconnect','auth_db'));
b9ddb2d5 70 return false;
71 }
8ae42b8d 72
7415aed1 73 if (!$rs->EOF) {
03cedd62 74 $rs->Close();
75 $authdb->Close();
7415aed1 76 // user exists externally
b9ddb2d5 77 // check username/password internally
a0a5ca25 78 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
b9ddb2d5 79 return validate_internal_user_password($user, $password);
80 }
81 } else {
03cedd62 82 $rs->Close();
83 $authdb->Close();
b9ddb2d5 84 // user does not exist externally
85 return false;
8ae42b8d 86 }
b9ddb2d5 87
8ae42b8d 88 } else {
7415aed1 89 // normal case: use external db for both usernames and passwords
b9ddb2d5 90
ba87b41b
PS
91 $authdb = $this->db_init();
92
b9ddb2d5 93 if ($this->config->passtype === 'md5') { // Re-format password accordingly
8ae42b8d 94 $extpassword = md5($extpassword);
95 } else if ($this->config->passtype === 'sha1') {
96 $extpassword = sha1($extpassword);
b9ddb2d5 97 }
98
8ae42b8d 99 $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
100 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'
101 AND {$this->config->fieldpass} = '".$this->ext_addslashes($extpassword)."' ");
b9ddb2d5 102 if (!$rs) {
03cedd62 103 $authdb->Close();
03ea0b32 104 debugging(get_string('auth_dbcantconnect','auth_db'));
b9ddb2d5 105 return false;
106 }
8ae42b8d 107
03cedd62 108 if (!$rs->EOF) {
109 $rs->Close();
110 $authdb->Close();
b9ddb2d5 111 return true;
112 } else {
03cedd62 113 $rs->Close();
114 $authdb->Close();
b9ddb2d5 115 return false;
8ae42b8d 116 }
117
b9ddb2d5 118 }
119 }
120
139ebfdb 121 function db_init() {
93901eb4 122 // Connect to the external database (forcing new connection)
ab6e0848 123 $authdb = ADONewConnection($this->config->type);
8ae42b8d 124 if (!empty($this->config->debugauthdb)) {
125 $authdb->debug = true;
126 ob_start();//start output buffer to allow later use of the page headers
127 }
128 $authdb->Connect($this->config->host, $this->config->user, $this->config->pass, $this->config->name, true);
b9ddb2d5 129 $authdb->SetFetchMode(ADODB_FETCH_ASSOC);
8ae42b8d 130 if (!empty($this->config->setupsql)) {
131 $authdb->Execute($this->config->setupsql);
132 }
b9ddb2d5 133
139ebfdb 134 return $authdb;
135 }
7415aed1 136
139ebfdb 137 /**
ab6e0848 138 * Returns user attribute mappings between moodle and ldap
139ebfdb 139 *
140 * @return array
141 */
142 function db_attributes() {
139ebfdb 143 $moodleattributes = array();
4105caff 144 foreach ($this->userfields as $field) {
139ebfdb 145 if (!empty($this->config->{"field_map_$field"})) {
146 $moodleattributes[$field] = $this->config->{"field_map_$field"};
0f02788f 147 }
148 }
5261baf1 149 $moodleattributes['username'] = $this->config->fielduser;
139ebfdb 150 return $moodleattributes;
151 }
152
153 /**
154 * Reads any other information for a user from external database,
155 * then returns it in an array
156 *
be544ec3 157 * @param string $username
139ebfdb 158 *
159 * @return array without magic quotes
160 */
161 function get_userinfo($username) {
139ebfdb 162 global $CFG;
163
ab6e0848 164 $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
139ebfdb 165
166 $authdb = $this->db_init();
167
168 //Array to map local fieldnames we want, to external fieldnames
169 $selectfields = $this->db_attributes();
170
0f02788f 171 $result = array();
172 //If at least one field is mapped from external db, get that mapped data:
173 if ($selectfields) {
174 $select = '';
175 foreach ($selectfields as $localname=>$externalname) {
176 $select .= ", $externalname AS $localname";
177 }
178 $select = 'SELECT ' . substr($select,1);
179 $sql = $select .
180 " FROM {$this->config->table}" .
8ae42b8d 181 " WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'";
0f02788f 182 if ($rs = $authdb->Execute($sql)) {
03cedd62 183 if ( !$rs->EOF ) {
245ac557 184 $fields_obj = $rs->FetchObj();
1ed6ae07 185 $fields_obj = (object)array_change_key_case((array)$fields_obj , CASE_LOWER);
0f02788f 186 foreach ($selectfields as $localname=>$externalname) {
ab6e0848 187 $result[$localname] = textlib::convert($fields_obj->{$localname}, $this->config->extencoding, 'utf-8');
0f02788f 188 }
189 }
245ac557 190 $rs->Close();
b9ddb2d5 191 }
192 }
193 $authdb->Close();
b9ddb2d5 194 return $result;
195 }
196
fb5c7739 197 /**
198 * Change a user's password
199 *
ae040d4b 200 * @param object $user User table object
201 * @param string $newpassword Plaintext password
8ae42b8d 202 *
fb5c7739 203 * @return bool True on success
204 */
da249a30 205 function user_update_password($user, $newpassword) {
5c28e3a8
PS
206 global $DB;
207
7415aed1 208 if ($this->is_internal()) {
5c28e3a8
PS
209 $puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
210 if (update_internal_user_password($puser, $newpassword)) {
211 $user->password = $puser->password;
212 return true;
213 } else {
214 return false;
215 }
b9ddb2d5 216 } else {
217 // we should have never been called!
218 return false;
219 }
220 }
221
222 /**
ab6e0848 223 * synchronizes user from external db to moodle user table
b9ddb2d5 224 *
ab6e0848 225 * Sync should be done by using idnumber attribute, not username.
b9ddb2d5 226 * You need to pass firstsync parameter to function to fill in
ab6e0848 227 * idnumbers if they don't exists in moodle user table.
8ae42b8d 228 *
ab6e0848 229 * Syncing users removes (disables) users that don't exists anymore in external db.
8ae42b8d 230 * Creates new users and updates coursecreator status of users.
231 *
b9ddb2d5 232 * This implementation is simpler but less scalable than the one found in the LDAP module.
233 *
7415aed1 234 * @param bool $do_updates Optional: set to true to force an update of existing accounts
ab6e0848
PS
235 * @param bool $verbose
236 * @return int 0 means success, 1 means failure
b9ddb2d5 237 */
ab6e0848 238 function sync_users($do_updates=false, $verbose=false) {
70ca450a 239 global $CFG, $DB;
b9ddb2d5 240
ab6e0848 241 // list external users
b9ddb2d5 242 $userlist = $this->get_userlist();
b9ddb2d5 243
ab6e0848 244 // delete obsolete internal users
139ebfdb 245 if (!empty($this->config->removeuser)) {
b9ddb2d5 246
28fd4d6c
PS
247 $suspendselect = "";
248 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
249 $suspendselect = "AND u.suspended = 0";
250 }
251
139ebfdb 252 // find obsolete users
253 if (count($userlist)) {
20d8d5c7
EL
254 list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
255 $params['authtype'] = $this->authtype;
f91f3f63 256 $sql = "SELECT u.*
bc31625a 257 FROM {user} u
28fd4d6c 258 WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql";
139ebfdb 259 } else {
f91f3f63 260 $sql = "SELECT u.*
bc31625a 261 FROM {user} u
28fd4d6c 262 WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect";
bc31625a 263 $params = array();
20d8d5c7 264 $params['authtype'] = $this->authtype;
139ebfdb 265 }
28fd4d6c 266 $params['mnethostid'] = $CFG->mnet_localhost_id;
bc31625a 267 $remove_users = $DB->get_records_sql($sql, $params);
139ebfdb 268
269 if (!empty($remove_users)) {
ab6e0848 270 if ($verbose) {
28fd4d6c 271 mtrace(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
ab6e0848 272 }
139ebfdb 273
139ebfdb 274 foreach ($remove_users as $user) {
6f87ef52 275 if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) {
ab6e0848
PS
276 delete_user($user);
277 if ($verbose) {
278 mtrace("\t".get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
139ebfdb 279 }
6f87ef52 280 } else if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
1dffbae2 281 $updateuser = new stdClass();
139ebfdb 282 $updateuser->id = $user->id;
28fd4d6c 283 $updateuser->suspended = 1;
a4d25731 284 $updateuser->timemodified = time();
dd88de0e 285 $DB->update_record('user', $updateuser);
ab6e0848
PS
286 if ($verbose) {
287 mtrace("\t".get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
288 }
139ebfdb 289 }
b9ddb2d5 290 }
8ae42b8d 291 }
139ebfdb 292 unset($remove_users); // free mem!
8ae42b8d 293 }
b9ddb2d5 294
295 if (!count($userlist)) {
296 // exit right here
297 // nothing else to do
ab6e0848 298 return 0;
b9ddb2d5 299 }
300
301 ///
302 /// update existing accounts
303 ///
304 if ($do_updates) {
305 // narrow down what fields we need to update
306 $all_keys = array_keys(get_object_vars($this->config));
307 $updatekeys = array();
308 foreach ($all_keys as $key) {
309 if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) {
310 if ($this->config->{$key} === 'onlogin') {
311 array_push($updatekeys, $match[1]); // the actual key name
312 }
313 }
314 }
315 // print_r($all_keys); print_r($updatekeys);
316 unset($all_keys); unset($key);
317
318 // only go ahead if we actually
319 // have fields to update locally
320 if (!empty($updatekeys)) {
20d8d5c7 321 list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
bc31625a
PS
322 $params['authtype'] = $this->authtype;
323 $sql = "SELECT u.id, u.username
324 FROM {user} u
325 WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
326 if ($update_users = $DB->get_records_sql($sql, $params)) {
ab6e0848
PS
327 if ($verbose) {
328 mtrace("User entries to update: ".count($update_users));
329 }
8ae42b8d 330
331 foreach ($update_users as $user) {
ab6e0848
PS
332 if ($this->update_user_record($user->username, $updatekeys)) {
333 if ($verbose) {
334 mtrace("\t".get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
335 }
336 } else {
337 if ($verbose) {
338 mtrace("\t".get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'));
339 }
139ebfdb 340 }
8ae42b8d 341 }
342 unset($update_users); // free memory
b9ddb2d5 343 }
b9ddb2d5 344 }
345 }
346
347
348 ///
349 /// create missing accounts
350 ///
351 // NOTE: this is very memory intensive
352 // and generally inefficient
28fd4d6c
PS
353 $suspendselect = "";
354 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
355 $suspendselect = "AND u.suspended = 0";
356 }
357 $sql = "SELECT u.id, u.username
358 FROM {user} u
359 WHERE u.auth=:authtype AND u.deleted='0' AND mnethostid=:mnethostid $suspendselect";
b9ddb2d5 360
28fd4d6c 361 $users = $DB->get_records_sql($sql, array('authtype'=>$this->authtype, 'mnethostid'=>$CFG->mnet_localhost_id));
8ae42b8d 362
b9ddb2d5 363 // simplify down to usernames
364 $usernames = array();
2b214bc1 365 if (!empty($users)) {
366 foreach ($users as $user) {
367 array_push($usernames, $user->username);
368 }
369 unset($users);
b9ddb2d5 370 }
b9ddb2d5 371
372 $add_users = array_diff($userlist, $usernames);
373 unset($usernames);
374
375 if (!empty($add_users)) {
ab6e0848
PS
376 if ($verbose) {
377 mtrace(get_string('auth_dbuserstoadd','auth_db',count($add_users)));
378 }
bee02209 379 // Do not use transactions around this foreach, we want to skip problematic users, not revert everything.
b9ddb2d5 380 foreach($add_users as $user) {
381 $username = $user;
28fd4d6c
PS
382 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
383 if ($old_user = $DB->get_record('user', array('username'=>$username, 'deleted'=>0, 'suspended'=>1, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
384 $DB->set_field('user', 'suspended', 0, array('id'=>$old_user->id));
385 if ($verbose) {
386 mtrace("\t".get_string('auth_dbreviveduser', 'auth_db', array('name'=>$username, 'id'=>$old_user->id)));
387 }
388 continue;
389 }
390 }
f0364be6
PS
391
392 // Do not try to undelete users here, instead select suspending if you ever expect users will reappear.
8ae42b8d 393
b9ddb2d5 394 // prep a few params
f0364be6 395 $user = $this->get_userinfo_asobj($user);
b7b50143 396 $user->username = $username;
b7b50143 397 $user->confirmed = 1;
5211c7ec 398 $user->auth = $this->authtype;
b7b50143 399 $user->mnethostid = $CFG->mnet_localhost_id;
8ae42b8d 400 if (empty($user->lang)) {
401 $user->lang = $CFG->lang;
402 }
f0364be6
PS
403 $user->timecreated = time();
404 $user->timemodified = $user->timecreated;
bee02209
PS
405 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')) {
406 if ($verbose) {
407 mtrace("\t".get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)));
408 }
409 continue;
410 }
f0364be6 411 try {
ab6e0848
PS
412 $id = $DB->insert_record ('user', $user); // it is truly a new user
413 if ($verbose) {
414 mtrace("\t".get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)));
415 }
f0364be6
PS
416 } catch (moodle_exception $e) {
417 if ($verbose) {
418 mtrace("\t".get_string('auth_dbinsertusererror', 'auth_db', $user->username));
b9ddb2d5 419 }
bee02209 420 continue;
b9ddb2d5 421 }
f0364be6
PS
422 // if relevant, tag for password generation
423 if ($this->is_internal()) {
424 set_user_preference('auth_forcepasswordchange', 1, $id);
425 set_user_preference('create_password', 1, $id);
426 }
bee02209
PS
427 // Make sure user context is present.
428 context_user::instance($id);
b9ddb2d5 429 }
b9ddb2d5 430 unset($add_users); // free mem
431 }
ab6e0848 432 return 0;
b9ddb2d5 433 }
434
139ebfdb 435 function user_exists($username) {
93901eb4 436
a7e32367 437 /// Init result value
438 $result = false;
439
ab6e0848 440 $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
8ae42b8d 441
139ebfdb 442 $authdb = $this->db_init();
b9ddb2d5 443
8ae42b8d 444 $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
445 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
b9ddb2d5 446
447 if (!$rs) {
2b06294b 448 print_error('auth_dbcantconnect','auth_db');
7415aed1 449 } else if (!$rs->EOF) {
ab6e0848 450 // user exists externally
03cedd62 451 $result = true;
8ae42b8d 452 }
a7e32367 453
454 $authdb->Close();
455 return $result;
b9ddb2d5 456 }
457
458
459 function get_userlist() {
93901eb4 460
a7e32367 461 /// Init result value
462 $result = array();
463
139ebfdb 464 $authdb = $this->db_init();
b9ddb2d5 465
466 // fetch userlist
467 $rs = $authdb->Execute("SELECT {$this->config->fielduser} AS username
468 FROM {$this->config->table} ");
b9ddb2d5 469
470 if (!$rs) {
2b06294b 471 print_error('auth_dbcantconnect','auth_db');
7415aed1 472 } else if (!$rs->EOF) {
245ac557 473 while ($rec = $rs->FetchRow()) {
e9366bf8 474 $rec = (object)array_change_key_case((array)$rec , CASE_LOWER);
475 array_push($result, $rec->username);
b9ddb2d5 476 }
8ae42b8d 477 }
a7e32367 478
479 $authdb->Close();
480 return $result;
b9ddb2d5 481 }
482
483 /**
ab6e0848 484 * reads user information from DB and return it in an object
b9ddb2d5 485 *
8ae42b8d 486 * @param string $username username (with system magic quotes)
b9ddb2d5 487 * @return array
488 */
489 function get_userinfo_asobj($username) {
490 $user_array = truncate_userinfo($this->get_userinfo($username));
1dffbae2 491 $user = new stdClass();
b9ddb2d5 492 foreach($user_array as $key=>$value) {
493 $user->{$key} = $value;
494 }
495 return $user;
496 }
497
8ae42b8d 498 /**
499 * will update a local user record from an external source.
500 * is a lighter version of the one in moodlelib -- won't do
b9ddb2d5 501 * expensive ops such as enrolment
502 *
8ae42b8d 503 * If you don't pass $updatekeys, there is a performance hit and
b9ddb2d5 504 * values removed from DB won't be removed from moodle.
8ae42b8d 505 *
185721a4 506 * @param string $username username
ab6e0848
PS
507 * @param bool $updatekeys
508 * @return stdClass
b9ddb2d5 509 */
139ebfdb 510 function update_user_record($username, $updatekeys=false) {
185721a4 511 global $CFG, $DB;
b9ddb2d5 512
b9ddb2d5 513 //just in case check text case
ab6e0848 514 $username = trim(textlib::strtolower($username));
8ae42b8d 515
b9ddb2d5 516 // get the current user record
185721a4 517 $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id));
b9ddb2d5 518 if (empty($user)) { // trouble
519 error_log("Cannot update non-existent user: $username");
2b06294b 520 print_error('auth_dbusernotexist','auth_db',$username);
b9ddb2d5 521 die;
522 }
523
b7b50143 524 // Ensure userid is not overwritten
525 $userid = $user->id;
a4d25731 526 $updated = false;
b7b50143 527
b9ddb2d5 528 if ($newinfo = $this->get_userinfo($username)) {
529 $newinfo = truncate_userinfo($newinfo);
8ae42b8d 530
b9ddb2d5 531 if (empty($updatekeys)) { // all keys? this does not support removing values
532 $updatekeys = array_keys($newinfo);
533 }
8ae42b8d 534
b9ddb2d5 535 foreach ($updatekeys as $key) {
b9ddb2d5 536 if (isset($newinfo[$key])) {
537 $value = $newinfo[$key];
b9ddb2d5 538 } else {
539 $value = '';
540 }
8ae42b8d 541
542 if (!empty($this->config->{'field_updatelocal_' . $key})) {
bc31625a 543 if (isset($user->{$key}) and $user->{$key} != $value) { // only update if it's changed
185721a4 544 $DB->set_field('user', $key, $value, array('id'=>$userid));
a4d25731 545 $updated = true;
139ebfdb 546 }
b9ddb2d5 547 }
548 }
549 }
a4d25731
PS
550 if ($updated) {
551 $DB->set_field('user', 'timemodified', time(), array('id'=>$userid));
552 }
185721a4 553 return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0));
139ebfdb 554 }
555
556 /**
557 * Called when the user record is updated.
558 * Modifies user in external database. It takes olduser (before changes) and newuser (after changes)
ab6e0848 559 * compares information saved modified information to external db.
139ebfdb 560 *
70ca450a 561 * @param mixed $olduser Userobject before modifications
562 * @param mixed $newuser Userobject new modified userobject
139ebfdb 563 * @return boolean result
564 *
565 */
566 function user_update($olduser, $newuser) {
567 if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) {
568 error_log("ERROR:User renaming not allowed in ext db");
569 return false;
570 }
571
5211c7ec 572 if (isset($olduser->auth) and $olduser->auth != $this->authtype) {
139ebfdb 573 return true; // just change auth and skip update
574 }
575
576 $curruser = $this->get_userinfo($olduser->username);
577 if (empty($curruser)) {
578 error_log("ERROR:User $olduser->username found in ext db");
579 return false;
580 }
581
ab6e0848 582 $extusername = textlib::convert($olduser->username, 'utf-8', $this->config->extencoding);
139ebfdb 583
584 $authdb = $this->db_init();
585
586 $update = array();
587 foreach($curruser as $key=>$value) {
588 if ($key == 'username') {
589 continue; // skip this
590 }
591 if (empty($this->config->{"field_updateremote_$key"})) {
592 continue; // remote update not requested
593 }
594 if (!isset($newuser->$key)) {
595 continue;
596 }
70ca450a 597 $nuvalue = $newuser->$key;
139ebfdb 598 if ($nuvalue != $value) {
ab6e0848 599 $update[] = $this->config->{"field_map_$key"}."='".$this->ext_addslashes(textlib::convert($nuvalue, 'utf-8', $this->config->extencoding))."'";
139ebfdb 600 }
601 }
602 if (!empty($update)) {
603 $authdb->Execute("UPDATE {$this->config->table}
7415aed1
PS
604 SET ".implode(',', $update)."
605 WHERE {$this->config->fielduser}='".$this->ext_addslashes($extusername)."'");
139ebfdb 606 }
607 $authdb->Close();
608 return true;
b9ddb2d5 609 }
610
8ae42b8d 611 /**
612 * A chance to validate form data, and last chance to
613 * do stuff before it is inserted in config_plugin
ab6e0848
PS
614 *
615 * @param stfdClass config form
616 * @param array $error errors
617 * @return void
8ae42b8d 618 */
ab6e0848 619 function validate_form($form, &$err) {
150b5fb0 620 if ($form->passtype === 'internal') {
b9ddb2d5 621 $this->config->changepasswordurl = '';
622 set_config('changepasswordurl', '', 'auth/db');
623 }
b9ddb2d5 624 }
625
edb5da83 626 function prevent_local_passwords() {
7415aed1 627 return !$this->is_internal();
edb5da83
PS
628 }
629
b9ddb2d5 630 /**
7415aed1
PS
631 * Returns true if this authentication plugin is "internal".
632 *
633 * Internal plugins use password hashes from Moodle user table for authentication.
b9ddb2d5 634 *
139ebfdb 635 * @return bool
b9ddb2d5 636 */
637 function is_internal() {
e79781f7
PS
638 if (!isset($this->config->passtype)) {
639 return true;
640 }
7415aed1
PS
641 return ($this->config->passtype === 'internal');
642 }
643
644 /**
645 * Indicates if moodle should automatically update internal user
646 * records with data from external sources using the information
647 * from auth_plugin_base::get_userinfo().
648 *
649 * @return bool true means automatically copy data from ext to user table
650 */
651 function is_synchronised_with_external() {
652 return true;
b9ddb2d5 653 }
654
655 /**
656 * Returns true if this authentication plugin can change the user's
657 * password.
658 *
139ebfdb 659 * @return bool
b9ddb2d5 660 */
661 function can_change_password() {
7415aed1 662 return ($this->is_internal() or !empty($this->config->changepasswordurl));
b9ddb2d5 663 }
664
665 /**
430759a5 666 * Returns the URL for changing the user's pw, or empty if the default can
b9ddb2d5 667 * be used.
668 *
99f9f85f 669 * @return moodle_url
b9ddb2d5 670 */
671 function change_password_url() {
7415aed1 672 if ($this->is_internal()) {
430759a5 673 // standard form
99f9f85f 674 return null;
430759a5 675 } else {
7415aed1 676 // use admin defined custom url
99f9f85f 677 return new moodle_url($this->config->changepasswordurl);
430759a5 678 }
b9ddb2d5 679 }
680
ab6ff8a4 681 /**
682 * Returns true if plugin allows resetting of internal password.
683 *
684 * @return bool
685 */
686 function can_reset_password() {
7415aed1 687 return $this->is_internal();
ab6ff8a4 688 }
689
b9ddb2d5 690 /**
691 * Prints a form for configuring this authentication plugin.
692 *
693 * This function is called from admin/auth.php, and outputs a full page with
694 * a form for configuring this plugin.
695 *
ab6e0848
PS
696 * @param stdClass $config
697 * @param array $err errors
698 * @param array $user_fields
699 * @return void
b9ddb2d5 700 */
139ebfdb 701 function config_form($config, $err, $user_fields) {
8ae42b8d 702 include 'config.html';
b9ddb2d5 703 }
704
705 /**
706 * Processes and stores configuration data for this authentication plugin.
ab6e0848
PS
707 * @param srdClass $config
708 * @return bool always true or exception
b9ddb2d5 709 */
710 function process_config($config) {
711 // set to defaults if undefined
712 if (!isset($config->host)) {
8ae42b8d 713 $config->host = 'localhost';
b9ddb2d5 714 }
715 if (!isset($config->type)) {
8ae42b8d 716 $config->type = 'mysql';
717 }
718 if (!isset($config->sybasequoting)) {
719 $config->sybasequoting = 0;
b9ddb2d5 720 }
721 if (!isset($config->name)) {
8ae42b8d 722 $config->name = '';
b9ddb2d5 723 }
724 if (!isset($config->user)) {
8ae42b8d 725 $config->user = '';
b9ddb2d5 726 }
727 if (!isset($config->pass)) {
8ae42b8d 728 $config->pass = '';
b9ddb2d5 729 }
730 if (!isset($config->table)) {
8ae42b8d 731 $config->table = '';
b9ddb2d5 732 }
733 if (!isset($config->fielduser)) {
8ae42b8d 734 $config->fielduser = '';
b9ddb2d5 735 }
736 if (!isset($config->fieldpass)) {
8ae42b8d 737 $config->fieldpass = '';
b9ddb2d5 738 }
739 if (!isset($config->passtype)) {
8ae42b8d 740 $config->passtype = 'plaintext';
741 }
742 if (!isset($config->extencoding)) {
743 $config->extencoding = 'utf-8';
744 }
745 if (!isset($config->setupsql)) {
746 $config->setupsql = '';
747 }
748 if (!isset($config->debugauthdb)) {
749 $config->debugauthdb = 0;
b9ddb2d5 750 }
139ebfdb 751 if (!isset($config->removeuser)) {
6f87ef52 752 $config->removeuser = AUTH_REMOVEUSER_KEEP;
139ebfdb 753 }
b9ddb2d5 754 if (!isset($config->changepasswordurl)) {
755 $config->changepasswordurl = '';
756 }
757
758 // save settings
8ae42b8d 759 set_config('host', $config->host, 'auth/db');
760 set_config('type', $config->type, 'auth/db');
761 set_config('sybasequoting', $config->sybasequoting, 'auth/db');
762 set_config('name', $config->name, 'auth/db');
763 set_config('user', $config->user, 'auth/db');
764 set_config('pass', $config->pass, 'auth/db');
765 set_config('table', $config->table, 'auth/db');
766 set_config('fielduser', $config->fielduser, 'auth/db');
767 set_config('fieldpass', $config->fieldpass, 'auth/db');
768 set_config('passtype', $config->passtype, 'auth/db');
769 set_config('extencoding', trim($config->extencoding), 'auth/db');
139ebfdb 770 set_config('setupsql', trim($config->setupsql),'auth/db');
8ae42b8d 771 set_config('debugauthdb', $config->debugauthdb, 'auth/db');
139ebfdb 772 set_config('removeuser', $config->removeuser, 'auth/db');
8ae42b8d 773 set_config('changepasswordurl', trim($config->changepasswordurl), 'auth/db');
774
b9ddb2d5 775 return true;
776 }
777
8ae42b8d 778 function ext_addslashes($text) {
779 // using custom made function for now
780 if (empty($this->config->sybasequoting)) {
781 $text = str_replace('\\', '\\\\', $text);
782 $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
783 } else {
784 $text = str_replace("'", "''", $text);
785 }
786 return $text;
787 }
b9ddb2d5 788}
789
5117d598 790