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