b9ddb2d5 |
1 | <?php |
2 | |
3 | /** |
4 | * @author Martin Dougiamas |
5 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License |
6 | * @package moodle multiauth |
7 | * |
8 | * Authentication Plugin: LDAP Authentication |
9 | * |
10 | * Authentication using LDAP (Lightweight Directory Access Protocol). |
11 | * |
12 | * 2006-08-28 File created. |
13 | */ |
14 | |
139ebfdb |
15 | if (!defined('MOODLE_INTERNAL')) { |
16 | die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page |
b9ddb2d5 |
17 | } |
18 | |
6bc1e5d5 |
19 | require_once($CFG->libdir.'/authlib.php'); |
20 | |
b9ddb2d5 |
21 | /** |
22 | * LDAP authentication plugin. |
23 | */ |
6bc1e5d5 |
24 | class auth_plugin_ldap extends auth_plugin_base { |
b9ddb2d5 |
25 | |
26 | /** |
139ebfdb |
27 | * Constructor with initialisation. |
b9ddb2d5 |
28 | */ |
29 | function auth_plugin_ldap() { |
6bc1e5d5 |
30 | $this->authtype = 'ldap'; |
b9ddb2d5 |
31 | $this->config = get_config('auth/ldap'); |
139ebfdb |
32 | if (empty($this->config->ldapencoding)) { |
33 | $this->config->ldapencoding = 'utf-8'; |
34 | } |
35 | if (empty($this->config->user_type)) { |
36 | $this->config->user_type = 'default'; |
37 | } |
38 | |
39 | $default = $this->ldap_getdefaults(); |
40 | |
41 | //use defaults if values not given |
42 | foreach ($default as $key => $value) { |
43 | // watch out - 0, false are correct values too |
44 | if (!isset($this->config->{$key}) or $this->config->{$key} == '') { |
45 | $this->config->{$key} = $value[$this->config->user_type]; |
46 | } |
47 | } |
48 | //hack prefix to objectclass |
430759a5 |
49 | if (empty($this->config->objectclass)) { // Can't send empty filter |
50 | $this->config->objectclass='objectClass=*'; |
51 | } else if (strpos($this->config->objectclass, 'objectClass=') !== 0) { |
52 | $this->config->objectclass = 'objectClass='.$this->config->objectclass; |
139ebfdb |
53 | } |
430759a5 |
54 | |
b9ddb2d5 |
55 | } |
56 | |
57 | /** |
58 | * Returns true if the username and password work and false if they are |
59 | * wrong or don't exist. |
60 | * |
139ebfdb |
61 | * @param string $username The username (with system magic quotes) |
62 | * @param string $password The password (with system magic quotes) |
63 | * |
64 | * @return bool Authentication success or failure. |
b9ddb2d5 |
65 | */ |
66 | function user_login($username, $password) { |
b7b50143 |
67 | if (! function_exists('ldap_bind')) { |
43c6650b |
68 | print_error('auth_ldapnotinstalled','auth'); |
b7b50143 |
69 | return false; |
70 | } |
b9ddb2d5 |
71 | |
b9ddb2d5 |
72 | if (!$username or !$password) { // Don't allow blank usernames or passwords |
73 | return false; |
74 | } |
139ebfdb |
75 | |
76 | $textlib = textlib_get_instance(); |
77 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
78 | $extpassword = $textlib->convert(stripslashes($password), 'utf-8', $this->config->ldapencoding); |
b9ddb2d5 |
79 | |
80 | $ldapconnection = $this->ldap_connect(); |
81 | |
82 | if ($ldapconnection) { |
139ebfdb |
83 | $ldap_user_dn = $this->ldap_find_userdn($ldapconnection, $extusername); |
84 | |
b9ddb2d5 |
85 | //if ldap_user_dn is empty, user does not exist |
86 | if (!$ldap_user_dn) { |
87 | ldap_close($ldapconnection); |
88 | return false; |
89 | } |
90 | |
91 | // Try to bind with current username and password |
139ebfdb |
92 | $ldap_login = @ldap_bind($ldapconnection, $ldap_user_dn, $extpassword); |
b9ddb2d5 |
93 | ldap_close($ldapconnection); |
94 | if ($ldap_login) { |
95 | return true; |
96 | } |
97 | } |
98 | else { |
99 | @ldap_close($ldapconnection); |
e8b9d76a |
100 | print_error('auth_ldap_noconnect','auth',$this->config->host_url); |
b9ddb2d5 |
101 | } |
102 | return false; |
103 | } |
104 | |
105 | /** |
106 | * reads userinformation from ldap and return it in array() |
107 | * |
108 | * Read user information from external database and returns it as array(). |
109 | * Function should return all information available. If you are saving |
110 | * this information to moodle user-table you should honor syncronization flags |
111 | * |
139ebfdb |
112 | * @param string $username username (with system magic quotes) |
113 | * |
114 | * @return mixed array with no magic quotes or false on error |
b9ddb2d5 |
115 | */ |
116 | function get_userinfo($username) { |
139ebfdb |
117 | $textlib = textlib_get_instance(); |
118 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
119 | |
b9ddb2d5 |
120 | $ldapconnection = $this->ldap_connect(); |
b9ddb2d5 |
121 | $attrmap = $this->ldap_attributes(); |
139ebfdb |
122 | |
b9ddb2d5 |
123 | $result = array(); |
124 | $search_attribs = array(); |
139ebfdb |
125 | |
b9ddb2d5 |
126 | foreach ($attrmap as $key=>$values) { |
127 | if (!is_array($values)) { |
128 | $values = array($values); |
129 | } |
130 | foreach ($values as $value) { |
131 | if (!in_array($value, $search_attribs)) { |
132 | array_push($search_attribs, $value); |
139ebfdb |
133 | } |
b9ddb2d5 |
134 | } |
135 | } |
136 | |
139ebfdb |
137 | $user_dn = $this->ldap_find_userdn($ldapconnection, $extusername); |
b9ddb2d5 |
138 | |
139ebfdb |
139 | if (!$user_info_result = ldap_read($ldapconnection, $user_dn, $this->config->objectclass, $search_attribs)) { |
140 | return false; // error! |
141 | } |
142 | $user_entry = $this->ldap_get_entries($ldapconnection, $user_info_result); |
143 | if (empty($user_entry)) { |
144 | return false; // entry not found |
145 | } |
146 | |
147 | foreach ($attrmap as $key=>$values) { |
148 | if (!is_array($values)) { |
149 | $values = array($values); |
150 | } |
151 | $ldapval = NULL; |
152 | foreach ($values as $value) { |
a8d58c58 |
153 | if ($value == 'dn') { |
154 | $result[$key] = $user_dn; |
155 | } |
139ebfdb |
156 | if (!array_key_exists($value, $user_entry[0])) { |
157 | continue; // wrong data mapping! |
b9ddb2d5 |
158 | } |
139ebfdb |
159 | if (is_array($user_entry[0][$value])) { |
160 | $newval = $textlib->convert($user_entry[0][$value][0], $this->config->ldapencoding, 'utf-8'); |
161 | } else { |
162 | $newval = $textlib->convert($user_entry[0][$value], $this->config->ldapencoding, 'utf-8'); |
b9ddb2d5 |
163 | } |
139ebfdb |
164 | if (!empty($newval)) { // favour ldap entries that are set |
165 | $ldapval = $newval; |
b9ddb2d5 |
166 | } |
167 | } |
139ebfdb |
168 | if (!is_null($ldapval)) { |
169 | $result[$key] = $ldapval; |
170 | } |
b9ddb2d5 |
171 | } |
172 | |
173 | @ldap_close($ldapconnection); |
b9ddb2d5 |
174 | return $result; |
175 | } |
176 | |
177 | /** |
178 | * reads userinformation from ldap and return it in an object |
179 | * |
139ebfdb |
180 | * @param string $username username (with system magic quotes) |
181 | * @return mixed object or false on error |
b9ddb2d5 |
182 | */ |
183 | function get_userinfo_asobj($username) { |
139ebfdb |
184 | $user_array = $this->get_userinfo($username); |
185 | if ($user_array == false) { |
186 | return false; //error or not found |
187 | } |
188 | $user_array = truncate_userinfo($user_array); |
189 | $user = new object(); |
b9ddb2d5 |
190 | foreach ($user_array as $key=>$value) { |
191 | $user->{$key} = $value; |
192 | } |
193 | return $user; |
194 | } |
195 | |
196 | /** |
197 | * returns all usernames from external database |
198 | * |
199 | * get_userlist returns all usernames from external database |
200 | * |
139ebfdb |
201 | * @return array |
b9ddb2d5 |
202 | */ |
203 | function get_userlist() { |
b9ddb2d5 |
204 | return $this->ldap_get_userlist("({$this->config->user_attribute}=*)"); |
205 | } |
206 | |
207 | /** |
208 | * checks if user exists on external db |
139ebfdb |
209 | * |
210 | * @param string $username (with system magic quotes) |
b9ddb2d5 |
211 | */ |
212 | function user_exists($username) { |
139ebfdb |
213 | |
214 | $textlib = textlib_get_instance(); |
215 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
216 | |
217 | //returns true if given username exist on ldap |
218 | $users = $this->ldap_get_userlist("({$this->config->user_attribute}=".$this->filter_addslashes($extusername).")"); |
219 | return count($users); |
b9ddb2d5 |
220 | } |
221 | |
222 | /** |
139ebfdb |
223 | * Creates a new user on external database. |
b9ddb2d5 |
224 | * By using information in userobject |
225 | * Use user_exists to prevent dublicate usernames |
226 | * |
139ebfdb |
227 | * @param mixed $userobject Moodle userobject (with system magic quotes) |
228 | * @param mixed $plainpass Plaintext password (with system magic quotes) |
b9ddb2d5 |
229 | */ |
230 | function user_create($userobject, $plainpass) { |
139ebfdb |
231 | $textlib = textlib_get_instance(); |
232 | $extusername = $textlib->convert(stripslashes($userobject->username), 'utf-8', $this->config->ldapencoding); |
233 | $extpassword = $textlib->convert(stripslashes($plainpass), 'utf-8', $this->config->ldapencoding); |
234 | |
344514fc |
235 | switch ($this->config->passtype) { |
236 | case 'md5': |
237 | $extpassword = '{MD5}' . base64_encode(pack('H*', md5($extpassword))); |
238 | break; |
239 | case 'sha1': |
240 | $extpassword = '{SHA}' . base64_encode(pack('H*', sha1($extpassword))); |
241 | break; |
242 | case 'plaintext': |
243 | default: |
244 | break; // plaintext |
245 | } |
246 | |
b9ddb2d5 |
247 | $ldapconnection = $this->ldap_connect(); |
248 | $attrmap = $this->ldap_attributes(); |
139ebfdb |
249 | |
b9ddb2d5 |
250 | $newuser = array(); |
139ebfdb |
251 | |
b9ddb2d5 |
252 | foreach ($attrmap as $key => $values) { |
253 | if (!is_array($values)) { |
254 | $values = array($values); |
255 | } |
256 | foreach ($values as $value) { |
257 | if (!empty($userobject->$key) ) { |
139ebfdb |
258 | $newuser[$value] = $textlib->convert(stripslashes($userobject->$key), 'utf-8', $this->config->ldapencoding); |
b9ddb2d5 |
259 | } |
260 | } |
261 | } |
139ebfdb |
262 | |
b9ddb2d5 |
263 | //Following sets all mandatory and other forced attribute values |
264 | //User should be creted as login disabled untill email confirmation is processed |
139ebfdb |
265 | //Feel free to add your user type and send patches to paca@sci.fi to add them |
b9ddb2d5 |
266 | //Moodle distribution |
267 | |
268 | switch ($this->config->user_type) { |
269 | case 'edir': |
139ebfdb |
270 | $newuser['objectClass'] = array("inetOrgPerson","organizationalPerson","person","top"); |
271 | $newuser['uniqueId'] = $extusername; |
272 | $newuser['logindisabled'] = "TRUE"; |
273 | $newuser['userpassword'] = $extpassword; |
b9ddb2d5 |
274 | break; |
275 | default: |
4db13f94 |
276 | print_error('auth_ldap_unsupportedusertype','auth','',$this->config->user_type); |
b9ddb2d5 |
277 | } |
139ebfdb |
278 | $uadd = $this->ldap_add($ldapconnection, $this->config->user_attribute.'="'.$this->ldap_addslashes($userobject->username).','.$this->config->create_context.'"', $newuser); |
b9ddb2d5 |
279 | ldap_close($ldapconnection); |
280 | return $uadd; |
b9ddb2d5 |
281 | |
b9ddb2d5 |
282 | } |
283 | |
4db13f94 |
284 | function can_signup() { |
285 | return (!empty($this->config->auth_user_create) and !empty($this->config->create_context)); |
286 | } |
287 | |
288 | /** |
289 | * Sign up a new user ready for confirmation. |
290 | * Password is passed in plaintext. |
291 | * |
292 | * @param object $user new user object (with system magic quotes) |
293 | * @param boolean $notify print notice with link and terminate |
294 | */ |
295 | function user_signup($user, $notify=true) { |
296 | if ($this->user_exists($user->username)) { |
297 | print_error('auth_ldap_user_exists', 'auth'); |
298 | } |
299 | |
300 | $plainslashedpassword = $user->password; |
301 | unset($user->password); |
302 | |
303 | if (! $this->user_create($user, $plainslashedpassword)) { |
304 | print_error('auth_ldap_create_error', 'auth'); |
305 | } |
306 | |
307 | if (! ($user->id = insert_record('user', $user)) ) { |
308 | print_error('auth_emailnoinsert', 'auth'); |
309 | } |
310 | |
311 | $this->update_user_record($user->username); |
312 | update_internal_user_password($user, $plainslashedpassword); |
313 | |
314 | if (! send_confirmation_email($user)) { |
315 | print_error('auth_emailnoemail', 'auth'); |
316 | } |
317 | |
318 | if ($notify) { |
319 | global $CFG; |
320 | $emailconfirm = get_string('emailconfirm'); |
321 | print_header($emailconfirm, $emailconfirm, $emailconfirm); |
322 | notice(get_string('emailconfirmsent', '', $user->email), "$CFG->wwwroot/index.php"); |
323 | } else { |
324 | return true; |
325 | } |
326 | } |
327 | |
328 | /** |
329 | * Returns true if plugin allows confirming of new users. |
330 | * |
331 | * @return bool |
332 | */ |
333 | function can_confirm() { |
334 | return $this->can_signup(); |
335 | } |
336 | |
337 | /** |
338 | * Confirm the new user as registered. |
339 | * |
340 | * @param string $username (with system magic quotes) |
341 | * @param string $confirmsecret (with system magic quotes) |
342 | */ |
343 | function user_confirm($username, $confirmsecret) { |
344 | $user = get_complete_user_data('username', $username); |
345 | |
346 | if (!empty($user)) { |
347 | if ($user->confirmed) { |
348 | return AUTH_CONFIRM_ALREADY; |
349 | |
350 | } else if ($user->auth != 'ldap') { |
351 | return AUTH_CONFIRM_ERROR; |
352 | |
353 | } else if ($user->secret == stripslashes($confirmsecret)) { // They have provided the secret key to get in |
354 | if (!$this->user_activate($username)) { |
355 | return AUTH_CONFIRM_FAIL; |
356 | } |
357 | if (!set_field("user", "confirmed", 1, "id", $user->id)) { |
358 | return AUTH_CONFIRM_FAIL; |
359 | } |
360 | if (!set_field("user", "firstaccess", time(), "id", $user->id)) { |
361 | return AUTH_CONFIRM_FAIL; |
362 | } |
363 | return AUTH_CONFIRM_OK; |
364 | } |
365 | } else { |
366 | return AUTH_CONFIRM_ERROR; |
367 | } |
368 | } |
369 | |
b9ddb2d5 |
370 | /** |
371 | * return number of days to user password expires |
372 | * |
373 | * If userpassword does not expire it should return 0. If password is already expired |
374 | * it should return negative value. |
375 | * |
6bc1e5d5 |
376 | * @param mixed $username username (with system magic quotes) |
b9ddb2d5 |
377 | * @return integer |
378 | */ |
379 | function password_expire($username) { |
2cef74f9 |
380 | $result = 0; |
139ebfdb |
381 | |
382 | $textlib = textlib_get_instance(); |
383 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
384 | |
b9ddb2d5 |
385 | $ldapconnection = $this->ldap_connect(); |
139ebfdb |
386 | $user_dn = $this->ldap_find_userdn($ldapconnection, $extusername); |
b9ddb2d5 |
387 | $search_attribs = array($this->config->expireattr); |
388 | $sr = ldap_read($ldapconnection, $user_dn, 'objectclass=*', $search_attribs); |
389 | if ($sr) { |
139ebfdb |
390 | $info = $this->ldap_get_entries($ldapconnection, $sr); |
2cef74f9 |
391 | if (!empty ($info) and !empty($info[0][$this->config->expireattr][0])) { |
392 | $expiretime = $this->ldap_expirationtime2unix($info[0][$this->config->expireattr][0], $ldapconnection, $user_dn); |
393 | if ($expiretime != 0) { |
394 | $now = time(); |
395 | if ($expiretime > $now) { |
396 | $result = ceil(($expiretime - $now) / DAYSECS); |
397 | } |
398 | else { |
399 | $result = floor(($expiretime - $now) / DAYSECS); |
400 | } |
139ebfdb |
401 | } |
b9ddb2d5 |
402 | } |
139ebfdb |
403 | } else { |
b9ddb2d5 |
404 | error_log("ldap: password_expire did't find expiration time."); |
405 | } |
406 | |
407 | //error_log("ldap: password_expire user $user_dn expires in $result days!"); |
408 | return $result; |
409 | } |
410 | |
411 | /** |
412 | * syncronizes user fron external db to moodle user table |
413 | * |
139ebfdb |
414 | * Sync is now using username attribute. |
415 | * |
416 | * Syncing users removes or suspends users that dont exists anymore in external db. |
417 | * Creates new users and updates coursecreator status of users. |
418 | * |
419 | * @param int $bulk_insert_records will insert $bulkinsert_records per insert statement |
420 | * valid only with $unsafe. increase to a couple thousand for |
421 | * blinding fast inserts -- but test it: you may hit mysqld's |
422 | * max_allowed_packet limit. |
423 | * @param bool $do_updates will do pull in data updates from ldap if relevant |
b9ddb2d5 |
424 | */ |
139ebfdb |
425 | function sync_users ($bulk_insert_records = 1000, $do_updates = true) { |
b9ddb2d5 |
426 | |
fa96bfaa |
427 | global $CFG; |
428 | |
139ebfdb |
429 | $textlib = textlib_get_instance(); |
430 | |
fa96bfaa |
431 | $droptablesql = array(); /// sql commands to drop the table (because session scope could be a problem for |
432 | /// some persistent drivers like ODBTP (mssql) or if this function is invoked |
433 | /// from within a PHP application using persistent connections |
b9ddb2d5 |
434 | |
139ebfdb |
435 | // configure a temp table |
436 | print "Configuring temp table\n"; |
fa96bfaa |
437 | switch (strtolower($CFG->dbfamily)) { |
438 | case 'mysql': |
439 | $temptable = $CFG->prefix . 'extuser'; |
440 | $droptablesql[] = 'DROP TEMPORARY TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) |
441 | execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later |
442 | echo "Creating temp table $temptable\n"; |
139ebfdb |
443 | execute_sql('CREATE TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username)) TYPE=MyISAM', false); |
fa96bfaa |
444 | break; |
445 | case 'postgres': |
446 | $temptable = $CFG->prefix . 'extuser'; |
447 | $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) |
448 | execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later |
449 | echo "Creating temp table $temptable\n"; |
450 | $bulk_insert_records = 1; // no support for multiple sets of values |
139ebfdb |
451 | execute_sql('CREATE TEMPORARY TABLE '. $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))', false); |
fa96bfaa |
452 | break; |
453 | case 'mssql': |
454 | $temptable = '#'.$CFG->prefix . 'extuser'; /// MSSQL temp tables begin with # |
455 | $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) |
456 | execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later |
457 | echo "Creating temp table $temptable\n"; |
458 | $bulk_insert_records = 1; // no support for multiple sets of values |
139ebfdb |
459 | execute_sql('CREATE TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))', false); |
fa96bfaa |
460 | break; |
461 | case 'oracle': |
462 | $temptable = $CFG->prefix . 'extuser'; |
463 | $droptablesql[] = 'TRUNCATE TABLE ' . $temptable; // oracle requires truncate before being able to drop a temp table |
464 | $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) |
465 | execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later |
466 | echo "Creating temp table $temptable\n"; |
467 | $bulk_insert_records = 1; // no support for multiple sets of values |
139ebfdb |
468 | execute_sql('CREATE GLOBAL TEMPORARY TABLE '.$temptable.' (username VARCHAR(64), PRIMARY KEY (username)) ON COMMIT PRESERVE ROWS', false); |
fa96bfaa |
469 | break; |
b9ddb2d5 |
470 | } |
471 | |
139ebfdb |
472 | print "Connecting to ldap...\n"; |
b9ddb2d5 |
473 | $ldapconnection = $this->ldap_connect(); |
474 | |
475 | if (!$ldapconnection) { |
476 | @ldap_close($ldapconnection); |
139ebfdb |
477 | print get_string('auth_ldap_noconnect','auth',$this->config->host_url); |
478 | exit; |
b9ddb2d5 |
479 | } |
480 | |
481 | //// |
482 | //// get user's list from ldap to sql in a scalable fashion |
483 | //// |
484 | // prepare some data we'll need |
b9ddb2d5 |
485 | $filter = "(&(".$this->config->user_attribute."=*)(".$this->config->objectclass."))"; |
486 | |
487 | $contexts = explode(";",$this->config->contexts); |
139ebfdb |
488 | |
b9ddb2d5 |
489 | if (!empty($this->config->create_context)) { |
490 | array_push($contexts, $this->config->create_context); |
491 | } |
492 | |
493 | $fresult = array(); |
b9ddb2d5 |
494 | foreach ($contexts as $context) { |
495 | $context = trim($context); |
496 | if (empty($context)) { |
497 | continue; |
498 | } |
499 | begin_sql(); |
500 | if ($this->config->search_sub) { |
501 | //use ldap_search to find first user from subtree |
502 | $ldap_result = ldap_search($ldapconnection, $context, |
503 | $filter, |
504 | array($this->config->user_attribute)); |
139ebfdb |
505 | } else { |
b9ddb2d5 |
506 | //search only in this context |
507 | $ldap_result = ldap_list($ldapconnection, $context, |
508 | $filter, |
509 | array($this->config->user_attribute)); |
510 | } |
511 | |
512 | if ($entry = ldap_first_entry($ldapconnection, $ldap_result)) { |
513 | do { |
139ebfdb |
514 | $value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute); |
515 | $value = $textlib->convert($value[0], $this->config->ldapencoding, 'utf-8'); |
b9ddb2d5 |
516 | array_push($fresult, $value); |
517 | if (count($fresult) >= $bulk_insert_records) { |
fa96bfaa |
518 | $this->ldap_bulk_insert($fresult, $temptable); |
139ebfdb |
519 | $fresult = array(); |
520 | } |
521 | } while ($entry = ldap_next_entry($ldapconnection, $entry)); |
b9ddb2d5 |
522 | } |
139ebfdb |
523 | unset($ldap_result); // free mem |
b9ddb2d5 |
524 | |
525 | // insert any remaining users and release mem |
526 | if (count($fresult)) { |
fa96bfaa |
527 | $this->ldap_bulk_insert($fresult, $temptable); |
139ebfdb |
528 | $fresult = array(); |
b9ddb2d5 |
529 | } |
530 | commit_sql(); |
531 | } |
b9ddb2d5 |
532 | |
533 | /// preserve our user database |
534 | /// if the temp table is empty, it probably means that something went wrong, exit |
535 | /// so as to avoid mass deletion of users; which is hard to undo |
139ebfdb |
536 | $count = get_record_sql('SELECT COUNT(username) AS count, 1 FROM ' . $temptable); |
b9ddb2d5 |
537 | $count = $count->{'count'}; |
538 | if ($count < 1) { |
539 | print "Did not get any users from LDAP -- error? -- exiting\n"; |
540 | exit; |
fa96bfaa |
541 | } else { |
139ebfdb |
542 | print "Got $count records from LDAP\n\n"; |
b9ddb2d5 |
543 | } |
544 | |
b9ddb2d5 |
545 | |
139ebfdb |
546 | /// User removal |
547 | // find users in DB that aren't in ldap -- to be removed! |
548 | // this is still not as scalable (but how often do we mass delete?) |
549 | if (!empty($this->config->removeuser)) { |
550 | $sql = "SELECT u.id, u.username, u.email |
551 | FROM {$CFG->prefix}user u |
552 | LEFT JOIN $temptable e ON u.username = e.username |
553 | WHERE u.auth='ldap' |
554 | AND u.deleted=0 |
555 | AND e.username IS NULL"; |
556 | $remove_users = get_records_sql($sql); |
557 | |
558 | if (!empty($remove_users)) { |
559 | print "User entries to remove: ". count($remove_users) . "\n"; |
560 | |
561 | begin_sql(); |
562 | foreach ($remove_users as $user) { |
563 | if ($this->config->removeuser == 2) { |
564 | //following is copy pasted from admin/user.php |
565 | //maybe this should moved to function in lib/datalib.php |
566 | $updateuser = new object(); |
567 | $updateuser->id = $user->id; |
568 | $updateuser->deleted = 1; |
569 | $updateuser->username = addslashes("$user->email.".time()); // Remember it just in case |
570 | $updateuser->email = ''; // Clear this field to free it up |
571 | $updateuser->idnumber = ''; // Clear this field to free it up |
572 | $updateuser->timemodified = time(); |
573 | if (update_record('user', $updateuser)) { |
574 | delete_records('role_assignments', 'userid', $user->id); // unassign all roles |
575 | //copy pasted part ends |
576 | echo "\t"; print_string('auth_dbdeleteuser', 'auth', array($user->username, $user->id)); echo "\n"; |
577 | } else { |
578 | echo "\t"; print_string('auth_dbdeleteusererror', 'auth', $user->username); echo "\n"; |
579 | } |
580 | } else if ($this->config->removeuser == 1) { |
581 | $updateuser = new object(); |
582 | $updateuser->id = $user->id; |
583 | $updateuser->auth = 'nologin'; |
584 | if (update_record('user', $updateuser)) { |
585 | echo "\t"; print_string('auth_dbsuspenduser', 'auth', array($user->username, $user->id)); echo "\n"; |
586 | } else { |
587 | echo "\t"; print_string('auth_dbsuspendusererror', 'auth', $user->username); echo "\n"; |
588 | } |
589 | } |
b9ddb2d5 |
590 | } |
139ebfdb |
591 | commit_sql(); |
592 | } else { |
593 | print "No user entries to be removed\n"; |
594 | } |
595 | unset($remove_users); // free mem! |
596 | } |
597 | |
598 | /// Revive suspended users |
599 | if (!empty($this->config->removeuser) and $this->config->removeuser == 1) { |
600 | $sql = "SELECT u.id, u.username |
601 | FROM $temptable e, {$CFG->prefix}user u |
602 | WHERE e.username=u.username |
603 | AND u.auth='nologin'"; |
604 | $revive_users = get_records_sql($sql); |
605 | |
606 | if (!empty($revive_users)) { |
607 | print "User entries to be revived: ". count($revive_users) . "\n"; |
608 | |
609 | begin_sql(); |
610 | foreach ($revive_users as $user) { |
611 | $updateuser = new object(); |
612 | $updateuser->id = $user->id; |
613 | $updateuser->auth = 'ldap'; |
614 | if (update_record('user', $updateuser)) { |
615 | echo "\t"; print_string('auth_dbreviveser', 'auth', array($user->username, $user->id)); echo "\n"; |
616 | } else { |
617 | echo "\t"; print_string('auth_dbreviveusererror', 'auth', $user->username); echo "\n"; |
618 | } |
b9ddb2d5 |
619 | } |
139ebfdb |
620 | commit_sql(); |
621 | } else { |
622 | print "No user entries to be revived\n"; |
623 | } |
624 | |
625 | unset($revive_users); |
fa96bfaa |
626 | } |
b9ddb2d5 |
627 | |
139ebfdb |
628 | |
629 | /// User Updates - time-consuming (optional) |
b9ddb2d5 |
630 | if ($do_updates) { |
631 | // narrow down what fields we need to update |
632 | $all_keys = array_keys(get_object_vars($this->config)); |
633 | $updatekeys = array(); |
634 | foreach ($all_keys as $key) { |
635 | if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) { |
636 | // if we have a field to update it from |
139ebfdb |
637 | // and it must be updated 'onlogin' we |
b9ddb2d5 |
638 | // update it on cron |
639 | if ( !empty($this->config->{'field_map_'.$match[1]}) |
139ebfdb |
640 | and $this->config->{$match[0]} === 'onlogin') { |
b9ddb2d5 |
641 | array_push($updatekeys, $match[1]); // the actual key name |
642 | } |
643 | } |
644 | } |
645 | // print_r($all_keys); print_r($updatekeys); |
646 | unset($all_keys); unset($key); |
139ebfdb |
647 | |
fa96bfaa |
648 | } else { |
649 | print "No updates to be done\n"; |
b9ddb2d5 |
650 | } |
139ebfdb |
651 | if ( $do_updates and !empty($updatekeys) ) { // run updates only if relevant |
652 | $users = get_records_sql("SELECT u.username, u.id |
653 | FROM {$CFG->prefix}user u |
654 | WHERE u.deleted=0 AND u.auth='ldap'"); |
b9ddb2d5 |
655 | if (!empty($users)) { |
656 | print "User entries to update: ". count($users). "\n"; |
139ebfdb |
657 | |
b9ddb2d5 |
658 | $sitecontext = get_context_instance(CONTEXT_SYSTEM); |
139ebfdb |
659 | if (!empty($this->config->creators) and !empty($this->config->memberattribute) |
660 | and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { |
661 | $creatorrole = array_shift($roles); // We can only use one, let's use the first one |
662 | } else { |
663 | $creatorrole = false; |
664 | } |
b9ddb2d5 |
665 | |
139ebfdb |
666 | begin_sql(); |
667 | $xcount = 0; |
668 | $maxxcount = 100; |
b9ddb2d5 |
669 | |
139ebfdb |
670 | foreach ($users as $user) { |
671 | echo "\t"; print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id)); |
672 | if (!$this->update_user_record(addslashes($user->username), $updatekeys)) { |
673 | echo " - ".get_string('skipped'); |
674 | } |
675 | echo "\n"; |
676 | $xcount++; |
677 | |
678 | // update course creators if needed |
679 | if ($creatorrole !== false) { |
680 | if ($this->iscreator($user->username)) { |
681 | role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap'); |
682 | } else { |
6bc1e5d5 |
683 | role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'ldap'); |
b9ddb2d5 |
684 | } |
139ebfdb |
685 | } |
686 | |
687 | if ($xcount++ > $maxxcount) { |
688 | commit_sql(); |
689 | begin_sql(); |
690 | $xcount = 0; |
691 | } |
b9ddb2d5 |
692 | } |
139ebfdb |
693 | commit_sql(); |
694 | unset($users); // free mem |
b9ddb2d5 |
695 | } |
fa96bfaa |
696 | } else { // end do updates |
697 | print "No updates to be done\n"; |
698 | } |
139ebfdb |
699 | |
700 | /// User Additions |
b9ddb2d5 |
701 | // find users missing in DB that are in LDAP |
702 | // note that get_records_sql wants at least 2 fields returned, |
703 | // and gives me a nifty object I don't want. |
139ebfdb |
704 | // note: we do not care about deleted accounts anymore, this feature was replaced by suspending to nologin auth plugin |
705 | $sql = "SELECT e.username, e.username |
706 | FROM $temptable e LEFT JOIN {$CFG->prefix}user u ON e.username = u.username |
707 | WHERE u.id IS NULL"; |
708 | $add_users = get_records_sql($sql); // get rid of the fat |
709 | |
b9ddb2d5 |
710 | if (!empty($add_users)) { |
711 | print "User entries to add: ". count($add_users). "\n"; |
712 | |
139ebfdb |
713 | $sitecontext = get_context_instance(CONTEXT_SYSTEM); |
714 | if (!empty($this->config->creators) and !empty($this->config->memberattribute) |
715 | and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { |
b9ddb2d5 |
716 | $creatorrole = array_shift($roles); // We can only use one, let's use the first one |
139ebfdb |
717 | } else { |
718 | $creatorrole = false; |
b9ddb2d5 |
719 | } |
720 | |
721 | begin_sql(); |
722 | foreach ($add_users as $user) { |
139ebfdb |
723 | $user = $this->get_userinfo_asobj(addslashes($user->username)); |
724 | |
b9ddb2d5 |
725 | // prep a few params |
b7b50143 |
726 | $user->modified = time(); |
727 | $user->confirmed = 1; |
139ebfdb |
728 | $user->auth = 'ldap'; |
b7b50143 |
729 | $user->mnethostid = $CFG->mnet_localhost_id; |
139ebfdb |
730 | if (empty($user->lang)) { |
731 | $user->lang = $CFG->lang; |
b9ddb2d5 |
732 | } |
139ebfdb |
733 | |
734 | $user = addslashes_recursive($user); |
735 | |
736 | if ($id = insert_record('user',$user)) { |
737 | echo "\t"; print_string('auth_dbinsertuser', 'auth', array(stripslashes($user->username), $id)); echo "\n"; |
738 | $userobj = $this->update_user_record($user->username); |
739 | if (!empty($this->config->forcechangepassword)) { |
740 | set_user_preference('auth_forcepasswordchange', 1, $userobj->id); |
b9ddb2d5 |
741 | } |
139ebfdb |
742 | } else { |
743 | echo "\t"; print_string('auth_dbinsertusererror', 'auth', $user->username); echo "\n"; |
744 | } |
745 | |
746 | // add course creators if needed |
747 | if ($creatorrole !== false and $this->iscreator(stripslashes($user->username))) { |
748 | role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap'); |
b9ddb2d5 |
749 | } |
750 | } |
751 | commit_sql(); |
752 | unset($add_users); // free mem |
fa96bfaa |
753 | } else { |
754 | print "No users to be added\n"; |
b9ddb2d5 |
755 | } |
756 | return true; |
757 | } |
758 | |
139ebfdb |
759 | /** |
760 | * Update a local user record from an external source. |
761 | * This is a lighter version of the one in moodlelib -- won't do |
b9ddb2d5 |
762 | * expensive ops such as enrolment. |
763 | * |
139ebfdb |
764 | * If you don't pass $updatekeys, there is a performance hit and |
765 | * values removed from LDAP won't be removed from moodle. |
766 | * |
767 | * @param string $username username (with system magic quotes) |
b9ddb2d5 |
768 | */ |
769 | function update_user_record($username, $updatekeys = false) { |
b9ddb2d5 |
770 | global $CFG; |
771 | |
772 | //just in case check text case |
773 | $username = trim(moodle_strtolower($username)); |
139ebfdb |
774 | |
b9ddb2d5 |
775 | // get the current user record |
b7b50143 |
776 | $user = get_record('user', 'username', $username, 'mnethostid', $CFG->mnet_localhost_id); |
b9ddb2d5 |
777 | if (empty($user)) { // trouble |
139ebfdb |
778 | error_log("Cannot update non-existent user: ".stripslashes($username)); |
779 | print_error('auth_dbusernotexist','auth',$username); |
b9ddb2d5 |
780 | die; |
781 | } |
782 | |
b7b50143 |
783 | // Protect the userid from being overwritten |
784 | $userid = $user->id; |
785 | |
139ebfdb |
786 | if ($newinfo = $this->get_userinfo($username)) { |
787 | $newinfo = truncate_userinfo($newinfo); |
788 | |
789 | if (empty($updatekeys)) { // all keys? this does not support removing values |
790 | $updatekeys = array_keys($newinfo); |
791 | } |
792 | |
793 | foreach ($updatekeys as $key) { |
794 | if (isset($newinfo[$key])) { |
795 | $value = $newinfo[$key]; |
796 | } else { |
797 | $value = ''; |
b9ddb2d5 |
798 | } |
139ebfdb |
799 | |
800 | if (!empty($this->config->{'field_updatelocal_' . $key})) { |
801 | if ($user->{$key} != $value) { // only update if it's changed |
802 | set_field('user', $key, addslashes($value), 'id', $userid); |
b9ddb2d5 |
803 | } |
804 | } |
805 | } |
139ebfdb |
806 | } else { |
807 | return false; |
b9ddb2d5 |
808 | } |
139ebfdb |
809 | return get_record_select('user', "id = $userid AND deleted = 0"); |
b9ddb2d5 |
810 | } |
811 | |
139ebfdb |
812 | /** |
813 | * Bulk insert in SQL's temp table |
814 | * @param array $users is an array of usernames |
815 | */ |
fa96bfaa |
816 | function ldap_bulk_insert($users, $temptable) { |
817 | |
b9ddb2d5 |
818 | // bulk insert -- superfast with $bulk_insert_records |
139ebfdb |
819 | $sql = 'INSERT INTO ' . $temptable . ' (username) VALUES '; |
b9ddb2d5 |
820 | // make those values safe |
139ebfdb |
821 | $users = addslashes_recursive($users); |
b9ddb2d5 |
822 | // join and quote the whole lot |
139ebfdb |
823 | $sql = $sql . "('" . implode("'),('", $users) . "')"; |
824 | print "\t+ " . count($users) . " users\n"; |
825 | execute_sql($sql, false); |
b9ddb2d5 |
826 | } |
827 | |
828 | |
139ebfdb |
829 | /** |
b9ddb2d5 |
830 | * Activates (enables) user in external db so user can login to external db |
831 | * |
139ebfdb |
832 | * @param mixed $username username (with system magic quotes) |
b9ddb2d5 |
833 | * @return boolen result |
834 | */ |
835 | function user_activate($username) { |
139ebfdb |
836 | $textlib = textlib_get_instance(); |
837 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
838 | |
b9ddb2d5 |
839 | $ldapconnection = $this->ldap_connect(); |
840 | |
139ebfdb |
841 | $userdn = $this->ldap_find_userdn($ldapconnection, $extusername); |
b9ddb2d5 |
842 | switch ($this->config->user_type) { |
843 | case 'edir': |
844 | $newinfo['loginDisabled']="FALSE"; |
845 | break; |
846 | default: |
139ebfdb |
847 | error ('auth: ldap user_activate() does not support selected usertype:"'.$this->config->user_type.'" (..yet)'); |
848 | } |
b9ddb2d5 |
849 | $result = ldap_modify($ldapconnection, $userdn, $newinfo); |
850 | ldap_close($ldapconnection); |
851 | return $result; |
852 | } |
853 | |
139ebfdb |
854 | /** |
b9ddb2d5 |
855 | * Disables user in external db so user can't login to external db |
856 | * |
857 | * @param mixed $username username |
858 | * @return boolean result |
859 | */ |
139ebfdb |
860 | /* function user_disable($username) { |
861 | $textlib = textlib_get_instance(); |
862 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
b9ddb2d5 |
863 | |
864 | $ldapconnection = $this->ldap_connect(); |
865 | |
139ebfdb |
866 | $userdn = $this->ldap_find_userdn($ldapconnection, $extusername); |
b9ddb2d5 |
867 | switch ($this->config->user_type) { |
868 | case 'edir': |
869 | $newinfo['loginDisabled']="TRUE"; |
870 | break; |
871 | default: |
139ebfdb |
872 | error ('auth: ldap user_disable() does not support selected usertype (..yet)'); |
873 | } |
b9ddb2d5 |
874 | $result = ldap_modify($ldapconnection, $userdn, $newinfo); |
875 | ldap_close($ldapconnection); |
876 | return $result; |
139ebfdb |
877 | }*/ |
b9ddb2d5 |
878 | |
139ebfdb |
879 | /** |
b9ddb2d5 |
880 | * Returns true if user should be coursecreator. |
881 | * |
6bc1e5d5 |
882 | * @param mixed $username username (without system magic quotes) |
b9ddb2d5 |
883 | * @return boolean result |
884 | */ |
6bc1e5d5 |
885 | function iscreator($username) { |
139ebfdb |
886 | if (empty($this->config->creators) or empty($this->config->memberattribute)) { |
6bc1e5d5 |
887 | return null; |
b9ddb2d5 |
888 | } |
139ebfdb |
889 | |
890 | $textlib = textlib_get_instance(); |
891 | $extusername = $textlib->convert($username, 'utf-8', $this->config->ldapencoding); |
892 | |
6bc1e5d5 |
893 | return (boolean)$this->ldap_isgroupmember($extusername, $this->config->creators); |
b9ddb2d5 |
894 | } |
895 | |
139ebfdb |
896 | /** |
b9ddb2d5 |
897 | * Called when the user record is updated. |
139ebfdb |
898 | * Modifies user in external database. It takes olduser (before changes) and newuser (after changes) |
b9ddb2d5 |
899 | * conpares information saved modified information to external db. |
900 | * |
139ebfdb |
901 | * @param mixed $olduser Userobject before modifications (without system magic quotes) |
902 | * @param mixed $newuser Userobject new modified userobject (without system magic quotes) |
b9ddb2d5 |
903 | * @return boolean result |
904 | * |
905 | */ |
906 | function user_update($olduser, $newuser) { |
907 | |
139ebfdb |
908 | global $USER; |
909 | |
910 | if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) { |
911 | error_log("ERROR:User renaming not allowed in LDAP"); |
912 | return false; |
913 | } |
914 | |
6bc1e5d5 |
915 | if (isset($olduser->auth) and $olduser->auth != 'ldap') { |
139ebfdb |
916 | return true; // just change auth and skip update |
917 | } |
918 | |
919 | $textlib = textlib_get_instance(); |
920 | $extoldusername = $textlib->convert($olduser->username, 'utf-8', $this->config->ldapencoding); |
b9ddb2d5 |
921 | |
922 | $ldapconnection = $this->ldap_connect(); |
139ebfdb |
923 | |
b9ddb2d5 |
924 | $search_attribs = array(); |
925 | |
139ebfdb |
926 | $attrmap = $this->ldap_attributes(); |
b9ddb2d5 |
927 | foreach ($attrmap as $key => $values) { |
928 | if (!is_array($values)) { |
929 | $values = array($values); |
930 | } |
931 | foreach ($values as $value) { |
932 | if (!in_array($value, $search_attribs)) { |
933 | array_push($search_attribs, $value); |
934 | } |
139ebfdb |
935 | } |
b9ddb2d5 |
936 | } |
937 | |
139ebfdb |
938 | $user_dn = $this->ldap_find_userdn($ldapconnection, $extoldusername); |
b9ddb2d5 |
939 | |
940 | $user_info_result = ldap_read($ldapconnection, $user_dn, |
941 | $this->config->objectclass, $search_attribs); |
942 | |
943 | if ($user_info_result) { |
944 | |
945 | $user_entry = $this->ldap_get_entries($ldapconnection, $user_info_result); |
139ebfdb |
946 | if (empty($user_entry)) { |
947 | return false; // old user not found! |
948 | } else if (count($user_entry) > 1) { |
b9ddb2d5 |
949 | trigger_error("ldap: Strange! More than one user record found in ldap. Only using the first one."); |
139ebfdb |
950 | return false; |
b9ddb2d5 |
951 | } |
952 | $user_entry = $user_entry[0]; |
953 | |
954 | //error_log(var_export($user_entry) . 'fpp' ); |
b9ddb2d5 |
955 | |
139ebfdb |
956 | foreach ($attrmap as $key => $ldapkeys) { |
b9ddb2d5 |
957 | // only process if the moodle field ($key) has changed and we |
958 | // are set to update LDAP with it |
139ebfdb |
959 | if (isset($olduser->$key) and isset($newuser->$key) |
960 | and $olduser->$key !== $newuser->$key |
961 | and !empty($this->config->{'field_updateremote_'. $key})) { |
962 | // for ldap values that could be in more than one |
963 | // ldap key, we will do our best to match |
b9ddb2d5 |
964 | // where they came from |
965 | $ambiguous = true; |
966 | $changed = false; |
967 | if (!is_array($ldapkeys)) { |
968 | $ldapkeys = array($ldapkeys); |
969 | } |
970 | if (count($ldapkeys) < 2) { |
971 | $ambiguous = false; |
972 | } |
139ebfdb |
973 | |
974 | $nuvalue = $textlib->convert($newuser->$key, 'utf-8', $this->config->ldapencoding); |
975 | $ouvalue = $textlib->convert($olduser->$key, 'utf-8', $this->config->ldapencoding); |
976 | |
b9ddb2d5 |
977 | foreach ($ldapkeys as $ldapkey) { |
139ebfdb |
978 | $ldapkey = $ldapkey; |
b9ddb2d5 |
979 | $ldapvalue = $user_entry[$ldapkey][0]; |
980 | if (!$ambiguous) { |
981 | // skip update if the values already match |
139ebfdb |
982 | if ($nuvalue !== $ldapvalue) { |
983 | //this might fail due to schema validation |
984 | if (@ldap_modify($ldapconnection, $user_dn, array($ldapkey => $nuvalue))) { |
985 | continue; |
986 | } else { |
987 | error_log('Error updating LDAP record. Error code: ' |
988 | . ldap_errno($ldapconnection) . '; Error string : ' |
989 | . ldap_err2str(ldap_errno($ldapconnection)) |
990 | . "\nKey ($key) - old moodle value: '$ouvalue' new value: '$nuvalue'"); |
991 | continue; |
992 | } |
b9ddb2d5 |
993 | } |
139ebfdb |
994 | } else { |
b9ddb2d5 |
995 | // ambiguous |
996 | // value empty before in Moodle (and LDAP) - use 1st ldap candidate field |
997 | // no need to guess |
139ebfdb |
998 | if ($ouvalue === '') { // value empty before - use 1st ldap candidate |
999 | //this might fail due to schema validation |
1000 | if (@ldap_modify($ldapconnection, $user_dn, array($ldapkey => $nuvalue))) { |
b9ddb2d5 |
1001 | $changed = true; |
139ebfdb |
1002 | continue; |
1003 | } else { |
1004 | error_log('Error updating LDAP record. Error code: ' |
1005 | . ldap_errno($ldapconnection) . '; Error string : ' |
1006 | . ldap_err2str(ldap_errno($ldapconnection)) |
1007 | . "\nKey ($key) - old moodle value: '$ouvalue' new value: '$nuvalue'"); |
1008 | continue; |
b9ddb2d5 |
1009 | } |
1010 | } |
1011 | |
139ebfdb |
1012 | // we found which ldap key to update! |
1013 | if ($ouvalue !== '' and $ouvalue === $ldapvalue ) { |
1014 | //this might fail due to schema validation |
1015 | if (@ldap_modify($ldapconnection, $user_dn, array($ldapkey => $nuvalue))) { |
b9ddb2d5 |
1016 | $changed = true; |
139ebfdb |
1017 | continue; |
1018 | } else { |
1019 | error_log('Error updating LDAP record. Error code: ' |
b9ddb2d5 |
1020 | . ldap_errno($ldapconnection) . '; Error string : ' |
139ebfdb |
1021 | . ldap_err2str(ldap_errno($ldapconnection)) |
1022 | . "\nKey ($key) - old moodle value: '$ouvalue' new value: '$nuvalue'"); |
1023 | continue; |
b9ddb2d5 |
1024 | } |
1025 | } |
1026 | } |
1027 | } |
139ebfdb |
1028 | |
b9ddb2d5 |
1029 | if ($ambiguous and !$changed) { |
139ebfdb |
1030 | error_log("Failed to update LDAP with ambiguous field $key". |
1031 | " old moodle value: '" . $ouvalue . |
1032 | "' new value '" . $nuvalue ); |
b9ddb2d5 |
1033 | } |
1034 | } |
1035 | } |
139ebfdb |
1036 | } else { |
b9ddb2d5 |
1037 | error_log("ERROR:No user found in LDAP"); |
1038 | @ldap_close($ldapconnection); |
1039 | return false; |
1040 | } |
1041 | |
1042 | @ldap_close($ldapconnection); |
139ebfdb |
1043 | |
b9ddb2d5 |
1044 | return true; |
1045 | |
1046 | } |
1047 | |
fb5c7739 |
1048 | /** |
b9ddb2d5 |
1049 | * changes userpassword in external db |
1050 | * |
1051 | * called when the user password is updated. |
1052 | * changes userpassword in external db |
1053 | * |
139ebfdb |
1054 | * @param object $user User table object (with system magic quotes) |
1055 | * @param string $newpassword Plaintext password (with system magic quotes) |
b9ddb2d5 |
1056 | * @return boolean result |
1057 | * |
1058 | */ |
b9ddb2d5 |
1059 | function user_update_password($user, $newpassword) { |
1060 | /// called when the user password is updated -- it assumes it is called by an admin |
1061 | /// or that you've otherwise checked the user's credentials |
1062 | /// IMPORTANT: $newpassword must be cleartext, not crypted/md5'ed |
1063 | |
139ebfdb |
1064 | global $USER; |
b9ddb2d5 |
1065 | $result = false; |
1066 | $username = $user->username; |
139ebfdb |
1067 | |
1068 | $textlib = textlib_get_instance(); |
1069 | $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); |
1070 | $extpassword = $textlib->convert(stripslashes($newpassword), 'utf-8', $this->config->ldapencoding); |
1071 | |
344514fc |
1072 | switch ($this->config->passtype) { |
1073 | case 'md5': |
1074 | $extpassword = '{MD5}' . base64_encode(pack('H*', md5($extpassword))); |
1075 | break; |
1076 | case 'sha1': |
1077 | $extpassword = '{SHA}' . base64_encode(pack('H*', sha1($extpassword))); |
1078 | break; |
1079 | case 'plaintext': |
1080 | default: |
1081 | break; // plaintext |
1082 | } |
1083 | |
b9ddb2d5 |
1084 | $ldapconnection = $this->ldap_connect(); |
1085 | |
139ebfdb |
1086 | $user_dn = $this->ldap_find_userdn($ldapconnection, $extusername); |
1087 | |
b9ddb2d5 |
1088 | if (!$user_dn) { |
139ebfdb |
1089 | error_log('LDAP Error in user_update_password(). No DN for: ' . stripslashes($user->username)); |
b9ddb2d5 |
1090 | return false; |
1091 | } |
1092 | |
1093 | switch ($this->config->user_type) { |
1094 | case 'edir': |
1095 | //Change password |
139ebfdb |
1096 | $result = ldap_modify($ldapconnection, $user_dn, array('userPassword' => $extpassword)); |
b9ddb2d5 |
1097 | if (!$result) { |
1098 | error_log('LDAP Error in user_update_password(). Error code: ' |
1099 | . ldap_errno($ldapconnection) . '; Error string : ' |
1100 | . ldap_err2str(ldap_errno($ldapconnection))); |
1101 | } |
1102 | //Update password expiration time, grace logins count |
1103 | $search_attribs = array($this->config->expireattr, 'passwordExpirationInterval','loginGraceLimit' ); |
1104 | $sr = ldap_read($ldapconnection, $user_dn, 'objectclass=*', $search_attribs); |
1105 | if ($sr) { |
1106 | $info=$this->ldap_get_entries($ldapconnection, $sr); |
1107 | $newattrs = array(); |
1108 | if (!empty($info[0][$this->config->expireattr][0])) { |
1109 | //Set expiration time only if passwordExpirationInterval is defined |
1110 | if (!empty($info[0]['passwordExpirationInterval'][0])) { |
139ebfdb |
1111 | $expirationtime = time() + $info[0]['passwordExpirationInterval'][0]; |
b9ddb2d5 |
1112 | $ldapexpirationtime = $this->ldap_unix2expirationtime($expirationtime); |
1113 | $newattrs['passwordExpirationTime'] = $ldapexpirationtime; |
139ebfdb |
1114 | } |
b9ddb2d5 |
1115 | |
1116 | //set gracelogin count |
1117 | if (!empty($info[0]['loginGraceLimit'][0])) { |
139ebfdb |
1118 | $newattrs['loginGraceRemaining']= $info[0]['loginGraceLimit'][0]; |
b9ddb2d5 |
1119 | } |
139ebfdb |
1120 | |
b9ddb2d5 |
1121 | //Store attribute changes to ldap |
1122 | $result = ldap_modify($ldapconnection, $user_dn, $newattrs); |
1123 | if (!$result) { |
1124 | error_log('LDAP Error in user_update_password() when modifying expirationtime and/or gracelogins. Error code: ' |
1125 | . ldap_errno($ldapconnection) . '; Error string : ' |
1126 | . ldap_err2str(ldap_errno($ldapconnection))); |
1127 | } |
1128 | } |
1129 | } |
1130 | else { |
1131 | error_log('LDAP Error in user_update_password() when reading password expiration time. Error code: ' |
1132 | . ldap_errno($ldapconnection) . '; Error string : ' |
1133 | . ldap_err2str(ldap_errno($ldapconnection))); |
d0e84e1b |
1134 | } |
1135 | break; |
1136 | |
1137 | case 'ad': |
1138 | // Passwords in Active Directory must be encoded as Unicode |
1139 | // strings (UCS-2 Little Endian format) and surrounded with |
1140 | // double quotes. See http://support.microsoft.com/?kbid=269190 |
1141 | if (!function_exists('mb_convert_encoding')) { |
1142 | error_log ('You need the mbstring extension to change passwords in Active Directory'); |
1143 | return false; |
1144 | } |
1145 | $extpassword = mb_convert_encoding('"'.$extpassword.'"', "UCS-2LE", $this->config->ldapencoding); |
1146 | $result = ldap_modify($ldapconnection, $user_dn, array('unicodePwd' => $extpassword)); |
1147 | if (!$result) { |
1148 | error_log('LDAP Error in user_update_password(). Error code: ' |
1149 | . ldap_errno($ldapconnection) . '; Error string : ' |
1150 | . ldap_err2str(ldap_errno($ldapconnection))); |
139ebfdb |
1151 | } |
b9ddb2d5 |
1152 | break; |
139ebfdb |
1153 | |
b9ddb2d5 |
1154 | default: |
1155 | $usedconnection = &$ldapconnection; |
1156 | // send ldap the password in cleartext, it will md5 it itself |
139ebfdb |
1157 | $result = ldap_modify($ldapconnection, $user_dn, array('userPassword' => $extpassword)); |
b9ddb2d5 |
1158 | if (!$result) { |
139ebfdb |
1159 | error_log('LDAP Error in user_update_password(). Error code: ' |
b9ddb2d5 |
1160 | . ldap_errno($ldapconnection) . '; Error string : ' |
1161 | . ldap_err2str(ldap_errno($ldapconnection))); |
1162 | } |
139ebfdb |
1163 | |
b9ddb2d5 |
1164 | } |
1165 | |
1166 | @ldap_close($ldapconnection); |
1167 | return $result; |
1168 | } |
1169 | |
1170 | //PRIVATE FUNCTIONS starts |
1171 | //private functions are named as ldap_* |
1172 | |
1173 | /** |
1174 | * returns predefined usertypes |
1175 | * |
1176 | * @return array of predefined usertypes |
1177 | */ |
b9ddb2d5 |
1178 | function ldap_suppported_usertypes() { |
139ebfdb |
1179 | $types = array(); |
b9ddb2d5 |
1180 | $types['edir']='Novell Edirectory'; |
1181 | $types['rfc2307']='posixAccount (rfc2307)'; |
1182 | $types['rfc2307bis']='posixAccount (rfc2307bis)'; |
1183 | $types['samba']='sambaSamAccount (v.3.0.7)'; |
139ebfdb |
1184 | $types['ad']='MS ActiveDirectory'; |
1185 | $types['default']=get_string('default'); |
b9ddb2d5 |
1186 | return $types; |
139ebfdb |
1187 | } |
1188 | |
b9ddb2d5 |
1189 | |
b9ddb2d5 |
1190 | /** |
139ebfdb |
1191 | * Initializes needed variables for ldap-module |
b9ddb2d5 |
1192 | * |
1193 | * Uses names defined in ldap_supported_usertypes. |
1194 | * $default is first defined as: |
1195 | * $default['pseudoname'] = array( |
1196 | * 'typename1' => 'value', |
1197 | * 'typename2' => 'value' |
1198 | * .... |
1199 | * ); |
1200 | * |
1201 | * @return array of default values |
1202 | */ |
1203 | function ldap_getdefaults() { |
1204 | $default['objectclass'] = array( |
1205 | 'edir' => 'User', |
139ebfdb |
1206 | 'rfc2307' => 'posixAccount', |
1207 | 'rfc2307bis' => 'posixAccount', |
b9ddb2d5 |
1208 | 'samba' => 'sambaSamAccount', |
1209 | 'ad' => 'user', |
1210 | 'default' => '*' |
1211 | ); |
1212 | $default['user_attribute'] = array( |
1213 | 'edir' => 'cn', |
1214 | 'rfc2307' => 'uid', |
1215 | 'rfc2307bis' => 'uid', |
1216 | 'samba' => 'uid', |
1217 | 'ad' => 'cn', |
1218 | 'default' => 'cn' |
1219 | ); |
1220 | $default['memberattribute'] = array( |
1221 | 'edir' => 'member', |
1222 | 'rfc2307' => 'member', |
1223 | 'rfc2307bis' => 'member', |
1224 | 'samba' => 'member', |
139ebfdb |
1225 | 'ad' => 'member', |
b9ddb2d5 |
1226 | 'default' => 'member' |
1227 | ); |
1228 | $default['memberattribute_isdn'] = array( |
1229 | 'edir' => '1', |
1230 | 'rfc2307' => '0', |
1231 | 'rfc2307bis' => '1', |
1232 | 'samba' => '0', //is this right? |
1233 | 'ad' => '1', |
1234 | 'default' => '0' |
1235 | ); |
1236 | $default['expireattr'] = array ( |
1237 | 'edir' => 'passwordExpirationTime', |
1238 | 'rfc2307' => 'shadowExpire', |
1239 | 'rfc2307bis' => 'shadowExpire', |
1240 | 'samba' => '', //No support yet |
1241 | 'ad' => '', //No support yet |
1242 | 'default' => '' |
1243 | ); |
139ebfdb |
1244 | return $default; |
b9ddb2d5 |
1245 | } |
1246 | |
1247 | /** |
1248 | * return binaryfields of selected usertype |
1249 | * |
1250 | * |
1251 | * @return array |
1252 | */ |
1253 | function ldap_getbinaryfields () { |
b9ddb2d5 |
1254 | $binaryfields = array ( |
1255 | 'edir' => array('guid'), |
139ebfdb |
1256 | 'rfc2307' => array(), |
1257 | 'rfc2307bis' => array(), |
b9ddb2d5 |
1258 | 'samba' => array(), |
1259 | 'ad' => array(), |
139ebfdb |
1260 | 'default' => array() |
b9ddb2d5 |
1261 | ); |
1262 | if (!empty($this->config->user_type)) { |
139ebfdb |
1263 | return $binaryfields[$this->config->user_type]; |
b9ddb2d5 |
1264 | } |
1265 | else { |
1266 | return $binaryfields['default']; |
139ebfdb |
1267 | } |
b9ddb2d5 |
1268 | } |
1269 | |
1270 | function ldap_isbinary ($field) { |
139ebfdb |
1271 | if (empty($field)) { |
1272 | return false; |
b9ddb2d5 |
1273 | } |
139ebfdb |
1274 | return array_search($field, $this->ldap_getbinaryfields()); |
b9ddb2d5 |
1275 | } |
1276 | |
1277 | /** |
1278 | * take expirationtime and return it as unixseconds |
139ebfdb |
1279 | * |
b9ddb2d5 |
1280 | * takes expriration timestamp as readed from ldap |
1281 | * returns it as unix seconds |
139ebfdb |
1282 | * depends on $this->config->user_type variable |
b9ddb2d5 |
1283 | * |
1284 | * @param mixed time Time stamp readed from ldap as it is. |
1285 | * @return timestamp |
1286 | */ |
1287 | function ldap_expirationtime2unix ($time) { |
b9ddb2d5 |
1288 | $result = false; |
1289 | switch ($this->config->user_type) { |
1290 | case 'edir': |
1291 | $yr=substr($time,0,4); |
1292 | $mo=substr($time,4,2); |
1293 | $dt=substr($time,6,2); |
1294 | $hr=substr($time,8,2); |
1295 | $min=substr($time,10,2); |
1296 | $sec=substr($time,12,2); |
139ebfdb |
1297 | $result = mktime($hr,$min,$sec,$mo,$dt,$yr); |
b9ddb2d5 |
1298 | break; |
1299 | case 'posix': |
1300 | $result = $time * DAYSECS; //The shadowExpire contains the number of DAYS between 01/01/1970 and the actual expiration date |
1301 | break; |
139ebfdb |
1302 | default: |
e8b9d76a |
1303 | print_error('auth_ldap_usertypeundefined', 'auth'); |
b9ddb2d5 |
1304 | } |
1305 | return $result; |
1306 | } |
1307 | |
1308 | /** |
1309 | * takes unixtime and return it formated for storing in ldap |
1310 | * |
1311 | * @param integer unix time stamp |
1312 | */ |
1313 | function ldap_unix2expirationtime($time) { |
b9ddb2d5 |
1314 | $result = false; |
1315 | switch ($this->config->user_type) { |
1316 | case 'edir': |
139ebfdb |
1317 | $result=date('YmdHis', $time).'Z'; |
b9ddb2d5 |
1318 | break; |
1319 | case 'posix': |
1320 | $result = $time ; //Already in correct format |
1321 | break; |
139ebfdb |
1322 | default: |
e8b9d76a |
1323 | print_error('auth_ldap_usertypeundefined2', 'auth'); |
139ebfdb |
1324 | } |
b9ddb2d5 |
1325 | return $result; |
1326 | |
1327 | } |
1328 | |
139ebfdb |
1329 | /** |
b9ddb2d5 |
1330 | * checks if user belong to specific group(s) |
1331 | * |
1332 | * Returns true if user belongs group in grupdns string. |
1333 | * |
1334 | * @param mixed $username username |
1335 | * @param mixed $groupdns string of group dn separated by ; |
1336 | * |
1337 | */ |
139ebfdb |
1338 | function ldap_isgroupmember($extusername='', $groupdns='') { |
b9ddb2d5 |
1339 | // Takes username and groupdn(s) , separated by ; |
1340 | // Returns true if user is member of any given groups |
1341 | |
b9ddb2d5 |
1342 | $ldapconnection = $this->ldap_connect(); |
139ebfdb |
1343 | |
cd874e21 |
1344 | if (empty($extusername) or empty($groupdns)) { |
1345 | return false; |
b9ddb2d5 |
1346 | } |
1347 | |
1348 | if ($this->config->memberattribute_isdn) { |
cd874e21 |
1349 | $memberuser = $this->ldap_find_userdn($ldapconnection, $extusername); |
1350 | } else { |
1351 | $memberuser = $extusername; |
b9ddb2d5 |
1352 | } |
cd874e21 |
1353 | |
1354 | if (empty($memberuser)) { |
1355 | return false; |
b9ddb2d5 |
1356 | } |
1357 | |
1358 | $groups = explode(";",$groupdns); |
139ebfdb |
1359 | |
cd874e21 |
1360 | $result = false; |
b9ddb2d5 |
1361 | foreach ($groups as $group) { |
1362 | $group = trim($group); |
1363 | if (empty($group)) { |
1364 | continue; |
1365 | } |
1366 | //echo "Checking group $group for member $username\n"; |
cd874e21 |
1367 | $search = ldap_read($ldapconnection, $group, '('.$this->config->memberattribute.'='.$this->filter_addslashes($memberuser).')', array($this->config->memberattribute)); |
1368 | if (!empty($search) and ldap_count_entries($ldapconnection, $search)) { |
1369 | $info = $this->ldap_get_entries($ldapconnection, $search); |
139ebfdb |
1370 | |
b9ddb2d5 |
1371 | if (count($info) > 0 ) { |
1372 | // user is member of group |
1373 | $result = true; |
1374 | break; |
1375 | } |
cd874e21 |
1376 | } |
b9ddb2d5 |
1377 | } |
b9ddb2d5 |
1378 | |
1379 | return $result; |
1380 | |
1381 | } |
1382 | |
1383 | /** |
1384 | * connects to ldap server |
1385 | * |
1386 | * Tries connect to specified ldap servers. |
1387 | * Returns connection result or error. |
1388 | * |
1389 | * @return connection result |
1390 | */ |
1391 | function ldap_connect($binddn='',$bindpwd='') { |
b9ddb2d5 |
1392 | //Select bind password, With empty values use |
1393 | //ldap_bind_* variables or anonymous bind if ldap_bind_* are empty |
1394 | if ($binddn == '' and $bindpwd == '') { |
1395 | if (!empty($this->config->bind_dn)) { |
1396 | $binddn = $this->config->bind_dn; |
1397 | } |
1398 | if (!empty($this->config->bind_pw)) { |
1399 | $bindpwd = $this->config->bind_pw; |
1400 | } |
1401 | } |
139ebfdb |
1402 | |
b9ddb2d5 |
1403 | $urls = explode(";",$this->config->host_url); |
139ebfdb |
1404 | |
b9ddb2d5 |
1405 | foreach ($urls as $server) { |
1406 | $server = trim($server); |
1407 | if (empty($server)) { |
1408 | continue; |
1409 | } |
1410 | |
1411 | $connresult = ldap_connect($server); |
1412 | //ldap_connect returns ALWAYS true |
139ebfdb |
1413 | |
b9ddb2d5 |
1414 | if (!empty($this->config->version)) { |
1415 | ldap_set_option($connresult, LDAP_OPT_PROTOCOL_VERSION, $this->config->version); |
1416 | } |
1417 | |
1418 | if (!empty($binddn)) { |
1419 | //bind with search-user |
139ebfdb |
1420 | //$debuginfo .= 'Using bind user'.$binddn.'and password:'.$bindpwd; |
b9ddb2d5 |
1421 | $bindresult=ldap_bind($connresult, $binddn,$bindpwd); |
1422 | } |
1423 | else { |
139ebfdb |
1424 | //bind anonymously |
b9ddb2d5 |
1425 | $bindresult=@ldap_bind($connresult); |
139ebfdb |
1426 | } |
1427 | |
b9ddb2d5 |
1428 | if (!empty($this->config->opt_deref)) { |
1429 | ldap_set_option($connresult, LDAP_OPT_DEREF, $this->config->opt_deref); |
1430 | } |
1431 | |
1432 | if ($bindresult) { |
1433 | return $connresult; |
1434 | } |
139ebfdb |
1435 | |
b9ddb2d5 |
1436 | $debuginfo .= "<br/>Server: '$server' <br/> Connection: '$connresult'<br/> Bind result: '$bindresult'</br>"; |
1437 | } |
1438 | |
1439 | //If any of servers are alive we have already returned connection |
e8b9d76a |
1440 | print_error('auth_ldap_noconnect_all','auth',$this->config->user_type); |
b9ddb2d5 |
1441 | return false; |
1442 | } |
1443 | |
1444 | /** |
1445 | * retuns dn of username |
1446 | * |
1447 | * Search specified contexts for username and return user dn |
1448 | * like: cn=username,ou=suborg,o=org |
1449 | * |
1450 | * @param mixed $ldapconnection $ldapconnection result |
139ebfdb |
1451 | * @param mixed $username username (external encoding no slashes) |
b9ddb2d5 |
1452 | * |
1453 | */ |
1454 | |
139ebfdb |
1455 | function ldap_find_userdn ($ldapconnection, $extusername) { |
b9ddb2d5 |
1456 | |
1457 | //default return value |
1458 | $ldap_user_dn = FALSE; |
1459 | |
1460 | //get all contexts and look for first matching user |
1461 | $ldap_contexts = explode(";",$this->config->contexts); |
139ebfdb |
1462 | |
b9ddb2d5 |
1463 | if (!empty($this->config->create_context)) { |
1464 | array_push($ldap_contexts, $this->config->create_context); |
1465 | } |
139ebfdb |
1466 | |
b9ddb2d5 |
1467 | foreach ($ldap_contexts as $context) { |
1468 | |
1469 | $context = trim($context); |
1470 | if (empty($context)) { |
1471 | continue; |
1472 | } |
1473 | |
1474 | if ($this->config->search_sub) { |
1475 | //use ldap_search to find first user from subtree |
139ebfdb |
1476 | $ldap_result = ldap_search($ldapconnection, $context, "(".$this->config->user_attribute."=".$this->filter_addslashes($extusername).")",array($this->config->user_attribute)); |
b9ddb2d5 |
1477 | |
1478 | } |
1479 | else { |
1480 | //search only in this context |
139ebfdb |
1481 | $ldap_result = ldap_list($ldapconnection, $context, "(".$this->config->user_attribute."=".$this->filter_addslashes($extusername).")",array($this->config->user_attribute)); |
b9ddb2d5 |
1482 | } |
139ebfdb |
1483 | |
b9ddb2d5 |
1484 | $entry = ldap_first_entry($ldapconnection,$ldap_result); |
1485 | |
1486 | if ($entry) { |
1487 | $ldap_user_dn = ldap_get_dn($ldapconnection, $entry); |
1488 | break ; |
1489 | } |
1490 | } |
1491 | |
1492 | return $ldap_user_dn; |
1493 | } |
1494 | |
1495 | /** |
1496 | * retuns user attribute mappings between moodle and ldap |
1497 | * |
1498 | * @return array |
1499 | */ |
1500 | |
1501 | function ldap_attributes () { |
139ebfdb |
1502 | $fields = array("firstname", "lastname", "email", "phone1", "phone2", |
1503 | "department", "address", "city", "country", "description", |
b9ddb2d5 |
1504 | "idnumber", "lang" ); |
1505 | $moodleattributes = array(); |
1506 | foreach ($fields as $field) { |
1507 | if (!empty($this->config->{"field_map_$field"})) { |
1508 | $moodleattributes[$field] = $this->config->{"field_map_$field"}; |
1509 | if (preg_match('/,/',$moodleattributes[$field])) { |
1510 | $moodleattributes[$field] = explode(',', $moodleattributes[$field]); // split ? |
1511 | } |
1512 | } |
1513 | } |
1514 | $moodleattributes['username'] = $this->config->user_attribute; |
1515 | return $moodleattributes; |
1516 | } |
1517 | |
1518 | /** |
1519 | * return all usernames from ldap |
1520 | * |
1521 | * @return array |
1522 | */ |
1523 | |
1524 | function ldap_get_userlist($filter="*") { |
1525 | /// returns all users from ldap servers |
b9ddb2d5 |
1526 | $fresult = array(); |
1527 | |
1528 | $ldapconnection = $this->ldap_connect(); |
1529 | |
1530 | if ($filter=="*") { |
1531 | $filter = "(&(".$this->config->user_attribute."=*)(".$this->config->objectclass."))"; |
1532 | } |
1533 | |
1534 | $contexts = explode(";",$this->config->contexts); |
139ebfdb |
1535 | |
b9ddb2d5 |
1536 | if (!empty($this->config->create_context)) { |
1537 | array_push($contexts, $this->config->create_context); |
1538 | } |
1539 | |
1540 | foreach ($contexts as $context) { |
1541 | |
1542 | $context = trim($context); |
1543 | if (empty($context)) { |
1544 | continue; |
1545 | } |
1546 | |
1547 | if ($this->config->search_sub) { |
1548 | //use ldap_search to find first user from subtree |
1549 | $ldap_result = ldap_search($ldapconnection, $context,$filter,array($this->config->user_attribute)); |
1550 | } |
1551 | else { |
1552 | //search only in this context |
1553 | $ldap_result = ldap_list($ldapconnection, $context, |
1554 | $filter, |
1555 | array($this->config->user_attribute)); |
1556 | } |
139ebfdb |
1557 | |
b9ddb2d5 |
1558 | $users = $this->ldap_get_entries($ldapconnection, $ldap_result); |
1559 | |
1560 | //add found users to list |
1561 | for ($i=0;$i<count($users);$i++) { |
1562 | array_push($fresult, ($users[$i][$this->config->user_attribute][0]) ); |
1563 | } |
1564 | } |
139ebfdb |
1565 | |
b9ddb2d5 |
1566 | return $fresult; |
1567 | } |
1568 | |
1569 | /** |
1570 | * return entries from ldap |
1571 | * |
1572 | * Returns values like ldap_get_entries but is |
1573 | * binary compatible and return all attributes as array |
1574 | * |
1575 | * @return array ldap-entries |
1576 | */ |
139ebfdb |
1577 | |
b9ddb2d5 |
1578 | function ldap_get_entries($conn, $searchresult) { |
1579 | //Returns values like ldap_get_entries but is |
1580 | //binary compatible |
1581 | $i=0; |
1582 | $fresult=array(); |
1583 | $entry = ldap_first_entry($conn, $searchresult); |
1584 | do { |
1585 | $attributes = @ldap_get_attributes($conn, $entry); |
1586 | for ($j=0; $j<$attributes['count']; $j++) { |
1587 | $values = ldap_get_values_len($conn, $entry,$attributes[$j]); |
1588 | if (is_array($values)) { |
1589 | $fresult[$i][$attributes[$j]] = $values; |
1590 | } |
1591 | else { |
1592 | $fresult[$i][$attributes[$j]] = array($values); |
1593 | } |
139ebfdb |
1594 | } |
1595 | $i++; |
b9ddb2d5 |
1596 | } |
1597 | while ($entry = @ldap_next_entry($conn, $entry)); |
1598 | //were done |
1599 | return ($fresult); |
1600 | } |
1601 | |
1602 | /** |
1603 | * Returns true if this authentication plugin is 'internal'. |
1604 | * |
139ebfdb |
1605 | * @return bool |
b9ddb2d5 |
1606 | */ |
1607 | function is_internal() { |
1608 | return false; |
1609 | } |
1610 | |
1611 | /** |
1612 | * Returns true if this authentication plugin can change the user's |
1613 | * password. |
1614 | * |
139ebfdb |
1615 | * @return bool |
b9ddb2d5 |
1616 | */ |
1617 | function can_change_password() { |
430759a5 |
1618 | return !empty($this->config->stdchangepassword) or !empty($this->config->changepasswordurl); |
b9ddb2d5 |
1619 | } |
139ebfdb |
1620 | |
b9ddb2d5 |
1621 | /** |
430759a5 |
1622 | * Returns the URL for changing the user's pw, or empty if the default can |
b9ddb2d5 |
1623 | * be used. |
1624 | * |
139ebfdb |
1625 | * @return string url |
b9ddb2d5 |
1626 | */ |
1627 | function change_password_url() { |
139ebfdb |
1628 | if (empty($this->config->stdchangepassword)) { |
1629 | return $this->config->changepasswordurl; |
1630 | } else { |
430759a5 |
1631 | return ''; |
139ebfdb |
1632 | } |
b9ddb2d5 |
1633 | } |
139ebfdb |
1634 | |
6bc1e5d5 |
1635 | /** |
1636 | * Sync roles for this user |
1637 | * |
1638 | * @param $user object user object (without system magic quotes) |
1639 | */ |
1640 | function sync_roles($user) { |
1641 | $iscreator = $this->iscreator($user->username); |
1642 | if ($iscreator === null) { |
1643 | return; //nothing to sync - creators not configured |
1644 | } |
1645 | |
1646 | if ($roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { |
1647 | $creatorrole = array_shift($roles); // We can only use one, let's use the first one |
1648 | $systemcontext = get_context_instance(CONTEXT_SYSTEM); |
1649 | |
1650 | if ($iscreator) { // Following calls will not create duplicates |
1651 | role_assign($creatorrole->id, $user->id, 0, $systemcontext->id, 0, 0, 0, 'ldap'); |
1652 | } else { |
1653 | //unassign only if previously assigned by this plugin! |
1654 | role_unassign($creatorrole->id, $user->id, 0, $systemcontext->id, 'ldap'); |
1655 | } |
1656 | } |
1657 | } |
1658 | |
b9ddb2d5 |
1659 | /** |
1660 | * Prints a form for configuring this authentication plugin. |
1661 | * |
1662 | * This function is called from admin/auth.php, and outputs a full page with |
1663 | * a form for configuring this plugin. |
1664 | * |
1665 | * @param array $page An object containing all the data for this page. |
1666 | */ |
139ebfdb |
1667 | function config_form($config, $err, $user_fields) { |
1668 | include 'config.html'; |
b9ddb2d5 |
1669 | } |
1670 | |
1671 | /** |
1672 | * Processes and stores configuration data for this authentication plugin. |
1673 | */ |
1674 | function process_config($config) { |
1675 | // set to defaults if undefined |
139ebfdb |
1676 | if (!isset($config->host_url)) |
b9ddb2d5 |
1677 | { $config->host_url = ''; } |
139ebfdb |
1678 | if (empty($config->ldapencoding)) |
1679 | { $config->ldapencoding = 'utf-8'; } |
1680 | if (!isset($config->contexts)) |
b9ddb2d5 |
1681 | { $config->contexts = ''; } |
139ebfdb |
1682 | if (!isset($config->user_type)) |
1683 | { $config->user_type = 'default'; } |
1684 | if (!isset($config->user_attribute)) |
b9ddb2d5 |
1685 | { $config->user_attribute = ''; } |
139ebfdb |
1686 | if (!isset($config->search_sub)) |
b9ddb2d5 |
1687 | { $config->search_sub = ''; } |
139ebfdb |
1688 | if (!isset($config->opt_deref)) |
b9ddb2d5 |
1689 | { $config->opt_deref = ''; } |
139ebfdb |
1690 | if (!isset($config->preventpassindb)) |
1691 | { $config->preventpassindb = 0; } |
1692 | if (!isset($config->bind_dn)) |
b9ddb2d5 |
1693 | {$config->bind_dn = ''; } |
139ebfdb |
1694 | if (!isset($config->bind_pw)) |
b9ddb2d5 |
1695 | {$config->bind_pw = ''; } |
139ebfdb |
1696 | if (!isset($config->version)) |
b9ddb2d5 |
1697 | {$config->version = '2'; } |
139ebfdb |
1698 | if (!isset($config->objectclass)) |
b9ddb2d5 |
1699 | {$config->objectclass = ''; } |
139ebfdb |
1700 | if (!isset($config->memberattribute)) |
b9ddb2d5 |
1701 | {$config->memberattribute = ''; } |
cd874e21 |
1702 | if (!isset($config->memberattribute_isdn)) |
1703 | {$config->memberattribute_isdn = ''; } |
139ebfdb |
1704 | if (!isset($config->creators)) |
b9ddb2d5 |
1705 | {$config->creators = ''; } |
139ebfdb |
1706 | if (!isset($config->create_context)) |
b9ddb2d5 |
1707 | {$config->create_context = ''; } |
139ebfdb |
1708 | if (!isset($config->expiration)) |
b9ddb2d5 |
1709 | {$config->expiration = ''; } |
139ebfdb |
1710 | if (!isset($config->expiration_warning)) |
b9ddb2d5 |
1711 | {$config->expiration_warning = '10'; } |
139ebfdb |
1712 | if (!isset($config->expireattr)) |
b9ddb2d5 |
1713 | {$config->expireattr = ''; } |
139ebfdb |
1714 | if (!isset($config->gracelogins)) |
b9ddb2d5 |
1715 | {$config->gracelogins = ''; } |
139ebfdb |
1716 | if (!isset($config->graceattr)) |
b9ddb2d5 |
1717 | {$config->graceattr = ''; } |
139ebfdb |
1718 | if (!isset($config->auth_user_create)) |
b9ddb2d5 |
1719 | {$config->auth_user_create = ''; } |
139ebfdb |
1720 | if (!isset($config->forcechangepassword)) |
1721 | {$config->forcechangepassword = 0; } |
b9ddb2d5 |
1722 | if (!isset($config->stdchangepassword)) |
344514fc |
1723 | {$config->forcechangepassword = 0; } |
1724 | if (!isset($config->passtype)) |
1725 | {$config->passtype = 'plaintext'; } |
b9ddb2d5 |
1726 | if (!isset($config->changepasswordurl)) |
1727 | {$config->changepasswordurl = ''; } |
139ebfdb |
1728 | if (!isset($config->removeuser)) |
1729 | {$config->removeuser = 0; } |
b9ddb2d5 |
1730 | |
1731 | // save settings |
1732 | set_config('host_url', $config->host_url, 'auth/ldap'); |
139ebfdb |
1733 | set_config('ldapencoding', $config->ldapencoding, 'auth/ldap'); |
1734 | set_config('host_url', $config->host_url, 'auth/ldap'); |
b9ddb2d5 |
1735 | set_config('contexts', $config->contexts, 'auth/ldap'); |
1736 | set_config('user_type', $config->user_type, 'auth/ldap'); |
1737 | set_config('user_attribute', $config->user_attribute, 'auth/ldap'); |
1738 | set_config('search_sub', $config->search_sub, 'auth/ldap'); |
1739 | set_config('opt_deref', $config->opt_deref, 'auth/ldap'); |
1740 | set_config('preventpassindb', $config->preventpassindb, 'auth/ldap'); |
1741 | set_config('bind_dn', $config->bind_dn, 'auth/ldap'); |
1742 | set_config('bind_pw', $config->bind_pw, 'auth/ldap'); |
1743 | set_config('version', $config->version, 'auth/ldap'); |
1744 | set_config('objectclass', $config->objectclass, 'auth/ldap'); |
1745 | set_config('memberattribute', $config->memberattribute, 'auth/ldap'); |
cd874e21 |
1746 | set_config('memberattribute_isdn', $config->memberattribute_isdn, 'auth/ldap'); |
b9ddb2d5 |
1747 | set_config('creators', $config->creators, 'auth/ldap'); |
1748 | set_config('create_context', $config->create_context, 'auth/ldap'); |
1749 | set_config('expiration', $config->expiration, 'auth/ldap'); |
1750 | set_config('expiration_warning', $config->expiration_warning, 'auth/ldap'); |
1751 | set_config('expireattr', $config->expireattr, 'auth/ldap'); |
1752 | set_config('gracelogins', $config->gracelogins, 'auth/ldap'); |
1753 | set_config('graceattr', $config->graceattr, 'auth/ldap'); |
1754 | set_config('auth_user_create', $config->auth_user_create, 'auth/ldap'); |
1755 | set_config('forcechangepassword', $config->forcechangepassword, 'auth/ldap'); |
1756 | set_config('stdchangepassword', $config->stdchangepassword, 'auth/ldap'); |
344514fc |
1757 | set_config('passtype', $config->passtype, 'auth/ldap'); |
b9ddb2d5 |
1758 | set_config('changepasswordurl', $config->changepasswordurl, 'auth/ldap'); |
139ebfdb |
1759 | set_config('removeuser', $config->removeuser, 'auth/ldap'); |
b9ddb2d5 |
1760 | |
1761 | return true; |
1762 | } |
1763 | |
139ebfdb |
1764 | /** |
1765 | * Quote control characters in texts used in ldap filters - see rfc2254.txt |
1766 | * |
1767 | * @param string |
1768 | */ |
1769 | function filter_addslashes($text) { |
1770 | $text = str_replace('\\', '\\5c', $text); |
1771 | $text = str_replace(array('*', '(', ')', "\0"), |
1772 | array('\\2a', '\\28', '\\29', '\\00'), $text); |
1773 | return $text; |
1774 | } |
1775 | |
1776 | /** |
1777 | * Quote control characters in quoted "texts" used in ldap |
1778 | * |
1779 | * @param string |
1780 | */ |
1781 | function ldap_addslashes($text) { |
1782 | $text = str_replace('\\', '\\\\', $text); |
1783 | $text = str_replace(array('"', "\0"), |
1784 | array('\\"', '\\00'), $text); |
1785 | return $text; |
1786 | } |
b9ddb2d5 |
1787 | } |
1788 | |
1789 | ?> |