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