[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/auth/cas/ -> auth.php (source)

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


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7