|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00015 if (!defined('MOODLE_INTERNAL')) { 00016 die('Direct access to this script is forbidden.'); 00017 } 00018 00019 require_once($CFG->libdir.'/authlib.php'); 00020 00024 class auth_plugin_mnet extends auth_plugin_base { 00025 00029 function auth_plugin_mnet() { 00030 $this->authtype = 'mnet'; 00031 $this->config = get_config('auth_mnet'); 00032 $this->mnet = get_mnet_environment(); 00033 } 00034 00044 function user_login($username, $password) { 00045 return false; // print_error("mnetlocal"); 00046 } 00047 00055 function user_authorise($token, $useragent) { 00056 global $CFG, $SITE, $DB; 00057 $remoteclient = get_mnet_remote_client(); 00058 require_once $CFG->dirroot . '/mnet/xmlrpc/serverlib.php'; 00059 00060 $mnet_session = $DB->get_record('mnet_session', array('token'=>$token, 'useragent'=>$useragent)); 00061 if (empty($mnet_session)) { 00062 throw new mnet_server_exception(1, 'authfail_nosessionexists'); 00063 } 00064 00065 // check session confirm timeout 00066 if ($mnet_session->confirm_timeout < time()) { 00067 throw new mnet_server_exception(2, 'authfail_sessiontimedout'); 00068 } 00069 00070 // session okay, try getting the user 00071 if (!$user = $DB->get_record('user', array('id'=>$mnet_session->userid))) { 00072 throw new mnet_server_exception(3, 'authfail_usermismatch'); 00073 } 00074 00075 $userdata = mnet_strip_user((array)$user, mnet_fields_to_send($remoteclient)); 00076 00077 // extra special ones 00078 $userdata['auth'] = 'mnet'; 00079 $userdata['wwwroot'] = $this->mnet->wwwroot; 00080 $userdata['session.gc_maxlifetime'] = ini_get('session.gc_maxlifetime'); 00081 00082 if (array_key_exists('picture', $userdata) && !empty($user->picture)) { 00083 $fs = get_file_storage(); 00084 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST); 00085 if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) { 00086 $userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified(); 00087 $userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype(); 00088 } else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) { 00089 $userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified(); 00090 $userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype(); 00091 } 00092 } 00093 00094 $userdata['myhosts'] = array(); 00095 if ($courses = enrol_get_users_courses($user->id, false)) { 00096 $userdata['myhosts'][] = array('name'=> $SITE->shortname, 'url' => $CFG->wwwroot, 'count' => count($courses)); 00097 } 00098 00099 $sql = "SELECT h.name AS hostname, h.wwwroot, h.id AS hostid, 00100 COUNT(c.id) AS count 00101 FROM {mnetservice_enrol_courses} c 00102 JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid) 00103 JOIN {mnet_host} h ON h.id = c.hostid 00104 WHERE e.userid = ? AND c.hostid = ? 00105 GROUP BY h.name, h.wwwroot, h.id"; 00106 00107 if ($courses = $DB->get_records_sql($sql, array($user->id, $remoteclient->id))) { 00108 foreach($courses as $course) { 00109 $userdata['myhosts'][] = array('name'=> $course->hostname, 'url' => $CFG->wwwroot.'/auth/mnet/jump.php?hostid='.$course->hostid, 'count' => $course->count); 00110 } 00111 } 00112 00113 return $userdata; 00114 } 00115 00119 function generate_token() { 00120 return sha1(str_shuffle('' . mt_rand() . time())); 00121 } 00122 00131 function start_jump_session($mnethostid, $wantsurl, $wantsurlbackhere=false) { 00132 global $CFG, $USER, $DB; 00133 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; 00134 00135 if (session_is_loggedinas()) { 00136 print_error('notpermittedtojumpas', 'mnet'); 00137 } 00138 00139 // check remote login permissions 00140 if (! has_capability('moodle/site:mnetlogintoremote', get_system_context()) 00141 or is_mnet_remote_user($USER) 00142 or isguestuser() 00143 or !isloggedin()) { 00144 print_error('notpermittedtojump', 'mnet'); 00145 } 00146 00147 // check for SSO publish permission first 00148 if ($this->has_service($mnethostid, 'sso_sp') == false) { 00149 print_error('hostnotconfiguredforsso', 'mnet'); 00150 } 00151 00152 // set RPC timeout to 30 seconds if not configured 00153 if (empty($this->config->rpc_negotiation_timeout)) { 00154 $this->config->rpc_negotiation_timeout = 30; 00155 set_config('rpc_negotiation_timeout', '30', 'auth_mnet'); 00156 } 00157 00158 // get the host info 00159 $mnet_peer = new mnet_peer(); 00160 $mnet_peer->set_id($mnethostid); 00161 00162 // set up the session 00163 $mnet_session = $DB->get_record('mnet_session', 00164 array('userid'=>$USER->id, 'mnethostid'=>$mnethostid, 00165 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT']))); 00166 if ($mnet_session == false) { 00167 $mnet_session = new stdClass(); 00168 $mnet_session->mnethostid = $mnethostid; 00169 $mnet_session->userid = $USER->id; 00170 $mnet_session->username = $USER->username; 00171 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); 00172 $mnet_session->token = $this->generate_token(); 00173 $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout; 00174 $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime'); 00175 $mnet_session->session_id = session_id(); 00176 $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session); 00177 } else { 00178 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); 00179 $mnet_session->token = $this->generate_token(); 00180 $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout; 00181 $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime'); 00182 $mnet_session->session_id = session_id(); 00183 $DB->update_record('mnet_session', $mnet_session); 00184 } 00185 00186 // construct the redirection URL 00187 //$transport = mnet_get_protocol($mnet_peer->transport); 00188 $wantsurl = urlencode($wantsurl); 00189 $url = "{$mnet_peer->wwwroot}{$mnet_peer->application->sso_land_url}?token={$mnet_session->token}&idp={$this->mnet->wwwroot}&wantsurl={$wantsurl}"; 00190 if ($wantsurlbackhere) { 00191 $url .= '&remoteurl=1'; 00192 } 00193 00194 return $url; 00195 } 00196 00206 function confirm_mnet_session($token, $remotepeer) { 00207 global $CFG, $DB; 00208 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; 00209 require_once $CFG->libdir . '/gdlib.php'; 00210 00211 // verify the remote host is configured locally before attempting RPC call 00212 if (! $remotehost = $DB->get_record('mnet_host', array('wwwroot' => $remotepeer->wwwroot, 'deleted' => 0))) { 00213 print_error('notpermittedtoland', 'mnet'); 00214 } 00215 00216 // set up the RPC request 00217 $mnetrequest = new mnet_xmlrpc_client(); 00218 $mnetrequest->set_method('auth/mnet/auth.php/user_authorise'); 00219 00220 // set $token and $useragent parameters 00221 $mnetrequest->add_param($token); 00222 $mnetrequest->add_param(sha1($_SERVER['HTTP_USER_AGENT'])); 00223 00224 // Thunderbirds are go! Do RPC call and store response 00225 if ($mnetrequest->send($remotepeer) === true) { 00226 $remoteuser = (object) $mnetrequest->response; 00227 } else { 00228 foreach ($mnetrequest->error as $errormessage) { 00229 list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); 00230 if($code == 702) { 00231 $site = get_site(); 00232 print_error('mnet_session_prohibited', 'mnet', $remotepeer->wwwroot, format_string($site->fullname)); 00233 exit; 00234 } 00235 $message .= "ERROR $code:<br/>$errormessage<br/>"; 00236 } 00237 print_error("rpcerror", '', '', $message); 00238 } 00239 unset($mnetrequest); 00240 00241 if (empty($remoteuser) or empty($remoteuser->username)) { 00242 print_error('unknownerror', 'mnet'); 00243 exit; 00244 } 00245 00246 if (user_not_fully_set_up($remoteuser)) { 00247 print_error('notenoughidpinfo', 'mnet'); 00248 exit; 00249 } 00250 00251 $remoteuser = mnet_strip_user($remoteuser, mnet_fields_to_import($remotepeer)); 00252 00253 $remoteuser->auth = 'mnet'; 00254 $remoteuser->wwwroot = $remotepeer->wwwroot; 00255 00256 // the user may roam from Moodle 1.x where lang has _utf8 suffix 00257 // also, make sure that the lang is actually installed, otherwise set site default 00258 if (isset($remoteuser->lang)) { 00259 $remoteuser->lang = clean_param(str_replace('_utf8', '', $remoteuser->lang), PARAM_LANG); 00260 } 00261 if (empty($remoteuser->lang)) { 00262 if (!empty($CFG->lang)) { 00263 $remoteuser->lang = $CFG->lang; 00264 } else { 00265 $remoteuser->lang = 'en'; 00266 } 00267 } 00268 $firsttime = false; 00269 00270 // get the local record for the remote user 00271 $localuser = $DB->get_record('user', array('username'=>$remoteuser->username, 'mnethostid'=>$remotehost->id)); 00272 00273 // add the remote user to the database if necessary, and if allowed 00274 // TODO: refactor into a separate function 00275 if (empty($localuser) || ! $localuser->id) { 00276 /* 00277 if (empty($this->config->auto_add_remote_users)) { 00278 print_error('nolocaluser', 'mnet'); 00279 } See MDL-21327 for why this is commented out 00280 */ 00281 $remoteuser->mnethostid = $remotehost->id; 00282 $remoteuser->firstaccess = time(); // First time user in this server, grab it here 00283 $remoteuser->confirmed = 1; 00284 00285 $remoteuser->id = $DB->insert_record('user', $remoteuser); 00286 $firsttime = true; 00287 $localuser = $remoteuser; 00288 } 00289 00290 // check sso access control list for permission first 00291 if (!$this->can_login_remotely($localuser->username, $remotehost->id)) { 00292 print_error('sso_mnet_login_refused', 'mnet', '', array('user'=>$localuser->username, 'host'=>$remotehost->name)); 00293 } 00294 00295 $fs = get_file_storage(); 00296 00297 // update the local user record with remote user data 00298 foreach ((array) $remoteuser as $key => $val) { 00299 00300 if ($key == '_mnet_userpicture_timemodified' and empty($CFG->disableuserimages) and isset($remoteuser->picture)) { 00301 // update the user picture if there is a newer verion at the identity provider 00302 $usercontext = get_context_instance(CONTEXT_USER, $localuser->id, MUST_EXIST); 00303 if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) { 00304 $localtimemodified = $usericonfile->get_timemodified(); 00305 } else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) { 00306 $localtimemodified = $usericonfile->get_timemodified(); 00307 } else { 00308 $localtimemodified = 0; 00309 } 00310 00311 if (!empty($val) and $localtimemodified < $val) { 00312 mnet_debug('refetching the user picture from the identity provider host'); 00313 $fetchrequest = new mnet_xmlrpc_client(); 00314 $fetchrequest->set_method('auth/mnet/auth.php/fetch_user_image'); 00315 $fetchrequest->add_param($localuser->username); 00316 if ($fetchrequest->send($remotepeer) === true) { 00317 if (strlen($fetchrequest->response['f1']) > 0) { 00318 $imagefilename = $CFG->tempdir . '/mnet-usericon-' . $localuser->id; 00319 $imagecontents = base64_decode($fetchrequest->response['f1']); 00320 file_put_contents($imagefilename, $imagecontents); 00321 if (process_new_icon($usercontext, 'user', 'icon', 0, $imagefilename)) { 00322 $localuser->picture = 1; 00323 } 00324 unlink($imagefilename); 00325 } 00326 // note that since Moodle 2.0 we ignore $fetchrequest->response['f2'] 00327 // the mimetype information provided is ignored and the type of the file is detected 00328 // by process_new_icon() 00329 } 00330 } 00331 } 00332 00333 if($key == 'myhosts') { 00334 $localuser->mnet_foreign_host_array = array(); 00335 foreach($val as $rhost) { 00336 $name = clean_param($rhost['name'], PARAM_ALPHANUM); 00337 $url = clean_param($rhost['url'], PARAM_URL); 00338 $count = clean_param($rhost['count'], PARAM_INT); 00339 $url_is_local = stristr($url , $CFG->wwwroot); 00340 if (!empty($name) && !empty($count) && empty($url_is_local)) { 00341 $localuser->mnet_foreign_host_array[] = array('name' => $name, 00342 'url' => $url, 00343 'count' => $count); 00344 } 00345 } 00346 } 00347 00348 $localuser->{$key} = $val; 00349 } 00350 00351 $localuser->mnethostid = $remotepeer->id; 00352 if (empty($localuser->firstaccess)) { // Now firstaccess, grab it here 00353 $localuser->firstaccess = time(); 00354 } 00355 00356 $DB->update_record('user', $localuser); 00357 00358 if (!$firsttime) { 00359 // repeat customer! let the IDP know about enrolments 00360 // we have for this user. 00361 // set up the RPC request 00362 $mnetrequest = new mnet_xmlrpc_client(); 00363 $mnetrequest->set_method('auth/mnet/auth.php/update_enrolments'); 00364 00365 // pass username and an assoc array of "my courses" 00366 // with info so that the IDP can maintain mnetservice_enrol_enrolments 00367 $mnetrequest->add_param($remoteuser->username); 00368 $fields = 'id, category, sortorder, fullname, shortname, idnumber, summary, startdate, visible'; 00369 $courses = enrol_get_users_courses($localuser->id, false, $fields, 'visible DESC,sortorder ASC'); 00370 if (is_array($courses) && !empty($courses)) { 00371 // Second request to do the JOINs that we'd have done 00372 // inside enrol_get_users_courses() if we had been allowed 00373 $sql = "SELECT c.id, 00374 cc.name AS cat_name, cc.description AS cat_description 00375 FROM {course} c 00376 JOIN {course_categories} cc ON c.category = cc.id 00377 WHERE c.id IN (" . join(',',array_keys($courses)) . ')'; 00378 $extra = $DB->get_records_sql($sql); 00379 00380 $keys = array_keys($courses); 00381 $defaultrole = reset(get_archetype_roles('student')); 00382 //$defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!! 00383 foreach ($keys AS $id) { 00384 if ($courses[$id]->visible == 0) { 00385 unset($courses[$id]); 00386 continue; 00387 } 00388 $courses[$id]->cat_id = $courses[$id]->category; 00389 $courses[$id]->defaultroleid = $defaultrole->id; 00390 unset($courses[$id]->category); 00391 unset($courses[$id]->visible); 00392 00393 $courses[$id]->cat_name = $extra[$id]->cat_name; 00394 $courses[$id]->cat_description = $extra[$id]->cat_description; 00395 $courses[$id]->defaultrolename = $defaultrole->name; 00396 // coerce to array 00397 $courses[$id] = (array)$courses[$id]; 00398 } 00399 } else { 00400 // if the array is empty, send it anyway 00401 // we may be clearing out stale entries 00402 $courses = array(); 00403 } 00404 $mnetrequest->add_param($courses); 00405 00406 // Call 0800-RPC Now! -- we don't care too much if it fails 00407 // as it's just informational. 00408 if ($mnetrequest->send($remotepeer) === false) { 00409 // error_log(print_r($mnetrequest->error,1)); 00410 } 00411 } 00412 00413 return $localuser; 00414 } 00415 00416 00425 public function update_mnet_session($user, $token, $remotepeer) { 00426 global $DB; 00427 $session_gc_maxlifetime = 1440; 00428 if (isset($user->session_gc_maxlifetime)) { 00429 $session_gc_maxlifetime = $user->session_gc_maxlifetime; 00430 } 00431 if (!$mnet_session = $DB->get_record('mnet_session', 00432 array('userid'=>$user->id, 'mnethostid'=>$remotepeer->id, 00433 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT'])))) { 00434 $mnet_session = new stdClass(); 00435 $mnet_session->mnethostid = $remotepeer->id; 00436 $mnet_session->userid = $user->id; 00437 $mnet_session->username = $user->username; 00438 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); 00439 $mnet_session->token = $token; // Needed to support simultaneous sessions 00440 // and preserving DB rec uniqueness 00441 $mnet_session->confirm_timeout = time(); 00442 $mnet_session->expires = time() + (integer)$session_gc_maxlifetime; 00443 $mnet_session->session_id = session_id(); 00444 $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session); 00445 } else { 00446 $mnet_session->expires = time() + (integer)$session_gc_maxlifetime; 00447 $DB->update_record('mnet_session', $mnet_session); 00448 } 00449 } 00450 00451 00452 00463 function update_enrolments($username, $courses) { 00464 global $CFG, $DB; 00465 $remoteclient = get_mnet_remote_client(); 00466 00467 if (empty($username) || !is_array($courses)) { 00468 return false; 00469 } 00470 // make sure it is a user we have an in active session 00471 // with that host... 00472 $mnetsessions = $DB->get_records('mnet_session', array('username' => $username, 'mnethostid' => $remoteclient->id), '', 'id, userid'); 00473 $userid = null; 00474 foreach ($mnetsessions as $mnetsession) { 00475 if (is_null($userid)) { 00476 $userid = $mnetsession->userid; 00477 continue; 00478 } 00479 if ($userid != $mnetsession->userid) { 00480 throw new mnet_server_exception(3, 'authfail_usermismatch'); 00481 } 00482 } 00483 00484 if (empty($courses)) { // no courses? clear out quickly 00485 $DB->delete_records('mnetservice_enrol_enrolments', array('hostid'=>$remoteclient->id, 'userid'=>$userid)); 00486 return true; 00487 } 00488 00489 // IMPORTANT: Ask for remoteid as the first element in the query, so 00490 // that the array that comes back is indexed on the same field as the 00491 // array that we have received from the remote client 00492 $sql = "SELECT c.remoteid, c.id, c.categoryid AS cat_id, c.categoryname AS cat_name, c.sortorder, 00493 c.fullname, c.shortname, c.idnumber, c.summary, c.summaryformat, c.startdate, 00494 e.id AS enrolmentid 00495 FROM {mnetservice_enrol_courses} c 00496 LEFT JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid) 00497 WHERE e.userid = ? AND c.hostid = ?"; 00498 00499 $currentcourses = $DB->get_records_sql($sql, array($userid, $remoteclient->id)); 00500 00501 $local_courseid_array = array(); 00502 foreach($courses as $ix => $course) { 00503 00504 $course['remoteid'] = $course['id']; 00505 $course['hostid'] = (int)$remoteclient->id; 00506 $userisregd = false; 00507 00508 // if we do not have the the information about the remote course, it is not available 00509 // to us for remote enrolment - skip 00510 if (array_key_exists($course['remoteid'], $currentcourses)) { 00511 // Pointer to current course: 00512 $currentcourse =& $currentcourses[$course['remoteid']]; 00513 // We have a record - is it up-to-date? 00514 $course['id'] = $currentcourse->id; 00515 00516 $saveflag = false; 00517 00518 foreach($course as $key => $value) { 00519 if ($currentcourse->$key != $value) { 00520 $saveflag = true; 00521 $currentcourse->$key = $value; 00522 } 00523 } 00524 00525 if ($saveflag) { 00526 $DB->update_record('mnetervice_enrol_courses', $currentcourse); 00527 } 00528 00529 if (isset($currentcourse->enrolmentid) && is_numeric($currentcourse->enrolmentid)) { 00530 $userisregd = true; 00531 } 00532 } else { 00533 unset ($courses[$ix]); 00534 continue; 00535 } 00536 00537 // By this point, we should always have a $dataObj->id 00538 $local_courseid_array[] = $course['id']; 00539 00540 // Do we have a record for this assignment? 00541 if ($userisregd) { 00542 // Yes - we know about this one already 00543 // We don't want to do updates because the new data is probably 00544 // 'less complete' than the data we have. 00545 } else { 00546 // No - create a record 00547 $assignObj = new stdClass(); 00548 $assignObj->userid = $userid; 00549 $assignObj->hostid = (int)$remoteclient->id; 00550 $assignObj->remotecourseid = $course['remoteid']; 00551 $assignObj->rolename = $course['defaultrolename']; 00552 $assignObj->id = $DB->insert_record('mnetservice_enrol_enrolments', $assignObj); 00553 } 00554 } 00555 00556 // Clean up courses that the user is no longer enrolled in. 00557 if (!empty($local_courseid_array)) { 00558 $local_courseid_string = implode(', ', $local_courseid_array); 00559 $whereclause = " userid = ? AND hostid = ? AND remotecourseid NOT IN ($local_courseid_string)"; 00560 $DB->delete_records_select('mnetservice_enrol_enrolments', $whereclause, array($userid, $remoteclient->id)); 00561 } 00562 } 00563 00564 function prevent_local_passwords() { 00565 return true; 00566 } 00567 00573 function is_internal() { 00574 return false; 00575 } 00576 00583 function can_change_password() { 00584 //TODO: it should be able to redirect, right? 00585 return false; 00586 } 00587 00594 function change_password_url() { 00595 return null; 00596 } 00597 00608 function config_form($config, $err, $user_fields) { 00609 global $CFG, $DB; 00610 00611 $query = " 00612 SELECT 00613 h.id, 00614 h.name as hostname, 00615 h.wwwroot, 00616 h2idp.publish as idppublish, 00617 h2idp.subscribe as idpsubscribe, 00618 idp.name as idpname, 00619 h2sp.publish as sppublish, 00620 h2sp.subscribe as spsubscribe, 00621 sp.name as spname 00622 FROM 00623 {mnet_host} h 00624 LEFT JOIN 00625 {mnet_host2service} h2idp 00626 ON 00627 (h.id = h2idp.hostid AND 00628 (h2idp.publish = 1 OR 00629 h2idp.subscribe = 1)) 00630 INNER JOIN 00631 {mnet_service} idp 00632 ON 00633 (h2idp.serviceid = idp.id AND 00634 idp.name = 'sso_idp') 00635 LEFT JOIN 00636 {mnet_host2service} h2sp 00637 ON 00638 (h.id = h2sp.hostid AND 00639 (h2sp.publish = 1 OR 00640 h2sp.subscribe = 1)) 00641 INNER JOIN 00642 {mnet_service} sp 00643 ON 00644 (h2sp.serviceid = sp.id AND 00645 sp.name = 'sso_sp') 00646 WHERE 00647 ((h2idp.publish = 1 AND h2sp.subscribe = 1) OR 00648 (h2sp.publish = 1 AND h2idp.subscribe = 1)) AND 00649 h.id != ? 00650 ORDER BY 00651 h.name ASC"; 00652 00653 $id_providers = array(); 00654 $service_providers = array(); 00655 if ($resultset = $DB->get_records_sql($query, array($CFG->mnet_localhost_id))) { 00656 foreach($resultset as $hostservice) { 00657 if(!empty($hostservice->idppublish) && !empty($hostservice->spsubscribe)) { 00658 $service_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot); 00659 } 00660 if(!empty($hostservice->idpsubscribe) && !empty($hostservice->sppublish)) { 00661 $id_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot); 00662 } 00663 } 00664 } 00665 00666 include "config.html"; 00667 } 00668 00672 function process_config($config) { 00673 // set to defaults if undefined 00674 if (!isset ($config->rpc_negotiation_timeout)) { 00675 $config->rpc_negotiation_timeout = '30'; 00676 } 00677 /* 00678 if (!isset ($config->auto_add_remote_users)) { 00679 $config->auto_add_remote_users = '0'; 00680 } See MDL-21327 for why this is commented out 00681 set_config('auto_add_remote_users', $config->auto_add_remote_users, 'auth_mnet'); 00682 */ 00683 00684 // save settings 00685 set_config('rpc_negotiation_timeout', $config->rpc_negotiation_timeout, 'auth_mnet'); 00686 00687 return true; 00688 } 00689 00696 function keepalive_client() { 00697 global $CFG, $DB; 00698 $cutoff = time() - 300; // TODO - find out what the remote server's session 00699 // cutoff is, and preempt that 00700 00701 $sql = " 00702 select 00703 id, 00704 username, 00705 mnethostid 00706 from 00707 {user} 00708 where 00709 lastaccess > ? AND 00710 mnethostid != ? 00711 order by 00712 mnethostid"; 00713 00714 $immigrants = $DB->get_records_sql($sql, array($cutoff, $CFG->mnet_localhost_id)); 00715 00716 if ($immigrants == false) { 00717 return true; 00718 } 00719 00720 $usersArray = array(); 00721 foreach($immigrants as $immigrant) { 00722 $usersArray[$immigrant->mnethostid][] = $immigrant->username; 00723 } 00724 00725 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; 00726 foreach($usersArray as $mnethostid => $users) { 00727 $mnet_peer = new mnet_peer(); 00728 $mnet_peer->set_id($mnethostid); 00729 00730 $mnet_request = new mnet_xmlrpc_client(); 00731 $mnet_request->set_method('auth/mnet/auth.php/keepalive_server'); 00732 00733 // set $token and $useragent parameters 00734 $mnet_request->add_param($users); 00735 00736 if ($mnet_request->send($mnet_peer) === true) { 00737 if (!isset($mnet_request->response['code'])) { 00738 debugging("Server side error has occured on host $mnethostid"); 00739 continue; 00740 } elseif ($mnet_request->response['code'] > 0) { 00741 debugging($mnet_request->response['message']); 00742 } 00743 00744 if (!isset($mnet_request->response['last log id'])) { 00745 debugging("Server side error has occured on host $mnethostid\nNo log ID was received."); 00746 continue; 00747 } 00748 } else { 00749 debugging("Server side error has occured on host $mnethostid: " . 00750 join("\n", $mnet_request->error)); 00751 break; 00752 } 00753 $mnethostlogssql = " 00754 SELECT 00755 mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip, 00756 mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action, 00757 mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename, 00758 c.modinfo 00759 FROM 00760 ( 00761 SELECT 00762 l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid, 00763 l.action, l.url, l.info, u.username 00764 FROM 00765 {user} u 00766 INNER JOIN {log} l on l.userid = u.id 00767 WHERE 00768 u.mnethostid = ? 00769 AND l.id > ? 00770 ORDER BY remoteid ASC 00771 LIMIT 500 00772 ) mhostlogs 00773 INNER JOIN {course} c on c.id = mhostlogs.course 00774 ORDER by mhostlogs.remoteid ASC"; 00775 00776 $mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id'])); 00777 00778 if ($mnethostlogs == false) { 00779 continue; 00780 } 00781 00782 $processedlogs = array(); 00783 00784 foreach($mnethostlogs as $hostlog) { 00785 // Extract the name of the relevant module instance from the 00786 // course modinfo if possible. 00787 if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) { 00788 $modinfo = unserialize($hostlog->modinfo); 00789 unset($hostlog->modinfo); 00790 $modulearray = array(); 00791 foreach($modinfo as $module) { 00792 $modulearray[$module->cm] = $module->name; 00793 } 00794 $hostlog->resource_name = $modulearray[$hostlog->cmid]; 00795 } else { 00796 $hostlog->resource_name = ''; 00797 } 00798 00799 $processedlogs[] = array ( 00800 'remoteid' => $hostlog->remoteid, 00801 'time' => $hostlog->time, 00802 'userid' => $hostlog->userid, 00803 'ip' => $hostlog->ip, 00804 'course' => $hostlog->course, 00805 'coursename' => $hostlog->coursename, 00806 'module' => $hostlog->module, 00807 'cmid' => $hostlog->cmid, 00808 'action' => $hostlog->action, 00809 'url' => $hostlog->url, 00810 'info' => $hostlog->info, 00811 'resource_name' => $hostlog->resource_name, 00812 'username' => $hostlog->username 00813 ); 00814 } 00815 00816 unset($hostlog); 00817 00818 $mnet_request = new mnet_xmlrpc_client(); 00819 $mnet_request->set_method('auth/mnet/auth.php/refresh_log'); 00820 00821 // set $token and $useragent parameters 00822 $mnet_request->add_param($processedlogs); 00823 00824 if ($mnet_request->send($mnet_peer) === true) { 00825 if ($mnet_request->response['code'] > 0) { 00826 debugging($mnet_request->response['message']); 00827 } 00828 } else { 00829 debugging("Server side error has occured on host $mnet_peer->ip: " .join("\n", $mnet_request->error)); 00830 } 00831 } 00832 } 00833 00841 function refresh_log($array) { 00842 global $CFG, $DB; 00843 $remoteclient = get_mnet_remote_client(); 00844 00845 // We don't want to output anything to the client machine 00846 $start = ob_start(); 00847 00848 $returnString = ''; 00849 $transaction = $DB->start_delegated_transaction(); 00850 $useridarray = array(); 00851 00852 foreach($array as $logEntry) { 00853 $logEntryObj = (object)$logEntry; 00854 $logEntryObj->hostid = $remoteclient->id; 00855 00856 if (isset($useridarray[$logEntryObj->username])) { 00857 $logEntryObj->userid = $useridarray[$logEntryObj->username]; 00858 } else { 00859 $logEntryObj->userid = $DB->get_field('user', 'id', array('username'=>$logEntryObj->username, 'mnethostid'=>(int)$logEntryObj->hostid)); 00860 if ($logEntryObj->userid == false) { 00861 $logEntryObj->userid = 0; 00862 } 00863 $useridarray[$logEntryObj->username] = $logEntryObj->userid; 00864 } 00865 00866 unset($logEntryObj->username); 00867 00868 $logEntryObj = $this->trim_logline($logEntryObj); 00869 $insertok = $DB->insert_record('mnet_log', $logEntryObj, false); 00870 00871 if ($insertok) { 00872 $remoteclient->last_log_id = $logEntryObj->remoteid; 00873 } else { 00874 $returnString .= 'Record with id '.$logEntryObj->remoteid." failed to insert.\n"; 00875 } 00876 } 00877 $remoteclient->commit(); 00878 $transaction->allow_commit(); 00879 00880 $end = ob_end_clean(); 00881 00882 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok'); 00883 return array('code' => 1, 'message' => $returnString); 00884 } 00885 00893 function keepalive_server($array) { 00894 global $CFG, $DB; 00895 $remoteclient = get_mnet_remote_client(); 00896 00897 // We don't want to output anything to the client machine 00898 $start = ob_start(); 00899 00900 // We'll get session records in batches of 30 00901 $superArray = array_chunk($array, 30); 00902 00903 $returnString = ''; 00904 00905 foreach($superArray as $subArray) { 00906 $subArray = array_values($subArray); 00907 $instring = "('".implode("', '",$subArray)."')"; 00908 $query = "select id, session_id, username from {mnet_session} where username in $instring"; 00909 $results = $DB->get_records_sql($query); 00910 00911 if ($results == false) { 00912 // We seem to have a username that breaks our query: 00913 // TODO: Handle this error appropriately 00914 $returnString .= "We failed to refresh the session for the following usernames: \n".implode("\n", $subArray)."\n\n"; 00915 } else { 00916 foreach($results as $emigrant) { 00917 session_touch($emigrant->session_id); 00918 } 00919 } 00920 } 00921 00922 $end = ob_end_clean(); 00923 00924 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok', 'last log id' => $remoteclient->last_log_id); 00925 return array('code' => 1, 'message' => $returnString, 'last log id' => $remoteclient->last_log_id); 00926 } 00927 00933 function cron() { 00934 global $DB; 00935 00936 // run the keepalive client 00937 $this->keepalive_client(); 00938 00939 // admin/cron.php should have run srand for us 00940 $random100 = rand(0,100); 00941 if ($random100 < 10) { // Approximately 10% of the time. 00942 // nuke olden sessions 00943 $longtime = time() - (1 * 3600 * 24); 00944 $DB->delete_records_select('mnet_session', "expires < ?", array($longtime)); 00945 } 00946 } 00947 00955 function prelogout_hook() { 00956 global $CFG, $USER; 00957 00958 if (!is_enabled_auth('mnet')) { 00959 return; 00960 } 00961 00962 // If the user is local to this Moodle: 00963 if ($USER->mnethostid == $this->mnet->id) { 00964 $this->kill_children($USER->username, sha1($_SERVER['HTTP_USER_AGENT'])); 00965 00966 // Else the user has hit 'logout' at a Service Provider Moodle: 00967 } else { 00968 $this->kill_parent($USER->username, sha1($_SERVER['HTTP_USER_AGENT'])); 00969 00970 } 00971 } 00972 00980 function kill_parent($username, $useragent) { 00981 global $CFG, $USER, $DB; 00982 00983 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php'; 00984 $sql = " 00985 select 00986 * 00987 from 00988 {mnet_session} s 00989 where 00990 s.username = ? AND 00991 s.useragent = ? AND 00992 s.mnethostid = ?"; 00993 00994 $mnetsessions = $DB->get_records_sql($sql, array($username, $useragent, $USER->mnethostid)); 00995 00996 $ignore = $DB->delete_records('mnet_session', 00997 array('username'=>$username, 00998 'useragent'=>$useragent, 00999 'mnethostid'=>$USER->mnethostid)); 01000 01001 if (false != $mnetsessions) { 01002 $mnet_peer = new mnet_peer(); 01003 $mnet_peer->set_id($USER->mnethostid); 01004 01005 $mnet_request = new mnet_xmlrpc_client(); 01006 $mnet_request->set_method('auth/mnet/auth.php/kill_children'); 01007 01008 // set $token and $useragent parameters 01009 $mnet_request->add_param($username); 01010 $mnet_request->add_param($useragent); 01011 if ($mnet_request->send($mnet_peer) === false) { 01012 debugging(join("\n", $mnet_request->error)); 01013 return false; 01014 } 01015 } 01016 01017 return true; 01018 } 01019 01027 function kill_children($username, $useragent) { 01028 global $CFG, $USER, $DB; 01029 $remoteclient = null; 01030 if (defined('MNET_SERVER')) { 01031 $remoteclient = get_mnet_remote_client(); 01032 } 01033 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php'; 01034 01035 $userid = $DB->get_field('user', 'id', array('mnethostid'=>$CFG->mnet_localhost_id, 'username'=>$username)); 01036 01037 $returnstring = ''; 01038 01039 $mnetsessions = $DB->get_records('mnet_session', array('userid' => $userid, 'useragent' => $useragent)); 01040 01041 if (false == $mnetsessions) { 01042 $returnstring .= "Could find no remote sessions\n"; 01043 $mnetsessions = array(); 01044 } 01045 01046 foreach($mnetsessions as $mnetsession) { 01047 // If this script is being executed by a remote peer, that means the user has clicked 01048 // logout on that peer, and the session on that peer can be deleted natively. 01049 // Skip over it. 01050 if (isset($remoteclient->id) && ($mnetsession->mnethostid == $remoteclient->id)) { 01051 continue; 01052 } 01053 $returnstring .= "Deleting session\n"; 01054 01055 $mnet_peer = new mnet_peer(); 01056 $mnet_peer->set_id($mnetsession->mnethostid); 01057 01058 $mnet_request = new mnet_xmlrpc_client(); 01059 $mnet_request->set_method('auth/mnet/auth.php/kill_child'); 01060 01061 // set $token and $useragent parameters 01062 $mnet_request->add_param($username); 01063 $mnet_request->add_param($useragent); 01064 if ($mnet_request->send($mnet_peer) === false) { 01065 debugging("Server side error has occured on host $mnetsession->mnethostid: " . 01066 join("\n", $mnet_request->error)); 01067 } 01068 } 01069 01070 $ignore = $DB->delete_records('mnet_session', 01071 array('useragent'=>$useragent, 'userid'=>$userid)); 01072 01073 if (isset($remoteclient) && isset($remoteclient->id)) { 01074 session_kill_user($userid); 01075 } 01076 return $returnstring; 01077 } 01078 01088 function kill_child($username, $useragent) { 01089 global $CFG, $DB; 01090 $remoteclient = get_mnet_remote_client(); 01091 $session = $DB->get_record('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent)); 01092 $DB->delete_records('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent)); 01093 if (false != $session) { 01094 session_kill($session->session_id); 01095 return true; 01096 } 01097 return false; 01098 } 01099 01107 function end_local_sessions(&$sessionArray) { 01108 global $CFG; 01109 if (is_array($sessionArray)) { 01110 while($session = array_pop($sessionArray)) { 01111 session_kill($session->session_id); 01112 } 01113 return true; 01114 } 01115 return false; 01116 } 01117 01134 function fetch_user_image($username) { 01135 global $CFG, $DB; 01136 01137 if ($user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id))) { 01138 $fs = get_file_storage(); 01139 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST); 01140 $return = array(); 01141 if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) { 01142 $return['f1'] = base64_encode($f1->get_content()); 01143 $return['f1_mimetype'] = $f1->get_mimetype(); 01144 } else if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) { 01145 $return['f1'] = base64_encode($f1->get_content()); 01146 $return['f1_mimetype'] = $f1->get_mimetype(); 01147 } 01148 if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.png')) { 01149 $return['f2'] = base64_encode($f2->get_content()); 01150 $return['f2_mimetype'] = $f2->get_mimetype(); 01151 } else if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.jpg')) { 01152 $return['f2'] = base64_encode($f2->get_content()); 01153 $return['f2_mimetype'] = $f2->get_mimetype(); 01154 } 01155 return $return; 01156 } 01157 return false; 01158 } 01159 01165 function fetch_theme_info() { 01166 global $CFG; 01167 01168 $themename = "$CFG->theme"; 01169 $logourl = "$CFG->wwwroot/theme/$CFG->theme/images/logo.jpg"; 01170 01171 $return['themename'] = $themename; 01172 $return['logourl'] = $logourl; 01173 return $return; 01174 } 01175 01183 function has_service($mnethostid, $servicename) { 01184 global $CFG, $DB; 01185 01186 $sql = " 01187 SELECT 01188 svc.id as serviceid, 01189 svc.name, 01190 svc.description, 01191 svc.offer, 01192 svc.apiversion, 01193 h2s.id as h2s_id 01194 FROM 01195 {mnet_host} h, 01196 {mnet_service} svc, 01197 {mnet_host2service} h2s 01198 WHERE 01199 h.deleted = '0' AND 01200 h.id = h2s.hostid AND 01201 h2s.hostid = ? AND 01202 h2s.serviceid = svc.id AND 01203 svc.name = ? AND 01204 h2s.subscribe = '1'"; 01205 01206 return $DB->get_records_sql($sql, array($mnethostid, $servicename)); 01207 } 01208 01217 function can_login_remotely($username, $mnethostid) { 01218 global $DB; 01219 01220 $accessctrl = 'allow'; 01221 $aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnethostid)); 01222 if (!empty($aclrecord)) { 01223 $accessctrl = $aclrecord->accessctrl; 01224 } 01225 return $accessctrl == 'allow'; 01226 } 01227 01228 function logoutpage_hook() { 01229 global $USER, $CFG, $redirect, $DB; 01230 01231 if (!empty($USER->mnethostid) and $USER->mnethostid != $CFG->mnet_localhost_id) { 01232 $host = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid)); 01233 $redirect = $host->wwwroot.'/'; 01234 } 01235 } 01236 01243 function trim_logline ($logline) { 01244 $limits = array('ip' => 15, 'coursename' => 40, 'module' => 20, 'action' => 40, 01245 'url' => 255); 01246 foreach ($limits as $property => $limit) { 01247 if (isset($logline->$property)) { 01248 $logline->$property = substr($logline->$property, 0, $limit); 01249 } 01250 } 01251 01252 return $logline; 01253 } 01254 01270 function loginpage_idp_list($wantsurl) { 01271 global $DB, $CFG; 01272 01273 // strip off wwwroot, since the remote site will prefix it's return url with this 01274 $wantsurl = preg_replace('/(' . preg_quote($CFG->wwwroot, '/') . '|' . preg_quote($CFG->httpswwwroot, '/') . ')/', '', $wantsurl); 01275 01276 $sql = "SELECT DISTINCT h.id, h.wwwroot, h.name, a.sso_jump_url, a.name as application 01277 FROM {mnet_host} h 01278 JOIN {mnet_host2service} m ON h.id = m.hostid 01279 JOIN {mnet_service} s ON s.id = m.serviceid 01280 JOIN {mnet_application} a ON h.applicationid = a.id 01281 WHERE s.name = ? AND h.deleted = ? AND m.publish = ?"; 01282 $params = array('sso_sp', 0, 1); 01283 01284 if (!empty($CFG->mnet_all_hosts_id)) { 01285 $sql .= " AND h.id <> ?"; 01286 $params[] = $CFG->mnet_all_hosts_id; 01287 } 01288 01289 if (!$hosts = $DB->get_records_sql($sql, $params)) { 01290 return array(); 01291 } 01292 01293 $idps = array(); 01294 foreach ($hosts as $host) { 01295 $idps[] = array( 01296 'url' => new moodle_url($host->wwwroot . $host->sso_jump_url, array('hostwwwroot' => $CFG->wwwroot, 'wantsurl' => $wantsurl, 'remoteurl' => 1)), 01297 'icon' => new pix_icon('i/' . $host->application . '_host', $host->name), 01298 'name' => $host->name, 01299 ); 01300 } 01301 return $idps; 01302 } 01303 }