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