MNet: fixed wrong column name in SQL cleanup code
[moodle.git] / auth / mnet / auth.php
CommitLineData
5117d598 1<?php
c72fe801 2
3/**
4 * @author Martin Dougiamas
5 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
6 * @package moodle multiauth
7 *
8 * Authentication Plugin: Moodle Network Authentication
9 *
10 * Multiple host authentication support for Moodle Network.
11 *
12 * 2006-11-01 File created.
13 */
14
139ebfdb 15if (!defined('MOODLE_INTERNAL')) {
16 die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
c72fe801 17}
18
6bc1e5d5 19require_once($CFG->libdir.'/authlib.php');
20
c72fe801 21/**
22 * Moodle Network authentication plugin.
23 */
6bc1e5d5 24class auth_plugin_mnet extends auth_plugin_base {
c72fe801 25
26 /**
27 * Constructor.
28 */
29 function auth_plugin_mnet() {
6bc1e5d5 30 $this->authtype = 'mnet';
94cf0a1e 31 $this->config = get_config('auth_mnet');
287efec6 32 $this->mnet = get_mnet_environment();
c72fe801 33 }
34
c72fe801 35 /**
36 * This function is normally used to determine if the username and password
37 * are correct for local logins. Always returns false, as local users do not
38 * need to login over mnet xmlrpc.
39 *
40 * @param string $username The username
41 * @param string $password The password
139ebfdb 42 * @return bool Authentication success or failure.
c72fe801 43 */
44 function user_login($username, $password) {
3db241b3 45 return false; // print_error("mnetlocal");
c72fe801 46 }
47
48 /**
49 * Return user data for the provided token, compare with user_agent string.
50 *
51 * @param string $token The unique ID provided by remotehost.
52 * @param string $UA User Agent string.
53 * @return array $userdata Array of user info for remote host
54 */
55 function user_authorise($token, $useragent) {
287efec6
PL
56 global $CFG, $SITE, $DB;
57 $remoteclient = get_mnet_remote_client();
de260e0f 58 require_once $CFG->dirroot . '/mnet/xmlrpc/serverlib.php';
c72fe801 59
7b0d12b2 60 $mnet_session = $DB->get_record('mnet_session', array('token'=>$token, 'useragent'=>$useragent));
c72fe801 61 if (empty($mnet_session)) {
d234faf3 62 throw new mnet_server_exception(1, 'authfail_nosessionexists');
c72fe801 63 }
64
65 // check session confirm timeout
66 if ($mnet_session->confirm_timeout < time()) {
d234faf3 67 throw new mnet_server_exception(2, 'authfail_sessiontimedout');
c72fe801 68 }
69
70 // session okay, try getting the user
7b0d12b2 71 if (!$user = $DB->get_record('user', array('id'=>$mnet_session->userid))) {
d234faf3 72 throw new mnet_server_exception(3, 'authfail_usermismatch');
c72fe801 73 }
74
61506902
PL
75 $userdata = mnet_strip_user((array)$user, mnet_fields_to_send($remoteclient));
76
77 // extra special ones
c72fe801 78 $userdata['auth'] = 'mnet';
287efec6 79 $userdata['wwwroot'] = $this->mnet->wwwroot;
c72fe801 80 $userdata['session.gc_maxlifetime'] = ini_get('session.gc_maxlifetime');
61506902
PL
81
82 if (array_key_exists('picture', $userdata) && !empty($user->picture)) {
35d76df3
DM
83 $fs = get_file_storage();
84 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST);
85 if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
86 $userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified();
87 $userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype();
88 } else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
89 $userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified();
90 $userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype();
c72fe801 91 }
92 }
4d8c087e 93
94 $userdata['myhosts'] = array();
df997f84 95 if ($courses = enrol_get_users_courses($user->id, false)) {
4d8c087e 96 $userdata['myhosts'][] = array('name'=> $SITE->shortname, 'url' => $CFG->wwwroot, 'count' => count($courses));
97 }
98
152a2273
DM
99 $sql = "SELECT h.name AS hostname, h.wwwroot, h.id AS hostid,
100 COUNT(c.id) AS count
101 FROM {mnetservice_enrol_courses} c
102 JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid)
103 JOIN {mnet_host} h ON h.id = c.hostid
104 WHERE e.userid = ? AND c.hostid = ?
105 GROUP BY h.name, h.wwwroot, h.id";
106
287efec6 107 if ($courses = $DB->get_records_sql($sql, array($user->id, $remoteclient->id))) {
4d8c087e 108 foreach($courses as $course) {
109 $userdata['myhosts'][] = array('name'=> $course->hostname, 'url' => $CFG->wwwroot.'/auth/mnet/jump.php?hostid='.$course->hostid, 'count' => $course->count);
110 }
111 }
112
c72fe801 113 return $userdata;
114 }
115
116 /**
117 * Generate a random string for use as an RPC session token.
118 */
119 function generate_token() {
120 return sha1(str_shuffle('' . mt_rand() . time()));
121 }
122
123 /**
124 * Starts an RPC jump session and returns the jump redirect URL.
d9be2106 125 *
126 * @param int $mnethostid id of the mnet host to jump to
127 * @param string $wantsurl url to redirect to after the jump (usually on remote system)
128 * @param boolean $wantsurlbackhere defaults to false, means that the remote system should bounce us back here
129 * rather than somewhere inside *its* wwwroot
c72fe801 130 */
d9be2106 131 function start_jump_session($mnethostid, $wantsurl, $wantsurlbackhere=false) {
287efec6 132 global $CFG, $USER, $DB;
c72fe801 133 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
134
135 // check remote login permissions
152a2273 136 if (! has_capability('moodle/site:mnetlogintoremote', get_system_context())
c72fe801 137 or is_mnet_remote_user($USER)
4f0c2d00
PS
138 or isguestuser()
139 or !isloggedin()) {
5a2a5331 140 print_error('notpermittedtojump', 'mnet');
c72fe801 141 }
142
143 // check for SSO publish permission first
144 if ($this->has_service($mnethostid, 'sso_sp') == false) {
5a2a5331 145 print_error('hostnotconfiguredforsso', 'mnet');
c72fe801 146 }
147
148 // set RPC timeout to 30 seconds if not configured
c72fe801 149 if (empty($this->config->rpc_negotiation_timeout)) {
09c1b1a2 150 $this->config->rpc_negotiation_timeout = 30;
94cf0a1e 151 set_config('rpc_negotiation_timeout', '30', 'auth_mnet');
c72fe801 152 }
153
154 // get the host info
155 $mnet_peer = new mnet_peer();
156 $mnet_peer->set_id($mnethostid);
157
158 // set up the session
7b0d12b2 159 $mnet_session = $DB->get_record('mnet_session',
160 array('userid'=>$USER->id, 'mnethostid'=>$mnethostid,
161 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT'])));
c72fe801 162 if ($mnet_session == false) {
1dffbae2 163 $mnet_session = new stdClass();
c72fe801 164 $mnet_session->mnethostid = $mnethostid;
165 $mnet_session->userid = $USER->id;
166 $mnet_session->username = $USER->username;
167 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
168 $mnet_session->token = $this->generate_token();
169 $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout;
170 $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime');
171 $mnet_session->session_id = session_id();
979425b5 172 $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session);
c72fe801 173 } else {
174 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
175 $mnet_session->token = $this->generate_token();
176 $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout;
177 $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime');
178 $mnet_session->session_id = session_id();
979425b5 179 $DB->update_record('mnet_session', $mnet_session);
c72fe801 180 }
181
182 // construct the redirection URL
183 //$transport = mnet_get_protocol($mnet_peer->transport);
184 $wantsurl = urlencode($wantsurl);
287efec6 185 $url = "{$mnet_peer->wwwroot}{$mnet_peer->application->sso_land_url}?token={$mnet_session->token}&idp={$this->mnet->wwwroot}&wantsurl={$wantsurl}";
d9be2106 186 if ($wantsurlbackhere) {
187 $url .= '&remoteurl=1';
188 }
c72fe801 189
190 return $url;
191 }
192
193 /**
194 * This function confirms the remote (ID provider) host's mnet session
195 * by communicating the token and UA over the XMLRPC transport layer, and
196 * returns the local user record on success.
197 *
2078b2a3
PL
198 * @param string $token The random session token.
199 * @param mnet_peer $remotepeer The ID provider mnet_peer object.
139ebfdb 200 * @return array The local user record.
c72fe801 201 */
2078b2a3 202 function confirm_mnet_session($token, $remotepeer) {
287efec6 203 global $CFG, $DB;
c72fe801 204 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
35d76df3 205 require_once $CFG->libdir . '/gdlib.php';
c72fe801 206
207 // verify the remote host is configured locally before attempting RPC call
2078b2a3 208 if (! $remotehost = $DB->get_record('mnet_host', array('wwwroot' => $remotepeer->wwwroot, 'deleted' => 0))) {
5a2a5331 209 print_error('notpermittedtoland', 'mnet');
c72fe801 210 }
211
c72fe801 212 // set up the RPC request
213 $mnetrequest = new mnet_xmlrpc_client();
214 $mnetrequest->set_method('auth/mnet/auth.php/user_authorise');
215
216 // set $token and $useragent parameters
217 $mnetrequest->add_param($token);
218 $mnetrequest->add_param(sha1($_SERVER['HTTP_USER_AGENT']));
219
220 // Thunderbirds are go! Do RPC call and store response
221 if ($mnetrequest->send($remotepeer) === true) {
222 $remoteuser = (object) $mnetrequest->response;
223 } else {
016bac47 224 foreach ($mnetrequest->error as $errormessage) {
225 list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
226 if($code == 702) {
227 $site = get_site();
2078b2a3 228 print_error('mnet_session_prohibited', 'mnet', $remotepeer->wwwroot, format_string($site->fullname));
016bac47 229 exit;
230 }
71502268 231 $message .= "ERROR $code:<br/>$errormessage<br/>";
c72fe801 232 }
e2490433 233 print_error("rpcerror", '', '', $message);
c72fe801 234 }
15e47723 235 unset($mnetrequest);
c72fe801 236
237 if (empty($remoteuser) or empty($remoteuser->username)) {
016bac47 238 print_error('unknownerror', 'mnet');
239 exit;
c72fe801 240 }
241
aa802711
PL
242 if (user_not_fully_set_up($remoteuser)) {
243 print_error('notenoughidpinfo', 'mnet');
244 exit;
245 }
246
61506902
PL
247 $remoteuser = mnet_strip_user($remoteuser, mnet_fields_to_import($remotepeer));
248
249 $remoteuser->auth = 'mnet';
250 $remoteuser->wwwroot = $remotepeer->wwwroot;
251
67c1d98d
DM
252 // the user may roam from Moodle 1.x where lang has _utf8 suffix
253 // also, make sure that the lang is actually installed, otherwise set site default
bf36ee9b
DM
254 if (isset($remoteuser->lang)) {
255 $remoteuser->lang = clean_param(str_replace('_utf8', '', $remoteuser->lang), PARAM_LANG);
256 }
67c1d98d
DM
257 if (empty($remoteuser->lang)) {
258 if (!empty($CFG->lang)) {
259 $remoteuser->lang = $CFG->lang;
260 } else {
261 $remoteuser->lang = 'en';
262 }
263 }
15e47723 264 $firsttime = false;
265
c72fe801 266 // get the local record for the remote user
7b0d12b2 267 $localuser = $DB->get_record('user', array('username'=>$remoteuser->username, 'mnethostid'=>$remotehost->id));
c72fe801 268
269 // add the remote user to the database if necessary, and if allowed
270 // TODO: refactor into a separate function
32d651c8 271 if (empty($localuser) || ! $localuser->id) {
4a3c3308 272 /*
c72fe801 273 if (empty($this->config->auto_add_remote_users)) {
5a2a5331 274 print_error('nolocaluser', 'mnet');
4a3c3308
PL
275 } See MDL-21327 for why this is commented out
276 */
c72fe801 277 $remoteuser->mnethostid = $remotehost->id;
a9b8a84c 278 $remoteuser->firstaccess = time(); // First time user in this server, grab it here
07ed083e 279
98dbda95 280 $remoteuser->id = $DB->insert_record('user', $remoteuser);
15e47723 281 $firsttime = true;
98dbda95 282 $localuser = $remoteuser;
c72fe801 283 }
284
285 // check sso access control list for permission first
286 if (!$this->can_login_remotely($localuser->username, $remotehost->id)) {
c94fc98c 287 print_error('sso_mnet_login_refused', 'mnet', '', array('user'=>$localuser->username, 'host'=>$remotehost->name));
c72fe801 288 }
289
35d76df3
DM
290 $fs = get_file_storage();
291
c72fe801 292 // update the local user record with remote user data
293 foreach ((array) $remoteuser as $key => $val) {
c72fe801 294
35d76df3
DM
295 if ($key == '_mnet_userpicture_timemodified' and empty($CFG->disableuserimages) and isset($remoteuser->picture)) {
296 // update the user picture if there is a newer verion at the identity provider
297 $usercontext = get_context_instance(CONTEXT_USER, $localuser->id, MUST_EXIST);
298 if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
299 $localtimemodified = $usericonfile->get_timemodified();
300 } else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
301 $localtimemodified = $usericonfile->get_timemodified();
302 } else {
303 $localtimemodified = 0;
c72fe801 304 }
305
35d76df3
DM
306 if (!empty($val) and $localtimemodified < $val) {
307 mnet_debug('refetching the user picture from the identity provider host');
c72fe801 308 $fetchrequest = new mnet_xmlrpc_client();
309 $fetchrequest->set_method('auth/mnet/auth.php/fetch_user_image');
310 $fetchrequest->add_param($localuser->username);
311 if ($fetchrequest->send($remotepeer) === true) {
312 if (strlen($fetchrequest->response['f1']) > 0) {
35d76df3 313 $imagefilename = $CFG->dataroot . '/temp/mnet-usericon-' . $localuser->id;
c72fe801 314 $imagecontents = base64_decode($fetchrequest->response['f1']);
35d76df3
DM
315 file_put_contents($imagefilename, $imagecontents);
316 if (process_new_icon($usercontext, 'user', 'icon', 0, $imagefilename)) {
317 $localuser->picture = 1;
318 }
319 unlink($imagefilename);
c72fe801 320 }
35d76df3
DM
321 // note that since Moodle 2.0 we ignore $fetchrequest->response['f2']
322 // the mimetype information provided is ignored and the type of the file is detected
323 // by process_new_icon()
c72fe801 324 }
325 }
326 }
327
4d8c087e 328 if($key == 'myhosts') {
0743661e 329 $localuser->mnet_foreign_host_array = array();
15e47723 330 foreach($val as $rhost) {
331 $name = clean_param($rhost['name'], PARAM_ALPHANUM);
332 $url = clean_param($rhost['url'], PARAM_URL);
333 $count = clean_param($rhost['count'], PARAM_INT);
4d8c087e 334 $url_is_local = stristr($url , $CFG->wwwroot);
335 if (!empty($name) && !empty($count) && empty($url_is_local)) {
139ebfdb 336 $localuser->mnet_foreign_host_array[] = array('name' => $name,
337 'url' => $url,
0743661e 338 'count' => $count);
4d8c087e 339 }
340 }
341 }
342
c72fe801 343 $localuser->{$key} = $val;
344 }
345
346 $localuser->mnethostid = $remotepeer->id;
a9b8a84c
EL
347 if (empty($localuser->firstaccess)) { // Now firstaccess, grab it here
348 $localuser->firstaccess = time();
349 }
c72fe801 350
a8c31db2 351 $DB->update_record('user', $localuser);
c72fe801 352
15e47723 353 if (!$firsttime) {
354 // repeat customer! let the IDP know about enrolments
139ebfdb 355 // we have for this user.
15e47723 356 // set up the RPC request
357 $mnetrequest = new mnet_xmlrpc_client();
358 $mnetrequest->set_method('auth/mnet/auth.php/update_enrolments');
359
360 // pass username and an assoc array of "my courses"
152a2273 361 // with info so that the IDP can maintain mnetservice_enrol_enrolments
15e47723 362 $mnetrequest->add_param($remoteuser->username);
df997f84
PS
363 $fields = 'id, category, sortorder, fullname, shortname, idnumber, summary, startdate, visible';
364 $courses = enrol_get_users_courses($localuser->id, false, $fields, 'visible DESC,sortorder ASC');
15e47723 365 if (is_array($courses) && !empty($courses)) {
366 // Second request to do the JOINs that we'd have done
df997f84 367 // inside enrol_get_users_courses() if we had been allowed
139ebfdb 368 $sql = "SELECT c.id,
df997f84 369 cc.name AS cat_name, cc.description AS cat_description
7b0d12b2 370 FROM {course} c
371 JOIN {course_categories} cc ON c.category = cc.id
7b0d12b2 372 WHERE c.id IN (" . join(',',array_keys($courses)) . ')';
373 $extra = $DB->get_records_sql($sql);
15e47723 374
375 $keys = array_keys($courses);
152a2273
DM
376 $defaultrole = reset(get_archetype_roles('student'));
377 //$defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!!
15e47723 378 foreach ($keys AS $id) {
573f8b02 379 if ($courses[$id]->visible == 0) {
380 unset($courses[$id]);
381 continue;
382 }
560b3acc 383 $courses[$id]->cat_id = $courses[$id]->category;
df997f84 384 $courses[$id]->defaultroleid = $defaultrole->id;
573f8b02 385 unset($courses[$id]->category);
573f8b02 386 unset($courses[$id]->visible);
387
15e47723 388 $courses[$id]->cat_name = $extra[$id]->cat_name;
389 $courses[$id]->cat_description = $extra[$id]->cat_description;
df997f84 390 $courses[$id]->defaultrolename = $defaultrole->name;
15e47723 391 // coerce to array
392 $courses[$id] = (array)$courses[$id];
393 }
62d78bf5 394 } else {
395 // if the array is empty, send it anyway
396 // we may be clearing out stale entries
139ebfdb 397 $courses = array();
62d78bf5 398 }
399 $mnetrequest->add_param($courses);
15e47723 400
62d78bf5 401 // Call 0800-RPC Now! -- we don't care too much if it fails
402 // as it's just informational.
403 if ($mnetrequest->send($remotepeer) === false) {
404 // error_log(print_r($mnetrequest->error,1));
15e47723 405 }
406 }
407
c72fe801 408 return $localuser;
409 }
410
2078b2a3
PL
411
412 /**
413 * creates (or updates) the mnet session once
414 * {@see confirm_mnet_session} and {@see complete_user_login} have both been called
415 *
416 * @param stdclass $user the local user (must exist already
417 * @param string $token the jump/land token
418 * @param mnet_peer $remotepeer the mnet_peer object of this users's idp
419 */
420 public function update_mnet_session($user, $token, $remotepeer) {
421 global $DB;
422 $session_gc_maxlifetime = 1440;
423 if (isset($user->session_gc_maxlifetime)) {
424 $session_gc_maxlifetime = $user->session_gc_maxlifetime;
425 }
426 if (!$mnet_session = $DB->get_record('mnet_session',
427 array('userid'=>$user->id, 'mnethostid'=>$remotepeer->id,
428 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT'])))) {
1dffbae2 429 $mnet_session = new stdClass();
2078b2a3
PL
430 $mnet_session->mnethostid = $remotepeer->id;
431 $mnet_session->userid = $user->id;
432 $mnet_session->username = $user->username;
433 $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
434 $mnet_session->token = $token; // Needed to support simultaneous sessions
435 // and preserving DB rec uniqueness
436 $mnet_session->confirm_timeout = time();
437 $mnet_session->expires = time() + (integer)$session_gc_maxlifetime;
438 $mnet_session->session_id = session_id();
439 $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session);
440 } else {
441 $mnet_session->expires = time() + (integer)$session_gc_maxlifetime;
442 $DB->update_record('mnet_session', $mnet_session);
443 }
444 }
445
446
447
62d78bf5 448 /**
449 * Invoke this function _on_ the IDP to update it with enrolment info local to
450 * the SP right after calling user_authorise()
451 *
de260e0f 452 * Normally called by the SP after calling user_authorise()
62d78bf5 453 *
de260e0f 454 * @param string $username The username
5c7bc383 455 * @param array $courses Assoc array of courses following the structure of mnetservice_enrol_courses
de260e0f 456 * @return bool
62d78bf5 457 */
458 function update_enrolments($username, $courses) {
287efec6
PL
459 global $CFG, $DB;
460 $remoteclient = get_mnet_remote_client();
62d78bf5 461
462 if (empty($username) || !is_array($courses)) {
463 return false;
464 }
465 // make sure it is a user we have an in active session
466 // with that host...
ee4cd8f1
DM
467 $mnetsessions = $DB->get_records('mnet_session', array('username' => $username, 'mnethostid' => $remoteclient->id), '', 'id, userid');
468 $userid = null;
469 foreach ($mnetsessions as $mnetsession) {
470 if (is_null($userid)) {
471 $userid = $mnetsession->userid;
472 continue;
473 }
474 if ($userid != $mnetsession->userid) {
475 throw new mnet_server_exception(3, 'authfail_usermismatch');
476 }
62d78bf5 477 }
478
479 if (empty($courses)) { // no courses? clear out quickly
152a2273 480 $DB->delete_records('mnetservice_enrol_enrolments', array('hostid'=>$remoteclient->id, 'userid'=>$userid));
62d78bf5 481 return true;
482 }
483
573f8b02 484 // IMPORTANT: Ask for remoteid as the first element in the query, so
485 // that the array that comes back is indexed on the same field as the
486 // array that we have received from the remote client
152a2273
DM
487 $sql = "SELECT c.remoteid, c.id, c.categoryid AS cat_id, c.categoryname AS cat_name, c.sortorder,
488 c.fullname, c.shortname, c.idnumber, c.summary, c.summaryformat, c.startdate,
489 e.id AS enrolmentid
490 FROM {mnetservice_enrol_courses} c
491 LEFT JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid)
492 WHERE e.userid = ? AND c.hostid = ?";
573f8b02 493
287efec6 494 $currentcourses = $DB->get_records_sql($sql, array($userid, $remoteclient->id));
573f8b02 495
496 $local_courseid_array = array();
152a2273 497 foreach($courses as $ix => $course) {
573f8b02 498
499 $course['remoteid'] = $course['id'];
287efec6 500 $course['hostid'] = (int)$remoteclient->id;
573f8b02 501 $userisregd = false;
502
152a2273
DM
503 // if we do not have the the information about the remote course, it is not available
504 // to us for remote enrolment - skip
505 if (array_key_exists($course['remoteid'], $currentcourses)) {
573f8b02 506 // Pointer to current course:
507 $currentcourse =& $currentcourses[$course['remoteid']];
508 // We have a record - is it up-to-date?
509 $course['id'] = $currentcourse->id;
510
511 $saveflag = false;
512
513 foreach($course as $key => $value) {
514 if ($currentcourse->$key != $value) {
515 $saveflag = true;
516 $currentcourse->$key = $value;
517 }
518 }
519
520 if ($saveflag) {
152a2273 521 $DB->update_record('mnetervice_enrol_courses', $currentcourse);
573f8b02 522 }
139ebfdb 523
152a2273 524 if (isset($currentcourse->enrolmentid) && is_numeric($currentcourse->enrolmentid)) {
573f8b02 525 $userisregd = true;
526 }
152a2273
DM
527 } else {
528 unset ($courses[$ix]);
529 continue;
573f8b02 530 }
531
532 // By this point, we should always have a $dataObj->id
533 $local_courseid_array[] = $course['id'];
534
535 // Do we have a record for this assignment?
536 if ($userisregd) {
537 // Yes - we know about this one already
538 // We don't want to do updates because the new data is probably
539 // 'less complete' than the data we have.
540 } else {
541 // No - create a record
542 $assignObj = new stdClass();
543 $assignObj->userid = $userid;
287efec6 544 $assignObj->hostid = (int)$remoteclient->id;
152a2273 545 $assignObj->remotecourseid = $course['remoteid'];
573f8b02 546 $assignObj->rolename = $course['defaultrolename'];
54f69781 547 $assignObj->id = $DB->insert_record('mnetservice_enrol_enrolments', $assignObj);
573f8b02 548 }
549 }
62d78bf5 550
573f8b02 551 // Clean up courses that the user is no longer enrolled in.
42ae4ff2
DM
552 if (!empty($local_courseid_array)) {
553 $local_courseid_string = implode(', ', $local_courseid_array);
554 $whereclause = " userid = ? AND hostid = ? AND remotecourseid NOT IN ($local_courseid_string)";
555 $DB->delete_records_select('mnetservice_enrol_enrolments', $whereclause, array($userid, $remoteclient->id));
556 }
62d78bf5 557 }
558
edb5da83
PS
559 function prevent_local_passwords() {
560 return true;
561 }
562
c72fe801 563 /**
564 * Returns true if this authentication plugin is 'internal'.
565 *
139ebfdb 566 * @return bool
c72fe801 567 */
568 function is_internal() {
569 return false;
570 }
571
572 /**
573 * Returns true if this authentication plugin can change the user's
574 * password.
575 *
139ebfdb 576 * @return bool
c72fe801 577 */
578 function can_change_password() {
430759a5 579 //TODO: it should be able to redirect, right?
c72fe801 580 return false;
581 }
582
583 /**
584 * Returns the URL for changing the user's pw, or false if the default can
585 * be used.
586 *
99f9f85f 587 * @return moodle_url
c72fe801 588 */
589 function change_password_url() {
99f9f85f 590 return null;
c72fe801 591 }
592
593 /**
594 * Prints a form for configuring this authentication plugin.
595 *
596 * This function is called from admin/auth.php, and outputs a full page with
597 * a form for configuring this plugin.
598 *
de260e0f
PL
599 * @param object $config
600 * @param object $err
601 * @param array $user_fields
c72fe801 602 */
139ebfdb 603 function config_form($config, $err, $user_fields) {
7b0d12b2 604 global $CFG, $DB;
14518364 605
606 $query = "
607 SELECT
608 h.id,
609 h.name as hostname,
610 h.wwwroot,
611 h2idp.publish as idppublish,
612 h2idp.subscribe as idpsubscribe,
613 idp.name as idpname,
614 h2sp.publish as sppublish,
615 h2sp.subscribe as spsubscribe,
616 sp.name as spname
617 FROM
7b0d12b2 618 {mnet_host} h
14518364 619 LEFT JOIN
7b0d12b2 620 {mnet_host2service} h2idp
14518364 621 ON
622 (h.id = h2idp.hostid AND
623 (h2idp.publish = 1 OR
624 h2idp.subscribe = 1))
625 INNER JOIN
7b0d12b2 626 {mnet_service} idp
14518364 627 ON
628 (h2idp.serviceid = idp.id AND
629 idp.name = 'sso_idp')
630 LEFT JOIN
7b0d12b2 631 {mnet_host2service} h2sp
14518364 632 ON
633 (h.id = h2sp.hostid AND
634 (h2sp.publish = 1 OR
635 h2sp.subscribe = 1))
636 INNER JOIN
7b0d12b2 637 {mnet_service} sp
14518364 638 ON
639 (h2sp.serviceid = sp.id AND
640 sp.name = 'sso_sp')
641 WHERE
642 ((h2idp.publish = 1 AND h2sp.subscribe = 1) OR
643 (h2sp.publish = 1 AND h2idp.subscribe = 1)) AND
7b0d12b2 644 h.id != ?
14518364 645 ORDER BY
646 h.name ASC";
647
14518364 648 $id_providers = array();
649 $service_providers = array();
7b0d12b2 650 if ($resultset = $DB->get_records_sql($query, array($CFG->mnet_localhost_id))) {
d525ca25 651 foreach($resultset as $hostservice) {
652 if(!empty($hostservice->idppublish) && !empty($hostservice->spsubscribe)) {
653 $service_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot);
654 }
655 if(!empty($hostservice->idpsubscribe) && !empty($hostservice->sppublish)) {
656 $id_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot);
657 }
14518364 658 }
659 }
139ebfdb 660
c72fe801 661 include "config.html";
662 }
663
664 /**
665 * Processes and stores configuration data for this authentication plugin.
666 */
667 function process_config($config) {
668 // set to defaults if undefined
669 if (!isset ($config->rpc_negotiation_timeout)) {
5671e77f 670 $config->rpc_negotiation_timeout = '30';
c72fe801 671 }
4a3c3308 672 /*
c72fe801 673 if (!isset ($config->auto_add_remote_users)) {
674 $config->auto_add_remote_users = '0';
4a3c3308 675 } See MDL-21327 for why this is commented out
94cf0a1e 676 set_config('auto_add_remote_users', $config->auto_add_remote_users, 'auth_mnet');
4a3c3308 677 */
c72fe801 678
679 // save settings
94cf0a1e 680 set_config('rpc_negotiation_timeout', $config->rpc_negotiation_timeout, 'auth_mnet');
c72fe801 681
682 return true;
683 }
684
685 /**
686 * Poll the IdP server to let it know that a user it has authenticated is still
687 * online
688 *
689 * @return void
690 */
691 function keepalive_client() {
287efec6 692 global $CFG, $DB;
c72fe801 693 $cutoff = time() - 300; // TODO - find out what the remote server's session
694 // cutoff is, and preempt that
695
696 $sql = "
697 select
698 id,
699 username,
700 mnethostid
701 from
7b0d12b2 702 {user}
c72fe801 703 where
7b0d12b2 704 lastaccess > ? AND
705 mnethostid != ?
c72fe801 706 order by
707 mnethostid";
708
7b0d12b2 709 $immigrants = $DB->get_records_sql($sql, array($cutoff, $CFG->mnet_localhost_id));
c72fe801 710
711 if ($immigrants == false) {
712 return true;
713 }
714
715 $usersArray = array();
716 foreach($immigrants as $immigrant) {
717 $usersArray[$immigrant->mnethostid][] = $immigrant->username;
718 }
719
720 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
721 foreach($usersArray as $mnethostid => $users) {
722 $mnet_peer = new mnet_peer();
723 $mnet_peer->set_id($mnethostid);
724
725 $mnet_request = new mnet_xmlrpc_client();
726 $mnet_request->set_method('auth/mnet/auth.php/keepalive_server');
727
728 // set $token and $useragent parameters
729 $mnet_request->add_param($users);
730
731 if ($mnet_request->send($mnet_peer) === true) {
732 if (!isset($mnet_request->response['code'])) {
733 debugging("Server side error has occured on host $mnethostid");
734 continue;
735 } elseif ($mnet_request->response['code'] > 0) {
736 debugging($mnet_request->response['message']);
737 }
139ebfdb 738
c72fe801 739 if (!isset($mnet_request->response['last log id'])) {
740 debugging("Server side error has occured on host $mnethostid\nNo log ID was received.");
741 continue;
742 }
743 } else {
139ebfdb 744 debugging("Server side error has occured on host $mnethostid: " .
c72fe801 745 join("\n", $mnet_request->error));
3d7e4468 746 break;
c72fe801 747 }
28c3e7d8 748 $mnethostlogssql = "
6ff7d16b 749 SELECT
750 mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip,
751 mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action,
752 mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename,
753 c.modinfo
754 FROM
755 (
756 SELECT
757 l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
758 l.action, l.url, l.info, u.username
759 FROM
760 {user} u
761 INNER JOIN {log} l on l.userid = u.id
762 WHERE
763 u.mnethostid = ?
764 AND l.id > ?
765 ORDER BY remoteid ASC
766 LIMIT 500
767 ) mhostlogs
768 INNER JOIN {course} c on c.id = mhostlogs.course
769 ORDER by mhostlogs.remoteid ASC";
c72fe801 770
995087b9 771 $mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id']));
c72fe801 772
6da155db 773 if ($mnethostlogs == false) {
774 continue;
775 }
c72fe801 776
995087b9 777 $processedlogs = array();
c72fe801 778
995087b9 779 foreach($mnethostlogs as $hostlog) {
780 // Extract the name of the relevant module instance from the
781 // course modinfo if possible.
782 if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) {
783 $modinfo = unserialize($hostlog->modinfo);
784 unset($hostlog->modinfo);
c72fe801 785 $modulearray = array();
786 foreach($modinfo as $module) {
9a9012dc 787 $modulearray[$module->cm] = $module->name;
c72fe801 788 }
995087b9 789 $hostlog->resource_name = $modulearray[$hostlog->cmid];
c72fe801 790 } else {
995087b9 791 $hostlog->resource_name = '';
c72fe801 792 }
793
995087b9 794 $processedlogs[] = array (
795 'remoteid' => $hostlog->remoteid,
796 'time' => $hostlog->time,
797 'userid' => $hostlog->userid,
798 'ip' => $hostlog->ip,
799 'course' => $hostlog->course,
800 'coursename' => $hostlog->coursename,
801 'module' => $hostlog->module,
802 'cmid' => $hostlog->cmid,
803 'action' => $hostlog->action,
804 'url' => $hostlog->url,
805 'info' => $hostlog->info,
806 'resource_name' => $hostlog->resource_name,
807 'username' => $hostlog->username
c72fe801 808 );
809 }
810
995087b9 811 unset($hostlog);
c72fe801 812
813 $mnet_request = new mnet_xmlrpc_client();
814 $mnet_request->set_method('auth/mnet/auth.php/refresh_log');
815
816 // set $token and $useragent parameters
995087b9 817 $mnet_request->add_param($processedlogs);
c72fe801 818
819 if ($mnet_request->send($mnet_peer) === true) {
820 if ($mnet_request->response['code'] > 0) {
821 debugging($mnet_request->response['message']);
822 }
823 } else {
824 debugging("Server side error has occured on host $mnet_peer->ip: " .join("\n", $mnet_request->error));
825 }
826 }
827 }
828
829 /**
830 * Receives an array of log entries from an SP and adds them to the mnet_log
831 * table
832 *
833 * @param array $array An array of usernames
834 * @return string "All ok" or an error message
835 */
836 function refresh_log($array) {
287efec6
PL
837 global $CFG, $DB;
838 $remoteclient = get_mnet_remote_client();
c72fe801 839
840 // We don't want to output anything to the client machine
841 $start = ob_start();
842
843 $returnString = '';
d5a8d9aa 844 $transaction = $DB->start_delegated_transaction();
c72fe801 845 $useridarray = array();
846
847 foreach($array as $logEntry) {
848 $logEntryObj = (object)$logEntry;
287efec6 849 $logEntryObj->hostid = $remoteclient->id;
c72fe801 850
851 if (isset($useridarray[$logEntryObj->username])) {
852 $logEntryObj->userid = $useridarray[$logEntryObj->username];
853 } else {
457c9729 854 $logEntryObj->userid = $DB->get_field('user', 'id', array('username'=>$logEntryObj->username, 'mnethostid'=>(int)$logEntryObj->hostid));
c72fe801 855 if ($logEntryObj->userid == false) {
856 $logEntryObj->userid = 0;
857 }
858 $useridarray[$logEntryObj->username] = $logEntryObj->userid;
859 }
860
861 unset($logEntryObj->username);
862
9dbc81ef 863 $logEntryObj = $this->trim_logline($logEntryObj);
5117d598 864 $insertok = $DB->insert_record('mnet_log', $logEntryObj, false);
c72fe801 865
866 if ($insertok) {
287efec6 867 $remoteclient->last_log_id = $logEntryObj->remoteid;
c72fe801 868 } else {
869 $returnString .= 'Record with id '.$logEntryObj->remoteid." failed to insert.\n";
870 }
871 }
287efec6 872 $remoteclient->commit();
d5a8d9aa 873 $transaction->allow_commit();
c72fe801 874
875 $end = ob_end_clean();
876
877 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok');
878 return array('code' => 1, 'message' => $returnString);
879 }
880
881 /**
882 * Receives an array of usernames from a remote machine and prods their
883 * sessions to keep them alive
884 *
885 * @param array $array An array of usernames
886 * @return string "All ok" or an error message
887 */
888 function keepalive_server($array) {
287efec6
PL
889 global $CFG, $DB;
890 $remoteclient = get_mnet_remote_client();
c72fe801 891
892 $CFG->usesid = true;
c72fe801 893
894 // We don't want to output anything to the client machine
895 $start = ob_start();
896
897 // We'll get session records in batches of 30
898 $superArray = array_chunk($array, 30);
899
900 $returnString = '';
901
902 foreach($superArray as $subArray) {
903 $subArray = array_values($subArray);
904 $instring = "('".implode("', '",$subArray)."')";
7b0d12b2 905 $query = "select id, session_id, username from {mnet_session} where username in $instring";
906 $results = $DB->get_records_sql($query);
c72fe801 907
908 if ($results == false) {
909 // We seem to have a username that breaks our query:
910 // TODO: Handle this error appropriately
911 $returnString .= "We failed to refresh the session for the following usernames: \n".implode("\n", $subArray)."\n\n";
912 } else {
c72fe801 913 foreach($results as $emigrant) {
2e38d703 914 session_touch($emigrant->session_id);
c72fe801 915 }
c72fe801 916 }
917 }
918
919 $end = ob_end_clean();
920
287efec6
PL
921 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok', 'last log id' => $remoteclient->last_log_id);
922 return array('code' => 1, 'message' => $returnString, 'last log id' => $remoteclient->last_log_id);
c72fe801 923 }
924
925 /**
926 * Cron function will be called automatically by cron.php every 5 minutes
927 *
928 * @return void
929 */
930 function cron() {
7b0d12b2 931 global $DB;
4c1c5d26 932
933 // run the keepalive client
c72fe801 934 $this->keepalive_client();
4c1c5d26 935
936 // admin/cron.php should have run srand for us
937 $random100 = rand(0,100);
938 if ($random100 < 10) { // Approximately 10% of the time.
939 // nuke olden sessions
f71a7f8f 940 $longtime = time() - (1 * 3600 * 24);
7b0d12b2 941 $DB->delete_records_select('mnet_session', "expires < ?", array($longtime));
4c1c5d26 942 }
c72fe801 943 }
944
945 /**
946 * Cleanup any remote mnet_sessions, kill the local mnet_session data
947 *
948 * This is called by require_logout in moodlelib
949 *
950 * @return void
951 */
f5fd4347 952 function prelogout_hook() {
287efec6
PL
953 global $CFG, $USER;
954
23a94798 955 if (!is_enabled_auth('mnet')) {
f5fd4347 956 return;
957 }
958
c72fe801 959 // If the user is local to this Moodle:
287efec6 960 if ($USER->mnethostid == $this->mnet->id) {
c72fe801 961 $this->kill_children($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
962
963 // Else the user has hit 'logout' at a Service Provider Moodle:
964 } else {
965 $this->kill_parent($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
966
967 }
968 }
969
970 /**
971 * The SP uses this function to kill the session on the parent IdP
972 *
973 * @param string $username Username for session to kill
974 * @param string $useragent SHA1 hash of user agent to look for
975 * @return string A plaintext report of what has happened
976 */
977 function kill_parent($username, $useragent) {
7b0d12b2 978 global $CFG, $USER, $DB;
979
c72fe801 980 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
981 $sql = "
982 select
983 *
984 from
7b0d12b2 985 {mnet_session} s
c72fe801 986 where
7b0d12b2 987 s.username = ? AND
988 s.useragent = ? AND
989 s.mnethostid = ?";
c72fe801 990
7b0d12b2 991 $mnetsessions = $DB->get_records_sql($sql, array($username, $useragent, $USER->mnethostid));
c72fe801 992
7b0d12b2 993 $ignore = $DB->delete_records('mnet_session',
994 array('username'=>$username,
995 'useragent'=>$useragent,
996 'mnethostid'=>$USER->mnethostid));
c72fe801 997
998 if (false != $mnetsessions) {
999 $mnet_peer = new mnet_peer();
1000 $mnet_peer->set_id($USER->mnethostid);
1001
1002 $mnet_request = new mnet_xmlrpc_client();
1003 $mnet_request->set_method('auth/mnet/auth.php/kill_children');
1004
1005 // set $token and $useragent parameters
1006 $mnet_request->add_param($username);
1007 $mnet_request->add_param($useragent);
1008 if ($mnet_request->send($mnet_peer) === false) {
1009 debugging(join("\n", $mnet_request->error));
139ebfdb 1010 return false;
c72fe801 1011 }
1012 }
1013
c72fe801 1014 return true;
1015 }
1016
1017 /**
1018 * The IdP uses this function to kill child sessions on other hosts
1019 *
1020 * @param string $username Username for session to kill
1021 * @param string $useragent SHA1 hash of user agent to look for
1022 * @return string A plaintext report of what has happened
1023 */
1024 function kill_children($username, $useragent) {
287efec6 1025 global $CFG, $USER, $DB;
48fb3941
PL
1026 $remoteclient = null;
1027 if (defined('MNET_SERVER')) {
1028 $remoteclient = get_mnet_remote_client();
1029 }
c72fe801 1030 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
1031
7b0d12b2 1032 $userid = $DB->get_field('user', 'id', array('mnethostid'=>$CFG->mnet_localhost_id, 'username'=>$username));
c72fe801 1033
1034 $returnstring = '';
1c85006c 1035
1036 $mnetsessions = $DB->get_records('mnet_session', array('userid' => $userid, 'useragent' => $useragent));
c72fe801 1037
c72fe801 1038 if (false == $mnetsessions) {
f213ba93 1039 $returnstring .= "Could find no remote sessions\n";
c72fe801 1040 $mnetsessions = array();
1041 }
1042
1043 foreach($mnetsessions as $mnetsession) {
4711957d 1044 // If this script is being executed by a remote peer, that means the user has clicked
1045 // logout on that peer, and the session on that peer can be deleted natively.
1046 // Skip over it.
287efec6 1047 if (isset($remoteclient->id) && ($mnetsession->mnethostid == $remoteclient->id)) {
4711957d 1048 continue;
1049 }
c72fe801 1050 $returnstring .= "Deleting session\n";
1051
c72fe801 1052 $mnet_peer = new mnet_peer();
1053 $mnet_peer->set_id($mnetsession->mnethostid);
1054
1055 $mnet_request = new mnet_xmlrpc_client();
1056 $mnet_request->set_method('auth/mnet/auth.php/kill_child');
1057
1058 // set $token and $useragent parameters
1059 $mnet_request->add_param($username);
1060 $mnet_request->add_param($useragent);
1061 if ($mnet_request->send($mnet_peer) === false) {
1a7601ca 1062 debugging("Server side error has occured on host $mnetsession->mnethostid: " .
c72fe801 1063 join("\n", $mnet_request->error));
1064 }
1065 }
1066
7b0d12b2 1067 $ignore = $DB->delete_records('mnet_session',
1068 array('useragent'=>$useragent, 'userid'=>$userid));
c72fe801 1069
287efec6 1070 if (isset($remoteclient) && isset($remoteclient->id)) {
2e38d703 1071 session_kill_user($userid);
c72fe801 1072 }
1073 return $returnstring;
1074 }
1075
1076 /**
2e38d703 1077 * When the IdP requests that child sessions are terminated,
c72fe801 1078 * this function will be called on each of the child hosts. The machine that
1079 * calls the function (over xmlrpc) provides us with the mnethostid we need.
1080 *
1081 * @param string $username Username for session to kill
1082 * @param string $useragent SHA1 hash of user agent to look for
1083 * @return bool True on success
1084 */
1085 function kill_child($username, $useragent) {
287efec6
PL
1086 global $CFG, $DB;
1087 $remoteclient = get_mnet_remote_client();
1088 $session = $DB->get_record('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
48fb3941 1089 $DB->delete_records('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
c72fe801 1090 if (false != $session) {
2e38d703 1091 session_kill($session->session_id);
c72fe801 1092 return true;
1093 }
1094 return false;
1095 }
1096
1097 /**
1098 * To delete a host, we must delete all current sessions that users from
1099 * that host are currently engaged in.
1100 *
1101 * @param string $sessionidarray An array of session hashes
1102 * @return bool True on success
1103 */
1104 function end_local_sessions(&$sessionArray) {
1105 global $CFG;
1106 if (is_array($sessionArray)) {
c72fe801 1107 while($session = array_pop($sessionArray)) {
2e38d703 1108 session_kill($session->session_id);
c72fe801 1109 }
c72fe801 1110 return true;
1111 }
1112 return false;
1113 }
1114
1115 /**
35d76df3 1116 * Returns the user's profile image info
c72fe801 1117 *
35d76df3
DM
1118 * If the user exists and has a profile picture, the returned array will contain keys:
1119 * f1 - the content of the default 100x100px image
1120 * f1_mimetype - the mimetype of the f1 file
1121 * f2 - the content of the 35x35px variant of the image
1122 * f2_mimetype - the mimetype of the f2 file
1123 *
1124 * The mimetype information was added in Moodle 2.0. In Moodle 1.x, images are always jpegs.
1125 *
1126 * @see process_new_icon()
1127 * @uses mnet_remote_client callable via MNet XML-RPC
c72fe801 1128 * @param int $userid The id of the user
35d76df3 1129 * @return false|array false if user not found, empty array if no picture exists, array with data otherwise
c72fe801 1130 */
1131 function fetch_user_image($username) {
7b0d12b2 1132 global $CFG, $DB;
c72fe801 1133
35d76df3
DM
1134 if ($user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id))) {
1135 $fs = get_file_storage();
1136 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST);
c72fe801 1137 $return = array();
35d76df3
DM
1138 if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
1139 $return['f1'] = base64_encode($f1->get_content());
1140 $return['f1_mimetype'] = $f1->get_mimetype();
1141 } else if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
1142 $return['f1'] = base64_encode($f1->get_content());
1143 $return['f1_mimetype'] = $f1->get_mimetype();
c72fe801 1144 }
35d76df3
DM
1145 if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.png')) {
1146 $return['f2'] = base64_encode($f2->get_content());
1147 $return['f2_mimetype'] = $f2->get_mimetype();
1148 } else if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.jpg')) {
1149 $return['f2'] = base64_encode($f2->get_content());
1150 $return['f2_mimetype'] = $f2->get_mimetype();
c72fe801 1151 }
1152 return $return;
1153 }
1154 return false;
1155 }
1156
1157 /**
1158 * Returns the theme information and logo url as strings.
1159 *
1160 * @return string The theme info
1161 */
1162 function fetch_theme_info() {
1163 global $CFG;
1164
1165 $themename = "$CFG->theme";
1166 $logourl = "$CFG->wwwroot/theme/$CFG->theme/images/logo.jpg";
1167
1168 $return['themename'] = $themename;
1169 $return['logourl'] = $logourl;
1170 return $return;
1171 }
1172
1173 /**
1174 * Determines if an MNET host is providing the nominated service.
1175 *
1176 * @param int $mnethostid The id of the remote host
1177 * @param string $servicename The name of the service
1178 * @return bool Whether the service is available on the remote host
1179 */
1180 function has_service($mnethostid, $servicename) {
7b0d12b2 1181 global $CFG, $DB;
c72fe801 1182
1183 $sql = "
1184 SELECT
1185 svc.id as serviceid,
1186 svc.name,
1187 svc.description,
1188 svc.offer,
1189 svc.apiversion,
1190 h2s.id as h2s_id
1191 FROM
dbca4e44 1192 {mnet_host} h,
7b0d12b2 1193 {mnet_service} svc,
1194 {mnet_host2service} h2s
c72fe801 1195 WHERE
dbca4e44 1196 h.deleted = '0' AND
1197 h.id = h2s.hostid AND
7b0d12b2 1198 h2s.hostid = ? AND
c72fe801 1199 h2s.serviceid = svc.id AND
7b0d12b2 1200 svc.name = ? AND
c72fe801 1201 h2s.subscribe = '1'";
1202
7b0d12b2 1203 return $DB->get_records_sql($sql, array($mnethostid, $servicename));
c72fe801 1204 }
1205
1206 /**
1207 * Checks the MNET access control table to see if the username/mnethost
1208 * is permitted to login to this moodle.
1209 *
1210 * @param string $username The username
1211 * @param int $mnethostid The id of the remote mnethost
1212 * @return bool Whether the user can login from the remote host
1213 */
1214 function can_login_remotely($username, $mnethostid) {
7b0d12b2 1215 global $DB;
1216
cdf22329 1217 $accessctrl = 'allow';
7b0d12b2 1218 $aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnethostid));
c72fe801 1219 if (!empty($aclrecord)) {
cdf22329 1220 $accessctrl = $aclrecord->accessctrl;
c72fe801 1221 }
cdf22329 1222 return $accessctrl == 'allow';
c72fe801 1223 }
6bc1e5d5 1224
f5fd4347 1225 function logoutpage_hook() {
7b0d12b2 1226 global $USER, $CFG, $redirect, $DB;
6bc1e5d5 1227
1228 if (!empty($USER->mnethostid) and $USER->mnethostid != $CFG->mnet_localhost_id) {
7b0d12b2 1229 $host = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid));
6bc1e5d5 1230 $redirect = $host->wwwroot.'/';
1231 }
1232 }
1233
9dbc81ef 1234 /**
1235 * Trims a log line from mnet peer to limit each part to a length which can be stored in our DB
1236 *
1237 * @param object $logline The log information to be trimmed
1238 * @return object The passed logline object trimmed to not exceed storable limits
1239 */
1240 function trim_logline ($logline) {
1241 $limits = array('ip' => 15, 'coursename' => 40, 'module' => 20, 'action' => 40,
1242 'url' => 255);
1243 foreach ($limits as $property => $limit) {
1244 if (isset($logline->$property)) {
1245 $logline->$property = substr($logline->$property, 0, $limit);
1246 }
1247 }
1248
1249 return $logline;
1250 }
1251
b257d7c4
PL
1252 /**
1253 * Returns a list of potential IdPs that this authentication plugin supports.
1254 * This is used to provide links on the login page.
1255 *
1256 * @param string $wantsurl the relative url fragment the user wants to get to. You can use this to compose a returnurl, for example
1257 *
1258 * @return array like:
1259 * array(
1260 * array(
1261 * 'url' => 'http://someurl',
1262 * 'icon' => new pix_icon(...),
1263 * 'name' => get_string('somename', 'auth_yourplugin'),
1264 * ),
1265 * )
1266 */
1267 function loginpage_idp_list($wantsurl) {
1268 global $DB, $CFG;
4d0552e8 1269
b257d7c4
PL
1270 // strip off wwwroot, since the remote site will prefix it's return url with this
1271 $wantsurl = preg_replace('/(' . preg_quote($CFG->wwwroot, '/') . '|' . preg_quote($CFG->httpswwwroot, '/') . ')/', '', $wantsurl);
4d0552e8
DM
1272
1273 $sql = "SELECT DISTINCT h.id, h.wwwroot, h.name, a.sso_jump_url, a.name as application
1274 FROM {mnet_host} h
1275 JOIN {mnet_host2service} m ON h.id = m.hostid
1276 JOIN {mnet_service} s ON s.id = m.serviceid
1277 JOIN {mnet_application} a ON h.applicationid = a.id
1278 WHERE s.name = ? AND h.deleted = ? AND m.publish = ?";
1279 $params = array('sso_sp', 0, 1);
1280
1281 if (!empty($CFG->mnet_all_hosts_id)) {
1282 $sql .= " AND h.id <> ?";
1283 $params[] = $CFG->mnet_all_hosts_id;
1284 }
1285
1286 if (!$hosts = $DB->get_records_sql($sql, $params)) {
b257d7c4
PL
1287 return array();
1288 }
4d0552e8 1289
b257d7c4
PL
1290 $idps = array();
1291 foreach ($hosts as $host) {
1292 $idps[] = array(
1293 'url' => new moodle_url($host->wwwroot . $host->sso_jump_url, array('hostwwwroot' => $CFG->wwwroot, 'wantsurl' => $wantsurl, 'remoteurl' => 1)),
1294 'icon' => new pix_icon('i/' . $host->application . '_host', $host->name),
1295 'name' => $host->name,
1296 );
1297 }
1298 return $idps;
1299 }
c72fe801 1300}