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