You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1202 lines
48 KiB
1202 lines
48 KiB
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Authentication Plugin: Moodle Network Authentication
|
|
* Multiple host authentication support for Moodle Network.
|
|
*
|
|
* @package auth_mnet
|
|
* @author Martin Dougiamas
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
require_once($CFG->libdir.'/authlib.php');
|
|
|
|
/**
|
|
* Moodle Network authentication plugin.
|
|
*/
|
|
class auth_plugin_mnet extends auth_plugin_base {
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct() {
|
|
$this->authtype = 'mnet';
|
|
$this->config = get_config('auth_mnet');
|
|
$this->mnet = get_mnet_environment();
|
|
}
|
|
|
|
/**
|
|
* Old syntax of class constructor. Deprecated in PHP7.
|
|
*
|
|
* @deprecated since Moodle 3.1
|
|
*/
|
|
public function auth_plugin_mnet() {
|
|
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
|
|
self::__construct();
|
|
}
|
|
|
|
/**
|
|
* This function is normally used to determine if the username and password
|
|
* are correct for local logins. Always returns false, as local users do not
|
|
* need to login over mnet xmlrpc.
|
|
*
|
|
* @param string $username The username
|
|
* @param string $password The password
|
|
* @return bool Authentication success or failure.
|
|
*/
|
|
function user_login($username, $password) {
|
|
return false; // print_error("mnetlocal");
|
|
}
|
|
|
|
/**
|
|
* Return user data for the provided token, compare with user_agent string.
|
|
*
|
|
* @param string $token The unique ID provided by remotehost.
|
|
* @param string $useragent User Agent string.
|
|
* @return array $userdata Array of user info for remote host
|
|
*/
|
|
function user_authorise($token, $useragent) {
|
|
global $CFG, $SITE, $DB;
|
|
$remoteclient = get_mnet_remote_client();
|
|
require_once $CFG->dirroot . '/mnet/xmlrpc/serverlib.php';
|
|
|
|
$mnet_session = $DB->get_record('mnet_session', array('token'=>$token, 'useragent'=>$useragent));
|
|
if (empty($mnet_session)) {
|
|
throw new mnet_server_exception(1, 'authfail_nosessionexists');
|
|
}
|
|
|
|
// check session confirm timeout
|
|
if ($mnet_session->confirm_timeout < time()) {
|
|
throw new mnet_server_exception(2, 'authfail_sessiontimedout');
|
|
}
|
|
|
|
// session okay, try getting the user
|
|
if (!$user = $DB->get_record('user', array('id'=>$mnet_session->userid))) {
|
|
throw new mnet_server_exception(3, 'authfail_usermismatch');
|
|
}
|
|
|
|
$userdata = mnet_strip_user((array)$user, mnet_fields_to_send($remoteclient));
|
|
|
|
// extra special ones
|
|
$userdata['auth'] = 'mnet';
|
|
$userdata['wwwroot'] = $this->mnet->wwwroot;
|
|
$userdata['session.gc_maxlifetime'] = ini_get('session.gc_maxlifetime');
|
|
|
|
if (array_key_exists('picture', $userdata) && !empty($user->picture)) {
|
|
$fs = get_file_storage();
|
|
$usercontext = context_user::instance($user->id, MUST_EXIST);
|
|
if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
|
|
$userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified();
|
|
$userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype();
|
|
} else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
|
|
$userdata['_mnet_userpicture_timemodified'] = $usericonfile->get_timemodified();
|
|
$userdata['_mnet_userpicture_mimetype'] = $usericonfile->get_mimetype();
|
|
}
|
|
}
|
|
|
|
$userdata['myhosts'] = array();
|
|
if ($courses = enrol_get_users_courses($user->id, false)) {
|
|
$userdata['myhosts'][] = array('name'=> $SITE->shortname, 'url' => $CFG->wwwroot, 'count' => count($courses));
|
|
}
|
|
|
|
$sql = "SELECT h.name AS hostname, h.wwwroot, h.id AS hostid,
|
|
COUNT(c.id) AS count
|
|
FROM {mnetservice_enrol_courses} c
|
|
JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid)
|
|
JOIN {mnet_host} h ON h.id = c.hostid
|
|
WHERE e.userid = ? AND c.hostid = ?
|
|
GROUP BY h.name, h.wwwroot, h.id";
|
|
|
|
if ($courses = $DB->get_records_sql($sql, array($user->id, $remoteclient->id))) {
|
|
foreach($courses as $course) {
|
|
$userdata['myhosts'][] = array('name'=> $course->hostname, 'url' => $CFG->wwwroot.'/auth/mnet/jump.php?hostid='.$course->hostid, 'count' => $course->count);
|
|
}
|
|
}
|
|
|
|
return $userdata;
|
|
}
|
|
|
|
/**
|
|
* Generate a random string for use as an RPC session token.
|
|
*/
|
|
function generate_token() {
|
|
return sha1(str_shuffle('' . mt_rand() . time()));
|
|
}
|
|
|
|
/**
|
|
* Starts an RPC jump session and returns the jump redirect URL.
|
|
*
|
|
* @param int $mnethostid id of the mnet host to jump to
|
|
* @param string $wantsurl url to redirect to after the jump (usually on remote system)
|
|
* @param boolean $wantsurlbackhere defaults to false, means that the remote system should bounce us back here
|
|
* rather than somewhere inside *its* wwwroot
|
|
*/
|
|
function start_jump_session($mnethostid, $wantsurl, $wantsurlbackhere=false) {
|
|
global $CFG, $USER, $DB;
|
|
require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
|
|
|
|
if (\core\session\manager::is_loggedinas()) {
|
|
print_error('notpermittedtojumpas', 'mnet');
|
|
}
|
|
|
|
// check remote login permissions
|
|
if (! has_capability('moodle/site:mnetlogintoremote', context_system::instance())
|
|
or is_mnet_remote_user($USER)
|
|
or isguestuser()
|
|
or !isloggedin()) {
|
|
print_error('notpermittedtojump', 'mnet');
|
|
}
|
|
|
|
// check for SSO publish permission first
|
|
if ($this->has_service($mnethostid, 'sso_sp') == false) {
|
|
print_error('hostnotconfiguredforsso', 'mnet');
|
|
}
|
|
|
|
// set RPC timeout to 30 seconds if not configured
|
|
if (empty($this->config->rpc_negotiation_timeout)) {
|
|
$this->config->rpc_negotiation_timeout = 30;
|
|
set_config('rpc_negotiation_timeout', '30', 'auth_mnet');
|
|
}
|
|
|
|
// get the host info
|
|
$mnet_peer = new mnet_peer();
|
|
$mnet_peer->set_id($mnethostid);
|
|
|
|
// set up the session
|
|
$mnet_session = $DB->get_record('mnet_session',
|
|
array('userid'=>$USER->id, 'mnethostid'=>$mnethostid,
|
|
'useragent'=>sha1($_SERVER['HTTP_USER_AGENT'])));
|
|
if ($mnet_session == false) {
|
|
$mnet_session = new stdClass();
|
|
$mnet_session->mnethostid = $mnethostid;
|
|
$mnet_session->userid = $USER->id;
|
|
$mnet_session->username = $USER->username;
|
|
$mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
|
|
$mnet_session->token = $this->generate_token();
|
|
$mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout;
|
|
$mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime');
|
|
$mnet_session->session_id = session_id();
|
|
$mnet_session->id = $DB->insert_record('mnet_session', $mnet_session);
|
|
} else {
|
|
$mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
|
|
$mnet_session->token = $this->generate_token();
|
|
$mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout;
|
|
$mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime');
|
|
$mnet_session->session_id = session_id();
|
|
$DB->update_record('mnet_session', $mnet_session);
|
|
}
|
|
|
|
// construct the redirection URL
|
|
//$transport = mnet_get_protocol($mnet_peer->transport);
|
|
$wantsurl = urlencode($wantsurl);
|
|
$url = "{$mnet_peer->wwwroot}{$mnet_peer->application->sso_land_url}?token={$mnet_session->token}&idp={$this->mnet->wwwroot}&wantsurl={$wantsurl}";
|
|
if ($wantsurlbackhere) {
|
|
$url .= '&remoteurl=1';
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* This function confirms the remote (ID provider) host's mnet session
|
|
* by communicating the token and UA over the XMLRPC transport layer, and
|
|
* returns the local user record on success.
|
|
*
|
|
* @param string $token The random session token.
|
|
* @param mnet_peer $remotepeer The ID provider mnet_peer object.
|
|
* @return array The local user record.
|
|
*/
|
|
function confirm_mnet_session($token, $remotepeer) {
|
|
global $CFG, $DB;
|
|
require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
|
|
require_once $CFG->libdir . '/gdlib.php';
|
|
require_once($CFG->dirroot.'/user/lib.php');
|
|
|
|
// verify the remote host is configured locally before attempting RPC call
|
|
if (! $remotehost = $DB->get_record('mnet_host', array('wwwroot' => $remotepeer->wwwroot, 'deleted' => 0))) {
|
|
print_error('notpermittedtoland', 'mnet');
|
|
}
|
|
|
|
// set up the RPC request
|
|
$mnetrequest = new mnet_xmlrpc_client();
|
|
$mnetrequest->set_method('auth/mnet/auth.php/user_authorise');
|
|
|
|
// set $token and $useragent parameters
|
|
$mnetrequest->add_param($token);
|
|
$mnetrequest->add_param(sha1($_SERVER['HTTP_USER_AGENT']));
|
|
|
|
// Thunderbirds are go! Do RPC call and store response
|
|
if ($mnetrequest->send($remotepeer) === true) {
|
|
$remoteuser = (object) $mnetrequest->response;
|
|
} else {
|
|
foreach ($mnetrequest->error as $errormessage) {
|
|
list($code, $message) = array_map('trim',explode(':', $errormessage, 2));
|
|
if($code == 702) {
|
|
$site = get_site();
|
|
print_error('mnet_session_prohibited', 'mnet', $remotepeer->wwwroot, format_string($site->fullname));
|
|
exit;
|
|
}
|
|
$message .= "ERROR $code:<br/>$errormessage<br/>";
|
|
}
|
|
print_error("rpcerror", '', '', $message);
|
|
}
|
|
unset($mnetrequest);
|
|
|
|
if (empty($remoteuser) or empty($remoteuser->username)) {
|
|
print_error('unknownerror', 'mnet');
|
|
exit;
|
|
}
|
|
|
|
if (user_not_fully_set_up($remoteuser, false)) {
|
|
print_error('notenoughidpinfo', 'mnet');
|
|
exit;
|
|
}
|
|
|
|
$remoteuser = mnet_strip_user($remoteuser, mnet_fields_to_import($remotepeer));
|
|
|
|
$remoteuser->auth = 'mnet';
|
|
$remoteuser->wwwroot = $remotepeer->wwwroot;
|
|
|
|
// the user may roam from Moodle 1.x where lang has _utf8 suffix
|
|
// also, make sure that the lang is actually installed, otherwise set site default
|
|
if (isset($remoteuser->lang)) {
|
|
$remoteuser->lang = clean_param(str_replace('_utf8', '', $remoteuser->lang), PARAM_LANG);
|
|
}
|
|
if (empty($remoteuser->lang)) {
|
|
if (!empty($CFG->lang)) {
|
|
$remoteuser->lang = $CFG->lang;
|
|
} else {
|
|
$remoteuser->lang = 'en';
|
|
}
|
|
}
|
|
$firsttime = false;
|
|
|
|
// get the local record for the remote user
|
|
$localuser = $DB->get_record('user', array('username'=>$remoteuser->username, 'mnethostid'=>$remotehost->id));
|
|
|
|
// add the remote user to the database if necessary, and if allowed
|
|
// TODO: refactor into a separate function
|
|
if (empty($localuser) || ! $localuser->id) {
|
|
/*
|
|
if (empty($this->config->auto_add_remote_users)) {
|
|
print_error('nolocaluser', 'mnet');
|
|
} See MDL-21327 for why this is commented out
|
|
*/
|
|
$remoteuser->mnethostid = $remotehost->id;
|
|
$remoteuser->firstaccess = 0;
|
|
$remoteuser->confirmed = 1;
|
|
|
|
$remoteuser->id = user_create_user($remoteuser, false);
|
|
$firsttime = true;
|
|
$localuser = $remoteuser;
|
|
}
|
|
|
|
// check sso access control list for permission first
|
|
if (!$this->can_login_remotely($localuser->username, $remotehost->id)) {
|
|
print_error('sso_mnet_login_refused', 'mnet', '', array('user'=>$localuser->username, 'host'=>$remotehost->name));
|
|
}
|
|
|
|
$fs = get_file_storage();
|
|
|
|
// update the local user record with remote user data
|
|
foreach ((array) $remoteuser as $key => $val) {
|
|
|
|
if ($key == '_mnet_userpicture_timemodified' and empty($CFG->disableuserimages) and isset($remoteuser->picture)) {
|
|
// update the user picture if there is a newer verion at the identity provider
|
|
$usercontext = context_user::instance($localuser->id, MUST_EXIST);
|
|
if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
|
|
$localtimemodified = $usericonfile->get_timemodified();
|
|
} else if ($usericonfile = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
|
|
$localtimemodified = $usericonfile->get_timemodified();
|
|
} else {
|
|
$localtimemodified = 0;
|
|
}
|
|
|
|
if (!empty($val) and $localtimemodified < $val) {
|
|
mnet_debug('refetching the user picture from the identity provider host');
|
|
$fetchrequest = new mnet_xmlrpc_client();
|
|
$fetchrequest->set_method('auth/mnet/auth.php/fetch_user_image');
|
|
$fetchrequest->add_param($localuser->username);
|
|
if ($fetchrequest->send($remotepeer) === true) {
|
|
if (strlen($fetchrequest->response['f1']) > 0) {
|
|
$imagefilename = $CFG->tempdir . '/mnet-usericon-' . $localuser->id;
|
|
$imagecontents = base64_decode($fetchrequest->response['f1']);
|
|
file_put_contents($imagefilename, $imagecontents);
|
|
if ($newrev = process_new_icon($usercontext, 'user', 'icon', 0, $imagefilename)) {
|
|
$localuser->picture = $newrev;
|
|
}
|
|
unlink($imagefilename);
|
|
}
|
|
// note that since Moodle 2.0 we ignore $fetchrequest->response['f2']
|
|
// the mimetype information provided is ignored and the type of the file is detected
|
|
// by process_new_icon()
|
|
}
|
|
}
|
|
}
|
|
|
|
if($key == 'myhosts') {
|
|
$localuser->mnet_foreign_host_array = array();
|
|
foreach($val as $rhost) {
|
|
$name = clean_param($rhost['name'], PARAM_ALPHANUM);
|
|
$url = clean_param($rhost['url'], PARAM_URL);
|
|
$count = clean_param($rhost['count'], PARAM_INT);
|
|
$url_is_local = stristr($url , $CFG->wwwroot);
|
|
if (!empty($name) && !empty($count) && empty($url_is_local)) {
|
|
$localuser->mnet_foreign_host_array[] = array('name' => $name,
|
|
'url' => $url,
|
|
'count' => $count);
|
|
}
|
|
}
|
|
}
|
|
|
|
$localuser->{$key} = $val;
|
|
}
|
|
|
|
$localuser->mnethostid = $remotepeer->id;
|
|
user_update_user($localuser, false);
|
|
|
|
if (!$firsttime) {
|
|
// repeat customer! let the IDP know about enrolments
|
|
// we have for this user.
|
|
// set up the RPC request
|
|
$mnetrequest = new mnet_xmlrpc_client();
|
|
$mnetrequest->set_method('auth/mnet/auth.php/update_enrolments');
|
|
|
|
// pass username and an assoc array of "my courses"
|
|
// with info so that the IDP can maintain mnetservice_enrol_enrolments
|
|
$mnetrequest->add_param($remoteuser->username);
|
|
$fields = 'id, category, sortorder, fullname, shortname, idnumber, summary, startdate, visible';
|
|
$courses = enrol_get_users_courses($localuser->id, false, $fields);
|
|
if (is_array($courses) && !empty($courses)) {
|
|
// Second request to do the JOINs that we'd have done
|
|
// inside enrol_get_users_courses() if we had been allowed
|
|
$sql = "SELECT c.id,
|
|
cc.name AS cat_name, cc.description AS cat_description
|
|
FROM {course} c
|
|
JOIN {course_categories} cc ON c.category = cc.id
|
|
WHERE c.id IN (" . join(',',array_keys($courses)) . ')';
|
|
$extra = $DB->get_records_sql($sql);
|
|
|
|
$keys = array_keys($courses);
|
|
$studentroles = get_archetype_roles('student');
|
|
if (!empty($studentroles)) {
|
|
$defaultrole = reset($studentroles);
|
|
//$defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!!
|
|
foreach ($keys AS $id) {
|
|
if ($courses[$id]->visible == 0) {
|
|
unset($courses[$id]);
|
|
continue;
|
|
}
|
|
$courses[$id]->cat_id = $courses[$id]->category;
|
|
$courses[$id]->defaultroleid = $defaultrole->id;
|
|
unset($courses[$id]->category);
|
|
unset($courses[$id]->visible);
|
|
|
|
$courses[$id]->cat_name = $extra[$id]->cat_name;
|
|
$courses[$id]->cat_description = $extra[$id]->cat_description;
|
|
$courses[$id]->defaultrolename = $defaultrole->name;
|
|
// coerce to array
|
|
$courses[$id] = (array)$courses[$id];
|
|
}
|
|
} else {
|
|
throw new moodle_exception('unknownrole', 'error', '', 'student');
|
|
}
|
|
} else {
|
|
// if the array is empty, send it anyway
|
|
// we may be clearing out stale entries
|
|
$courses = array();
|
|
}
|
|
$mnetrequest->add_param($courses);
|
|
|
|
// Call 0800-RPC Now! -- we don't care too much if it fails
|
|
// as it's just informational.
|
|
if ($mnetrequest->send($remotepeer) === false) {
|
|
// error_log(print_r($mnetrequest->error,1));
|
|
}
|
|
}
|
|
|
|
return $localuser;
|
|
}
|
|
|
|
|
|
/**
|
|
* creates (or updates) the mnet session once
|
|
* {@see confirm_mnet_session} and {@see complete_user_login} have both been called
|
|
*
|
|
* @param stdclass $user the local user (must exist already
|
|
* @param string $token the jump/land token
|
|
* @param mnet_peer $remotepeer the mnet_peer object of this users's idp
|
|
*/
|
|
public function update_mnet_session($user, $token, $remotepeer) {
|
|
global $DB;
|
|
$session_gc_maxlifetime = 1440;
|
|
if (isset($user->session_gc_maxlifetime)) {
|
|
$session_gc_maxlifetime = $user->session_gc_maxlifetime;
|
|
}
|
|
if (!$mnet_session = $DB->get_record('mnet_session',
|
|
array('userid'=>$user->id, 'mnethostid'=>$remotepeer->id,
|
|
'useragent'=>sha1($_SERVER['HTTP_USER_AGENT'])))) {
|
|
$mnet_session = new stdClass();
|
|
$mnet_session->mnethostid = $remotepeer->id;
|
|
$mnet_session->userid = $user->id;
|
|
$mnet_session->username = $user->username;
|
|
$mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']);
|
|
$mnet_session->token = $token; // Needed to support simultaneous sessions
|
|
// and preserving DB rec uniqueness
|
|
$mnet_session->confirm_timeout = time();
|
|
$mnet_session->expires = time() + (integer)$session_gc_maxlifetime;
|
|
$mnet_session->session_id = session_id();
|
|
$mnet_session->id = $DB->insert_record('mnet_session', $mnet_session);
|
|
} else {
|
|
$mnet_session->expires = time() + (integer)$session_gc_maxlifetime;
|
|
$DB->update_record('mnet_session', $mnet_session);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Invoke this function _on_ the IDP to update it with enrolment info local to
|
|
* the SP right after calling user_authorise()
|
|
*
|
|
* Normally called by the SP after calling user_authorise()
|
|
*
|
|
* @param string $username The username
|
|
* @param array $courses Assoc array of courses following the structure of mnetservice_enrol_courses
|
|
* @return bool
|
|
*/
|
|
function update_enrolments($username, $courses) {
|
|
global $CFG, $DB;
|
|
$remoteclient = get_mnet_remote_client();
|
|
|
|
if (empty($username) || !is_array($courses)) {
|
|
return false;
|
|
}
|
|
// make sure it is a user we have an in active session
|
|
// with that host...
|
|
$mnetsessions = $DB->get_records('mnet_session', array('username' => $username, 'mnethostid' => $remoteclient->id), '', 'id, userid');
|
|
$userid = null;
|
|
foreach ($mnetsessions as $mnetsession) {
|
|
if (is_null($userid)) {
|
|
$userid = $mnetsession->userid;
|
|
continue;
|
|
}
|
|
if ($userid != $mnetsession->userid) {
|
|
throw new mnet_server_exception(3, 'authfail_usermismatch');
|
|
}
|
|
}
|
|
|
|
if (empty($courses)) { // no courses? clear out quickly
|
|
$DB->delete_records('mnetservice_enrol_enrolments', array('hostid'=>$remoteclient->id, 'userid'=>$userid));
|
|
return true;
|
|
}
|
|
|
|
// IMPORTANT: Ask for remoteid as the first element in the query, so
|
|
// that the array that comes back is indexed on the same field as the
|
|
// array that we have received from the remote client
|
|
$sql = "SELECT c.remoteid, c.id, c.categoryid AS cat_id, c.categoryname AS cat_name, c.sortorder,
|
|
c.fullname, c.shortname, c.idnumber, c.summary, c.summaryformat, c.startdate,
|
|
e.id AS enrolmentid
|
|
FROM {mnetservice_enrol_courses} c
|
|
LEFT JOIN {mnetservice_enrol_enrolments} e ON (e.hostid = c.hostid AND e.remotecourseid = c.remoteid)
|
|
WHERE e.userid = ? AND c.hostid = ?";
|
|
|
|
$currentcourses = $DB->get_records_sql($sql, array($userid, $remoteclient->id));
|
|
|
|
$local_courseid_array = array();
|
|
foreach($courses as $ix => $course) {
|
|
|
|
$course['remoteid'] = $course['id'];
|
|
$course['hostid'] = (int)$remoteclient->id;
|
|
$userisregd = false;
|
|
|
|
// if we do not have the the information about the remote course, it is not available
|
|
// to us for remote enrolment - skip
|
|
if (array_key_exists($course['remoteid'], $currentcourses)) {
|
|
// Pointer to current course:
|
|
$currentcourse =& $currentcourses[$course['remoteid']];
|
|
// We have a record - is it up-to-date?
|
|
$course['id'] = $currentcourse->id;
|
|
|
|
$saveflag = false;
|
|
|
|
foreach($course as $key => $value) {
|
|
if ($currentcourse->$key != $value) {
|
|
$saveflag = true;
|
|
$currentcourse->$key = $value;
|
|
}
|
|
}
|
|
|
|
if ($saveflag) {
|
|
$DB->update_record('mnetervice_enrol_courses', $currentcourse);
|
|
}
|
|
|
|
if (isset($currentcourse->enrolmentid) && is_numeric($currentcourse->enrolmentid)) {
|
|
$userisregd = true;
|
|
}
|
|
} else {
|
|
unset ($courses[$ix]);
|
|
continue;
|
|
}
|
|
|
|
// By this point, we should always have a $dataObj->id
|
|
$local_courseid_array[] = $course['id'];
|
|
|
|
// Do we have a record for this assignment?
|
|
if ($userisregd) {
|
|
// Yes - we know about this one already
|
|
// We don't want to do updates because the new data is probably
|
|
// 'less complete' than the data we have.
|
|
} else {
|
|
// No - create a record
|
|
$assignObj = new stdClass();
|
|
$assignObj->userid = $userid;
|
|
$assignObj->hostid = (int)$remoteclient->id;
|
|
$assignObj->remotecourseid = $course['remoteid'];
|
|
$assignObj->rolename = $course['defaultrolename'];
|
|
$assignObj->id = $DB->insert_record('mnetservice_enrol_enrolments', $assignObj);
|
|
}
|
|
}
|
|
|
|
// Clean up courses that the user is no longer enrolled in.
|
|
if (!empty($local_courseid_array)) {
|
|
$local_courseid_string = implode(', ', $local_courseid_array);
|
|
$whereclause = " userid = ? AND hostid = ? AND remotecourseid NOT IN ($local_courseid_string)";
|
|
$DB->delete_records_select('mnetservice_enrol_enrolments', $whereclause, array($userid, $remoteclient->id));
|
|
}
|
|
}
|
|
|
|
function prevent_local_passwords() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this authentication plugin is 'internal'.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function is_internal() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this authentication plugin can change the user's
|
|
* password.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function can_change_password() {
|
|
//TODO: it should be able to redirect, right?
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the URL for changing the user's pw, or false if the default can
|
|
* be used.
|
|
*
|
|
* @return moodle_url
|
|
*/
|
|
function change_password_url() {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Poll the IdP server to let it know that a user it has authenticated is still
|
|
* online
|
|
*
|
|
* @return void
|
|
*/
|
|
function keepalive_client() {
|
|
global $CFG, $DB;
|
|
$cutoff = time() - 300; // TODO - find out what the remote server's session
|
|
// cutoff is, and preempt that
|
|
|
|
$sql = "
|
|
select
|
|
id,
|
|
username,
|
|
mnethostid
|
|
from
|
|
{user}
|
|
where
|
|
lastaccess > ? AND
|
|
mnethostid != ?
|
|
order by
|
|
mnethostid";
|
|
|
|
$immigrants = $DB->get_records_sql($sql, array($cutoff, $CFG->mnet_localhost_id));
|
|
|
|
if ($immigrants == false) {
|
|
return true;
|
|
}
|
|
|
|
$usersArray = array();
|
|
foreach($immigrants as $immigrant) {
|
|
$usersArray[$immigrant->mnethostid][] = $immigrant->username;
|
|
}
|
|
|
|
require_once $CFG->dirroot . '/mnet/xmlrpc/client.php';
|
|
foreach($usersArray as $mnethostid => $users) {
|
|
$mnet_peer = new mnet_peer();
|
|
$mnet_peer->set_id($mnethostid);
|
|
|
|
$mnet_request = new mnet_xmlrpc_client();
|
|
$mnet_request->set_method('auth/mnet/auth.php/keepalive_server');
|
|
|
|
// set $token and $useragent parameters
|
|
$mnet_request->add_param($users);
|
|
|
|
if ($mnet_request->send($mnet_peer) === true) {
|
|
if (!isset($mnet_request->response['code'])) {
|
|
debugging("Server side error has occured on host $mnethostid");
|
|
continue;
|
|
} elseif ($mnet_request->response['code'] > 0) {
|
|
debugging($mnet_request->response['message']);
|
|
}
|
|
|
|
if (!isset($mnet_request->response['last log id'])) {
|
|
debugging("Server side error has occured on host $mnethostid\nNo log ID was received.");
|
|
continue;
|
|
}
|
|
} else {
|
|
debugging("Server side error has occured on host $mnethostid: " .
|
|
join("\n", $mnet_request->error));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Receives an array of log entries from an SP and adds them to the mnet_log
|
|
* table
|
|
*
|
|
* @deprecated since Moodle 2.8 Please don't use this function for recording mnet logs.
|
|
* @param array $array An array of usernames
|
|
* @return string "All ok" or an error message
|
|
*/
|
|
function refresh_log($array) {
|
|
debugging('refresh_log() is deprecated, The transfer of logs through mnet are no longer recorded.', DEBUG_DEVELOPER);
|
|
return array('code' => 0, 'message' => 'All ok');
|
|
}
|
|
|
|
/**
|
|
* Receives an array of usernames from a remote machine and prods their
|
|
* sessions to keep them alive
|
|
*
|
|
* @param array $array An array of usernames
|
|
* @return string "All ok" or an error message
|
|
*/
|
|
function keepalive_server($array) {
|
|
global $CFG, $DB;
|
|
$remoteclient = get_mnet_remote_client();
|
|
|
|
// We don't want to output anything to the client machine
|
|
$start = ob_start();
|
|
|
|
// We'll get session records in batches of 30
|
|
$superArray = array_chunk($array, 30);
|
|
|
|
$returnString = '';
|
|
|
|
foreach($superArray as $subArray) {
|
|
$subArray = array_values($subArray);
|
|
$instring = "('".implode("', '",$subArray)."')";
|
|
$query = "select id, session_id, username from {mnet_session} where username in $instring";
|
|
$results = $DB->get_records_sql($query);
|
|
|
|
if ($results == false) {
|
|
// We seem to have a username that breaks our query:
|
|
// TODO: Handle this error appropriately
|
|
$returnString .= "We failed to refresh the session for the following usernames: \n".implode("\n", $subArray)."\n\n";
|
|
} else {
|
|
foreach($results as $emigrant) {
|
|
\core\session\manager::touch_session($emigrant->session_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
$end = ob_end_clean();
|
|
|
|
if (empty($returnString)) return array('code' => 0, 'message' => 'All ok', 'last log id' => $remoteclient->last_log_id);
|
|
return array('code' => 1, 'message' => $returnString, 'last log id' => $remoteclient->last_log_id);
|
|
}
|
|
|
|
/**
|
|
* Cleanup any remote mnet_sessions, kill the local mnet_session data
|
|
*
|
|
* This is called by require_logout in moodlelib
|
|
*
|
|
* @return void
|
|
*/
|
|
function prelogout_hook() {
|
|
global $CFG, $USER;
|
|
|
|
if (!is_enabled_auth('mnet')) {
|
|
return;
|
|
}
|
|
|
|
// If the user is local to this Moodle:
|
|
if ($USER->mnethostid == $this->mnet->id) {
|
|
$this->kill_children($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
|
|
|
|
// Else the user has hit 'logout' at a Service Provider Moodle:
|
|
} else {
|
|
$this->kill_parent($USER->username, sha1($_SERVER['HTTP_USER_AGENT']));
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The SP uses this function to kill the session on the parent IdP
|
|
*
|
|
* @param string $username Username for session to kill
|
|
* @param string $useragent SHA1 hash of user agent to look for
|
|
* @return string A plaintext report of what has happened
|
|
*/
|
|
function kill_parent($username, $useragent) {
|
|
global $CFG, $USER, $DB;
|
|
|
|
require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
|
|
$sql = "
|
|
select
|
|
*
|
|
from
|
|
{mnet_session} s
|
|
where
|
|
s.username = ? AND
|
|
s.useragent = ? AND
|
|
s.mnethostid = ?";
|
|
|
|
$mnetsessions = $DB->get_records_sql($sql, array($username, $useragent, $USER->mnethostid));
|
|
|
|
$ignore = $DB->delete_records('mnet_session',
|
|
array('username'=>$username,
|
|
'useragent'=>$useragent,
|
|
'mnethostid'=>$USER->mnethostid));
|
|
|
|
if (false != $mnetsessions) {
|
|
$mnet_peer = new mnet_peer();
|
|
$mnet_peer->set_id($USER->mnethostid);
|
|
|
|
$mnet_request = new mnet_xmlrpc_client();
|
|
$mnet_request->set_method('auth/mnet/auth.php/kill_children');
|
|
|
|
// set $token and $useragent parameters
|
|
$mnet_request->add_param($username);
|
|
$mnet_request->add_param($useragent);
|
|
if ($mnet_request->send($mnet_peer) === false) {
|
|
debugging(join("\n", $mnet_request->error));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The IdP uses this function to kill child sessions on other hosts
|
|
*
|
|
* @param string $username Username for session to kill
|
|
* @param string $useragent SHA1 hash of user agent to look for
|
|
* @return string A plaintext report of what has happened
|
|
*/
|
|
function kill_children($username, $useragent) {
|
|
global $CFG, $USER, $DB;
|
|
$remoteclient = null;
|
|
if (defined('MNET_SERVER')) {
|
|
$remoteclient = get_mnet_remote_client();
|
|
}
|
|
require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
|
|
|
|
$userid = $DB->get_field('user', 'id', array('mnethostid'=>$CFG->mnet_localhost_id, 'username'=>$username));
|
|
|
|
$returnstring = '';
|
|
|
|
$mnetsessions = $DB->get_records('mnet_session', array('userid' => $userid, 'useragent' => $useragent));
|
|
|
|
if (false == $mnetsessions) {
|
|
$returnstring .= "Could find no remote sessions\n";
|
|
$mnetsessions = array();
|
|
}
|
|
|
|
foreach($mnetsessions as $mnetsession) {
|
|
// If this script is being executed by a remote peer, that means the user has clicked
|
|
// logout on that peer, and the session on that peer can be deleted natively.
|
|
// Skip over it.
|
|
if (isset($remoteclient->id) && ($mnetsession->mnethostid == $remoteclient->id)) {
|
|
continue;
|
|
}
|
|
$returnstring .= "Deleting session\n";
|
|
|
|
$mnet_peer = new mnet_peer();
|
|
$mnet_peer->set_id($mnetsession->mnethostid);
|
|
|
|
$mnet_request = new mnet_xmlrpc_client();
|
|
$mnet_request->set_method('auth/mnet/auth.php/kill_child');
|
|
|
|
// set $token and $useragent parameters
|
|
$mnet_request->add_param($username);
|
|
$mnet_request->add_param($useragent);
|
|
if ($mnet_request->send($mnet_peer) === false) {
|
|
debugging("Server side error has occured on host $mnetsession->mnethostid: " .
|
|
join("\n", $mnet_request->error));
|
|
}
|
|
}
|
|
|
|
$ignore = $DB->delete_records('mnet_session',
|
|
array('useragent'=>$useragent, 'userid'=>$userid));
|
|
|
|
if (isset($remoteclient) && isset($remoteclient->id)) {
|
|
\core\session\manager::kill_user_sessions($userid);
|
|
}
|
|
return $returnstring;
|
|
}
|
|
|
|
/**
|
|
* When the IdP requests that child sessions are terminated,
|
|
* this function will be called on each of the child hosts. The machine that
|
|
* calls the function (over xmlrpc) provides us with the mnethostid we need.
|
|
*
|
|
* @param string $username Username for session to kill
|
|
* @param string $useragent SHA1 hash of user agent to look for
|
|
* @return bool True on success
|
|
*/
|
|
function kill_child($username, $useragent) {
|
|
global $CFG, $DB;
|
|
$remoteclient = get_mnet_remote_client();
|
|
$session = $DB->get_record('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
|
|
$DB->delete_records('mnet_session', array('username'=>$username, 'mnethostid'=>$remoteclient->id, 'useragent'=>$useragent));
|
|
if (false != $session) {
|
|
\core\session\manager::kill_session($session->session_id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* To delete a host, we must delete all current sessions that users from
|
|
* that host are currently engaged in.
|
|
*
|
|
* @param string $sessionidarray An array of session hashes
|
|
* @return bool True on success
|
|
*/
|
|
function end_local_sessions(&$sessionArray) {
|
|
global $CFG;
|
|
if (is_array($sessionArray)) {
|
|
while($session = array_pop($sessionArray)) {
|
|
\core\session\manager::kill_session($session->session_id);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the user's profile image info
|
|
*
|
|
* If the user exists and has a profile picture, the returned array will contain keys:
|
|
* f1 - the content of the default 100x100px image
|
|
* f1_mimetype - the mimetype of the f1 file
|
|
* f2 - the content of the 35x35px variant of the image
|
|
* f2_mimetype - the mimetype of the f2 file
|
|
*
|
|
* The mimetype information was added in Moodle 2.0. In Moodle 1.x, images are always jpegs.
|
|
*
|
|
* @see process_new_icon()
|
|
* @uses mnet_remote_client callable via MNet XML-RPC
|
|
* @param int $username The id of the user
|
|
* @return false|array false if user not found, empty array if no picture exists, array with data otherwise
|
|
*/
|
|
function fetch_user_image($username) {
|
|
global $CFG, $DB;
|
|
|
|
if ($user = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id))) {
|
|
$fs = get_file_storage();
|
|
$usercontext = context_user::instance($user->id, MUST_EXIST);
|
|
$return = array();
|
|
if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.png')) {
|
|
$return['f1'] = base64_encode($f1->get_content());
|
|
$return['f1_mimetype'] = $f1->get_mimetype();
|
|
} else if ($f1 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f1.jpg')) {
|
|
$return['f1'] = base64_encode($f1->get_content());
|
|
$return['f1_mimetype'] = $f1->get_mimetype();
|
|
}
|
|
if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.png')) {
|
|
$return['f2'] = base64_encode($f2->get_content());
|
|
$return['f2_mimetype'] = $f2->get_mimetype();
|
|
} else if ($f2 = $fs->get_file($usercontext->id, 'user', 'icon', 0, '/', 'f2.jpg')) {
|
|
$return['f2'] = base64_encode($f2->get_content());
|
|
$return['f2_mimetype'] = $f2->get_mimetype();
|
|
}
|
|
return $return;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the theme information and logo url as strings.
|
|
*
|
|
* @return string The theme info
|
|
*/
|
|
function fetch_theme_info() {
|
|
global $CFG;
|
|
|
|
$themename = "$CFG->theme";
|
|
$logourl = "$CFG->wwwroot/theme/$CFG->theme/images/logo.jpg";
|
|
|
|
$return['themename'] = $themename;
|
|
$return['logourl'] = $logourl;
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Determines if an MNET host is providing the nominated service.
|
|
*
|
|
* @param int $mnethostid The id of the remote host
|
|
* @param string $servicename The name of the service
|
|
* @return bool Whether the service is available on the remote host
|
|
*/
|
|
function has_service($mnethostid, $servicename) {
|
|
global $CFG, $DB;
|
|
|
|
$sql = "
|
|
SELECT
|
|
svc.id as serviceid,
|
|
svc.name,
|
|
svc.description,
|
|
svc.offer,
|
|
svc.apiversion,
|
|
h2s.id as h2s_id
|
|
FROM
|
|
{mnet_host} h,
|
|
{mnet_service} svc,
|
|
{mnet_host2service} h2s
|
|
WHERE
|
|
h.deleted = '0' AND
|
|
h.id = h2s.hostid AND
|
|
h2s.hostid = ? AND
|
|
h2s.serviceid = svc.id AND
|
|
svc.name = ? AND
|
|
h2s.subscribe = '1'";
|
|
|
|
return $DB->get_records_sql($sql, array($mnethostid, $servicename));
|
|
}
|
|
|
|
/**
|
|
* Checks the MNET access control table to see if the username/mnethost
|
|
* is permitted to login to this moodle.
|
|
*
|
|
* @param string $username The username
|
|
* @param int $mnethostid The id of the remote mnethost
|
|
* @return bool Whether the user can login from the remote host
|
|
*/
|
|
function can_login_remotely($username, $mnethostid) {
|
|
global $DB;
|
|
|
|
$accessctrl = 'allow';
|
|
$aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnethostid));
|
|
if (!empty($aclrecord)) {
|
|
$accessctrl = $aclrecord->accessctrl;
|
|
}
|
|
return $accessctrl == 'allow';
|
|
}
|
|
|
|
function logoutpage_hook() {
|
|
global $USER, $CFG, $redirect, $DB;
|
|
|
|
if (!empty($USER->mnethostid) and $USER->mnethostid != $CFG->mnet_localhost_id) {
|
|
$host = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid));
|
|
$redirect = $host->wwwroot.'/';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trims a log line from mnet peer to limit each part to a length which can be stored in our DB
|
|
*
|
|
* @param object $logline The log information to be trimmed
|
|
* @return object The passed logline object trimmed to not exceed storable limits
|
|
*/
|
|
function trim_logline ($logline) {
|
|
$limits = array('ip' => 15, 'coursename' => 40, 'module' => 20, 'action' => 40,
|
|
'url' => 255);
|
|
foreach ($limits as $property => $limit) {
|
|
if (isset($logline->$property)) {
|
|
$logline->$property = substr($logline->$property, 0, $limit);
|
|
}
|
|
}
|
|
|
|
return $logline;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of MNet IdPs that the user can roam from.
|
|
*
|
|
* @param string $wantsurl The relative url fragment the user wants to get to.
|
|
* @return array List of arrays with keys url, icon and name.
|
|
*/
|
|
function loginpage_idp_list($wantsurl) {
|
|
global $DB, $CFG;
|
|
|
|
// strip off wwwroot, since the remote site will prefix it's return url with this
|
|
$wantsurl = preg_replace('/(' . preg_quote($CFG->wwwroot, '/') . ')/', '', $wantsurl);
|
|
|
|
$sql = "SELECT DISTINCT h.id, h.wwwroot, h.name, a.sso_jump_url, a.name as application
|
|
FROM {mnet_host} h
|
|
JOIN {mnet_host2service} m ON h.id = m.hostid
|
|
JOIN {mnet_service} s ON s.id = m.serviceid
|
|
JOIN {mnet_application} a ON h.applicationid = a.id
|
|
WHERE s.name = ? AND h.deleted = ? AND m.publish = ?";
|
|
$params = array('sso_sp', 0, 1);
|
|
|
|
if (!empty($CFG->mnet_all_hosts_id)) {
|
|
$sql .= " AND h.id <> ?";
|
|
$params[] = $CFG->mnet_all_hosts_id;
|
|
}
|
|
|
|
if (!$hosts = $DB->get_records_sql($sql, $params)) {
|
|
return array();
|
|
}
|
|
|
|
$idps = array();
|
|
foreach ($hosts as $host) {
|
|
$idps[] = array(
|
|
'url' => new moodle_url($host->wwwroot . $host->sso_jump_url, array('hostwwwroot' => $CFG->wwwroot, 'wantsurl' => $wantsurl, 'remoteurl' => 1)),
|
|
'icon' => new pix_icon('i/' . $host->application . '_host', $host->name),
|
|
'name' => $host->name,
|
|
);
|
|
}
|
|
return $idps;
|
|
}
|
|
|
|
/**
|
|
* Test if settings are correct, print info to output.
|
|
*/
|
|
public function test_settings() {
|
|
global $CFG, $OUTPUT, $DB;
|
|
|
|
// Generate warning if MNET is disabled.
|
|
if (empty($CFG->mnet_dispatcher_mode) || $CFG->mnet_dispatcher_mode !== 'strict') {
|
|
echo $OUTPUT->notification(get_string('mnetdisabled', 'mnet'), 'notifyproblem');
|
|
return;
|
|
}
|
|
|
|
// Generate full list of ID and service providers.
|
|
$query = "
|
|
SELECT
|
|
h.id,
|
|
h.name as hostname,
|
|
h.wwwroot,
|
|
h2idp.publish as idppublish,
|
|
h2idp.subscribe as idpsubscribe,
|
|
idp.name as idpname,
|
|
h2sp.publish as sppublish,
|
|
h2sp.subscribe as spsubscribe,
|
|
sp.name as spname
|
|
FROM
|
|
{mnet_host} h
|
|
LEFT JOIN
|
|
{mnet_host2service} h2idp
|
|
ON
|
|
(h.id = h2idp.hostid AND
|
|
(h2idp.publish = 1 OR
|
|
h2idp.subscribe = 1))
|
|
INNER JOIN
|
|
{mnet_service} idp
|
|
ON
|
|
(h2idp.serviceid = idp.id AND
|
|
idp.name = 'sso_idp')
|
|
LEFT JOIN
|
|
{mnet_host2service} h2sp
|
|
ON
|
|
(h.id = h2sp.hostid AND
|
|
(h2sp.publish = 1 OR
|
|
h2sp.subscribe = 1))
|
|
INNER JOIN
|
|
{mnet_service} sp
|
|
ON
|
|
(h2sp.serviceid = sp.id AND
|
|
sp.name = 'sso_sp')
|
|
WHERE
|
|
((h2idp.publish = 1 AND h2sp.subscribe = 1) OR
|
|
(h2sp.publish = 1 AND h2idp.subscribe = 1)) AND
|
|
h.id != ?
|
|
ORDER BY
|
|
h.name ASC";
|
|
|
|
$idproviders = array();
|
|
$serviceproviders = array();
|
|
if ($resultset = $DB->get_records_sql($query, array($CFG->mnet_localhost_id))) {
|
|
foreach ($resultset as $hostservice) {
|
|
if (!empty($hostservice->idppublish) && !empty($hostservice->spsubscribe)) {
|
|
$serviceproviders[] = array('id' => $hostservice->id,
|
|
'name' => $hostservice->hostname,
|
|
'wwwroot' => $hostservice->wwwroot);
|
|
}
|
|
if (!empty($hostservice->idpsubscribe) && !empty($hostservice->sppublish)) {
|
|
$idproviders[] = array('id' => $hostservice->id,
|
|
'name' => $hostservice->hostname,
|
|
'wwwroot' => $hostservice->wwwroot);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ID Providers.
|
|
$table = html_writer::start_tag('table', array('class' => 'generaltable'));
|
|
|
|
$count = 0;
|
|
foreach ($idproviders as $host) {
|
|
$table .= html_writer::start_tag('tr');
|
|
$table .= html_writer::start_tag('td');
|
|
$table .= $host['name'];
|
|
$table .= html_writer::end_tag('td');
|
|
$table .= html_writer::start_tag('td');
|
|
$table .= $host['wwwroot'];
|
|
$table .= html_writer::end_tag('td');
|
|
$table .= html_writer::end_tag('tr');
|
|
$count++;
|
|
}
|
|
$table .= html_writer::end_tag('table');
|
|
|
|
if ($count > 0) {
|
|
echo html_writer::tag('h3', get_string('auth_mnet_roamin', 'auth_mnet'));
|
|
echo $table;
|
|
}
|
|
|
|
// Service Providers.
|
|
unset($table);
|
|
$table = html_writer::start_tag('table', array('class' => 'generaltable'));
|
|
$count = 0;
|
|
foreach ($serviceproviders as $host) {
|
|
$table .= html_writer::start_tag('tr');
|
|
$table .= html_writer::start_tag('td');
|
|
$table .= $host['name'];
|
|
$table .= html_writer::end_tag('td');
|
|
$table .= html_writer::start_tag('td');
|
|
$table .= $host['wwwroot'];
|
|
$table .= html_writer::end_tag('td');
|
|
$table .= html_writer::end_tag('tr');
|
|
$count++;
|
|
}
|
|
$table .= html_writer::end_tag('table');
|
|
if ($count > 0) {
|
|
echo html_writer::tag('h3', get_string('auth_mnet_roamout', 'auth_mnet'));
|
|
echo $table;
|
|
}
|
|
}
|
|
}
|
|
|