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; | |
352 | $user = $this->get_userinfo_asobj($user); | |
8ae42b8d | 353 | |
b9ddb2d5 | 354 | // prep a few params |
b7b50143 | 355 | $user->username = $username; |
b7b50143 | 356 | $user->confirmed = 1; |
5211c7ec | 357 | $user->auth = $this->authtype; |
b7b50143 | 358 | $user->mnethostid = $CFG->mnet_localhost_id; |
8ae42b8d | 359 | if (empty($user->lang)) { |
360 | $user->lang = $CFG->lang; | |
361 | } | |
362 | ||
b9ddb2d5 | 363 | // maybe the user has been deleted before |
a4d25731 PS |
364 | if ($old_user = $DB->get_record('user', array('username'=>$user->username, 'deleted'=>1, 'mnethostid'=>$user->mnethostid, 'auth'=>$user->auth))) { |
365 | // note: this undeleting is deprecated and will be eliminated soon | |
366 | $DB->set_field('user', 'deleted', 0, array('id'=>$old_user->id)); | |
367 | $DB->set_field('user', 'timemodified', time(), array('id'=>$old_user->id)); | |
ab6e0848 | 368 | if ($verbose) { |
a4d25731 | 369 | mtrace("\t".get_string('auth_dbreviveduser', 'auth_db', array('name'=>$old_user->username, 'id'=>$old_user->id))); |
ab6e0848 | 370 | } |
6b8ad965 | 371 | |
a9637e7d | 372 | } else { |
a4d25731 PS |
373 | $user->timecreated = time(); |
374 | $user->timemodified = $user->timecreated; | |
ab6e0848 PS |
375 | $id = $DB->insert_record ('user', $user); // it is truly a new user |
376 | if ($verbose) { | |
377 | mtrace("\t".get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id))); | |
378 | } | |
b9ddb2d5 | 379 | // if relevant, tag for password generation |
7415aed1 | 380 | if ($this->is_internal()) { |
b9ddb2d5 | 381 | set_user_preference('auth_forcepasswordchange', 1, $id); |
382 | set_user_preference('create_password', 1, $id); | |
383 | } | |
b9ddb2d5 | 384 | } |
b9ddb2d5 | 385 | } |
d5a8d9aa | 386 | $transaction->allow_commit(); |
b9ddb2d5 | 387 | unset($add_users); // free mem |
388 | } | |
ab6e0848 | 389 | return 0; |
b9ddb2d5 | 390 | } |
391 | ||
139ebfdb | 392 | function user_exists($username) { |
93901eb4 | 393 | |
a7e32367 | 394 | /// Init result value |
395 | $result = false; | |
396 | ||
ab6e0848 | 397 | $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding); |
8ae42b8d | 398 | |
139ebfdb | 399 | $authdb = $this->db_init(); |
b9ddb2d5 | 400 | |
8ae42b8d | 401 | $rs = $authdb->Execute("SELECT * FROM {$this->config->table} |
402 | WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' "); | |
b9ddb2d5 | 403 | |
404 | if (!$rs) { | |
2b06294b | 405 | print_error('auth_dbcantconnect','auth_db'); |
7415aed1 | 406 | } else if (!$rs->EOF) { |
ab6e0848 | 407 | // user exists externally |
03cedd62 | 408 | $result = true; |
8ae42b8d | 409 | } |
a7e32367 | 410 | |
411 | $authdb->Close(); | |
412 | return $result; | |
b9ddb2d5 | 413 | } |
414 | ||
415 | ||
416 | function get_userlist() { | |
93901eb4 | 417 | |
a7e32367 | 418 | /// Init result value |
419 | $result = array(); | |
420 | ||
139ebfdb | 421 | $authdb = $this->db_init(); |
b9ddb2d5 | 422 | |
423 | // fetch userlist | |
424 | $rs = $authdb->Execute("SELECT {$this->config->fielduser} AS username | |
425 | FROM {$this->config->table} "); | |
b9ddb2d5 | 426 | |
427 | if (!$rs) { | |
2b06294b | 428 | print_error('auth_dbcantconnect','auth_db'); |
7415aed1 | 429 | } else if (!$rs->EOF) { |
245ac557 | 430 | while ($rec = $rs->FetchRow()) { |
e9366bf8 | 431 | $rec = (object)array_change_key_case((array)$rec , CASE_LOWER); |
432 | array_push($result, $rec->username); | |
b9ddb2d5 | 433 | } |
8ae42b8d | 434 | } |
a7e32367 | 435 | |
436 | $authdb->Close(); | |
437 | return $result; | |
b9ddb2d5 | 438 | } |
439 | ||
440 | /** | |
ab6e0848 | 441 | * reads user information from DB and return it in an object |
b9ddb2d5 | 442 | * |
8ae42b8d | 443 | * @param string $username username (with system magic quotes) |
b9ddb2d5 | 444 | * @return array |
445 | */ | |
446 | function get_userinfo_asobj($username) { | |
447 | $user_array = truncate_userinfo($this->get_userinfo($username)); | |
1dffbae2 | 448 | $user = new stdClass(); |
b9ddb2d5 | 449 | foreach($user_array as $key=>$value) { |
450 | $user->{$key} = $value; | |
451 | } | |
452 | return $user; | |
453 | } | |
454 | ||
8ae42b8d | 455 | /** |
456 | * will update a local user record from an external source. | |
457 | * is a lighter version of the one in moodlelib -- won't do | |
b9ddb2d5 | 458 | * expensive ops such as enrolment |
459 | * | |
8ae42b8d | 460 | * If you don't pass $updatekeys, there is a performance hit and |
b9ddb2d5 | 461 | * values removed from DB won't be removed from moodle. |
8ae42b8d | 462 | * |
185721a4 | 463 | * @param string $username username |
ab6e0848 PS |
464 | * @param bool $updatekeys |
465 | * @return stdClass | |
b9ddb2d5 | 466 | */ |
139ebfdb | 467 | function update_user_record($username, $updatekeys=false) { |
185721a4 | 468 | global $CFG, $DB; |
b9ddb2d5 | 469 | |
b9ddb2d5 | 470 | //just in case check text case |
ab6e0848 | 471 | $username = trim(textlib::strtolower($username)); |
8ae42b8d | 472 | |
b9ddb2d5 | 473 | // get the current user record |
185721a4 | 474 | $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id)); |
b9ddb2d5 | 475 | if (empty($user)) { // trouble |
476 | error_log("Cannot update non-existent user: $username"); | |
2b06294b | 477 | print_error('auth_dbusernotexist','auth_db',$username); |
b9ddb2d5 | 478 | die; |
479 | } | |
480 | ||
b7b50143 | 481 | // Ensure userid is not overwritten |
482 | $userid = $user->id; | |
a4d25731 | 483 | $updated = false; |
b7b50143 | 484 | |
b9ddb2d5 | 485 | if ($newinfo = $this->get_userinfo($username)) { |
486 | $newinfo = truncate_userinfo($newinfo); | |
8ae42b8d | 487 | |
b9ddb2d5 | 488 | if (empty($updatekeys)) { // all keys? this does not support removing values |
489 | $updatekeys = array_keys($newinfo); | |
490 | } | |
8ae42b8d | 491 | |
b9ddb2d5 | 492 | foreach ($updatekeys as $key) { |
b9ddb2d5 | 493 | if (isset($newinfo[$key])) { |
494 | $value = $newinfo[$key]; | |
b9ddb2d5 | 495 | } else { |
496 | $value = ''; | |
497 | } | |
8ae42b8d | 498 | |
499 | if (!empty($this->config->{'field_updatelocal_' . $key})) { | |
bc31625a | 500 | if (isset($user->{$key}) and $user->{$key} != $value) { // only update if it's changed |
185721a4 | 501 | $DB->set_field('user', $key, $value, array('id'=>$userid)); |
a4d25731 | 502 | $updated = true; |
139ebfdb | 503 | } |
b9ddb2d5 | 504 | } |
505 | } | |
506 | } | |
a4d25731 PS |
507 | if ($updated) { |
508 | $DB->set_field('user', 'timemodified', time(), array('id'=>$userid)); | |
509 | } | |
185721a4 | 510 | return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0)); |
139ebfdb | 511 | } |
512 | ||
513 | /** | |
514 | * Called when the user record is updated. | |
515 | * Modifies user in external database. It takes olduser (before changes) and newuser (after changes) | |
ab6e0848 | 516 | * compares information saved modified information to external db. |
139ebfdb | 517 | * |
70ca450a | 518 | * @param mixed $olduser Userobject before modifications |
519 | * @param mixed $newuser Userobject new modified userobject | |
139ebfdb | 520 | * @return boolean result |
521 | * | |
522 | */ | |
523 | function user_update($olduser, $newuser) { | |
524 | if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) { | |
525 | error_log("ERROR:User renaming not allowed in ext db"); | |
526 | return false; | |
527 | } | |
528 | ||
5211c7ec | 529 | if (isset($olduser->auth) and $olduser->auth != $this->authtype) { |
139ebfdb | 530 | return true; // just change auth and skip update |
531 | } | |
532 | ||
533 | $curruser = $this->get_userinfo($olduser->username); | |
534 | if (empty($curruser)) { | |
535 | error_log("ERROR:User $olduser->username found in ext db"); | |
536 | return false; | |
537 | } | |
538 | ||
ab6e0848 | 539 | $extusername = textlib::convert($olduser->username, 'utf-8', $this->config->extencoding); |
139ebfdb | 540 | |
541 | $authdb = $this->db_init(); | |
542 | ||
543 | $update = array(); | |
544 | foreach($curruser as $key=>$value) { | |
545 | if ($key == 'username') { | |
546 | continue; // skip this | |
547 | } | |
548 | if (empty($this->config->{"field_updateremote_$key"})) { | |
549 | continue; // remote update not requested | |
550 | } | |
551 | if (!isset($newuser->$key)) { | |
552 | continue; | |
553 | } | |
70ca450a | 554 | $nuvalue = $newuser->$key; |
139ebfdb | 555 | if ($nuvalue != $value) { |
ab6e0848 | 556 | $update[] = $this->config->{"field_map_$key"}."='".$this->ext_addslashes(textlib::convert($nuvalue, 'utf-8', $this->config->extencoding))."'"; |
139ebfdb | 557 | } |
558 | } | |
559 | if (!empty($update)) { | |
560 | $authdb->Execute("UPDATE {$this->config->table} | |
7415aed1 PS |
561 | SET ".implode(',', $update)." |
562 | WHERE {$this->config->fielduser}='".$this->ext_addslashes($extusername)."'"); | |
139ebfdb | 563 | } |
564 | $authdb->Close(); | |
565 | return true; | |
b9ddb2d5 | 566 | } |
567 | ||
8ae42b8d | 568 | /** |
569 | * A chance to validate form data, and last chance to | |
570 | * do stuff before it is inserted in config_plugin | |
ab6e0848 PS |
571 | * |
572 | * @param stfdClass config form | |
573 | * @param array $error errors | |
574 | * @return void | |
8ae42b8d | 575 | */ |
ab6e0848 | 576 | function validate_form($form, &$err) { |
150b5fb0 | 577 | if ($form->passtype === 'internal') { |
b9ddb2d5 | 578 | $this->config->changepasswordurl = ''; |
579 | set_config('changepasswordurl', '', 'auth/db'); | |
580 | } | |
b9ddb2d5 | 581 | } |
582 | ||
edb5da83 | 583 | function prevent_local_passwords() { |
7415aed1 | 584 | return !$this->is_internal(); |
edb5da83 PS |
585 | } |
586 | ||
b9ddb2d5 | 587 | /** |
7415aed1 PS |
588 | * Returns true if this authentication plugin is "internal". |
589 | * | |
590 | * Internal plugins use password hashes from Moodle user table for authentication. | |
b9ddb2d5 | 591 | * |
139ebfdb | 592 | * @return bool |
b9ddb2d5 | 593 | */ |
594 | function is_internal() { | |
e79781f7 PS |
595 | if (!isset($this->config->passtype)) { |
596 | return true; | |
597 | } | |
7415aed1 PS |
598 | return ($this->config->passtype === 'internal'); |
599 | } | |
600 | ||
601 | /** | |
602 | * Indicates if moodle should automatically update internal user | |
603 | * records with data from external sources using the information | |
604 | * from auth_plugin_base::get_userinfo(). | |
605 | * | |
606 | * @return bool true means automatically copy data from ext to user table | |
607 | */ | |
608 | function is_synchronised_with_external() { | |
609 | return true; | |
b9ddb2d5 | 610 | } |
611 | ||
612 | /** | |
613 | * Returns true if this authentication plugin can change the user's | |
614 | * password. | |
615 | * | |
139ebfdb | 616 | * @return bool |
b9ddb2d5 | 617 | */ |
618 | function can_change_password() { | |
7415aed1 | 619 | return ($this->is_internal() or !empty($this->config->changepasswordurl)); |
b9ddb2d5 | 620 | } |
621 | ||
622 | /** | |
430759a5 | 623 | * Returns the URL for changing the user's pw, or empty if the default can |
b9ddb2d5 | 624 | * be used. |
625 | * | |
99f9f85f | 626 | * @return moodle_url |
b9ddb2d5 | 627 | */ |
628 | function change_password_url() { | |
7415aed1 | 629 | if ($this->is_internal()) { |
430759a5 | 630 | // standard form |
99f9f85f | 631 | return null; |
430759a5 | 632 | } else { |
7415aed1 | 633 | // use admin defined custom url |
99f9f85f | 634 | return new moodle_url($this->config->changepasswordurl); |
430759a5 | 635 | } |
b9ddb2d5 | 636 | } |
637 | ||
ab6ff8a4 | 638 | /** |
639 | * Returns true if plugin allows resetting of internal password. | |
640 | * | |
641 | * @return bool | |
642 | */ | |
643 | function can_reset_password() { | |
7415aed1 | 644 | return $this->is_internal(); |
ab6ff8a4 | 645 | } |
646 | ||
b9ddb2d5 | 647 | /** |
648 | * Prints a form for configuring this authentication plugin. | |
649 | * | |
650 | * This function is called from admin/auth.php, and outputs a full page with | |
651 | * a form for configuring this plugin. | |
652 | * | |
ab6e0848 PS |
653 | * @param stdClass $config |
654 | * @param array $err errors | |
655 | * @param array $user_fields | |
656 | * @return void | |
b9ddb2d5 | 657 | */ |
139ebfdb | 658 | function config_form($config, $err, $user_fields) { |
8ae42b8d | 659 | include 'config.html'; |
b9ddb2d5 | 660 | } |
661 | ||
662 | /** | |
663 | * Processes and stores configuration data for this authentication plugin. | |
ab6e0848 PS |
664 | * @param srdClass $config |
665 | * @return bool always true or exception | |
b9ddb2d5 | 666 | */ |
667 | function process_config($config) { | |
668 | // set to defaults if undefined | |
669 | if (!isset($config->host)) { | |
8ae42b8d | 670 | $config->host = 'localhost'; |
b9ddb2d5 | 671 | } |
672 | if (!isset($config->type)) { | |
8ae42b8d | 673 | $config->type = 'mysql'; |
674 | } | |
675 | if (!isset($config->sybasequoting)) { | |
676 | $config->sybasequoting = 0; | |
b9ddb2d5 | 677 | } |
678 | if (!isset($config->name)) { | |
8ae42b8d | 679 | $config->name = ''; |
b9ddb2d5 | 680 | } |
681 | if (!isset($config->user)) { | |
8ae42b8d | 682 | $config->user = ''; |
b9ddb2d5 | 683 | } |
684 | if (!isset($config->pass)) { | |
8ae42b8d | 685 | $config->pass = ''; |
b9ddb2d5 | 686 | } |
687 | if (!isset($config->table)) { | |
8ae42b8d | 688 | $config->table = ''; |
b9ddb2d5 | 689 | } |
690 | if (!isset($config->fielduser)) { | |
8ae42b8d | 691 | $config->fielduser = ''; |
b9ddb2d5 | 692 | } |
693 | if (!isset($config->fieldpass)) { | |
8ae42b8d | 694 | $config->fieldpass = ''; |
b9ddb2d5 | 695 | } |
696 | if (!isset($config->passtype)) { | |
8ae42b8d | 697 | $config->passtype = 'plaintext'; |
698 | } | |
699 | if (!isset($config->extencoding)) { | |
700 | $config->extencoding = 'utf-8'; | |
701 | } | |
702 | if (!isset($config->setupsql)) { | |
703 | $config->setupsql = ''; | |
704 | } | |
705 | if (!isset($config->debugauthdb)) { | |
706 | $config->debugauthdb = 0; | |
b9ddb2d5 | 707 | } |
139ebfdb | 708 | if (!isset($config->removeuser)) { |
6f87ef52 | 709 | $config->removeuser = AUTH_REMOVEUSER_KEEP; |
139ebfdb | 710 | } |
b9ddb2d5 | 711 | if (!isset($config->changepasswordurl)) { |
712 | $config->changepasswordurl = ''; | |
713 | } | |
714 | ||
715 | // save settings | |
8ae42b8d | 716 | set_config('host', $config->host, 'auth/db'); |
717 | set_config('type', $config->type, 'auth/db'); | |
718 | set_config('sybasequoting', $config->sybasequoting, 'auth/db'); | |
719 | set_config('name', $config->name, 'auth/db'); | |
720 | set_config('user', $config->user, 'auth/db'); | |
721 | set_config('pass', $config->pass, 'auth/db'); | |
722 | set_config('table', $config->table, 'auth/db'); | |
723 | set_config('fielduser', $config->fielduser, 'auth/db'); | |
724 | set_config('fieldpass', $config->fieldpass, 'auth/db'); | |
725 | set_config('passtype', $config->passtype, 'auth/db'); | |
726 | set_config('extencoding', trim($config->extencoding), 'auth/db'); | |
139ebfdb | 727 | set_config('setupsql', trim($config->setupsql),'auth/db'); |
8ae42b8d | 728 | set_config('debugauthdb', $config->debugauthdb, 'auth/db'); |
139ebfdb | 729 | set_config('removeuser', $config->removeuser, 'auth/db'); |
8ae42b8d | 730 | set_config('changepasswordurl', trim($config->changepasswordurl), 'auth/db'); |
731 | ||
b9ddb2d5 | 732 | return true; |
733 | } | |
734 | ||
8ae42b8d | 735 | function ext_addslashes($text) { |
736 | // using custom made function for now | |
737 | if (empty($this->config->sybasequoting)) { | |
738 | $text = str_replace('\\', '\\\\', $text); | |
739 | $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text); | |
740 | } else { | |
741 | $text = str_replace("'", "''", $text); | |
742 | } | |
743 | return $text; | |
744 | } | |
b9ddb2d5 | 745 | } |
746 | ||
5117d598 | 747 |