Prevent MNet error message when multiple records for the user are found in mnet_sessi...
[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.
552 $local_courseid_string = implode(', ', $local_courseid_array);
7b0d12b2 553 $whereclause = " userid = ? AND hostid = ? AND courseid NOT IN ($local_courseid_string)";
54f69781 554 $DB->delete_records_select('mnetservice_enrol_enrolments', $whereclause, array($userid, $remoteclient->id));
62d78bf5 555 }
556
edb5da83
PS
557 function prevent_local_passwords() {
558 return true;
559 }
560
c72fe801 561 /**
562 * Returns true if this authentication plugin is 'internal'.
563 *
139ebfdb 564 * @return bool
c72fe801 565 */
566 function is_internal() {
567 return false;
568 }
569
570 /**
571 * Returns true if this authentication plugin can change the user's
572 * password.
573 *
139ebfdb 574 * @return bool
c72fe801 575 */
576 function can_change_password() {
430759a5 577 //TODO: it should be able to redirect, right?
c72fe801 578 return false;
579 }
580
581 /**
582 * Returns the URL for changing the user's pw, or false if the default can
583 * be used.
584 *
99f9f85f 585 * @return moodle_url
c72fe801 586 */
587 function change_password_url() {
99f9f85f 588 return null;
c72fe801 589 }
590
591 /**
592 * Prints a form for configuring this authentication plugin.
593 *
594 * This function is called from admin/auth.php, and outputs a full page with
595 * a form for configuring this plugin.
596 *
de260e0f
PL
597 * @param object $config
598 * @param object $err
599 * @param array $user_fields
c72fe801 600 */
139ebfdb 601 function config_form($config, $err, $user_fields) {
7b0d12b2 602 global $CFG, $DB;
14518364 603
604 $query = "
605 SELECT
606 h.id,
607 h.name as hostname,
608 h.wwwroot,
609 h2idp.publish as idppublish,
610 h2idp.subscribe as idpsubscribe,
611 idp.name as idpname,
612 h2sp.publish as sppublish,
613 h2sp.subscribe as spsubscribe,
614 sp.name as spname
615 FROM
7b0d12b2 616 {mnet_host} h
14518364 617 LEFT JOIN
7b0d12b2 618 {mnet_host2service} h2idp
14518364 619 ON
620 (h.id = h2idp.hostid AND
621 (h2idp.publish = 1 OR
622 h2idp.subscribe = 1))
623 INNER JOIN
7b0d12b2 624 {mnet_service} idp
14518364 625 ON
626 (h2idp.serviceid = idp.id AND
627 idp.name = 'sso_idp')
628 LEFT JOIN
7b0d12b2 629 {mnet_host2service} h2sp
14518364 630 ON
631 (h.id = h2sp.hostid AND
632 (h2sp.publish = 1 OR
633 h2sp.subscribe = 1))
634 INNER JOIN
7b0d12b2 635 {mnet_service} sp
14518364 636 ON
637 (h2sp.serviceid = sp.id AND
638 sp.name = 'sso_sp')
639 WHERE
640 ((h2idp.publish = 1 AND h2sp.subscribe = 1) OR
641 (h2sp.publish = 1 AND h2idp.subscribe = 1)) AND
7b0d12b2 642 h.id != ?
14518364 643 ORDER BY
644 h.name ASC";
645
14518364 646 $id_providers = array();
647 $service_providers = array();
7b0d12b2 648 if ($resultset = $DB->get_records_sql($query, array($CFG->mnet_localhost_id))) {
d525ca25 649 foreach($resultset as $hostservice) {
650 if(!empty($hostservice->idppublish) && !empty($hostservice->spsubscribe)) {
651 $service_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot);
652 }
653 if(!empty($hostservice->idpsubscribe) && !empty($hostservice->sppublish)) {
654 $id_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot);
655 }
14518364 656 }
657 }
139ebfdb 658
c72fe801 659 include "config.html";
660 }
661
662 /**
663 * Processes and stores configuration data for this authentication plugin.
664 */
665 function process_config($config) {
666 // set to defaults if undefined
667 if (!isset ($config->rpc_negotiation_timeout)) {
5671e77f 668 $config->rpc_negotiation_timeout = '30';
c72fe801 669 }
4a3c3308 670 /*
c72fe801 671 if (!isset ($config->auto_add_remote_users)) {
672 $config->auto_add_remote_users = '0';
4a3c3308 673 } See MDL-21327 for why this is commented out
94cf0a1e 674 set_config('auto_add_remote_users', $config->auto_add_remote_users, 'auth_mnet');
4a3c3308 675 */
c72fe801 676
677 // save settings
94cf0a1e 678 set_config('rpc_negotiation_timeout', $config->rpc_negotiation_timeout, 'auth_mnet');
c72fe801 679
680 return true;
681 }
682
683 /**
684 * Poll the IdP server to let it know that a user it has authenticated is still
685 * online
686 *
687 * @return void
688 */
689 function keepalive_client() {
287efec6 690 global $CFG, $DB;
c72fe801 691 $cutoff = time() - 300; // TODO - find out what the remote server's session
692 // cutoff is, and preempt that
693
694 $sql = "
695 select
696 id,
697 username,
698 mnethostid
699 from
7b0d12b2 700 {user}
c72fe801 701 where
7b0d12b2 702 lastaccess > ? AND
703 mnethostid != ?
c72fe801 704 order by
705 mnethostid";
706
7b0d12b2 707 $immigrants = $DB->get_records_sql($sql, array($cutoff, $CFG->mnet_localhost_id));
c72fe801 708
709 if ($immigrants == false) {
710 return true;
711 }
712
713 $usersArray = array();
714 foreach($immigrants as $immigrant) {
715 $usersArray[$immigrant->mnethostid][] = $immigrant->username;
716 }
717
718 require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
719 foreach($usersArray as $mnethostid => $users) {
720 $mnet_peer = new mnet_peer();
721 $mnet_peer->set_id($mnethostid);
722
723 $mnet_request = new mnet_xmlrpc_client();
724 $mnet_request->set_method('auth/mnet/auth.php/keepalive_server');
725
726 // set $token and $useragent parameters
727 $mnet_request->add_param($users);
728
729 if ($mnet_request->send($mnet_peer) === true) {
730 if (!isset($mnet_request->response['code'])) {
731 debugging("Server side error has occured on host $mnethostid");
732 continue;
733 } elseif ($mnet_request->response['code'] > 0) {
734 debugging($mnet_request->response['message']);
735 }
139ebfdb 736
c72fe801 737 if (!isset($mnet_request->response['last log id'])) {
738 debugging("Server side error has occured on host $mnethostid\nNo log ID was received.");
739 continue;
740 }
741 } else {
139ebfdb 742 debugging("Server side error has occured on host $mnethostid: " .
c72fe801 743 join("\n", $mnet_request->error));
3d7e4468 744 break;
c72fe801 745 }
28c3e7d8 746 $mnethostlogssql = "
6ff7d16b 747 SELECT
748 mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip,
749 mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action,
750 mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename,
751 c.modinfo
752 FROM
753 (
754 SELECT
755 l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
756 l.action, l.url, l.info, u.username
757 FROM
758 {user} u
759 INNER JOIN {log} l on l.userid = u.id
760 WHERE
761 u.mnethostid = ?
762 AND l.id > ?
763 ORDER BY remoteid ASC
764 LIMIT 500
765 ) mhostlogs
766 INNER JOIN {course} c on c.id = mhostlogs.course
767 ORDER by mhostlogs.remoteid ASC";
c72fe801 768
995087b9 769 $mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id']));
c72fe801 770
6da155db 771 if ($mnethostlogs == false) {
772 continue;
773 }
c72fe801 774
995087b9 775 $processedlogs = array();
c72fe801 776
995087b9 777 foreach($mnethostlogs as $hostlog) {
778 // Extract the name of the relevant module instance from the
779 // course modinfo if possible.
780 if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) {
781 $modinfo = unserialize($hostlog->modinfo);
782 unset($hostlog->modinfo);
c72fe801 783 $modulearray = array();
784 foreach($modinfo as $module) {
9a9012dc 785 $modulearray[$module->cm] = $module->name;
c72fe801 786 }
995087b9 787 $hostlog->resource_name = $modulearray[$hostlog->cmid];
c72fe801 788 } else {
995087b9 789 $hostlog->resource_name = '';
c72fe801 790 }
791
995087b9 792 $processedlogs[] = array (
793 'remoteid' => $hostlog->remoteid,
794 'time' => $hostlog->time,
795 'userid' => $hostlog->userid,
796 'ip' => $hostlog->ip,
797 'course' => $hostlog->course,
798 'coursename' => $hostlog->coursename,
799 'module' => $hostlog->module,
800 'cmid' => $hostlog->cmid,
801 'action' => $hostlog->action,
802 'url' => $hostlog->url,
803 'info' => $hostlog->info,
804 'resource_name' => $hostlog->resource_name,
805 'username' => $hostlog->username
c72fe801 806 );
807 }
808
995087b9 809 unset($hostlog);
c72fe801 810
811 $mnet_request = new mnet_xmlrpc_client();
812 $mnet_request->set_method('auth/mnet/auth.php/refresh_log');
813
814 // set $token and $useragent parameters
995087b9 815 $mnet_request->add_param($processedlogs);
c72fe801 816
817 if ($mnet_request->send($mnet_peer) === true) {
818 if ($mnet_request->response['code'] > 0) {
819 debugging($mnet_request->response['message']);
820 }
821 } else {
822 debugging("Server side error has occured on host $mnet_peer->ip: " .join("\n", $mnet_request->error));
823 }
824 }
825 }
826
827 /**
828 * Receives an array of log entries from an SP and adds them to the mnet_log
829 * table
830 *
831 * @param array $array An array of usernames
832 * @return string "All ok" or an error message
833 */
834 function refresh_log($array) {
287efec6
PL
835 global $CFG, $DB;
836 $remoteclient = get_mnet_remote_client();
c72fe801 837
838 // We don't want to output anything to the client machine
839 $start = ob_start();
840
841 $returnString = '';
d5a8d9aa 842 $transaction = $DB->start_delegated_transaction();
c72fe801 843 $useridarray = array();
844
845 foreach($array as $logEntry) {
846 $logEntryObj = (object)$logEntry;
287efec6 847 $logEntryObj->hostid = $remoteclient->id;
c72fe801 848
849 if (isset($useridarray[$logEntryObj->username])) {
850 $logEntryObj->userid = $useridarray[$logEntryObj->username];
851 } else {
457c9729 852 $logEntryObj->userid = $DB->get_field('user', 'id', array('username'=>$logEntryObj->username, 'mnethostid'=>(int)$logEntryObj->hostid));
c72fe801 853 if ($logEntryObj->userid == false) {
854 $logEntryObj->userid = 0;
855 }
856 $useridarray[$logEntryObj->username] = $logEntryObj->userid;
857 }
858
859 unset($logEntryObj->username);
860
9dbc81ef 861 $logEntryObj = $this->trim_logline($logEntryObj);
5117d598 862 $insertok = $DB->insert_record('mnet_log', $logEntryObj, false);
c72fe801 863
864 if ($insertok) {
287efec6 865 $remoteclient->last_log_id = $logEntryObj->remoteid;
c72fe801 866 } else {
867 $returnString .= 'Record with id '.$logEntryObj->remoteid." failed to insert.\n";
868 }
869 }
287efec6 870 $remoteclient->commit();
d5a8d9aa 871 $transaction->allow_commit();
c72fe801 872
873 $end = ob_end_clean();
874
875 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok');
876 return array('code' => 1, 'message' => $returnString);
877 }
878
879 /**
880 * Receives an array of usernames from a remote machine and prods their
881 * sessions to keep them alive
882 *
883 * @param array $array An array of usernames
884 * @return string "All ok" or an error message
885 */
886 function keepalive_server($array) {
287efec6
PL
887 global $CFG, $DB;
888 $remoteclient = get_mnet_remote_client();
c72fe801 889
890 $CFG->usesid = true;
c72fe801 891
892 // We don't want to output anything to the client machine
893 $start = ob_start();
894
895 // We'll get session records in batches of 30
896 $superArray = array_chunk($array, 30);
897
898 $returnString = '';
899
900 foreach($superArray as $subArray) {
901 $subArray = array_values($subArray);
902 $instring = "('".implode("', '",$subArray)."')";
7b0d12b2 903 $query = "select id, session_id, username from {mnet_session} where username in $instring";
904 $results = $DB->get_records_sql($query);
c72fe801 905
906 if ($results == false) {
907 // We seem to have a username that breaks our query:
908 // TODO: Handle this error appropriately
909 $returnString .= "We failed to refresh the session for the following usernames: \n".implode("\n", $subArray)."\n\n";
910 } else {
c72fe801 911 foreach($results as $emigrant) {
2e38d703 912 session_touch($emigrant->session_id);
c72fe801 913 }
c72fe801 914 }
915 }
916
917 $end = ob_end_clean();
918
287efec6
PL
919 if (empty($returnString)) return array('code' => 0, 'message' => 'All ok', 'last log id' => $remoteclient->last_log_id);
920 return array('code' => 1, 'message' => $returnString, 'last log id' => $remoteclient->last_log_id);
c72fe801 921 }
922
923 /**
924 * Cron function will be called automatically by cron.php every 5 minutes
925 *
926 * @return void
927 */
928 function cron() {
7b0d12b2 929 global $DB;
4c1c5d26 930
931 // run the keepalive client
c72fe801 932 $this->keepalive_client();
4c1c5d26 933
934 // admin/cron.php should have run srand for us
935 $random100 = rand(0,100);
936 if ($random100 < 10) { // Approximately 10% of the time.
937 // nuke olden sessions
f71a7f8f 938 $longtime = time() - (1 * 3600 * 24);
7b0d12b2 939 $DB->delete_records_select('mnet_session', "expires < ?", array($longtime));
4c1c5d26 940 }
c72fe801 941 }
942
943 /**
944 * Cleanup any remote mnet_sessions, kill the local mnet_session data
945 *
946 * This is called by require_logout in moodlelib
947 *
948 * @return void
949 */
f5fd4347 950 function prelogout_hook() {
287efec6
PL
951 global $CFG, $USER;
952
23a94798 953 if (!is_enabled_auth('mnet')) {
f5fd4347 954 return;
955 }
956
c72fe801 957 // If the user is local to this Moodle:
287efec6 958 if ($USER->mnethostid == $this->mnet->id) {
c72fe801 959 $this->kill_children($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
960
961 // Else the user has hit 'logout' at a Service Provider Moodle:
962 } else {
963 $this->kill_parent($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
964
965 }
966 }
967
968 /**
969 * The SP uses this function to kill the session on the parent IdP
970 *
971 * @param string $username Username for session to kill
972 * @param string $useragent SHA1 hash of user agent to look for
973 * @return string A plaintext report of what has happened
974 */
975 function kill_parent($username, $useragent) {
7b0d12b2 976 global $CFG, $USER, $DB;
977
c72fe801 978 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
979 $sql = "
980 select
981 *
982 from
7b0d12b2 983 {mnet_session} s
c72fe801 984 where
7b0d12b2 985 s.username = ? AND
986 s.useragent = ? AND
987 s.mnethostid = ?";
c72fe801 988
7b0d12b2 989 $mnetsessions = $DB->get_records_sql($sql, array($username, $useragent, $USER->mnethostid));
c72fe801 990
7b0d12b2 991 $ignore = $DB->delete_records('mnet_session',
992 array('username'=>$username,
993 'useragent'=>$useragent,
994 'mnethostid'=>$USER->mnethostid));
c72fe801 995
996 if (false != $mnetsessions) {
997 $mnet_peer = new mnet_peer();
998 $mnet_peer->set_id($USER->mnethostid);
999
1000 $mnet_request = new mnet_xmlrpc_client();
1001 $mnet_request->set_method('auth/mnet/auth.php/kill_children');
1002
1003 // set $token and $useragent parameters
1004 $mnet_request->add_param($username);
1005 $mnet_request->add_param($useragent);
1006 if ($mnet_request->send($mnet_peer) === false) {
1007 debugging(join("\n", $mnet_request->error));
139ebfdb 1008 return false;
c72fe801 1009 }
1010 }
1011
c72fe801 1012 return true;
1013 }
1014
1015 /**
1016 * The IdP uses this function to kill child sessions on other hosts
1017 *
1018 * @param string $username Username for session to kill
1019 * @param string $useragent SHA1 hash of user agent to look for
1020 * @return string A plaintext report of what has happened
1021 */
1022 function kill_children($username, $useragent) {
287efec6 1023 global $CFG, $USER, $DB;
48fb3941
PL
1024 $remoteclient = null;
1025 if (defined('MNET_SERVER')) {
1026 $remoteclient = get_mnet_remote_client();
1027 }
c72fe801 1028 require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
1029
7b0d12b2 1030 $userid = $DB->get_field('user', 'id', array('mnethostid'=>$CFG->mnet_localhost_id, 'username'=>$username));
c72fe801 1031
1032 $returnstring = '';
1c85006c 1033
1034 $mnetsessions = $DB->get_records('mnet_session', array('userid' => $userid, 'useragent' => $useragent));
c72fe801 1035
c72fe801 1036 if (false == $mnetsessions) {
f213ba93 1037 $returnstring .= "Could find no remote sessions\n";
c72fe801 1038 $mnetsessions = array();
1039 }
1040
1041 foreach($mnetsessions as $mnetsession) {
4711957d 1042 // If this script is being executed by a remote peer, that means the user has clicked
1043 // logout on that peer, and the session on that peer can be deleted natively.
1044 // Skip over it.
287efec6 1045 if (isset($remoteclient->id) && ($mnetsession->mnethostid == $remoteclient->id)) {
4711957d 1046 continue;
1047 }
c72fe801 1048 $returnstring .= "Deleting session\n";
1049
c72fe801 1050 $mnet_peer = new mnet_peer();
1051 $mnet_peer->set_id($mnetsession->mnethostid);
1052
1053 $mnet_request = new mnet_xmlrpc_client();
1054 $mnet_request->set_method('auth/mnet/auth.php/kill_child');
1055
1056 // set $token and $useragent parameters
1057 $mnet_request->add_param($username);
1058 $mnet_request->add_param($useragent);
1059 if ($mnet_request->send($mnet_peer) === false) {
1a7601ca 1060 debugging("Server side error has occured on host $mnetsession->mnethostid: " .
c72fe801 1061 join("\n", $mnet_request->error));
1062 }
1063 }
1064
7b0d12b2 1065 $ignore = $DB->delete_records('mnet_session',
1066 array('useragent'=>$useragent, 'userid'=>$userid));
c72fe801 1067
287efec6 1068 if (isset($remoteclient) && isset($remoteclient->id)) {
2e38d703 1069 session_kill_user($userid);
c72fe801 1070 }
1071 return $returnstring;
1072 }
1073
1074 /**
2e38d703 1075 * When the IdP requests that child sessions are terminated,
c72fe801 1076 * this function will be called on each of the child hosts. The machine that
1077 * calls the function (over xmlrpc) provides us with the mnethostid we need.
1078 *
1079 * @param string $username Username for session to kill
1080 * @param string $useragent SHA1 hash of user agent to look for
1081 * @return bool True on success
1082 */
1083 function kill_child($username, $useragent) {
287efec6
PL
1084 global $CFG, $DB;
1085 $remoteclient = get_mnet_remote_client();
1086 $session = $DB->get_record('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
48fb3941 1087 $DB->delete_records('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
c72fe801 1088 if (false != $session) {
2e38d703 1089 session_kill($session->session_id);
c72fe801 1090 return true;
1091 }
1092 return false;
1093 }
1094
1095 /**
1096 * To delete a host, we must delete all current sessions that users from
1097 * that host are currently engaged in.
1098 *
1099 * @param string $sessionidarray An array of session hashes
1100 * @return bool True on success
1101 */
1102 function end_local_sessions(&$sessionArray) {
1103 global $CFG;
1104 if (is_array($sessionArray)) {
c72fe801 1105 while($session = array_pop($sessionArray)) {
2e38d703 1106 session_kill($session->session_id);
c72fe801 1107 }
c72fe801 1108 return true;
1109 }
1110 return false;
1111 }
1112
1113 /**
35d76df3 1114 * Returns the user's profile image info
c72fe801 1115 *
35d76df3
DM
1116 * If the user exists and has a profile picture, the returned array will contain keys:
1117 * f1 - the content of the default 100x100px image
1118 * f1_mimetype - the mimetype of the f1 file
1119 * f2 - the content of the 35x35px variant of the image
1120 * f2_mimetype - the mimetype of the f2 file
1121 *
1122 * The mimetype information was added in Moodle 2.0. In Moodle 1.x, images are always jpegs.
1123 *
1124 * @see process_new_icon()
1125 * @uses mnet_remote_client callable via MNet XML-RPC
c72fe801 1126 * @param int $userid The id of the user
35d76df3 1127 * @return false|array false if user not found, empty array if no picture exists, array with data otherwise
c72fe801 1128 */
1129 function fetch_user_image($username) {
7b0d12b2 1130 global $CFG, $DB;
c72fe801 1131
35d76df3
DM
1132 if ($user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id))) {
1133 $fs = get_file_storage();
1134 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST);
c72fe801 1135 $return = array();
35d76df3
DM
1136 if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
1137 $return['f1'] = base64_encode($f1->get_content());
1138 $return['f1_mimetype'] = $f1->get_mimetype();
1139 } else if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
1140 $return['f1'] = base64_encode($f1->get_content());
1141 $return['f1_mimetype'] = $f1->get_mimetype();
c72fe801 1142 }
35d76df3
DM
1143 if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.png')) {
1144 $return['f2'] = base64_encode($f2->get_content());
1145 $return['f2_mimetype'] = $f2->get_mimetype();
1146 } else if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.jpg')) {
1147 $return['f2'] = base64_encode($f2->get_content());
1148 $return['f2_mimetype'] = $f2->get_mimetype();
c72fe801 1149 }
1150 return $return;
1151 }
1152 return false;
1153 }
1154
1155 /**
1156 * Returns the theme information and logo url as strings.
1157 *
1158 * @return string The theme info
1159 */
1160 function fetch_theme_info() {
1161 global $CFG;
1162
1163 $themename = "$CFG->theme";
1164 $logourl = "$CFG->wwwroot/theme/$CFG->theme/images/logo.jpg";
1165
1166 $return['themename'] = $themename;
1167 $return['logourl'] = $logourl;
1168 return $return;
1169 }
1170
1171 /**
1172 * Determines if an MNET host is providing the nominated service.
1173 *
1174 * @param int $mnethostid The id of the remote host
1175 * @param string $servicename The name of the service
1176 * @return bool Whether the service is available on the remote host
1177 */
1178 function has_service($mnethostid, $servicename) {
7b0d12b2 1179 global $CFG, $DB;
c72fe801 1180
1181 $sql = "
1182 SELECT
1183 svc.id as serviceid,
1184 svc.name,
1185 svc.description,
1186 svc.offer,
1187 svc.apiversion,
1188 h2s.id as h2s_id
1189 FROM
dbca4e44 1190 {mnet_host} h,
7b0d12b2 1191 {mnet_service} svc,
1192 {mnet_host2service} h2s
c72fe801 1193 WHERE
dbca4e44 1194 h.deleted = '0' AND
1195 h.id = h2s.hostid AND
7b0d12b2 1196 h2s.hostid = ? AND
c72fe801 1197 h2s.serviceid = svc.id AND
7b0d12b2 1198 svc.name = ? AND
c72fe801 1199 h2s.subscribe = '1'";
1200
7b0d12b2 1201 return $DB->get_records_sql($sql, array($mnethostid, $servicename));
c72fe801 1202 }
1203
1204 /**
1205 * Checks the MNET access control table to see if the username/mnethost
1206 * is permitted to login to this moodle.
1207 *
1208 * @param string $username The username
1209 * @param int $mnethostid The id of the remote mnethost
1210 * @return bool Whether the user can login from the remote host
1211 */
1212 function can_login_remotely($username, $mnethostid) {
7b0d12b2 1213 global $DB;
1214
cdf22329 1215 $accessctrl = 'allow';
7b0d12b2 1216 $aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnethostid));
c72fe801 1217 if (!empty($aclrecord)) {
cdf22329 1218 $accessctrl = $aclrecord->accessctrl;
c72fe801 1219 }
cdf22329 1220 return $accessctrl == 'allow';
c72fe801 1221 }
6bc1e5d5 1222
f5fd4347 1223 function logoutpage_hook() {
7b0d12b2 1224 global $USER, $CFG, $redirect, $DB;
6bc1e5d5 1225
1226 if (!empty($USER->mnethostid) and $USER->mnethostid != $CFG->mnet_localhost_id) {
7b0d12b2 1227 $host = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid));
6bc1e5d5 1228 $redirect = $host->wwwroot.'/';
1229 }
1230 }
1231
9dbc81ef 1232 /**
1233 * Trims a log line from mnet peer to limit each part to a length which can be stored in our DB
1234 *
1235 * @param object $logline The log information to be trimmed
1236 * @return object The passed logline object trimmed to not exceed storable limits
1237 */
1238 function trim_logline ($logline) {
1239 $limits = array('ip' => 15, 'coursename' => 40, 'module' => 20, 'action' => 40,
1240 'url' => 255);
1241 foreach ($limits as $property => $limit) {
1242 if (isset($logline->$property)) {
1243 $logline->$property = substr($logline->$property, 0, $limit);
1244 }
1245 }
1246
1247 return $logline;
1248 }
1249
b257d7c4
PL
1250 /**
1251 * Returns a list of potential IdPs that this authentication plugin supports.
1252 * This is used to provide links on the login page.
1253 *
1254 * @param string $wantsurl the relative url fragment the user wants to get to. You can use this to compose a returnurl, for example
1255 *
1256 * @return array like:
1257 * array(
1258 * array(
1259 * 'url' => 'http://someurl',
1260 * 'icon' => new pix_icon(...),
1261 * 'name' => get_string('somename', 'auth_yourplugin'),
1262 * ),
1263 * )
1264 */
1265 function loginpage_idp_list($wantsurl) {
1266 global $DB, $CFG;
4d0552e8 1267
b257d7c4
PL
1268 // strip off wwwroot, since the remote site will prefix it's return url with this
1269 $wantsurl = preg_replace('/(' . preg_quote($CFG->wwwroot, '/') . '|' . preg_quote($CFG->httpswwwroot, '/') . ')/', '', $wantsurl);
4d0552e8
DM
1270
1271 $sql = "SELECT DISTINCT h.id, h.wwwroot, h.name, a.sso_jump_url, a.name as application
1272 FROM {mnet_host} h
1273 JOIN {mnet_host2service} m ON h.id = m.hostid
1274 JOIN {mnet_service} s ON s.id = m.serviceid
1275 JOIN {mnet_application} a ON h.applicationid = a.id
1276 WHERE s.name = ? AND h.deleted = ? AND m.publish = ?";
1277 $params = array('sso_sp', 0, 1);
1278
1279 if (!empty($CFG->mnet_all_hosts_id)) {
1280 $sql .= " AND h.id <> ?";
1281 $params[] = $CFG->mnet_all_hosts_id;
1282 }
1283
1284 if (!$hosts = $DB->get_records_sql($sql, $params)) {
b257d7c4
PL
1285 return array();
1286 }
4d0552e8 1287
b257d7c4
PL
1288 $idps = array();
1289 foreach ($hosts as $host) {
1290 $idps[] = array(
1291 'url' => new moodle_url($host->wwwroot . $host->sso_jump_url, array('hostwwwroot' => $CFG->wwwroot, 'wantsurl' => $wantsurl, 'remoteurl' => 1)),
1292 'icon' => new pix_icon('i/' . $host->application . '_host', $host->name),
1293 'name' => $host->name,
1294 );
1295 }
1296 return $idps;
1297 }
c72fe801 1298}