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