6817efbc |
1 | <?php // $Id$ |
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 |
15 | if (!defined('MOODLE_INTERNAL')) { |
16 | die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page |
c72fe801 |
17 | } |
18 | |
6bc1e5d5 |
19 | require_once($CFG->libdir.'/authlib.php'); |
20 | |
c72fe801 |
21 | /** |
22 | * Moodle Network authentication plugin. |
23 | */ |
6bc1e5d5 |
24 | class auth_plugin_mnet extends auth_plugin_base { |
c72fe801 |
25 | |
26 | /** |
27 | * Constructor. |
28 | */ |
29 | function auth_plugin_mnet() { |
6bc1e5d5 |
30 | $this->authtype = 'mnet'; |
c72fe801 |
31 | $this->config = get_config('auth/mnet'); |
32 | } |
33 | |
34 | /** |
35 | * Provides the allowed RPC services from this class as an array. |
36 | * @return array Allowed RPC services. |
37 | */ |
38 | function mnet_publishes() { |
39 | |
40 | $sso_idp = array(); |
41 | $sso_idp['name'] = 'sso_idp'; // Name & Description go in lang file |
42 | $sso_idp['apiversion'] = 1; |
139ebfdb |
43 | $sso_idp['methods'] = array('user_authorise','keepalive_server', 'kill_children', |
44 | 'refresh_log', 'fetch_user_image', 'fetch_theme_info', |
62d78bf5 |
45 | 'update_enrolments'); |
c72fe801 |
46 | |
47 | $sso_sp = array(); |
48 | $sso_sp['name'] = 'sso_sp'; // Name & Description go in lang file |
49 | $sso_sp['apiversion'] = 1; |
0a127169 |
50 | $sso_sp['methods'] = array('keepalive_client','kill_child'); |
c72fe801 |
51 | |
52 | return array($sso_idp, $sso_sp); |
53 | } |
54 | |
55 | /** |
56 | * This function is normally used to determine if the username and password |
57 | * are correct for local logins. Always returns false, as local users do not |
58 | * need to login over mnet xmlrpc. |
59 | * |
60 | * @param string $username The username |
61 | * @param string $password The password |
139ebfdb |
62 | * @return bool Authentication success or failure. |
c72fe801 |
63 | */ |
64 | function user_login($username, $password) { |
3db241b3 |
65 | return false; // print_error("mnetlocal"); |
c72fe801 |
66 | } |
67 | |
68 | /** |
69 | * Return user data for the provided token, compare with user_agent string. |
70 | * |
71 | * @param string $token The unique ID provided by remotehost. |
72 | * @param string $UA User Agent string. |
73 | * @return array $userdata Array of user info for remote host |
74 | */ |
75 | function user_authorise($token, $useragent) { |
7b0d12b2 |
76 | global $CFG, $MNET, $SITE, $MNET_REMOTE_CLIENT, $DB; |
c72fe801 |
77 | require_once $CFG->dirroot . '/mnet/xmlrpc/server.php'; |
78 | |
7b0d12b2 |
79 | $mnet_session = $DB->get_record('mnet_session', array('token'=>$token, 'useragent'=>$useragent)); |
c72fe801 |
80 | if (empty($mnet_session)) { |
81 | echo mnet_server_fault(1, get_string('authfail_nosessionexists', 'mnet')); |
82 | exit; |
83 | } |
84 | |
85 | // check session confirm timeout |
86 | if ($mnet_session->confirm_timeout < time()) { |
87 | echo mnet_server_fault(2, get_string('authfail_sessiontimedout', 'mnet')); |
88 | exit; |
89 | } |
90 | |
91 | // session okay, try getting the user |
7b0d12b2 |
92 | if (!$user = $DB->get_record('user', array('id'=>$mnet_session->userid))) { |
c72fe801 |
93 | echo mnet_server_fault(3, get_string('authfail_usermismatch', 'mnet')); |
94 | exit; |
95 | } |
96 | |
97 | $userdata = array(); |
98 | $userdata['username'] = $user->username; |
99 | $userdata['email'] = $user->email; |
100 | $userdata['auth'] = 'mnet'; |
101 | $userdata['confirmed'] = $user->confirmed; |
102 | $userdata['deleted'] = $user->deleted; |
103 | $userdata['firstname'] = $user->firstname; |
104 | $userdata['lastname'] = $user->lastname; |
105 | $userdata['city'] = $user->city; |
106 | $userdata['country'] = $user->country; |
107 | $userdata['lang'] = $user->lang; |
108 | $userdata['timezone'] = $user->timezone; |
109 | $userdata['description'] = $user->description; |
110 | $userdata['mailformat'] = $user->mailformat; |
111 | $userdata['maildigest'] = $user->maildigest; |
112 | $userdata['maildisplay'] = $user->maildisplay; |
113 | $userdata['htmleditor'] = $user->htmleditor; |
114 | $userdata['wwwroot'] = $MNET->wwwroot; |
115 | $userdata['session.gc_maxlifetime'] = ini_get('session.gc_maxlifetime'); |
116 | $userdata['picture'] = $user->picture; |
117 | if (!empty($user->picture)) { |
56a1a882 |
118 | $imagefile = make_user_directory($user->id, true) . "/f1.jpg"; |
c72fe801 |
119 | if (file_exists($imagefile)) { |
120 | $userdata['imagehash'] = sha1(file_get_contents($imagefile)); |
121 | } |
122 | } |
4d8c087e |
123 | |
124 | $userdata['myhosts'] = array(); |
125 | if($courses = get_my_courses($user->id, 'id', 'id, visible')) { |
126 | $userdata['myhosts'][] = array('name'=> $SITE->shortname, 'url' => $CFG->wwwroot, 'count' => count($courses)); |
127 | } |
128 | |
129 | $sql = " |
130 | SELECT |
131 | h.name as hostname, |
132 | h.wwwroot, |
133 | h.id as hostid, |
134 | count(c.id) as count |
139ebfdb |
135 | FROM |
7b0d12b2 |
136 | {mnet_enrol_course} c, |
137 | {mnet_enrol_assignments} a, |
138 | {mnet_host} h |
4d8c087e |
139 | WHERE |
140 | c.id = a.courseid AND |
141 | c.hostid = h.id AND |
7b0d12b2 |
142 | a.userid = ? AND |
143 | c.hostid != ? |
4d8c087e |
144 | GROUP BY |
145 | h.name, |
146 | h.id, |
147 | h.wwwroot"; |
7b0d12b2 |
148 | if ($courses = $DB->get_records_sql($sql, array($user->id, $MNET_REMOTE_CLIENT->id))) { |
4d8c087e |
149 | foreach($courses as $course) { |
150 | $userdata['myhosts'][] = array('name'=> $course->hostname, 'url' => $CFG->wwwroot.'/auth/mnet/jump.php?hostid='.$course->hostid, 'count' => $course->count); |
151 | } |
152 | } |
153 | |
c72fe801 |
154 | return $userdata; |
155 | } |
156 | |
157 | /** |
158 | * Generate a random string for use as an RPC session token. |
159 | */ |
160 | function generate_token() { |
161 | return sha1(str_shuffle('' . mt_rand() . time())); |
162 | } |
163 | |
164 | /** |
165 | * Starts an RPC jump session and returns the jump redirect URL. |
d9be2106 |
166 | * |
167 | * @param int $mnethostid id of the mnet host to jump to |
168 | * @param string $wantsurl url to redirect to after the jump (usually on remote system) |
169 | * @param boolean $wantsurlbackhere defaults to false, means that the remote system should bounce us back here |
170 | * rather than somewhere inside *its* wwwroot |
c72fe801 |
171 | */ |
d9be2106 |
172 | function start_jump_session($mnethostid, $wantsurl, $wantsurlbackhere=false) { |
7b0d12b2 |
173 | global $CFG, $USER, $MNET, $DB; |
c72fe801 |
174 | require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; |
175 | |
176 | // check remote login permissions |
957f6fc9 |
177 | if (! has_capability('moodle/site:mnetlogintoremote', get_context_instance(CONTEXT_SYSTEM)) |
c72fe801 |
178 | or is_mnet_remote_user($USER) |
179 | or $USER->username == 'guest' |
180 | or empty($USER->id)) { |
5a2a5331 |
181 | print_error('notpermittedtojump', 'mnet'); |
c72fe801 |
182 | } |
183 | |
184 | // check for SSO publish permission first |
185 | if ($this->has_service($mnethostid, 'sso_sp') == false) { |
5a2a5331 |
186 | print_error('hostnotconfiguredforsso', 'mnet'); |
c72fe801 |
187 | } |
188 | |
189 | // set RPC timeout to 30 seconds if not configured |
190 | // TODO: Is this needed/useful/problematic? |
191 | if (empty($this->config->rpc_negotiation_timeout)) { |
192 | set_config('rpc_negotiation_timeout', '30', 'auth/mnet'); |
193 | } |
194 | |
195 | // get the host info |
196 | $mnet_peer = new mnet_peer(); |
197 | $mnet_peer->set_id($mnethostid); |
198 | |
199 | // set up the session |
7b0d12b2 |
200 | $mnet_session = $DB->get_record('mnet_session', |
201 | array('userid'=>$USER->id, 'mnethostid'=>$mnethostid, |
202 | 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT']))); |
c72fe801 |
203 | if ($mnet_session == false) { |
204 | $mnet_session = new object(); |
205 | $mnet_session->mnethostid = $mnethostid; |
206 | $mnet_session->userid = $USER->id; |
207 | $mnet_session->username = $USER->username; |
208 | $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); |
209 | $mnet_session->token = $this->generate_token(); |
210 | $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout; |
211 | $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime'); |
212 | $mnet_session->session_id = session_id(); |
979425b5 |
213 | $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session); |
c72fe801 |
214 | } else { |
215 | $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); |
216 | $mnet_session->token = $this->generate_token(); |
217 | $mnet_session->confirm_timeout = time() + $this->config->rpc_negotiation_timeout; |
218 | $mnet_session->expires = time() + (integer)ini_get('session.gc_maxlifetime'); |
219 | $mnet_session->session_id = session_id(); |
979425b5 |
220 | $DB->update_record('mnet_session', $mnet_session); |
c72fe801 |
221 | } |
222 | |
223 | // construct the redirection URL |
224 | //$transport = mnet_get_protocol($mnet_peer->transport); |
225 | $wantsurl = urlencode($wantsurl); |
25202581 |
226 | $url = "{$mnet_peer->wwwroot}{$mnet_peer->application->sso_land_url}?token={$mnet_session->token}&idp={$MNET->wwwroot}&wantsurl={$wantsurl}"; |
d9be2106 |
227 | if ($wantsurlbackhere) { |
228 | $url .= '&remoteurl=1'; |
229 | } |
c72fe801 |
230 | |
231 | return $url; |
232 | } |
233 | |
234 | /** |
235 | * This function confirms the remote (ID provider) host's mnet session |
236 | * by communicating the token and UA over the XMLRPC transport layer, and |
237 | * returns the local user record on success. |
238 | * |
239 | * @param string $token The random session token. |
240 | * @param string $remotewwwroot The ID provider wwwroot. |
139ebfdb |
241 | * @return array The local user record. |
c72fe801 |
242 | */ |
243 | function confirm_mnet_session($token, $remotewwwroot) { |
a5d424df |
244 | global $CFG, $MNET, $SESSION, $DB; |
c72fe801 |
245 | require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; |
246 | |
247 | // verify the remote host is configured locally before attempting RPC call |
dbca4e44 |
248 | if (! $remotehost = $DB->get_record('mnet_host', array('wwwroot' => $remotewwwroot, 'deleted' => 0))) { |
5a2a5331 |
249 | print_error('notpermittedtoland', 'mnet'); |
c72fe801 |
250 | } |
251 | |
252 | // get the originating (ID provider) host info |
253 | $remotepeer = new mnet_peer(); |
254 | $remotepeer->set_wwwroot($remotewwwroot); |
255 | |
256 | // set up the RPC request |
257 | $mnetrequest = new mnet_xmlrpc_client(); |
258 | $mnetrequest->set_method('auth/mnet/auth.php/user_authorise'); |
259 | |
260 | // set $token and $useragent parameters |
261 | $mnetrequest->add_param($token); |
262 | $mnetrequest->add_param(sha1($_SERVER['HTTP_USER_AGENT'])); |
263 | |
264 | // Thunderbirds are go! Do RPC call and store response |
265 | if ($mnetrequest->send($remotepeer) === true) { |
266 | $remoteuser = (object) $mnetrequest->response; |
267 | } else { |
016bac47 |
268 | foreach ($mnetrequest->error as $errormessage) { |
269 | list($code, $message) = array_map('trim',explode(':', $errormessage, 2)); |
270 | if($code == 702) { |
271 | $site = get_site(); |
5a2a5331 |
272 | print_error('mnet_session_prohibited', 'mnet', $remotewwwroot, format_string($site->fullname)); |
016bac47 |
273 | exit; |
274 | } |
71502268 |
275 | $message .= "ERROR $code:<br/>$errormessage<br/>"; |
c72fe801 |
276 | } |
e2490433 |
277 | print_error("rpcerror", '', '', $message); |
c72fe801 |
278 | } |
15e47723 |
279 | unset($mnetrequest); |
c72fe801 |
280 | |
281 | if (empty($remoteuser) or empty($remoteuser->username)) { |
016bac47 |
282 | print_error('unknownerror', 'mnet'); |
283 | exit; |
c72fe801 |
284 | } |
285 | |
15e47723 |
286 | $firsttime = false; |
287 | |
c72fe801 |
288 | // get the local record for the remote user |
7b0d12b2 |
289 | $localuser = $DB->get_record('user', array('username'=>$remoteuser->username, 'mnethostid'=>$remotehost->id)); |
c72fe801 |
290 | |
291 | // add the remote user to the database if necessary, and if allowed |
292 | // TODO: refactor into a separate function |
32d651c8 |
293 | if (empty($localuser) || ! $localuser->id) { |
c72fe801 |
294 | if (empty($this->config->auto_add_remote_users)) { |
5a2a5331 |
295 | print_error('nolocaluser', 'mnet'); |
c72fe801 |
296 | } |
297 | $remoteuser->mnethostid = $remotehost->id; |
979425b5 |
298 | $DB->insert_record('user', $remoteuser); |
15e47723 |
299 | $firsttime = true; |
7b0d12b2 |
300 | if (! $localuser = $DB->get_record('user', array('username'=>$remoteuser->username, 'mnethostid'=>$remotehost->id))) { |
5a2a5331 |
301 | print_error('nolocaluser', 'mnet'); |
c72fe801 |
302 | } |
303 | } |
304 | |
305 | // check sso access control list for permission first |
306 | if (!$this->can_login_remotely($localuser->username, $remotehost->id)) { |
573f8b02 |
307 | print_error('sso_mnet_login_refused', 'mnet', '', array($localuser->username, $remotehost->name)); |
c72fe801 |
308 | } |
309 | |
310 | $session_gc_maxlifetime = 1440; |
311 | |
312 | // update the local user record with remote user data |
313 | foreach ((array) $remoteuser as $key => $val) { |
314 | if ($key == 'session.gc_maxlifetime') { |
315 | $session_gc_maxlifetime = $val; |
316 | continue; |
317 | } |
318 | |
319 | // TODO: fetch image if it has changed |
320 | if ($key == 'imagehash') { |
56a1a882 |
321 | $dirname = make_user_directory($localuser->id, true); |
c72fe801 |
322 | $filename = "$dirname/f1.jpg"; |
323 | |
324 | $localhash = ''; |
325 | if (file_exists($filename)) { |
326 | $localhash = sha1(file_get_contents($filename)); |
327 | } elseif (!file_exists($dirname)) { |
328 | mkdir($dirname); |
329 | } |
330 | |
331 | if ($localhash != $val) { |
332 | // fetch image from remote host |
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) { |
338 | $imagecontents = base64_decode($fetchrequest->response['f1']); |
339 | file_put_contents($filename, $imagecontents); |
25202581 |
340 | $localuser->picture = 1; |
c72fe801 |
341 | } |
342 | if (strlen($fetchrequest->response['f2']) > 0) { |
343 | $imagecontents = base64_decode($fetchrequest->response['f2']); |
344 | file_put_contents($dirname.'/f2.jpg', $imagecontents); |
345 | } |
346 | } |
347 | } |
348 | } |
349 | |
4d8c087e |
350 | if($key == 'myhosts') { |
0743661e |
351 | $localuser->mnet_foreign_host_array = array(); |
15e47723 |
352 | foreach($val as $rhost) { |
353 | $name = clean_param($rhost['name'], PARAM_ALPHANUM); |
354 | $url = clean_param($rhost['url'], PARAM_URL); |
355 | $count = clean_param($rhost['count'], PARAM_INT); |
4d8c087e |
356 | $url_is_local = stristr($url , $CFG->wwwroot); |
357 | if (!empty($name) && !empty($count) && empty($url_is_local)) { |
139ebfdb |
358 | $localuser->mnet_foreign_host_array[] = array('name' => $name, |
359 | 'url' => $url, |
0743661e |
360 | 'count' => $count); |
4d8c087e |
361 | } |
362 | } |
363 | } |
364 | |
c72fe801 |
365 | $localuser->{$key} = $val; |
366 | } |
367 | |
368 | $localuser->mnethostid = $remotepeer->id; |
369 | |
a8c31db2 |
370 | $DB->update_record('user', $localuser); |
c72fe801 |
371 | |
372 | // set up the session |
7b0d12b2 |
373 | $mnet_session = $DB->get_record('mnet_session', |
374 | array('userid'=>$localuser->id, 'mnethostid'=>$remotepeer->id, |
375 | 'useragent'=>sha1($_SERVER['HTTP_USER_AGENT']))); |
c72fe801 |
376 | if ($mnet_session == false) { |
377 | $mnet_session = new object(); |
378 | $mnet_session->mnethostid = $remotepeer->id; |
379 | $mnet_session->userid = $localuser->id; |
380 | $mnet_session->username = $localuser->username; |
381 | $mnet_session->useragent = sha1($_SERVER['HTTP_USER_AGENT']); |
4e047727 |
382 | $mnet_session->token = $token; // Needed to support simultaneous sessions |
383 | // and preserving DB rec uniqueness |
c72fe801 |
384 | $mnet_session->confirm_timeout = time(); |
385 | $mnet_session->expires = time() + (integer)$session_gc_maxlifetime; |
386 | $mnet_session->session_id = session_id(); |
979425b5 |
387 | $mnet_session->id = $DB->insert_record('mnet_session', $mnet_session); |
c72fe801 |
388 | } else { |
389 | $mnet_session->expires = time() + (integer)$session_gc_maxlifetime; |
7b0d12b2 |
390 | $DB->update_record('mnet_session', $mnet_session); |
c72fe801 |
391 | } |
392 | |
15e47723 |
393 | if (!$firsttime) { |
394 | // repeat customer! let the IDP know about enrolments |
139ebfdb |
395 | // we have for this user. |
15e47723 |
396 | // set up the RPC request |
397 | $mnetrequest = new mnet_xmlrpc_client(); |
398 | $mnetrequest->set_method('auth/mnet/auth.php/update_enrolments'); |
399 | |
400 | // pass username and an assoc array of "my courses" |
401 | // with info so that the IDP can maintain mnet_enrol_assignments |
402 | $mnetrequest->add_param($remoteuser->username); |
139ebfdb |
403 | $fields = 'id, category, sortorder, fullname, shortname, idnumber, summary, |
15e47723 |
404 | startdate, cost, currency, defaultrole, visible'; |
405 | $courses = get_my_courses($localuser->id, 'visible DESC,sortorder ASC', $fields); |
406 | if (is_array($courses) && !empty($courses)) { |
407 | // Second request to do the JOINs that we'd have done |
408 | // inside get_my_courses() if we had been allowed |
139ebfdb |
409 | $sql = "SELECT c.id, |
15e47723 |
410 | cc.name AS cat_name, cc.description AS cat_description, |
411 | r.shortname as defaultrolename |
7b0d12b2 |
412 | FROM {course} c |
413 | JOIN {course_categories} cc ON c.category = cc.id |
414 | LEFT OUTER JOIN {role} r ON c.defaultrole = r.id |
415 | WHERE c.id IN (" . join(',',array_keys($courses)) . ')'; |
416 | $extra = $DB->get_records_sql($sql); |
15e47723 |
417 | |
418 | $keys = array_keys($courses); |
a5d424df |
419 | $defaultrolename = $DB->get_field('role', 'shortname', array('id'=>$CFG->defaultcourseroleid)); |
15e47723 |
420 | foreach ($keys AS $id) { |
573f8b02 |
421 | if ($courses[$id]->visible == 0) { |
422 | unset($courses[$id]); |
423 | continue; |
424 | } |
560b3acc |
425 | $courses[$id]->cat_id = $courses[$id]->category; |
426 | $courses[$id]->defaultroleid = $courses[$id]->defaultrole; |
573f8b02 |
427 | unset($courses[$id]->category); |
428 | unset($courses[$id]->defaultrole); |
429 | unset($courses[$id]->visible); |
430 | |
15e47723 |
431 | $courses[$id]->cat_name = $extra[$id]->cat_name; |
432 | $courses[$id]->cat_description = $extra[$id]->cat_description; |
433 | if (!empty($extra[$id]->defaultrolename)) { |
434 | $courses[$id]->defaultrolename = $extra[$id]->defaultrolename; |
435 | } else { |
436 | $courses[$id]->defaultrolename = $defaultrolename; |
437 | } |
438 | // coerce to array |
439 | $courses[$id] = (array)$courses[$id]; |
440 | } |
62d78bf5 |
441 | } else { |
442 | // if the array is empty, send it anyway |
443 | // we may be clearing out stale entries |
139ebfdb |
444 | $courses = array(); |
62d78bf5 |
445 | } |
446 | $mnetrequest->add_param($courses); |
15e47723 |
447 | |
62d78bf5 |
448 | // Call 0800-RPC Now! -- we don't care too much if it fails |
449 | // as it's just informational. |
450 | if ($mnetrequest->send($remotepeer) === false) { |
451 | // error_log(print_r($mnetrequest->error,1)); |
15e47723 |
452 | } |
453 | } |
454 | |
c72fe801 |
455 | return $localuser; |
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 | * |
462 | * Normally called by the SP after calling |
463 | * |
464 | * @param string $username The username |
465 | * @param string $courses Assoc array of courses following the structure of mnet_enrol_course |
139ebfdb |
466 | * @return bool |
62d78bf5 |
467 | */ |
468 | function update_enrolments($username, $courses) { |
8618fd2a |
469 | global $MNET_REMOTE_CLIENT, $CFG, $DB; |
62d78bf5 |
470 | |
471 | if (empty($username) || !is_array($courses)) { |
472 | return false; |
473 | } |
474 | // make sure it is a user we have an in active session |
475 | // with that host... |
7b0d12b2 |
476 | $userid = $DB->get_field('mnet_session', 'userid', |
477 | array('username'=>$username, 'mnethostid'=>$MNET_REMOTE_CLIENT->id)); |
62d78bf5 |
478 | if (!$userid) { |
479 | return false; |
480 | } |
481 | |
482 | if (empty($courses)) { // no courses? clear out quickly |
8618fd2a |
483 | $DB->delete_records('mnet_enrol_assignments', array('hostid'=>$MNET_REMOTE_CLIENT->id, 'userid'=>$userid)); |
62d78bf5 |
484 | return true; |
485 | } |
486 | |
573f8b02 |
487 | // IMPORTANT: Ask for remoteid as the first element in the query, so |
488 | // that the array that comes back is indexed on the same field as the |
489 | // array that we have received from the remote client |
490 | $sql = ' |
491 | SELECT |
492 | c.remoteid, |
493 | c.id, |
494 | c.cat_id, |
495 | c.cat_name, |
496 | c.cat_description, |
497 | c.sortorder, |
498 | c.fullname, |
499 | c.shortname, |
500 | c.idnumber, |
501 | c.summary, |
502 | c.startdate, |
503 | c.cost, |
504 | c.currency, |
505 | c.defaultroleid, |
506 | c.defaultrolename, |
507 | a.id as assignmentid |
508 | FROM |
7b0d12b2 |
509 | {mnet_enrol_course} c |
510 | LEFT JOIN {mnet_enrol_assignments} a |
573f8b02 |
511 | ON |
512 | (a.courseid = c.id AND |
513 | a.hostid = c.hostid AND |
7b0d12b2 |
514 | a.userid = ?) |
573f8b02 |
515 | WHERE |
7b0d12b2 |
516 | c.hostid = ?'; |
573f8b02 |
517 | |
7b0d12b2 |
518 | $currentcourses = $DB->get_records_sql($sql, array($userid, $MNET_REMOTE_CLIENT->id)); |
573f8b02 |
519 | |
520 | $local_courseid_array = array(); |
521 | foreach($courses as $course) { |
522 | |
523 | $course['remoteid'] = $course['id']; |
524 | $course['hostid'] = (int)$MNET_REMOTE_CLIENT->id; |
525 | $userisregd = false; |
526 | |
527 | // First up - do we have a record for this course? |
528 | if (!array_key_exists($course['remoteid'], $currentcourses)) { |
529 | // No record - we must create it |
7b0d12b2 |
530 | $course['id'] = $DB->insert_record('mnet_enrol_course', (object)$course); |
573f8b02 |
531 | $currentcourse = (object)$course; |
532 | } else { |
533 | // Pointer to current course: |
534 | $currentcourse =& $currentcourses[$course['remoteid']]; |
535 | // We have a record - is it up-to-date? |
536 | $course['id'] = $currentcourse->id; |
537 | |
538 | $saveflag = false; |
539 | |
540 | foreach($course as $key => $value) { |
541 | if ($currentcourse->$key != $value) { |
542 | $saveflag = true; |
543 | $currentcourse->$key = $value; |
544 | } |
545 | } |
546 | |
547 | if ($saveflag) { |
7b0d12b2 |
548 | $DB->update_record('mnet_enrol_course', $currentcourse); |
573f8b02 |
549 | } |
139ebfdb |
550 | |
573f8b02 |
551 | if (isset($currentcourse->assignmentid) && is_numeric($currentcourse->assignmentid)) { |
552 | $userisregd = true; |
553 | } |
554 | } |
555 | |
556 | // By this point, we should always have a $dataObj->id |
557 | $local_courseid_array[] = $course['id']; |
558 | |
559 | // Do we have a record for this assignment? |
560 | if ($userisregd) { |
561 | // Yes - we know about this one already |
562 | // We don't want to do updates because the new data is probably |
563 | // 'less complete' than the data we have. |
564 | } else { |
565 | // No - create a record |
566 | $assignObj = new stdClass(); |
567 | $assignObj->userid = $userid; |
568 | $assignObj->hostid = (int)$MNET_REMOTE_CLIENT->id; |
569 | $assignObj->courseid = $course['id']; |
570 | $assignObj->rolename = $course['defaultrolename']; |
7b0d12b2 |
571 | $assignObj->id = $DB->insert_record('mnet_enrol_assignments', $assignObj); |
573f8b02 |
572 | } |
573 | } |
62d78bf5 |
574 | |
573f8b02 |
575 | // Clean up courses that the user is no longer enrolled in. |
576 | $local_courseid_string = implode(', ', $local_courseid_array); |
7b0d12b2 |
577 | $whereclause = " userid = ? AND hostid = ? AND courseid NOT IN ($local_courseid_string)"; |
578 | $DB->delete_records_select('mnet_enrol_assignments', $whereclause, array($userid, $MNET_REMOTE_CLIENT->id)); |
62d78bf5 |
579 | } |
580 | |
c72fe801 |
581 | /** |
582 | * Returns true if this authentication plugin is 'internal'. |
583 | * |
139ebfdb |
584 | * @return bool |
c72fe801 |
585 | */ |
586 | function is_internal() { |
587 | return false; |
588 | } |
589 | |
590 | /** |
591 | * Returns true if this authentication plugin can change the user's |
592 | * password. |
593 | * |
139ebfdb |
594 | * @return bool |
c72fe801 |
595 | */ |
596 | function can_change_password() { |
430759a5 |
597 | //TODO: it should be able to redirect, right? |
c72fe801 |
598 | return false; |
599 | } |
600 | |
601 | /** |
602 | * Returns the URL for changing the user's pw, or false if the default can |
603 | * be used. |
604 | * |
430759a5 |
605 | * @return string |
c72fe801 |
606 | */ |
607 | function change_password_url() { |
430759a5 |
608 | return ''; |
c72fe801 |
609 | } |
610 | |
611 | /** |
612 | * Prints a form for configuring this authentication plugin. |
613 | * |
614 | * This function is called from admin/auth.php, and outputs a full page with |
615 | * a form for configuring this plugin. |
616 | * |
617 | * @param array $page An object containing all the data for this page. |
618 | */ |
139ebfdb |
619 | function config_form($config, $err, $user_fields) { |
7b0d12b2 |
620 | global $CFG, $DB; |
14518364 |
621 | |
622 | $query = " |
623 | SELECT |
624 | h.id, |
625 | h.name as hostname, |
626 | h.wwwroot, |
627 | h2idp.publish as idppublish, |
628 | h2idp.subscribe as idpsubscribe, |
629 | idp.name as idpname, |
630 | h2sp.publish as sppublish, |
631 | h2sp.subscribe as spsubscribe, |
632 | sp.name as spname |
633 | FROM |
7b0d12b2 |
634 | {mnet_host} h |
14518364 |
635 | LEFT JOIN |
7b0d12b2 |
636 | {mnet_host2service} h2idp |
14518364 |
637 | ON |
638 | (h.id = h2idp.hostid AND |
639 | (h2idp.publish = 1 OR |
640 | h2idp.subscribe = 1)) |
641 | INNER JOIN |
7b0d12b2 |
642 | {mnet_service} idp |
14518364 |
643 | ON |
644 | (h2idp.serviceid = idp.id AND |
645 | idp.name = 'sso_idp') |
646 | LEFT JOIN |
7b0d12b2 |
647 | {mnet_host2service} h2sp |
14518364 |
648 | ON |
649 | (h.id = h2sp.hostid AND |
650 | (h2sp.publish = 1 OR |
651 | h2sp.subscribe = 1)) |
652 | INNER JOIN |
7b0d12b2 |
653 | {mnet_service} sp |
14518364 |
654 | ON |
655 | (h2sp.serviceid = sp.id AND |
656 | sp.name = 'sso_sp') |
657 | WHERE |
658 | ((h2idp.publish = 1 AND h2sp.subscribe = 1) OR |
659 | (h2sp.publish = 1 AND h2idp.subscribe = 1)) AND |
7b0d12b2 |
660 | h.id != ? |
14518364 |
661 | ORDER BY |
662 | h.name ASC"; |
663 | |
14518364 |
664 | $id_providers = array(); |
665 | $service_providers = array(); |
7b0d12b2 |
666 | if ($resultset = $DB->get_records_sql($query, array($CFG->mnet_localhost_id))) { |
d525ca25 |
667 | foreach($resultset as $hostservice) { |
668 | if(!empty($hostservice->idppublish) && !empty($hostservice->spsubscribe)) { |
669 | $service_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot); |
670 | } |
671 | if(!empty($hostservice->idpsubscribe) && !empty($hostservice->sppublish)) { |
672 | $id_providers[]= array('id' => $hostservice->id, 'name' => $hostservice->hostname, 'wwwroot' => $hostservice->wwwroot); |
673 | } |
14518364 |
674 | } |
675 | } |
139ebfdb |
676 | |
c72fe801 |
677 | include "config.html"; |
678 | } |
679 | |
680 | /** |
681 | * Processes and stores configuration data for this authentication plugin. |
682 | */ |
683 | function process_config($config) { |
684 | // set to defaults if undefined |
685 | if (!isset ($config->rpc_negotiation_timeout)) { |
5671e77f |
686 | $config->rpc_negotiation_timeout = '30'; |
c72fe801 |
687 | } |
688 | if (!isset ($config->auto_add_remote_users)) { |
689 | $config->auto_add_remote_users = '0'; |
690 | } |
691 | |
692 | // save settings |
693 | set_config('rpc_negotiation_timeout', $config->rpc_negotiation_timeout, 'auth/mnet'); |
694 | set_config('auto_add_remote_users', $config->auto_add_remote_users, 'auth/mnet'); |
695 | |
696 | return true; |
697 | } |
698 | |
699 | /** |
700 | * Poll the IdP server to let it know that a user it has authenticated is still |
701 | * online |
702 | * |
703 | * @return void |
704 | */ |
705 | function keepalive_client() { |
7b0d12b2 |
706 | global $CFG, $MNET, $DB; |
c72fe801 |
707 | $cutoff = time() - 300; // TODO - find out what the remote server's session |
708 | // cutoff is, and preempt that |
709 | |
710 | $sql = " |
711 | select |
712 | id, |
713 | username, |
714 | mnethostid |
715 | from |
7b0d12b2 |
716 | {user} |
c72fe801 |
717 | where |
7b0d12b2 |
718 | lastaccess > ? AND |
719 | mnethostid != ? |
c72fe801 |
720 | order by |
721 | mnethostid"; |
722 | |
7b0d12b2 |
723 | $immigrants = $DB->get_records_sql($sql, array($cutoff, $CFG->mnet_localhost_id)); |
c72fe801 |
724 | |
725 | if ($immigrants == false) { |
726 | return true; |
727 | } |
728 | |
729 | $usersArray = array(); |
730 | foreach($immigrants as $immigrant) { |
731 | $usersArray[$immigrant->mnethostid][] = $immigrant->username; |
732 | } |
733 | |
734 | require_once $CFG->dirroot . '/mnet/xmlrpc/client.php'; |
735 | foreach($usersArray as $mnethostid => $users) { |
736 | $mnet_peer = new mnet_peer(); |
737 | $mnet_peer->set_id($mnethostid); |
738 | |
739 | $mnet_request = new mnet_xmlrpc_client(); |
740 | $mnet_request->set_method('auth/mnet/auth.php/keepalive_server'); |
741 | |
742 | // set $token and $useragent parameters |
743 | $mnet_request->add_param($users); |
744 | |
745 | if ($mnet_request->send($mnet_peer) === true) { |
746 | if (!isset($mnet_request->response['code'])) { |
747 | debugging("Server side error has occured on host $mnethostid"); |
748 | continue; |
749 | } elseif ($mnet_request->response['code'] > 0) { |
750 | debugging($mnet_request->response['message']); |
751 | } |
139ebfdb |
752 | |
c72fe801 |
753 | if (!isset($mnet_request->response['last log id'])) { |
754 | debugging("Server side error has occured on host $mnethostid\nNo log ID was received."); |
755 | continue; |
756 | } |
757 | } else { |
139ebfdb |
758 | debugging("Server side error has occured on host $mnethostid: " . |
c72fe801 |
759 | join("\n", $mnet_request->error)); |
3d7e4468 |
760 | break; |
c72fe801 |
761 | } |
28c3e7d8 |
762 | $mnethostlogssql = " |
6ff7d16b |
763 | SELECT |
764 | mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip, |
765 | mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action, |
766 | mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename, |
767 | c.modinfo |
768 | FROM |
769 | ( |
770 | SELECT |
771 | l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid, |
772 | l.action, l.url, l.info, u.username |
773 | FROM |
774 | {user} u |
775 | INNER JOIN {log} l on l.userid = u.id |
776 | WHERE |
777 | u.mnethostid = ? |
778 | AND l.id > ? |
779 | ORDER BY remoteid ASC |
780 | LIMIT 500 |
781 | ) mhostlogs |
782 | INNER JOIN {course} c on c.id = mhostlogs.course |
783 | ORDER by mhostlogs.remoteid ASC"; |
c72fe801 |
784 | |
995087b9 |
785 | $mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id'])); |
c72fe801 |
786 | |
6da155db |
787 | if ($mnethostlogs == false) { |
788 | continue; |
789 | } |
c72fe801 |
790 | |
995087b9 |
791 | $processedlogs = array(); |
c72fe801 |
792 | |
995087b9 |
793 | foreach($mnethostlogs as $hostlog) { |
794 | // Extract the name of the relevant module instance from the |
795 | // course modinfo if possible. |
796 | if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) { |
797 | $modinfo = unserialize($hostlog->modinfo); |
798 | unset($hostlog->modinfo); |
c72fe801 |
799 | $modulearray = array(); |
800 | foreach($modinfo as $module) { |
801 | $modulearray[$module->cm] = urldecode($module->name); |
802 | } |
995087b9 |
803 | $hostlog->resource_name = $modulearray[$hostlog->cmid]; |
c72fe801 |
804 | } else { |
995087b9 |
805 | $hostlog->resource_name = ''; |
c72fe801 |
806 | } |
807 | |
995087b9 |
808 | $processedlogs[] = array ( |
809 | 'remoteid' => $hostlog->remoteid, |
810 | 'time' => $hostlog->time, |
811 | 'userid' => $hostlog->userid, |
812 | 'ip' => $hostlog->ip, |
813 | 'course' => $hostlog->course, |
814 | 'coursename' => $hostlog->coursename, |
815 | 'module' => $hostlog->module, |
816 | 'cmid' => $hostlog->cmid, |
817 | 'action' => $hostlog->action, |
818 | 'url' => $hostlog->url, |
819 | 'info' => $hostlog->info, |
820 | 'resource_name' => $hostlog->resource_name, |
821 | 'username' => $hostlog->username |
c72fe801 |
822 | ); |
823 | } |
824 | |
995087b9 |
825 | unset($hostlog); |
c72fe801 |
826 | |
827 | $mnet_request = new mnet_xmlrpc_client(); |
828 | $mnet_request->set_method('auth/mnet/auth.php/refresh_log'); |
829 | |
830 | // set $token and $useragent parameters |
995087b9 |
831 | $mnet_request->add_param($processedlogs); |
c72fe801 |
832 | |
833 | if ($mnet_request->send($mnet_peer) === true) { |
834 | if ($mnet_request->response['code'] > 0) { |
835 | debugging($mnet_request->response['message']); |
836 | } |
837 | } else { |
838 | debugging("Server side error has occured on host $mnet_peer->ip: " .join("\n", $mnet_request->error)); |
839 | } |
840 | } |
841 | } |
842 | |
843 | /** |
844 | * Receives an array of log entries from an SP and adds them to the mnet_log |
845 | * table |
846 | * |
847 | * @param array $array An array of usernames |
848 | * @return string "All ok" or an error message |
849 | */ |
850 | function refresh_log($array) { |
7b0d12b2 |
851 | global $CFG, $MNET_REMOTE_CLIENT, $DB; |
c72fe801 |
852 | |
853 | // We don't want to output anything to the client machine |
854 | $start = ob_start(); |
855 | |
856 | $returnString = ''; |
7b0d12b2 |
857 | $DB->begin_sql(); |
c72fe801 |
858 | $useridarray = array(); |
859 | |
860 | foreach($array as $logEntry) { |
861 | $logEntryObj = (object)$logEntry; |
862 | $logEntryObj->hostid = $MNET_REMOTE_CLIENT->id; |
863 | |
864 | if (isset($useridarray[$logEntryObj->username])) { |
865 | $logEntryObj->userid = $useridarray[$logEntryObj->username]; |
866 | } else { |
457c9729 |
867 | $logEntryObj->userid = $DB->get_field('user', 'id', array('username'=>$logEntryObj->username, 'mnethostid'=>(int)$logEntryObj->hostid)); |
c72fe801 |
868 | if ($logEntryObj->userid == false) { |
869 | $logEntryObj->userid = 0; |
870 | } |
871 | $useridarray[$logEntryObj->username] = $logEntryObj->userid; |
872 | } |
873 | |
874 | unset($logEntryObj->username); |
875 | |
9dbc81ef |
876 | $logEntryObj = $this->trim_logline($logEntryObj); |
877 | $insertok = $DB->insert_record('mnet_log', $logEntryObj, false); |
c72fe801 |
878 | |
879 | if ($insertok) { |
880 | $MNET_REMOTE_CLIENT->last_log_id = $logEntryObj->remoteid; |
881 | } else { |
882 | $returnString .= 'Record with id '.$logEntryObj->remoteid." failed to insert.\n"; |
883 | } |
884 | } |
885 | $MNET_REMOTE_CLIENT->commit(); |
7b0d12b2 |
886 | $DB->commit_sql(); |
c72fe801 |
887 | |
888 | $end = ob_end_clean(); |
889 | |
890 | if (empty($returnString)) return array('code' => 0, 'message' => 'All ok'); |
891 | return array('code' => 1, 'message' => $returnString); |
892 | } |
893 | |
894 | /** |
895 | * Receives an array of usernames from a remote machine and prods their |
896 | * sessions to keep them alive |
897 | * |
898 | * @param array $array An array of usernames |
899 | * @return string "All ok" or an error message |
900 | */ |
901 | function keepalive_server($array) { |
7b0d12b2 |
902 | global $MNET_REMOTE_CLIENT, $CFG, $DB; |
c72fe801 |
903 | |
904 | $CFG->usesid = true; |
c72fe801 |
905 | |
906 | // We don't want to output anything to the client machine |
907 | $start = ob_start(); |
908 | |
909 | // We'll get session records in batches of 30 |
910 | $superArray = array_chunk($array, 30); |
911 | |
912 | $returnString = ''; |
913 | |
914 | foreach($superArray as $subArray) { |
915 | $subArray = array_values($subArray); |
916 | $instring = "('".implode("', '",$subArray)."')"; |
7b0d12b2 |
917 | $query = "select id, session_id, username from {mnet_session} where username in $instring"; |
918 | $results = $DB->get_records_sql($query); |
c72fe801 |
919 | |
920 | if ($results == false) { |
921 | // We seem to have a username that breaks our query: |
922 | // TODO: Handle this error appropriately |
923 | $returnString .= "We failed to refresh the session for the following usernames: \n".implode("\n", $subArray)."\n\n"; |
924 | } else { |
925 | // TODO: This process of killing and re-starting the session |
926 | // will cause PHP to forget any custom session_set_save_handler |
927 | // stuff. Subsequent attempts to prod existing sessions will |
928 | // fail, because PHP will look in wherever the default place |
929 | // may be (files?) and probably create a new session with the |
930 | // right session ID in that location. If it doesn't have write- |
931 | // access to that location, then it will fail... not sure how |
932 | // apparent that will be. |
933 | // There is no way to capture what the custom session handler |
934 | // is and then reset it on each pass - I checked that out |
935 | // already. |
d87234ce |
936 | $sesscache = $_SESSION; |
c72fe801 |
937 | $sessidcache = session_id(); |
938 | session_write_close(); |
939 | unset($_SESSION); |
940 | |
941 | $uc = ini_get('session.use_cookies'); |
942 | ini_set('session.use_cookies', false); |
943 | foreach($results as $emigrant) { |
944 | |
945 | unset($_SESSION); |
946 | session_name('MoodleSession'.$CFG->sessioncookie); |
947 | session_id($emigrant->session_id); |
948 | session_start(); |
949 | session_write_close(); |
950 | } |
951 | |
952 | ini_set('session.use_cookies', $uc); |
953 | session_name('MoodleSession'.$CFG->sessioncookie); |
954 | session_id($sessidcache); |
955 | session_start(); |
d87234ce |
956 | $_SESSION = $sesscache; |
c72fe801 |
957 | session_write_close(); |
958 | } |
959 | } |
960 | |
961 | $end = ob_end_clean(); |
962 | |
963 | if (empty($returnString)) return array('code' => 0, 'message' => 'All ok', 'last log id' => $MNET_REMOTE_CLIENT->last_log_id); |
964 | return array('code' => 1, 'message' => $returnString, 'last log id' => $MNET_REMOTE_CLIENT->last_log_id); |
965 | } |
966 | |
967 | /** |
968 | * Cron function will be called automatically by cron.php every 5 minutes |
969 | * |
970 | * @return void |
971 | */ |
972 | function cron() { |
7b0d12b2 |
973 | global $DB; |
4c1c5d26 |
974 | |
975 | // run the keepalive client |
c72fe801 |
976 | $this->keepalive_client(); |
4c1c5d26 |
977 | |
978 | // admin/cron.php should have run srand for us |
979 | $random100 = rand(0,100); |
980 | if ($random100 < 10) { // Approximately 10% of the time. |
981 | // nuke olden sessions |
f71a7f8f |
982 | $longtime = time() - (1 * 3600 * 24); |
7b0d12b2 |
983 | $DB->delete_records_select('mnet_session', "expires < ?", array($longtime)); |
4c1c5d26 |
984 | } |
c72fe801 |
985 | } |
986 | |
987 | /** |
988 | * Cleanup any remote mnet_sessions, kill the local mnet_session data |
989 | * |
990 | * This is called by require_logout in moodlelib |
991 | * |
992 | * @return void |
993 | */ |
f5fd4347 |
994 | function prelogout_hook() { |
c72fe801 |
995 | global $MNET, $CFG, $USER; |
23a94798 |
996 | if (!is_enabled_auth('mnet')) { |
f5fd4347 |
997 | return; |
998 | } |
999 | |
c72fe801 |
1000 | require_once $CFG->dirroot.'/mnet/xmlrpc/client.php'; |
1001 | |
1002 | // If the user is local to this Moodle: |
1003 | if ($USER->mnethostid == $MNET->id) { |
1004 | $this->kill_children($USER->username, sha1($_SERVER['HTTP_USER_AGENT'])); |
1005 | |
1006 | // Else the user has hit 'logout' at a Service Provider Moodle: |
1007 | } else { |
1008 | $this->kill_parent($USER->username, sha1($_SERVER['HTTP_USER_AGENT'])); |
1009 | |
1010 | } |
1011 | } |
1012 | |
1013 | /** |
1014 | * The SP uses this function to kill the session on the parent IdP |
1015 | * |
1016 | * @param string $username Username for session to kill |
1017 | * @param string $useragent SHA1 hash of user agent to look for |
1018 | * @return string A plaintext report of what has happened |
1019 | */ |
1020 | function kill_parent($username, $useragent) { |
7b0d12b2 |
1021 | global $CFG, $USER, $DB; |
1022 | |
c72fe801 |
1023 | require_once $CFG->dirroot.'/mnet/xmlrpc/client.php'; |
1024 | $sql = " |
1025 | select |
1026 | * |
1027 | from |
7b0d12b2 |
1028 | {mnet_session} s |
c72fe801 |
1029 | where |
7b0d12b2 |
1030 | s.username = ? AND |
1031 | s.useragent = ? AND |
1032 | s.mnethostid = ?"; |
c72fe801 |
1033 | |
7b0d12b2 |
1034 | $mnetsessions = $DB->get_records_sql($sql, array($username, $useragent, $USER->mnethostid)); |
c72fe801 |
1035 | |
7b0d12b2 |
1036 | $ignore = $DB->delete_records('mnet_session', |
1037 | array('username'=>$username, |
1038 | 'useragent'=>$useragent, |
1039 | 'mnethostid'=>$USER->mnethostid)); |
c72fe801 |
1040 | |
1041 | if (false != $mnetsessions) { |
1042 | $mnet_peer = new mnet_peer(); |
1043 | $mnet_peer->set_id($USER->mnethostid); |
1044 | |
1045 | $mnet_request = new mnet_xmlrpc_client(); |
1046 | $mnet_request->set_method('auth/mnet/auth.php/kill_children'); |
1047 | |
1048 | // set $token and $useragent parameters |
1049 | $mnet_request->add_param($username); |
1050 | $mnet_request->add_param($useragent); |
1051 | if ($mnet_request->send($mnet_peer) === false) { |
1052 | debugging(join("\n", $mnet_request->error)); |
139ebfdb |
1053 | return false; |
c72fe801 |
1054 | } |
1055 | } |
1056 | |
1057 | $_SESSION = array(); |
1058 | return true; |
1059 | } |
1060 | |
1061 | /** |
1062 | * The IdP uses this function to kill child sessions on other hosts |
1063 | * |
1064 | * @param string $username Username for session to kill |
1065 | * @param string $useragent SHA1 hash of user agent to look for |
1066 | * @return string A plaintext report of what has happened |
1067 | */ |
1068 | function kill_children($username, $useragent) { |
7b0d12b2 |
1069 | global $CFG, $USER, $MNET_REMOTE_CLIENT, $DB; |
c72fe801 |
1070 | require_once $CFG->dirroot.'/mnet/xmlrpc/client.php'; |
1071 | |
7b0d12b2 |
1072 | $userid = $DB->get_field('user', 'id', array('mnethostid'=>$CFG->mnet_localhost_id, 'username'=>$username)); |
c72fe801 |
1073 | |
1074 | $returnstring = ''; |
1c85006c |
1075 | |
1076 | $mnetsessions = $DB->get_records('mnet_session', array('userid' => $userid, 'useragent' => $useragent)); |
c72fe801 |
1077 | |
c72fe801 |
1078 | if (false == $mnetsessions) { |
f213ba93 |
1079 | $returnstring .= "Could find no remote sessions\n"; |
c72fe801 |
1080 | $mnetsessions = array(); |
1081 | } |
1082 | |
1083 | foreach($mnetsessions as $mnetsession) { |
4711957d |
1084 | // If this script is being executed by a remote peer, that means the user has clicked |
1085 | // logout on that peer, and the session on that peer can be deleted natively. |
1086 | // Skip over it. |
1087 | if (isset($MNET_REMOTE_CLIENT->id) && ($mnetsession->mnethostid == $MNET_REMOTE_CLIENT->id)) { |
1088 | continue; |
1089 | } |
c72fe801 |
1090 | $returnstring .= "Deleting session\n"; |
1091 | |
c72fe801 |
1092 | $mnet_peer = new mnet_peer(); |
1093 | $mnet_peer->set_id($mnetsession->mnethostid); |
1094 | |
1095 | $mnet_request = new mnet_xmlrpc_client(); |
1096 | $mnet_request->set_method('auth/mnet/auth.php/kill_child'); |
1097 | |
1098 | // set $token and $useragent parameters |
1099 | $mnet_request->add_param($username); |
1100 | $mnet_request->add_param($useragent); |
1101 | if ($mnet_request->send($mnet_peer) === false) { |
1a7601ca |
1102 | debugging("Server side error has occured on host $mnetsession->mnethostid: " . |
c72fe801 |
1103 | join("\n", $mnet_request->error)); |
1104 | } |
1105 | } |
1106 | |
7b0d12b2 |
1107 | $ignore = $DB->delete_records('mnet_session', |
1108 | array('useragent'=>$useragent, 'userid'=>$userid)); |
c72fe801 |
1109 | |
1110 | if (isset($MNET_REMOTE_CLIENT) && isset($MNET_REMOTE_CLIENT->id)) { |
1111 | $start = ob_start(); |
1112 | |
ae18d3e6 |
1113 | // Save current session and cookie-use status |
a01ee313 |
1114 | $cookieuse = ini_get('session.use_cookies'); |
c72fe801 |
1115 | ini_set('session.use_cookies', false); |
210d7a46 |
1116 | $sesscache = $_SESSION; |
c72fe801 |
1117 | $sessidcache = session_id(); |
a01ee313 |
1118 | |
1119 | // Replace existing mnet session with user session & unset |
c72fe801 |
1120 | session_write_close(); |
1121 | unset($_SESSION); |
c72fe801 |
1122 | session_id($mnetsession->session_id); |
1123 | session_start(); |
1124 | session_unregister("USER"); |
1125 | session_unregister("SESSION"); |
1126 | unset($_SESSION); |
1127 | $_SESSION = array(); |
1128 | session_write_close(); |
1129 | |
a01ee313 |
1130 | // Restore previous info |
1131 | ini_set('session.use_cookies', $cookieuse); |
c72fe801 |
1132 | session_name('MoodleSession'.$CFG->sessioncookie); |
1133 | session_id($sessidcache); |
1134 | session_start(); |
210d7a46 |
1135 | $_SESSION = $sesscache; |
c72fe801 |
1136 | session_write_close(); |
1137 | |
1138 | $end = ob_end_clean(); |
1139 | } else { |
1140 | $_SESSION = array(); |
1141 | } |
1142 | return $returnstring; |
1143 | } |
1144 | |
1145 | /** |
1146 | * TODO:Untested When the IdP requests that child sessions are terminated, |
1147 | * this function will be called on each of the child hosts. The machine that |
1148 | * calls the function (over xmlrpc) provides us with the mnethostid we need. |
1149 | * |
1150 | * @param string $username Username for session to kill |
1151 | * @param string $useragent SHA1 hash of user agent to look for |
1152 | * @return bool True on success |
1153 | */ |
1154 | function kill_child($username, $useragent) { |
7b0d12b2 |
1155 | global $CFG, $MNET_REMOTE_CLIENT, $DB; |
1156 | $session = $DB->get_record('mnet_session', array('username'=>$username, 'mnethostid'=>$MNET_REMOTE_CLIENT->id, 'useragent'=>$useragent)); |
c72fe801 |
1157 | if (false != $session) { |
1158 | $start = ob_start(); |
1159 | |
1160 | $uc = ini_get('session.use_cookies'); |
1161 | ini_set('session.use_cookies', false); |
d87234ce |
1162 | $sesscache = $_SESSION; |
c72fe801 |
1163 | $sessidcache = session_id(); |
1164 | session_write_close(); |
1165 | unset($_SESSION); |
1166 | |
1167 | |
1168 | session_id($session->session_id); |
1169 | session_start(); |
1170 | session_unregister("USER"); |
1171 | session_unregister("SESSION"); |
1172 | unset($_SESSION); |
1173 | $_SESSION = array(); |
1174 | session_write_close(); |
1175 | |
1176 | |
1177 | ini_set('session.use_cookies', $uc); |
1178 | session_name('MoodleSession'.$CFG->sessioncookie); |
1179 | session_id($sessidcache); |
1180 | session_start(); |
d87234ce |
1181 | $_SESSION = $sesscache; |
c72fe801 |
1182 | session_write_close(); |
1183 | |
1184 | $end = ob_end_clean(); |
1185 | return true; |
1186 | } |
1187 | return false; |
1188 | } |
1189 | |
1190 | /** |
1191 | * To delete a host, we must delete all current sessions that users from |
1192 | * that host are currently engaged in. |
1193 | * |
1194 | * @param string $sessionidarray An array of session hashes |
1195 | * @return bool True on success |
1196 | */ |
1197 | function end_local_sessions(&$sessionArray) { |
1198 | global $CFG; |
1199 | if (is_array($sessionArray)) { |
1200 | $start = ob_start(); |
1201 | |
1202 | $uc = ini_get('session.use_cookies'); |
1203 | ini_set('session.use_cookies', false); |
d87234ce |
1204 | $sesscache = $_SESSION; |
c72fe801 |
1205 | $sessidcache = session_id(); |
1206 | session_write_close(); |
1207 | unset($_SESSION); |
1208 | |
1209 | while($session = array_pop($sessionArray)) { |
1210 | session_id($session->session_id); |
1211 | session_start(); |
1212 | session_unregister("USER"); |
1213 | session_unregister("SESSION"); |
1214 | unset($_SESSION); |
1215 | $_SESSION = array(); |
1216 | session_write_close(); |
1217 | } |
1218 | |
1219 | ini_set('session.use_cookies', $uc); |
1220 | session_name('MoodleSession'.$CFG->sessioncookie); |
1221 | session_id($sessidcache); |
1222 | session_start(); |
d87234ce |
1223 | $_SESSION = $sesscache; |
c72fe801 |
1224 | |
1225 | $end = ob_end_clean(); |
1226 | return true; |
1227 | } |
1228 | return false; |
1229 | } |
1230 | |
1231 | /** |
1232 | * Returns the user's image as a base64 encoded string. |
1233 | * |
1234 | * @param int $userid The id of the user |
1235 | * @return string The encoded image |
1236 | */ |
1237 | function fetch_user_image($username) { |
7b0d12b2 |
1238 | global $CFG, $DB; |
c72fe801 |
1239 | |
7b0d12b2 |
1240 | if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) { |
56a1a882 |
1241 | $filename1 = make_user_directory($user->id, true) . "/f1.jpg"; |
1242 | $filename2 = make_user_directory($user->id, true) . "/f2.jpg"; |
c72fe801 |
1243 | $return = array(); |
1244 | if (file_exists($filename1)) { |
1245 | $return['f1'] = base64_encode(file_get_contents($filename1)); |
1246 | } |
1247 | if (file_exists($filename2)) { |
1248 | $return['f2'] = base64_encode(file_get_contents($filename2)); |
1249 | } |
1250 | return $return; |
1251 | } |
1252 | return false; |
1253 | } |
1254 | |
1255 | /** |
1256 | * Returns the theme information and logo url as strings. |
1257 | * |
1258 | * @return string The theme info |
1259 | */ |
1260 | function fetch_theme_info() { |
1261 | global $CFG; |
1262 | |
1263 | $themename = "$CFG->theme"; |
1264 | $logourl = "$CFG->wwwroot/theme/$CFG->theme/images/logo.jpg"; |
1265 | |
1266 | $return['themename'] = $themename; |
1267 | $return['logourl'] = $logourl; |
1268 | return $return; |
1269 | } |
1270 | |
1271 | /** |
1272 | * Determines if an MNET host is providing the nominated service. |
1273 | * |
1274 | * @param int $mnethostid The id of the remote host |
1275 | * @param string $servicename The name of the service |
1276 | * @return bool Whether the service is available on the remote host |
1277 | */ |
1278 | function has_service($mnethostid, $servicename) { |
7b0d12b2 |
1279 | global $CFG, $DB; |
c72fe801 |
1280 | |
1281 | $sql = " |
1282 | SELECT |
1283 | svc.id as serviceid, |
1284 | svc.name, |
1285 | svc.description, |
1286 | svc.offer, |
1287 | svc.apiversion, |
1288 | h2s.id as h2s_id |
1289 | FROM |
dbca4e44 |
1290 | {mnet_host} h, |
7b0d12b2 |
1291 | {mnet_service} svc, |
1292 | {mnet_host2service} h2s |
c72fe801 |
1293 | WHERE |
dbca4e44 |
1294 | h.deleted = '0' AND |
1295 | h.id = h2s.hostid AND |
7b0d12b2 |
1296 | h2s.hostid = ? AND |
c72fe801 |
1297 | h2s.serviceid = svc.id AND |
7b0d12b2 |
1298 | svc.name = ? AND |
c72fe801 |
1299 | h2s.subscribe = '1'"; |
1300 | |
7b0d12b2 |
1301 | return $DB->get_records_sql($sql, array($mnethostid, $servicename)); |
c72fe801 |
1302 | } |
1303 | |
1304 | /** |
1305 | * Checks the MNET access control table to see if the username/mnethost |
1306 | * is permitted to login to this moodle. |
1307 | * |
1308 | * @param string $username The username |
1309 | * @param int $mnethostid The id of the remote mnethost |
1310 | * @return bool Whether the user can login from the remote host |
1311 | */ |
1312 | function can_login_remotely($username, $mnethostid) { |
7b0d12b2 |
1313 | global $DB; |
1314 | |
cdf22329 |
1315 | $accessctrl = 'allow'; |
7b0d12b2 |
1316 | $aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnethostid)); |
c72fe801 |
1317 | if (!empty($aclrecord)) { |
cdf22329 |
1318 | $accessctrl = $aclrecord->accessctrl; |
c72fe801 |
1319 | } |
cdf22329 |
1320 | return $accessctrl == 'allow'; |
c72fe801 |
1321 | } |
6bc1e5d5 |
1322 | |
f5fd4347 |
1323 | function logoutpage_hook() { |
7b0d12b2 |
1324 | global $USER, $CFG, $redirect, $DB; |
6bc1e5d5 |
1325 | |
1326 | if (!empty($USER->mnethostid) and $USER->mnethostid != $CFG->mnet_localhost_id) { |
7b0d12b2 |
1327 | $host = $DB->get_record('mnet_host', array('id'=>$USER->mnethostid)); |
6bc1e5d5 |
1328 | $redirect = $host->wwwroot.'/'; |
1329 | } |
1330 | } |
1331 | |
9dbc81ef |
1332 | /** |
1333 | * Trims a log line from mnet peer to limit each part to a length which can be stored in our DB |
1334 | * |
1335 | * @param object $logline The log information to be trimmed |
1336 | * @return object The passed logline object trimmed to not exceed storable limits |
1337 | */ |
1338 | function trim_logline ($logline) { |
1339 | $limits = array('ip' => 15, 'coursename' => 40, 'module' => 20, 'action' => 40, |
1340 | 'url' => 255); |
1341 | foreach ($limits as $property => $limit) { |
1342 | if (isset($logline->$property)) { |
1343 | $logline->$property = substr($logline->$property, 0, $limit); |
1344 | } |
1345 | } |
1346 | |
1347 | return $logline; |
1348 | } |
1349 | |
1350 | |
c72fe801 |
1351 | } |
1352 | |
1353 | ?> |