| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @author Martin Dougiamas 4 * @authro Jerome GUTIERREZ 5 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 6 * @package moodle multiauth 7 * 8 * Authentication Plugin: CAS Authentication 9 * 10 * Authentication using CAS (Central Authentication Server). 11 * 12 * 2006-08-28 File created. 13 */ 14 if (!defined('MOODLE_INTERNAL')) { 15 die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page 16 } 17 require_once($CFG->libdir.'/authlib.php'); 18 require_once($CFG->dirroot.'/auth/cas/CAS/CAS.php'); 19 /** 20 * CAS authentication plugin. 21 */ 22 class auth_plugin_cas extends auth_plugin_base { 23 /** 24 * Constructor. 25 */ 26 function auth_plugin_cas() { 27 $this->authtype = 'cas'; 28 $this->config = get_config('auth/cas'); 29 if (empty($this->config->ldapencoding)) { 30 $this->config->ldapencoding = 'utf-8'; 31 } 32 if (empty($this->config->user_type)) { 33 $this->config->user_type = 'default'; 34 } 35 $default = $this->ldap_getdefaults(); 36 //use defaults if values not given 37 foreach ($default as $key => $value) { 38 // watch out - 0, false are correct values too 39 if (!isset($this->config->{$key}) or $this->config->{$key} == '') { 40 $this->config->{$key} = $value[$this->config->user_type]; 41 } 42 } 43 //hack prefix to objectclass 44 if (empty($this->config->objectclass)) { // Can't send empty filter 45 $this->config->objectclass='objectClass=*'; 46 } else if (strpos($this->config->objectclass, 'objectClass=') !== 0) { 47 $this->config->objectclass = 'objectClass='.$this->config->objectclass; 48 } 49 } 50 /** 51 * Authenticates user againt CAS 52 * Returns true if the username and password work and false if they are 53 * wrong or don't exist. 54 * 55 * @param string $username The username 56 * @param string $password The password 57 * @return bool Authentication success or failure. 58 */ 59 function user_login ($username, $password) { 60 $this->connectCAS(); 61 return phpCAS::isAuthenticated(); 62 } 63 /** 64 * Returns true if this authentication plugin is 'internal'. 65 * 66 * @return bool 67 */ 68 function is_internal() { 69 return false; 70 } 71 /** 72 * Returns true if this authentication plugin can change the user's 73 * password. 74 * 75 * @return bool 76 */ 77 function can_change_password() { 78 return false; 79 } 80 /** 81 * authentication choice (CAS or other) 82 * redirection to the CAS form or to login/index.php 83 * for other authentication 84 */ 85 function loginpage_hook() { 86 global $frm; 87 global $CFG; 88 global $SESSION; 89 90 $site = get_site(); 91 $CASform = get_string("CASform","auth"); 92 $username = optional_param("username"); 93 94 if (!empty($username)) { 95 if (strstr($SESSION->wantsurl,'ticket') || strstr($SESSION->wantsurl,'NOCAS')) 96 unset($SESSION->wantsurl); 97 return; 98 } 99 100 101 102 // Test si cas activ� et param�tres non remplis 103 if (empty($this->config->hostname)) { 104 return; 105 } 106 107 // Connection to CAS server 108 $this->connectCAS(); 109 110 // Gestion de la connection CAS si acc�s direct d'un ent ou autre 111 if (phpCAS::checkAuthentication()) { 112 $frm->username=phpCAS::getUser(); 113 // if (phpCAS::getUser()=='esup9992') 114 // $frm->username='erhar0062'; 115 $frm->password="passwdCas"; 116 return; 117 } 118 119 if ($_GET["loginguest"]== true) { 120 $frm->username="guest"; 121 $frm->password="guest"; 122 return; 123 } 124 125 if ($this->config->multiauth) { 126 $authCAS = optional_param("authCAS"); 127 if ($authCAS=="NOCAS") 128 return; 129 130 // choice authentication form for multi-authentication 131 // test pgtIou parameter for proxy mode (https connection 132 // in background from CAS server to the php server) 133 if ($authCAS!="CAS" && !isset($_GET["pgtIou"])) { 134 $navlinks = array(); 135 $navlinks[] = array('name' => $CASform, 'link' => null, 'type' => 'misc'); 136 $navigation = build_navigation($navlinks); 137 138 print_header("$site->fullname: $CASform", $site->fullname, $navigation); 139 include($CFG->dirroot."/auth/cas/cas_form.html"); 140 print_footer(); 141 exit(); 142 } 143 } 144 // CAS authentication 145 if (!phpCAS::isAuthenticated()) 146 {phpCAS::forceAuthentication();} 147 } 148 /** 149 * logout from the cas 150 * 151 * This function is called from admin/auth.php 152 * 153 */ 154 function prelogout_hook() { 155 global $CFG; 156 if ($this->config->logoutcas ) { 157 $backurl = $CFG->wwwroot; 158 $this->connectCAS(); 159 phpCAS::logout($backurl); 160 } 161 } 162 /** 163 * Connect to the cas (clientcas connection or proxycas connection 164 * 165 * This function is called from admin/auth.php 166 * 167 */ 168 function connectCAS() { 169 170 global $PHPCAS_CLIENT; 171 // mode proxy CAS 172 if ( !is_object($PHPCAS_CLIENT) ) { 173 if ($this->config->proxycas) { 174 phpCAS::proxy($this->config->casversion, $this-> config->hostname, (int) $this->config->port, $this->config->baseuri); 175 } 176 // mode client CAS 177 else { 178 phpCAS::client($this->config->casversion, $this-> config->hostname, (int) $this->config->port, $this->config->baseuri); 179 } 180 } 181 182 } 183 /** 184 * Prints a form for configuring this authentication plugin. 185 * 186 * This function is called from admin/auth.php, and outputs a full page with 187 * a form for configuring this plugin. 188 * 189 * @param array $page An object containing all the data for this page. 190 */ 191 function config_form($config, $err, $user_fields) { 192 include 'config.html'; 193 } 194 /** 195 * Returns the URL for changing the user's pw, or empty if the default can 196 * be used. 197 * 198 * @return string 199 */ 200 function change_password_url() { 201 return ""; 202 } 203 /** 204 * returns predefined usertypes 205 * 206 * @return array of predefined usertypes 207 */ 208 function ldap_suppported_usertypes() { 209 $types = array(); 210 $types['edir']='Novell Edirectory'; 211 $types['rfc2307']='posixAccount (rfc2307)'; 212 $types['rfc2307bis']='posixAccount (rfc2307bis)'; 213 $types['samba']='sambaSamAccount (v.3.0.7)'; 214 $types['ad']='MS ActiveDirectory'; 215 $types['default']=get_string('default'); 216 return $types; 217 } 218 /** 219 * Processes and stores configuration data for this authentication plugin. 220 */ 221 function process_config($config) { 222 // set to defaults if undefined 223 // CAS settings 224 if (!isset ($config->hostname)) 225 $config->hostname = ''; 226 if (!isset ($config->port)) 227 $config->port = ''; 228 if (!isset ($config->casversion)) 229 $config->casversion = ''; 230 if (!isset ($config->baseuri)) 231 $config->baseuri = ''; 232 if (!isset ($config->language)) 233 $config->language = ''; 234 if (!isset ($config->proxycas)) 235 $config->proxycas = ''; 236 if (!isset ($config->logoutcas)) 237 $config->logoutcas = ''; 238 if (!isset ($config->multiauth)) 239 $config->multiauth = ''; 240 // LDAP settings 241 if (!isset($config->host_url)) 242 { $config->host_url = ''; } 243 if (empty($config->ldapencoding)) 244 { $config->ldapencoding = 'utf-8'; } 245 if (!isset($config->contexts)) 246 { $config->contexts = ''; } 247 if (!isset($config->user_type)) 248 { $config->user_type = 'default'; } 249 if (!isset($config->user_attribute)) 250 { $config->user_attribute = ''; } 251 if (!isset($config->search_sub)) 252 { $config->search_sub = ''; } 253 if (!isset($config->opt_deref)) 254 { $config->opt_deref = ''; } 255 if (!isset($config->bind_dn)) 256 {$config->bind_dn = ''; } 257 if (!isset($config->bind_pw)) 258 {$config->bind_pw = ''; } 259 if (!isset($config->version)) 260 {$config->version = '2'; } 261 if (!isset($config->objectclass)) 262 {$config->objectclass = ''; } 263 if (!isset($config->memberattribute)) 264 {$config->memberattribute = ''; } 265 if (!isset($config->memberattribute_isdn)) 266 {$config->memberattribute_isdn = ''; } 267 if (!isset($config->attrcreators)) 268 {$config->attrcreators = ''; } 269 if (!isset($config->groupecreators)) 270 {$config->groupecreators = ''; } 271 if (!isset($config->removeuser)) 272 {$config->removeuser = 0; } 273 // save CAS settings 274 set_config('hostname', $config->hostname, 'auth/cas'); 275 set_config('port', $config->port, 'auth/cas'); 276 set_config('casversion', $config->casversion, 'auth/cas'); 277 set_config('baseuri', $config->baseuri, 'auth/cas'); 278 set_config('language', $config->language, 'auth/cas'); 279 set_config('proxycas', $config->proxycas, 'auth/cas'); 280 set_config('logoutcas', $config->logoutcas, 'auth/cas'); 281 set_config('multiauth', $config->multiauth, 'auth/cas'); 282 // save LDAP settings 283 set_config('host_url', $config->host_url, 'auth/cas'); 284 set_config('ldapencoding', $config->ldapencoding, 'auth/cas'); 285 set_config('host_url', $config->host_url, 'auth/cas'); 286 set_config('contexts', $config->contexts, 'auth/cas'); 287 set_config('user_type', $config->user_type, 'auth/cas'); 288 set_config('user_attribute', $config->user_attribute, 'auth/cas'); 289 set_config('search_sub', $config->search_sub, 'auth/cas'); 290 set_config('opt_deref', $config->opt_deref, 'auth/cas'); 291 set_config('bind_dn', $config->bind_dn, 'auth/cas'); 292 set_config('bind_pw', $config->bind_pw, 'auth/cas'); 293 set_config('version', $config->version, 'auth/cas'); 294 set_config('objectclass', $config->objectclass, 'auth/cas'); 295 set_config('memberattribute', $config->memberattribute, 'auth/cas'); 296 set_config('memberattribute_isdn', $config->memberattribute_isdn, 'auth/cas'); 297 set_config('attrcreators', $config->attrcreators, 'auth/cas'); 298 set_config('groupecreators', $config->groupecreators, 'auth/cas'); 299 set_config('removeuser', $config->removeuser, 'auth/cas'); 300 return true; 301 } 302 /** 303 * Initializes needed ldap variables for cas-module 304 * 305 * Uses names defined in ldap_supported_usertypes. 306 * $default is first defined as: 307 * $default['pseudoname'] = array( 308 * 'typename1' => 'value', 309 * 'typename2' => 'value' 310 * .... 311 * ); 312 * 313 * @return array of default values 314 */ 315 function ldap_getdefaults() { 316 $default['objectclass'] = array( 317 'edir' => 'User', 318 'rfc2307' => 'posixAccount', 319 'rfc2307bis' => 'posixAccount', 320 'samba' => 'sambaSamAccount', 321 'ad' => 'user', 322 'default' => '*' 323 ); 324 $default['user_attribute'] = array( 325 'edir' => 'cn', 326 'rfc2307' => 'uid', 327 'rfc2307bis' => 'uid', 328 'samba' => 'uid', 329 'ad' => 'cn', 330 'default' => 'cn' 331 ); 332 $default['memberattribute'] = array( 333 'edir' => 'member', 334 'rfc2307' => 'member', 335 'rfc2307bis' => 'member', 336 'samba' => 'member', 337 'ad' => 'member', 338 'default' => 'member' 339 ); 340 $default['memberattribute_isdn'] = array( 341 'edir' => '1', 342 'rfc2307' => '0', 343 'rfc2307bis' => '1', 344 'samba' => '0', //is this right? 345 'ad' => '1', 346 'default' => '0' 347 ); 348 return $default; 349 } 350 /** 351 * reads userinformation from ldap and return it in array() 352 * 353 * Read user information from external database and returns it as array(). 354 * Function should return all information available. If you are saving 355 * this information to moodle user-table you should honor syncronization flags 356 * 357 * @param string $username username (with system magic quotes) 358 * 359 * @return mixed array with no magic quotes or false on error 360 */ 361 function get_userinfo($username) { 362 $textlib = textlib_get_instance(); 363 $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); 364 $ldapconnection = $this->ldap_connect(); 365 $attrmap = $this->ldap_attributes(); 366 $result = array(); 367 $search_attribs = array(); 368 foreach ($attrmap as $key=>$values) { 369 if (!is_array($values)) { 370 $values = array($values); 371 } 372 foreach ($values as $value) { 373 if (!in_array($value, $search_attribs)) { 374 array_push($search_attribs, $value); 375 } 376 } 377 } 378 $user_dn = $this->ldap_find_userdn($ldapconnection, $extusername); 379 if (!$user_info_result = ldap_read($ldapconnection, $user_dn, $this->config->objectclass, $search_attribs)) { 380 return false; // error! 381 } 382 $user_entry = $this->ldap_get_entries($ldapconnection, $user_info_result); 383 if (empty($user_entry)) { 384 return false; // entry not found 385 } 386 foreach ($attrmap as $key=>$values) { 387 if (!is_array($values)) { 388 $values = array($values); 389 } 390 $ldapval = NULL; 391 foreach ($values as $value) { 392 if ($value == 'dn') { 393 $result[$key] = $user_dn; 394 } 395 if (!array_key_exists(strtolower($value), $user_entry[0])) { 396 continue; // wrong data mapping! 397 } 398 if (is_array($user_entry[0][strtolower($value)])) { 399 $newval = $textlib->convert($user_entry[0][strtolower($value)][0], $this->config->ldapencoding, 'utf-8'); 400 } else { 401 $newval = $textlib->convert($user_entry[0][strtolower($value)], $this->config->ldapencoding, 'utf-8'); 402 } 403 404 if (!empty($newval)) { // favour ldap entries that are set 405 $ldapval = $newval; 406 } 407 } 408 if (!is_null($ldapval)) { 409 $result[$key] = $ldapval; 410 } 411 } 412 @ldap_close($ldapconnection); 413 return $result; 414 } 415 /** 416 * reads userinformation from ldap and return it in an object 417 * 418 * @param string $username username (with system magic quotes) 419 * @return mixed object or false on error 420 */ 421 function get_userinfo_asobj($username) { 422 $user_array = $this->get_userinfo($username); 423 if ($user_array == false) { 424 return false; //error or not found 425 } 426 $user_array = truncate_userinfo($user_array); 427 $user = new object(); 428 foreach ($user_array as $key=>$value) { 429 $user->{$key} = $value; 430 } 431 return $user; 432 } 433 /** 434 * connects to ldap server 435 * 436 * Tries connect to specified ldap servers. 437 * Returns connection result or error. 438 * 439 * @return connection result 440 */ 441 function ldap_connect($binddn='',$bindpwd='') { 442 //Select bind password, With empty values use 443 //ldap_bind_* variables or anonymous bind if ldap_bind_* are empty 444 if ($binddn == '' and $bindpwd == '') { 445 if (!empty($this->config->bind_dn)) { 446 $binddn = $this->config->bind_dn; 447 } 448 if (!empty($this->config->bind_pw)) { 449 $bindpwd = $this->config->bind_pw; 450 } 451 } 452 $urls = explode(";",$this->config->host_url); 453 foreach ($urls as $server) { 454 $server = trim($server); 455 if (empty($server)) { 456 continue; 457 } 458 $connresult = ldap_connect($server); 459 //ldap_connect returns ALWAYS true 460 if (!empty($this->config->version)) { 461 ldap_set_option($connresult, LDAP_OPT_PROTOCOL_VERSION, $this->config->version); 462 } 463 if (!empty($binddn)) { 464 //bind with search-user 465 //$debuginfo .= 'Using bind user'.$binddn.'and password:'.$bindpwd; 466 $bindresult=ldap_bind($connresult, $binddn,$bindpwd); 467 } 468 else { 469 //bind anonymously 470 $bindresult=@ldap_bind($connresult); 471 } 472 if (!empty($this->config->opt_deref)) { 473 ldap_set_option($connresult, LDAP_OPT_DEREF, $this->config->opt_deref); 474 } 475 if ($bindresult) { 476 return $connresult; 477 } 478 $debuginfo .= "<br/>Server: '$server' <br/> Connection: '$connresult'<br/> Bind result: '$bindresult'</br>"; 479 } 480 //If any of servers are alive we have already returned connection 481 print_error('auth_ldap_noconnect_all','auth',$this->config->user_type); 482 return false; 483 } 484 /** 485 * retuns user attribute mappings between moodle and ldap 486 * 487 * @return array 488 */ 489 function ldap_attributes () { 490 $moodleattributes = array(); 491 foreach ($this->userfields as $field) { 492 if (!empty($this->config->{"field_map_$field"})) { 493 $moodleattributes[$field] = $this->config->{"field_map_$field"}; 494 if (preg_match('/,/',$moodleattributes[$field])) { 495 $moodleattributes[$field] = explode(',', $moodleattributes[$field]); // split ? 496 } 497 } 498 } 499 $moodleattributes['username'] = $this->config->user_attribute; 500 return $moodleattributes; 501 } 502 /** 503 * retuns dn of username 504 * 505 * Search specified contexts for username and return user dn 506 * like: cn=username,ou=suborg,o=org 507 * 508 * @param mixed $ldapconnection $ldapconnection result 509 * @param mixed $username username (external encoding no slashes) 510 * 511 */ 512 function ldap_find_userdn ($ldapconnection, $extusername) { 513 //default return value 514 $ldap_user_dn = FALSE; 515 //get all contexts and look for first matching user 516 $ldap_contexts = explode(";",$this->config->contexts); 517 if (!empty($this->config->create_context)) { 518 array_push($ldap_contexts, $this->config->create_context); 519 } 520 foreach ($ldap_contexts as $context) { 521 $context = trim($context); 522 if (empty($context)) { 523 continue; 524 } 525 if ($this->config->search_sub) { 526 //use ldap_search to find first user from subtree 527 $ldap_result = ldap_search($ldapconnection, $context, "(".$this->config->user_attribute."=".$this->filter_addslashes($extusername).")",array($this->config->user_attribute)); 528 } 529 else { 530 //search only in this context 531 $ldap_result = ldap_list($ldapconnection, $context, "(".$this->config->user_attribute."=".$this->filter_addslashes($extusername).")",array($this->config->user_attribute)); 532 } 533 $entry = ldap_first_entry($ldapconnection,$ldap_result); 534 if ($entry) { 535 $ldap_user_dn = ldap_get_dn($ldapconnection, $entry); 536 break ; 537 } 538 } 539 return $ldap_user_dn; 540 } 541 /** 542 * Quote control characters in quoted "texts" used in ldap 543 * 544 * @param string 545 */ 546 function ldap_addslashes($text) { 547 $text = str_replace('\\', '\\\\', $text); 548 $text = str_replace(array('"', "\0"), 549 array('\\"', '\\00'), $text); 550 return $text; 551 } 552 /** 553 * returns all usernames from external database 554 * 555 * get_userlist returns all usernames from external database 556 * 557 * @return array 558 */ 559 function get_userlist() { 560 return $this->ldap_get_userlist("({$this->config->user_attribute}=*)"); 561 } 562 /** 563 * checks if user exists on external db 564 * 565 * @param string $username (with system magic quotes) 566 */ 567 function user_exists($username) { 568 $textlib = textlib_get_instance(); 569 $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->ldapencoding); 570 //returns true if given username exist on ldap 571 $users = $this->ldap_get_userlist("({$this->config->user_attribute}=".$this->filter_addslashes($extusername).")"); 572 return count($users); 573 } 574 /** 575 * syncronizes user fron external db to moodle user table 576 * 577 * Sync is now using username attribute. 578 * 579 * Syncing users removes or suspends users that dont exists anymore in external db. 580 * Creates new users and updates coursecreator status of users. 581 * 582 * @param int $bulk_insert_records will insert $bulkinsert_records per insert statement 583 * valid only with $unsafe. increase to a couple thousand for 584 * blinding fast inserts -- but test it: you may hit mysqld's 585 * max_allowed_packet limit. 586 * @param bool $do_updates will do pull in data updates from ldap if relevant 587 */ 588 function sync_users ($bulk_insert_records = 1000, $do_updates = true) { 589 global $CFG; 590 $textlib = textlib_get_instance(); 591 $droptablesql = array(); /// sql commands to drop the table (because session scope could be a problem for 592 /// some persistent drivers like ODBTP (mssql) or if this function is invoked 593 /// from within a PHP application using persistent connections 594 // configure a temp table 595 print "Configuring temp table\n"; 596 switch (strtolower($CFG->dbfamily)) { 597 case 'mysql': 598 $temptable = $CFG->prefix . 'extuser'; 599 $droptablesql[] = 'DROP TEMPORARY TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) 600 execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later 601 echo "Creating temp table $temptable\n"; 602 execute_sql('CREATE TEMPORARY TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username)) TYPE=MyISAM', false); 603 break; 604 case 'postgres': 605 $temptable = $CFG->prefix . 'extuser'; 606 $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) 607 execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later 608 echo "Creating temp table $temptable\n"; 609 $bulk_insert_records = 1; // no support for multiple sets of values 610 execute_sql('CREATE TEMPORARY TABLE '. $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))', false); 611 break; 612 case 'mssql': 613 $temptable = '#'.$CFG->prefix . 'extuser'; /// MSSQL temp tables begin with # 614 $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) 615 execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later 616 echo "Creating temp table $temptable\n"; 617 $bulk_insert_records = 1; // no support for multiple sets of values 618 execute_sql('CREATE TABLE ' . $temptable . ' (username VARCHAR(64), PRIMARY KEY (username))', false); 619 break; 620 case 'oracle': 621 $temptable = $CFG->prefix . 'extuser'; 622 $droptablesql[] = 'TRUNCATE TABLE ' . $temptable; // oracle requires truncate before being able to drop a temp table 623 $droptablesql[] = 'DROP TABLE ' . $temptable; // sql command to drop the table (because session scope could be a problem) 624 execute_sql_arr($droptablesql, true, false); /// Drop temp table to avoid persistence problems later 625 echo "Creating temp table $temptable\n"; 626 $bulk_insert_records = 1; // no support for multiple sets of values 627 execute_sql('CREATE GLOBAL TEMPORARY TABLE '.$temptable.' (username VARCHAR(64), PRIMARY KEY (username)) ON COMMIT PRESERVE ROWS', false); 628 break; 629 } 630 print "Connecting to ldap...\n"; 631 $ldapconnection = $this->ldap_connect(); 632 if (!$ldapconnection) { 633 @ldap_close($ldapconnection); 634 print get_string('auth_ldap_noconnect','auth',$this->config->host_url); 635 exit; 636 } 637 //// 638 //// get user's list from ldap to sql in a scalable fashion 639 //// 640 // prepare some data we'll need 641 $filter = "(&(".$this->config->user_attribute."=*)(".$this->config->objectclass."))"; 642 $contexts = explode(";",$this->config->contexts); 643 if (!empty($this->config->create_context)) { 644 array_push($contexts, $this->config->create_context); 645 } 646 $fresult = array(); 647 foreach ($contexts as $context) { 648 $context = trim($context); 649 if (empty($context)) { 650 continue; 651 } 652 begin_sql(); 653 if ($this->config->search_sub) { 654 //use ldap_search to find first user from subtree 655 $ldap_result = ldap_search($ldapconnection, $context, 656 $filter, 657 array($this->config->user_attribute)); 658 } else { 659 //search only in this context 660 $ldap_result = ldap_list($ldapconnection, $context, 661 $filter, 662 array($this->config->user_attribute)); 663 } 664 if ($entry = ldap_first_entry($ldapconnection, $ldap_result)) { 665 do { 666 $value = ldap_get_values_len($ldapconnection, $entry, $this->config->user_attribute); 667 $value = $textlib->convert($value[0], $this->config->ldapencoding, 'utf-8'); 668 array_push($fresult, $value); 669 if (count($fresult) >= $bulk_insert_records) { 670 $this->ldap_bulk_insert($fresult, $temptable); 671 $fresult = array(); 672 } 673 } while ($entry = ldap_next_entry($ldapconnection, $entry)); 674 } 675 unset($ldap_result); // free mem 676 // insert any remaining users and release mem 677 if (count($fresult)) { 678 $this->ldap_bulk_insert($fresult, $temptable); 679 $fresult = array(); 680 } 681 commit_sql(); 682 } 683 /// preserve our user database 684 /// if the temp table is empty, it probably means that something went wrong, exit 685 /// so as to avoid mass deletion of users; which is hard to undo 686 $count = get_record_sql('SELECT COUNT(username) AS count, 1 FROM ' . $temptable); 687 $count = $count->{'count'}; 688 if ($count < 1) { 689 print "Did not get any users from LDAP -- error? -- exiting\n"; 690 exit; 691 } else { 692 print "Got $count records from LDAP\n\n"; 693 } 694 /// User removal 695 // find users in DB that aren't in ldap -- to be removed! 696 // this is still not as scalable (but how often do we mass delete?) 697 if (!empty($this->config->removeuser)) { 698 $sql = "SELECT u.id, u.username, u.email, u.auth 699 FROM {$CFG->prefix}user u 700 LEFT JOIN $temptable e ON u.username = e.username 701 WHERE u.auth='cas' 702 AND u.deleted=0 703 AND e.username IS NULL"; 704 $remove_users = get_records_sql($sql); 705 if (!empty($remove_users)) { 706 print "User entries to remove: ". count($remove_users) . "\n"; 707 foreach ($remove_users as $user) { 708 if ($this->config->removeuser == 2) { 709 if (delete_user($user)) { 710 echo "\t"; print_string('auth_dbdeleteuser', 'auth', array($user->username, $user->id)); echo "\n"; 711 } else { 712 echo "\t"; print_string('auth_dbdeleteusererror', 'auth', $user->username); echo "\n"; 713 } 714 } else if ($this->config->removeuser == 1) { 715 $updateuser = new object(); 716 $updateuser->id = $user->id; 717 $updateuser->auth = 'nologin'; 718 if (update_record('user', $updateuser)) { 719 echo "\t"; print_string('auth_dbsuspenduser', 'auth', array($user->username, $user->id)); echo "\n"; 720 } else { 721 echo "\t"; print_string('auth_dbsuspendusererror', 'auth', $user->username); echo "\n"; 722 } 723 } 724 } 725 } else { 726 print "No user entries to be removed\n"; 727 } 728 unset($remove_users); // free mem! 729 } 730 /// Revive suspended users 731 if (!empty($this->config->removeuser) and $this->config->removeuser == 1) { 732 $sql = "SELECT u.id, u.username 733 FROM $temptable e, {$CFG->prefix}user u 734 WHERE e.username=u.username 735 AND u.auth='nologin'"; 736 $revive_users = get_records_sql($sql); 737 if (!empty($revive_users)) { 738 print "User entries to be revived: ". count($revive_users) . "\n"; 739 begin_sql(); 740 foreach ($revive_users as $user) { 741 $updateuser = new object(); 742 $updateuser->id = $user->id; 743 $updateuser->auth = 'cas'; 744 if (update_record('user', $updateuser)) { 745 echo "\t"; print_string('auth_dbreviveser', 'auth', array($user->username, $user->id)); echo "\n"; 746 } else { 747 echo "\t"; print_string('auth_dbreviveusererror', 'auth', $user->username); echo "\n"; 748 } 749 } 750 commit_sql(); 751 } else { 752 print "No user entries to be revived\n"; 753 } 754 unset($revive_users); 755 } 756 /// User Updates - time-consuming (optional) 757 if ($do_updates) { 758 // narrow down what fields we need to update 759 $all_keys = array_keys(get_object_vars($this->config)); 760 $updatekeys = array(); 761 foreach ($all_keys as $key) { 762 if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) { 763 // if we have a field to update it from 764 // and it must be updated 'onlogin' we 765 // update it on cron 766 if ( !empty($this->config->{'field_map_'.$match[1]}) 767 and $this->config->{$match[0]} === 'onlogin') { 768 array_push($updatekeys, $match[1]); // the actual key name 769 } 770 } 771 } 772 // print_r($all_keys); print_r($updatekeys); 773 unset($all_keys); unset($key); 774 } else { 775 print "No updates to be done\n"; 776 } 777 if ( $do_updates and !empty($updatekeys) ) { // run updates only if relevant 778 $users = get_records_sql("SELECT u.username, u.id 779 FROM {$CFG->prefix}user u 780 WHERE u.deleted=0 AND u.auth='cas'"); 781 if (!empty($users)) { 782 print "User entries to update: ". count($users). "\n"; 783 $sitecontext = get_context_instance(CONTEXT_SYSTEM); 784 if (!empty($this->config->creators) and !empty($this->config->memberattribute) 785 and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { 786 $creatorrole = array_shift($roles); // We can only use one, let's use the first one 787 } else { 788 $creatorrole = false; 789 } 790 begin_sql(); 791 $xcount = 0; 792 $maxxcount = 100; 793 foreach ($users as $user) { 794 echo "\t"; print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id)); 795 if (!$this->update_user_record(addslashes($user->username), $updatekeys)) { 796 echo " - ".get_string('skipped'); 797 } 798 echo "\n"; 799 $xcount++; 800 // update course creators if needed 801 if ($creatorrole !== false) { 802 if ($this->iscreator($user->username)) { 803 role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'cas'); 804 } else { 805 role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'cas'); 806 } 807 } 808 if ($xcount++ > $maxxcount) { 809 commit_sql(); 810 begin_sql(); 811 $xcount = 0; 812 } 813 } 814 commit_sql(); 815 unset($users); // free mem 816 } 817 } else { // end do updates 818 print "No updates to be done\n"; 819 } 820 /// User Additions 821 // find users missing in DB that are in LDAP 822 // note that get_records_sql wants at least 2 fields returned, 823 // and gives me a nifty object I don't want. 824 // note: we do not care about deleted accounts anymore, this feature was replaced by suspending to nologin auth plugin 825 $sql = "SELECT e.username, e.username 826 FROM $temptable e LEFT JOIN {$CFG->prefix}user u ON e.username = u.username 827 WHERE u.id IS NULL"; 828 $add_users = get_records_sql($sql); // get rid of the fat 829 if (!empty($add_users)) { 830 print "User entries to add: ". count($add_users). "\n"; 831 $sitecontext = get_context_instance(CONTEXT_SYSTEM); 832 if (!empty($this->config->creators) and !empty($this->config->memberattribute) 833 and $roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { 834 $creatorrole = array_shift($roles); // We can only use one, let's use the first one 835 } else { 836 $creatorrole = false; 837 } 838 begin_sql(); 839 foreach ($add_users as $user) { 840 $user = $this->get_userinfo_asobj(addslashes($user->username)); 841 // prep a few params 842 $user->modified = time(); 843 $user->confirmed = 1; 844 $user->auth = 'cas'; 845 $user->mnethostid = $CFG->mnet_localhost_id; 846 if (empty($user->lang)) { 847 $user->lang = $CFG->lang; 848 } 849 $user = addslashes_recursive($user); 850 if ($id = insert_record('user',$user)) { 851 echo "\t"; print_string('auth_dbinsertuser', 'auth', array(stripslashes($user->username), $id)); echo "\n"; 852 $userobj = $this->update_user_record($user->username); 853 if (!empty($this->config->forcechangepassword)) { 854 set_user_preference('auth_forcepasswordchange', 1, $userobj->id); 855 } 856 } else { 857 echo "\t"; print_string('auth_dbinsertusererror', 'auth', $user->username); echo "\n"; 858 } 859 // add course creators if needed 860 if ($creatorrole !== false and $this->iscreator(stripslashes($user->username))) { 861 role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'cas'); 862 } 863 } 864 commit_sql(); 865 unset($add_users); // free mem 866 } else { 867 print "No users to be added\n"; 868 } 869 return true; 870 } 871 /** 872 * Update a local user record from an external source. 873 * This is a lighter version of the one in moodlelib -- won't do 874 * expensive ops such as enrolment. 875 * 876 * If you don't pass $updatekeys, there is a performance hit and 877 * values removed from LDAP won't be removed from moodle. 878 * 879 * @param string $username username (with system magic quotes) 880 */ 881 function update_user_record($username, $updatekeys = false) { 882 global $CFG; 883 //just in case check text case 884 $username = trim(moodle_strtolower($username)); 885 // get the current user record 886 $user = get_record('user', 'username', $username, 'mnethostid', $CFG->mnet_localhost_id); 887 if (empty($user)) { // trouble 888 error_log("Cannot update non-existent user: ".stripslashes($username)); 889 print_error('auth_dbusernotexist','auth',$username); 890 die; 891 } 892 // Protect the userid from being overwritten 893 $userid = $user->id; 894 if ($newinfo = $this->get_userinfo($username)) { 895 $newinfo = truncate_userinfo($newinfo); 896 if (empty($updatekeys)) { // all keys? this does not support removing values 897 $updatekeys = array_keys($newinfo); 898 } 899 foreach ($updatekeys as $key) { 900 if (isset($newinfo[$key])) { 901 $value = $newinfo[$key]; 902 } else { 903 $value = ''; 904 } 905 if (!empty($this->config->{'field_updatelocal_' . $key})) { 906 if ($user->{$key} != $value) { // only update if it's changed 907 set_field('user', $key, addslashes($value), 'id', $userid); 908 } 909 } 910 } 911 } else { 912 return false; 913 } 914 return get_record_select('user', "id = $userid AND deleted = 0"); 915 } 916 /** 917 * Bulk insert in SQL's temp table 918 * @param array $users is an array of usernames 919 */ 920 function ldap_bulk_insert($users, $temptable) { 921 // bulk insert -- superfast with $bulk_insert_records 922 $sql = 'INSERT INTO ' . $temptable . ' (username) VALUES '; 923 // make those values safe 924 $users = addslashes_recursive($users); 925 // join and quote the whole lot 926 $sql = $sql . "('" . implode("'),('", $users) . "')"; 927 print "\t+ " . count($users) . " users\n"; 928 execute_sql($sql, false); 929 } 930 /** 931 * Returns true if user should be coursecreator. 932 * 933 * @param mixed $username username (without system magic quotes) 934 * @return boolean result 935 */ 936 function iscreator($username) { 937 if ((empty($this->config->attrcreators) && empty($this->config->groupecreators)) or empty($this->config->memberattribute)) { 938 return null; 939 } 940 $textlib = textlib_get_instance(); 941 $extusername = $textlib->convert($username, 'utf-8', $this->config->ldapencoding); 942 //test for groupe creator 943 if (!empty($this->config->groupecreators)) 944 if ((boolean)$this->ldap_isgroupmember($extusername, $this->config->groupecreators)) 945 return true; 946 //build filter for attrcreator 947 if (!empty($this->config->attrcreators)) { 948 $attrs = explode(";",$this->config->attrcreators); 949 $filter = "(& (".$this->config->user_attribute."=$username)(|"; 950 foreach ($attrs as $attr){ 951 if(strpos($attr, "=")) 952 $filter .= "($attr)"; 953 else 954 $filter .= "(".$this->config->memberattribute."=$attr)"; 955 } 956 $filter .= "))"; 957 //search 958 $result = $this->ldap_get_userlist($filter); 959 if (count($result)!=0) 960 return true; 961 } 962 963 return false; 964 } 965 /** 966 * checks if user belong to specific group(s) 967 * 968 * Returns true if user belongs group in grupdns string. 969 * 970 * @param mixed $username username 971 * @param mixed $groupdns string of group dn separated by ; 972 * 973 */ 974 function ldap_isgroupmember($extusername='', $groupdns='') { 975 // Takes username and groupdn(s) , separated by ; 976 // Returns true if user is member of any given groups 977 $ldapconnection = $this->ldap_connect(); 978 if (empty($extusername) or empty($groupdns)) { 979 return false; 980 } 981 if ($this->config->memberattribute_isdn) { 982 $memberuser = $this->ldap_find_userdn($ldapconnection, $extusername); 983 } else { 984 $memberuser = $extusername; 985 } 986 if (empty($memberuser)) { 987 return false; 988 } 989 $groups = explode(";",$groupdns); 990 $result = false; 991 foreach ($groups as $group) { 992 $group = trim($group); 993 if (empty($group)) { 994 continue; 995 } 996 //echo "Checking group $group for member $username\n"; 997 $search = ldap_read($ldapconnection, $group, '('.$this->config->memberattribute.'='.$this->filter_addslashes($memberuser).')', array($this->config->memberattribute)); 998 if (!empty($search) and ldap_count_entries($ldapconnection, $search)) { 999 $info = $this->ldap_get_entries($ldapconnection, $search); 1000 if (count($info) > 0 ) { 1001 // user is member of group 1002 $result = true; 1003 break; 1004 } 1005 } 1006 } 1007 return $result; 1008 } 1009 /** 1010 * return all usernames from ldap 1011 * 1012 * @return array 1013 */ 1014 function ldap_get_userlist($filter="*") { 1015 /// returns all users from ldap servers 1016 $fresult = array(); 1017 $ldapconnection = $this->ldap_connect(); 1018 if ($filter=="*") { 1019 $filter = "(&(".$this->config->user_attribute."=*)(".$this->config->objectclass."))"; 1020 } 1021 $contexts = explode(";",$this->config->contexts); 1022 if (!empty($this->config->create_context)) { 1023 array_push($contexts, $this->config->create_context); 1024 } 1025 foreach ($contexts as $context) { 1026 $context = trim($context); 1027 if (empty($context)) { 1028 continue; 1029 } 1030 if ($this->config->search_sub) { 1031 //use ldap_search to find first user from subtree 1032 $ldap_result = ldap_search($ldapconnection, $context,$filter,array($this->config->user_attribute)); 1033 } 1034 else { 1035 //search only in this context 1036 $ldap_result = ldap_list($ldapconnection, $context, 1037 $filter, 1038 array($this->config->user_attribute)); 1039 } 1040 $users = $this->ldap_get_entries($ldapconnection, $ldap_result); 1041 //add found users to list 1042 for ($i=0;$i<count($users);$i++) { 1043 array_push($fresult, ($users[$i][$this->config->user_attribute][0]) ); 1044 } 1045 } 1046 return $fresult; 1047 } 1048 /** 1049 * return entries from ldap 1050 * 1051 * Returns values like ldap_get_entries but is 1052 * binary compatible and return all attributes as array 1053 * 1054 * @return array ldap-entries 1055 */ 1056 function ldap_get_entries($conn, $searchresult) { 1057 //Returns values like ldap_get_entries but is 1058 //binary compatible 1059 $i=0; 1060 $fresult=array(); 1061 $entry = ldap_first_entry($conn, $searchresult); 1062 do { 1063 $attributes = @ldap_get_attributes($conn, $entry); 1064 for ($j=0; $j<$attributes['count']; $j++) { 1065 $values = ldap_get_values_len($conn, $entry,$attributes[$j]); 1066 1067 if (is_array($values)) { 1068 $fresult[$i][strtolower($attributes[$j])] = $values; 1069 } 1070 else { 1071 $fresult[$i][strtolower($attributes[$j])] = array($values); 1072 } 1073 } 1074 $i++; 1075 } 1076 while ($entry = @ldap_next_entry($conn, $entry)); 1077 //were done 1078 1079 return ($fresult); 1080 } 1081 /** 1082 * Sync roles for this user 1083 * 1084 * @param $user object user object (without system magic quotes) 1085 */ 1086 function sync_roles($user) { 1087 $iscreator = $this->iscreator($user->username); 1088 if ($iscreator === null) { 1089 return; //nothing to sync - creators not configured 1090 } 1091 if ($roles = get_roles_with_capability('moodle/legacy:coursecreator', CAP_ALLOW)) { 1092 $creatorrole = array_shift($roles); // We can only use one, let's use the first one 1093 $systemcontext = get_context_instance(CONTEXT_SYSTEM); 1094 if ($iscreator) { // Following calls will not create duplicates 1095 role_assign($creatorrole->id, $user->id, 0, $systemcontext->id, 0, 0, 0, 'cas'); 1096 } else { 1097 //unassign only if previously assigned by this plugin! 1098 role_unassign($creatorrole->id, $user->id, 0, $systemcontext->id, 'cas'); 1099 } 1100 } 1101 } 1102 /** 1103 * Quote control characters in texts used in ldap filters - see rfc2254.txt 1104 * 1105 * @param string 1106 */ 1107 function filter_addslashes($text) { 1108 $text = str_replace('\\', '\\5c', $text); 1109 $text = str_replace(array('*', '(', ')', "\0"), 1110 array('\\2a', '\\28', '\\29', '\\00'), $text); 1111 return $text; 1112 } 1113 } 1114 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |