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 | |
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 |