mnet MDL-21261 mnet overhaul - adding and removing files I missed in the last big commit

This commit is contained in:
Penny Leach 2010-01-28 20:17:12 +00:00
parent 64d187aa12
commit d2ac37c1ab
9 changed files with 1044 additions and 222 deletions

195
admin/mnet/testclient.php Normal file
View File

@ -0,0 +1,195 @@
<?php
/**
* A service browser for remote Moodles
*
* This script 'remotely' executes the reflection methods on a remote Moodle,
* and publishes the details of the available services
*
* @author Donal McMullan donal@catalyst.net.nz
* @version 0.0.1
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mnet
*/
require_once(dirname(dirname(dirname(__FILE__))) . '/config.php');
require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
require_once($CFG->libdir.'/adminlib.php');
include_once($CFG->dirroot.'/mnet/lib.php');
if ($CFG->mnet_dispatcher_mode === 'off') {
print_error('mnetdisabled', 'mnet');
}
require_login();
admin_externalpage_setup('mnettestclient');
$context = get_context_instance(CONTEXT_SYSTEM);
require_capability('moodle/site:config', $context);
error_reporting(E_ALL);
admin_externalpage_print_header();
if (!extension_loaded('openssl')) {
print_error('requiresopenssl', 'mnet', '', NULL, true);
}
// optional drilling down parameters
$hostid = optional_param('hostid', 0, PARAM_INT);
$servicename = optional_param('servicename', '', PARAM_SAFEDIR);
$methodid = optional_param('method', 0, PARAM_INT);
$hosts = $DB->get_records('mnet_host');
$moodleapplicationid = $DB->get_field('mnet_application', 'id', array('name' => 'moodle'));
$url = new moodle_url('/admin/mnet/testclient.php');
$PAGE->set_url($url);
echo $OUTPUT->heading(get_string('hostlist', 'mnet'));
foreach ($hosts as $id => $host) {
if (empty($host->wwwroot) || $host->wwwroot == $CFG->wwwroot) {
continue;
}
$newurl = new moodle_url($url, array('hostid' => $host->id));
echo '<p>' . $OUTPUT->link($newurl, $host->wwwroot) . '</p>';
}
if (!empty($hostid) && array_key_exists($hostid, $hosts)) {
$host = $hosts[$hostid];
if ($host->applicationid != $moodleapplicationid) {
echo $OUTPUT->notification(get_string('notmoodleapplication', 'mnet'));
}
$mnet_peer = new mnet_peer();
$mnet_peer->set_wwwroot($host->wwwroot);
$mnet_request = new mnet_xmlrpc_client();
$mnet_request->set_method('system/listServices');
$mnet_request->send($mnet_peer);
$services = $mnet_request->response;
$yesno = array('No', 'Yes');
$servicenames = array();
echo $OUTPUT->heading(get_string('servicesavailableonhost', 'mnet', $host->wwwroot));
$table = new html_table();
$table->head = array(
get_string('serviceid', 'mnet'),
get_string('service', 'mnet'),
get_string('version', 'mnet'),
get_string('theypublish', 'mnet'),
get_string('theysubscribe', 'mnet'),
get_string('options', 'mnet'),
);
$table->data = array();
$sql = 'SELECT s.name, min(r.plugintype) AS plugintype, min(r.pluginname) AS pluginname
FROM {mnet_service} s
JOIN {mnet_service2rpc} s2r ON s2r.serviceid = s.id
JOIN {mnet_rpc} r ON r.id = s2r.rpcid
GROUP BY s.name';
$yesno = array(get_string('no'), get_string('yes'));
$serviceinfo = $DB->get_records_sql($sql);
foreach ($services as $id => $servicedata) {
if (array_key_exists($servicedata['name'], $serviceinfo)) {
$service = $serviceinfo[$servicedata['name']];
$servicedata['humanname'] = get_string($servicedata['name'].'_name', $service->plugintype . '_' . $service->pluginname);
} else {
$servicedata['humanname'] = get_string('unknown', 'mnet');
}
$newurl = new moodle_url($url, array('hostid' => $host->id, 'servicename' => $servicedata['name']));
$table->data[] = array(
$servicedata['name'],
$servicedata['humanname'],
$servicedata['apiversion'],
$yesno[$servicedata['publish']],
$yesno[$servicedata['subscribe']],
$OUTPUT->link($newurl, get_string('listservices', 'mnet'))
);
}
echo $OUTPUT->table($table);
$mnet_request->set_method('system/listMethods');
if (isset($servicename) && array_key_exists($servicename, $serviceinfo)) {
echo $OUTPUT->heading(get_string('methodsavailableonhostinservice', 'mnet', array('host' => $host->wwwroot, 'service' => $servicename)));
$service = $serviceinfo[$servicename];
$mnet_request->add_param($servicename, 'string');
} else {
echo $OUTPUT->heading(get_string('methodsavailableonhost', 'mnet', $host->wwwroot));
}
$mnet_request->send($mnet_peer);
$methods = $mnet_request->response;
$table = new html_table();
$table->head = array(
get_string('method', 'mnet'),
get_string('options', 'mnet'),
);
$table->data = array();
foreach ($methods as $id => $method) {
$params = array('hostid' => $host->id, 'method' => $id);
if (isset($servicename)) {
$params['servicename'] = $servicename;
}
$newurl = new moodle_url($url, $params);
$table->data[] = array(
$method,
$OUTPUT->link($newurl, get_string('inspect', 'mnet'))
);
}
echo $OUTPUT->table($table);
if (isset($methodid) && array_key_exists($methodid, $methods)) {
$method = $methods[$methodid];
$mnet_request = new mnet_xmlrpc_client();
$mnet_request->set_method('system/methodSignature');
$mnet_request->add_param($method, 'string');
$mnet_request->send($mnet_peer);
$signature = $mnet_request->response;
echo $OUTPUT->heading(get_string('methodsignature', 'mnet', $method));
$table = new html_table();
$table->head = array(
get_string('position', 'mnet'),
get_string('name', 'mnet'),
get_string('type', 'mnet'),
get_string('description', 'mnet'),
);
$table->data = array();
$params = $signature['parameters'];
foreach ($params as $pos => $details) {
$table->data[] = array(
$pos,
$details['name'],
$details['type'],
$details['description'],
);
}
$table->data[] = array(
get_string('returnvalue', 'mnet'),
'',
$signature['return']['type'],
$signature['return']['description']
);
echo $OUTPUT->table($table);
$mnet_request->set_method('system/methodHelp');
$mnet_request->add_param($method, 'string');
$mnet_request->send($mnet_peer);
$help = $mnet_request->response;
echo $OUTPUT->heading(get_string('methodhelp', 'mnet', $method));
echo(str_replace('\n', '<br />',$help));
}
}
echo $OUTPUT->footer();
?>

52
auth/mnet/db/mnet.php Normal file
View File

@ -0,0 +1,52 @@
<?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/>.
/**
* This file contains the mnet services for the mnet authentication plugin
*
* @since 2.0
* @package moodlecore
* @subpackage auth
* @copyright 2010 Penny Leach
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$publishes = array(
'sso_idp' => array(
'apiversion' => 1,
'classname' => 'auth_plugin_mnet',
'filename' => 'auth.php',
'methods' => array(
'user_authorise',
'keepalive_server',
'kill_children',
'refresh_log',
'fetch_user_image',
'fetch_theme_info',
'update_enrolments',
),
),
'sso_sp' => array(
'apiversion' => 1,
'classname' => 'auth_plugin_mnet',
'filename' => 'auth.php',
'methods' => array(
'keepalive_client',
'kill_child'
)
)
);

41
enrol/mnet/db/mnet.php Normal file
View File

@ -0,0 +1,41 @@
<?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/>.
/**
* This file contains the mnet services for the mnet enrolment plugin
*
* @since 2.0
* @package moodlecore
* @subpackage enrolment
* @copyright 2010 Penny Leach
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$publishes = array(
'mnet_enrol' => array(
'apiversion' => 1,
'classname' => 'enrolment_plugin_mnet',
'filename' => 'enrol.php',
'methods' => array(
'available_courses',
'user_enrolments',
'enrol_user',
'unenrol_user',
'course_enrolments'
),
),
);

3
enrol/mnet/version.php Normal file
View File

@ -0,0 +1,3 @@
<?php
$plugin->version = 2010012600;

View File

@ -1,78 +0,0 @@
<?php
/**
* Some dummy functions to test XML-RPC with
*/
/**
* The xxxx_RPC_OK must exist and return TRUE for the remote call to be
* permitted
*
* @return bool True if the related function can be executed remotely
*/
function mnet_concatenate_strings_RPC_OK() {
return true;
}
function mnet_publishes() {
$servicelist = array();
$service['name'] = 'sso';
$function['name'] = 'mnet_concatenate_strings';
// first argument
$argument['type'] = 'string';
$argument['default'] = '';
$function['arguments'][] = $argument;
// second argument
$argument['type'] = 'string';
$argument['default'] = '';
$function['arguments'][] = $argument;
// third argument
$argument['type'] = 'string';
$argument['default'] = '';
$function['arguments'][] = $argument;
$function['description'] = get_string($function['name'], 'mnet');
$service['functions'][] = $function;
$servicelist[] = $service;
return $servicelist;
}
//header('Content-type: text/plain');
//var_dump(mnet_publishes());
/**
* Concatenate (up to) 3 strings and return the result
* @service sso
* @param string $string1 Some string
* @param string $string2 Some string
* @param string $string3 Some string
* @return string The parameter strings, concatenated together
*/
function mnet_concatenate_strings($string1='', $string2='', $string3='') {
return $string1.$string2.$string3;
}
class testClass {
function testClass() {
$this->first = 'last';
$this->last = 'first';
}
function augment_first($newval) {
$this->first = $this->first.$newval;
return $this->first;
}
function augment_first_RPC_OK() {
return true;
}
function mnet_concatenate_strings_RPC_OK() {
return true;
}
function mnet_concatenate_strings($string1='', $string2='', $string3='') {
return $string1.$string2.$string3;
}
}

View File

@ -1,144 +0,0 @@
<?php
/**
* A service browser for remote Moodles
*
* This script 'remotely' executes the reflection methods on a remote Moodle,
* and publishes the details of the available services
*
* @author Donal McMullan donal@catalyst.net.nz
* @version 0.0.1
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mnet
*/
require_once(dirname(dirname(__FILE__)) . '/config.php');
require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
if ($CFG->mnet_dispatcher_mode === 'off') {
print_error('mnetdisabled', 'mnet');
}
// Site admins only, thanks.
require_login();
$context = get_context_instance(CONTEXT_SYSTEM);
require_capability('moodle/site:config', $context);
error_reporting(E_ALL);
// Some HTML sugar
echo '<?xml version="1.0" encoding="utf-8"?>';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
<head><title>Moodle MNET Test Client</title></head><body>
<H1>Hosts</H1>
<?php
$hosts = $DB->get_records('mnet_host');
foreach ($hosts as $id => $host) {
// Skip the 'all hosts' option
if(empty($host->wwwroot)) continue;
// Skip localhost
if($host->wwwroot == $CFG->wwwroot) continue;
// Skip non-moodle non-mahara hosts
if($host->applicationid != 1 && $host->applicationid != 2) continue; //TODO: get rid of magic numbers.
echo '<p><a href="testclient.php?hostid='.$host->id.'">'.$host->wwwroot."</a></p>\n";
}
if (!empty($_GET['hostid']) && array_key_exists($_GET['hostid'], $hosts)) {
$host = $hosts[$_GET['hostid']];
$mnet_peer = new mnet_peer();
$mnet_peer->set_wwwroot($host->wwwroot);
$mnet_request = new mnet_xmlrpc_client();
// Tell it the path to the method that we want to execute
$mnet_request->set_method('system/listServices');
$mnet_request->send($mnet_peer);
$services = $mnet_request->response;
$yesno = array('No', 'Yes');
$servicenames = array();
echo '<hr /><br /><h3>Services available on host: '.$host->wwwroot .'</h3><table><tr valign="top"><th>&nbsp;&nbsp;Service ID&nbsp;&nbsp;</th><th>&nbsp;&nbsp;Service&nbsp;&nbsp;</th><th>&nbsp;&nbsp;Version&nbsp;&nbsp;</th><th>&nbsp;&nbsp;They Publish&nbsp;&nbsp;</th><th>&nbsp;&nbsp;They Subscribe&nbsp;&nbsp;</th><th></th></tr>';
foreach ($services as $id => $service) {
$sql = 'select c.id, c.parent_type, c.parent from {mnet_service2rpc} a, {mnet_service} b, {mnet_rpc} c where a.serviceid = b.id and b.name=? and c.id = a.rpcid ';
echo '<tr valign="top">
<td>'.$service['name'].'</td>';
if ($detail = $DB->get_record_sql($sql, array($service['name']))) {
$service['humanname'] = get_string($service['name'].'_name', $detail->parent_type.'_'.$detail->parent);
echo '<td>'.$service['humanname'].'</td>';
} else {
$service['humanname'] = $service['name'];
echo '<td> unknown </td>';
}
echo '
<td>'.$service['apiversion'].'</td>
<td>'.$yesno[$service['publish']].'</td>
<td>'.$yesno[$service['subscribe']].'</td>
<td><a href="testclient.php?hostid='.$host->id.'&service='.$service['name'].'">List methods</a></td>
</tr>'."\n";
$servicenames[$service['name']] = $service;
}
echo '</table>';
if (isset($_GET['service']) && array_key_exists($_GET['service'], $servicenames)) {
$service = $servicenames[$_GET['service']];
// Tell it the path to the method that we want to execute
$mnet_request->set_method('system/listMethods');
$mnet_request->add_param($service['name'], 'string');
$mnet_request->send($mnet_peer);
$methods = $mnet_request->response;
echo '<hr /><br /><h3>Methods in the '.$service['humanname'] .' service</h3><table><th>Method</th><th colspan="2">Options</th>';
foreach ($methods as $id => $method) {
echo '<tr><td>'.$method.'</td><td> <a href="testclient.php?hostid='.$host->id.'&service='.$service['name'].'&method='.$id.'&show=sig">Inspect</a></td></tr>'."\n";
}
echo '</table>';
} else {
// Tell it the path to the method that we want to execute
$mnet_request->set_method('system/listMethods');
$mnet_request->send($mnet_peer);
$methods = $mnet_request->response;
echo '<hr /><br /><h3>Methods '.$host->wwwroot .'</h3><table><th>Method</th><th colspan="2">Options</th>';
foreach ($methods as $id => $method) {
echo '<tr><td>'.$method.'</td><td> <a href="testclient.php?hostid='.$host->id.'&method='.$id.'&show=sig">Inspect</a></td></tr>'."\n";
}
echo '</table>';
}
if (isset($_GET['method']) && array_key_exists($_GET['method'], $methods)) {
$method = $methods[$_GET['method']];
$mnet_request = new mnet_xmlrpc_client();
// Tell it the path to the method that we want to execute
$mnet_request->set_method('system/methodSignature');
$mnet_request->add_param($method, 'string');
$mnet_request->send($mnet_peer);
$signature = $mnet_request->response;
echo '<hr /><br /><h3>Method signature for '.$method.':</h3><table border="1"><th>Position</th><th>Type</th><th>Description</th>';
$params = array_pop($signature);
foreach ($params as $pos => $details) {
echo '<tr><td>'.$pos.'</td><td>'.$details['type'].'</td><td>'.$details['description'].'</td></tr>';
}
echo '</table>';
// Tell it the path to the method that we want to execute
$mnet_request->set_method('system/methodHelp');
$mnet_request->add_param($method, 'string');
$mnet_request->send($mnet_peer);
$help = $mnet_request->response;
echo '<hr /><br /><h3>Help details from docblock for '.$method.':</h3>';
echo(str_replace('\n', '<br />',$help));
echo '</pre>';
}
}
?>
</body>
</html>

676
mnet/xmlrpc/serverlib.php Normal file
View File

@ -0,0 +1,676 @@
<?php
/**
* -----XML-Envelope---------------------------------
* | |
* | Encrypted-Symmetric-key---------------- |
* | |_____________________________________| |
* | |
* | Encrypted data------------------------- |
* | | | |
* | | -XML-Envelope------------------ | |
* | | | | | |
* | | | --Signature------------- | | |
* | | | |______________________| | | |
* | | | | | |
* | | | --Signed-Payload-------- | | |
* | | | | | | | |
* | | | | XML-RPC Request | | | |
* | | | |______________________| | | |
* | | | | | |
* | | |_____________________________| | |
* | |_____________________________________| |
* | |
* |________________________________________________|
*
*/
/* Strip encryption envelope (if present) and decrypt data
*
* @param string $HTTP_RAW_POST_DATA The XML that the client sent
*
* @throws mnet_server_exception
*
* @return string XML with any encryption envolope removed
*/
function mnet_server_strip_encryption($HTTP_RAW_POST_DATA) {
global $MNET, $MNET_REMOTE_CLIENT;
$crypt_parser = new mnet_encxml_parser();
$crypt_parser->parse($HTTP_RAW_POST_DATA);
if (!$crypt_parser->payload_encrypted) {
return $HTTP_RAW_POST_DATA;
}
// Make sure we know who we're talking to
$host_record_exists = $MNET_REMOTE_CLIENT->set_wwwroot($crypt_parser->remote_wwwroot);
if (false == $host_record_exists) {
throw new mnet_server_exception(7020, 'wrong-wwwroot', $crypt_parser->remote_wwwroot);
}
// This key is symmetric, and is itself encrypted. Can be decrypted using our private key
$key = array_pop($crypt_parser->cipher);
// This data is symmetrically encrypted, can be decrypted using the above key
$data = array_pop($crypt_parser->cipher);
$crypt_parser->free_resource();
$payload = ''; // Initialize payload var
// &$payload
$isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $MNET->get_private_key());
if ($isOpen) {
$MNET_REMOTE_CLIENT->was_encrypted();
return $payload;
}
// Decryption failed... let's try our archived keys
$openssl_history = get_config('mnet', 'openssl_history');
if(empty($openssl_history)) {
$openssl_history = array();
set_config('openssl_history', serialize($openssl_history), 'mnet');
} else {
$openssl_history = unserialize($openssl_history);
}
foreach($openssl_history as $keyset) {
$keyresource = openssl_pkey_get_private($keyset['keypair_PEM']);
$isOpen = openssl_open(base64_decode($data), $payload, base64_decode($key), $keyresource);
if ($isOpen) {
// It's an older code, sir, but it checks out
$MNET_REMOTE_CLIENT->was_encrypted();
$MNET_REMOTE_CLIENT->encrypted_to($keyresource);
$MNET_REMOTE_CLIENT->set_pushkey();
return $payload;
}
}
//If after all that we still couldn't decrypt the message, error out.
throw new mnet_server_exception(7023, 'encryption-invalid');
}
/* Strip signature envelope (if present), try to verify any signature using our record of remote peer's public key.
*
* @param string $plaintextmessage XML envelope containing XMLRPC request and signature
*
* @return string XMLRPC request
*/
function mnet_server_strip_signature($plaintextmessage) {
global $MNET, $MNET_REMOTE_CLIENT;
$sig_parser = new mnet_encxml_parser();
$sig_parser->parse($plaintextmessage);
if ($sig_parser->signature == '') {
return $plaintextmessage;
}
// Record that the request was signed in some way
$MNET_REMOTE_CLIENT->was_signed();
// Load any information we have about this mnet peer
$MNET_REMOTE_CLIENT->set_wwwroot($sig_parser->remote_wwwroot);
$payload = base64_decode($sig_parser->data_object);
$signature = base64_decode($sig_parser->signature);
$certificate = $MNET_REMOTE_CLIENT->public_key;
// If we don't have any certificate for the host, don't try to check the signature
// Just return the parsed request
if ($certificate == false) {
return $payload;
}
// Does the signature match the data and the public cert?
$signature_verified = openssl_verify($payload, $signature, $certificate);
if ($signature_verified == 0) {
// $signature was not generated for $payload using $certificate
// Get the key the remote peer is currently publishing:
$currkey = mnet_get_public_key($MNET_REMOTE_CLIENT->wwwroot, $MNET_REMOTE_CLIENT->application);
// If the key the remote peer is currently publishing is different to $certificate
if($currkey != $certificate) {
// Try and get the server's new key through trusted means
$MNET_REMOTE_CLIENT->refresh_key();
// If we did manage to re-key, try to verify the signature again using the new public key.
$certificate = $MNET_REMOTE_CLIENT->public_key;
$signature_verified = openssl_verify($payload, $signature, $certificate);
}
}
if ($signature_verified == 1) {
$MNET_REMOTE_CLIENT->signature_verified();
$MNET_REMOTE_CLIENT->touch();
}
$sig_parser->free_resource();
return $payload;
}
/**
* Return the proper XML-RPC content to report an error in the local language.
*
* @param int $code The ID code of the error message
* @param string $text The array-key of the error message in the lang file
* or the full string (will be detected by the function
* @param string $param The $a param for the error message in the lang file
* @return string $text The text of the error message
*/
function mnet_server_fault($code, $text, $param = null) {
global $MNET_REMOTE_CLIENT;
if (!is_numeric($code)) {
$code = 0;
}
$code = intval($code);
$string = get_string($text, 'mnet', $param);
if (strpos($string, '[[') === 0) {
$string = $text;
}
return mnet_server_fault_xml($code, $string);
}
/**
* Return the proper XML-RPC content to report an error.
*
* @param int $code The ID code of the error message
* @param string $text The error message
* @param resource $privatekey The private key that should be used to sign the response
* @return string $text The XML text of the error message
*/
function mnet_server_fault_xml($code, $text, $privatekey = null) {
global $MNET_REMOTE_CLIENT, $CFG;
// Replace illegal XML chars - is this already in a lib somewhere?
$text = str_replace(array('<','>','&','"',"'"), array('&lt;','&gt;','&amp;','&quot;','&apos;'), $text);
$return = mnet_server_prepare_response('<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>'.$code.'</int></value>
</member>
<member>
<name>faultString</name>
<value><string>'.$text.'</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>', $privatekey);
if (!empty($CFG->mnet_rpcdebug)) {
trigger_error("XMLRPC Error Response $code: $text");
trigger_error(print_r($return,1));
}
return $return;
}
/**
* Package a response in any required envelope, and return it to the client
*
* @param string $response The XMLRPC response string
* @param resource $privatekey The private key to sign the response with
* @return string The encoded response string
*/
function mnet_server_prepare_response($response, $privatekey = null) {
global $MNET_REMOTE_CLIENT;
if ($MNET_REMOTE_CLIENT->request_was_signed) {
$response = mnet_sign_message($response, $privatekey);
}
if ($MNET_REMOTE_CLIENT->request_was_encrypted) {
$response = mnet_encrypt_message($response, $MNET_REMOTE_CLIENT->public_key);
}
return $response;
}
/**
* If security checks are passed, dispatch the request to the function/method
*
* The config variable 'mnet_dispatcher_mode' can be:
* strict: Only execute functions that are in specific files
* off: The default - don't execute anything
*
* @param string $payload The XML-RPC request
*
* @throws mnet_server_exception
*
* @return No return val - just echo the response
*/
function mnet_server_dispatch($payload) {
global $CFG, $MNET_REMOTE_CLIENT, $DB;
// xmlrpc_decode_request returns an array of parameters, and the $method
// variable (which is passed by reference) is instantiated with the value from
// the methodName tag in the xml payload
// xmlrpc_decode_request($xml, &$method)
$params = xmlrpc_decode_request($payload, $method);
// $method is something like: "mod/forum/lib.php/forum_add_instance"
// $params is an array of parameters. A parameter might itself be an array.
// Whitelist characters that are permitted in a method name
// The method name must not begin with a / - avoid absolute paths
// A dot character . is only allowed in the filename, i.e. something.php
if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_\.-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) {
throw new mnet_server_exception(713, 'nosuchfunction');
}
if(preg_match("/^system\./", $method)) {
$callstack = explode('.', $method);
} else {
$callstack = explode('/', $method);
// callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance');
}
/**
* What has the site administrator chosen as his dispatcher setting?
* strict: Only execute functions that are in specific files
* off: The default - don't execute anything
*/
////////////////////////////////////// OFF
if (!isset($CFG->mnet_dispatcher_mode) ) {
set_config('mnet_dispatcher_mode', 'off');
throw new mnet_server_exception(704, 'nosuchservice');
} elseif ('off' == $CFG->mnet_dispatcher_mode) {
throw new mnet_server_exception(704, 'nosuchservice');
////////////////////////////////////// SYSTEM METHODS
} elseif ($callstack[0] == 'system') {
$functionname = $callstack[1];
$xmlrpcserver = xmlrpc_server_create();
// register all the system methods
$systemmethods = array('listMethods', 'methodSignature', 'methodHelp', 'listServices', 'listFiles', 'retrieveFile', 'keyswap');
foreach ($systemmethods as $m) {
// I'm adding the canonical xmlrpc references here, however we've
// already forbidden that the period (.) should be allowed in the call
// stack, so if someone tries to access our XMLRPC in the normal way,
// they'll already have received a RPC server fault message.
// Maybe we should allow an easement so that regular XMLRPC clients can
// call our system methods, and find out what we have to offer?
$handler = 'mnet_system';
if ($m == 'keyswap') {
$handler = 'mnet_keyswap';
}
if ($method == 'system.' . $m || $method == 'system/' . $m) {
xmlrpc_server_register_method($xmlrpcserver, $method, $handler);
$response = xmlrpc_server_call_method($xmlrpcserver, $payload, $MNET_REMOTE_CLIENT, array("encoding" => "utf-8"));
$response = mnet_server_prepare_response($response);
echo $response;
xmlrpc_server_destroy($xmlrpcserver);
return;
}
}
throw new mnet_server_exception(7018, 'nosuchfunction');
//////////////////////////////////// NORMAL PLUGIN DISPATCHER
} else {
// anything else comes from some sort of plugin
if ($rpcrecord = $DB->get_record('mnet_rpc', array('xmlrpc_path' => $method))) {
$response = mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload);
$response = mnet_server_prepare_response($response);
echo $response;
return;
// if the rpc record isn't found, check to see if dangerous mode is on
////////////////////////////////////// DANGEROUS
} else if ('dangerous' == $CFG->mnet_dispatcher_mode && $MNET_REMOTE_CLIENT->plaintext_is_ok()) {
$functionname = array_pop($callstack);
$filename = clean_param(implode('/',$callstack), PARAM_PATH);
if (0 == preg_match("/php$/", $filename)) {
// Filename doesn't end in 'php'; possible attack?
// Generate error response - unable to locate function
throw new mnet_server_exception(7012, 'nosuchfunction');
}
// The call stack holds the path to any include file
$includefile = $CFG->dirroot.'/'.$filename;
$response = mnet_server_invoke_dangerous_method($includefile, $functionname, $method, $payload);
echo $response;
return;
}
}
throw new mnet_server_exception(7012, 'nosuchfunction');
}
/**
* Execute the system functions - mostly for introspection
*
* @param string $method XMLRPC method name, e.g. system.listMethods
* @param array $params Array of parameters from the XMLRPC request
* @param string $hostinfo Hostinfo object from the mnet_host table
*
* @throws mnet_server_exception
*
* @return mixed Response data - any kind of PHP variable
*/
function mnet_system($method, $params, $hostinfo) {
global $CFG, $DB;
if (empty($hostinfo)) return array();
$id_list = $hostinfo->id;
if (!empty($CFG->mnet_all_hosts_id)) {
$id_list .= ', '.$CFG->mnet_all_hosts_id;
}
if ('system.listMethods' == $method || 'system/listMethods' == $method) {
$query = '
SELECT DISTINCT
rpc.function_name,
rpc.xmlrpc_path
FROM
{mnet_host2service} h2s
JOIN {mnet_service2rpc} s2r ON h2s.serviceid = s2r.serviceid
JOIN {mnet_rpc} rpc ON s2r.rpcid = rpc.id
JOIN {mnet_service} svc ON svc.id = s2r.serviceid
WHERE
h2s.hostid in ('.$id_list .') AND
h2s.publish = 1 AND rpc.enabled = 1
' . ((count($params) > 0) ? 'AND svc.name = ? ' : '') . '
ORDER BY
rpc.xmlrpc_path ASC';
if (count($params) > 0) {
$params = array($params[0]);
}
$methods = array();
foreach ($DB->get_records_sql($query, $params) as $result) {
$methods[] = $result->xmlrpc_path;
}
return $methods;
} elseif (in_array($method, array('system.methodSignature', 'system/methodSignature', 'system.methodHelp', 'system/methodHelp'))) {
$query = '
SELECT DISTINCT
rpc.function_name,
rpc.help,
rpc.profile
FROM
{mnet_host2service} h2s,
{mnet_service2rpc} s2r,
{mnet_rpc} rpc
WHERE
rpc.xmlrpc_path = ? AND
s2r.rpcid = rpc.id AND
h2s.publish = 1 AND rpc.enabled = 1 AND
h2s.serviceid = s2r.serviceid AND
h2s.hostid in ('.$id_list .')';
$params = array($params[0]);
if (!$result = $DB->get_record_sql($query, $params)) {
return false;
}
if (strpos($method, 'methodSignature') !== false) {
return unserialize($result->profile);
}
return $result->help;
} elseif ('system.listServices' == $method || 'system/listServices' == $method) {
$query = '
SELECT DISTINCT
s.id,
s.name,
s.apiversion,
h2s.publish,
h2s.subscribe
FROM
{mnet_host2service} h2s,
{mnet_service} s
WHERE
h2s.serviceid = s.id AND
(h2s.publish = 1 OR h2s.subscribe = 1) AND
h2s.hostid in ('.$id_list .')
ORDER BY
s.name ASC';
$params = array();
$result = $DB->get_records_sql($query, $params);
$services = array();
if (is_array($result)) {
foreach($result as $service) {
$services[] = array('name' => $service->name,
'apiversion' => $service->apiversion,
'publish' => $service->publish,
'subscribe' => $service->subscribe);
}
}
return $services;
}
throw new mnet_server_exception(7019, 'nosuchfunction');
}
/**
* Invoke a normal style plugin method
* This will verify permissions first.
*
* @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise
* @param array $callstack the exploded callstack
* @param stdclass $rpcrecord the record from mnet_rpc
*
* @return mixed the response from the invoked method
*/
function mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload) {
mnet_verify_permissions($rpcrecord); // will throw exceptions
mnet_setup_dummy_method($method, $callstack, $rpcrecord);
$methodname = array_pop($callstack);
$xmlrpcserver = xmlrpc_server_create();
xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method');
$response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8"));
xmlrpc_server_destroy($xmlrpcserver);
return $response;
}
/**
* Initialize the object (if necessary), execute the method or function, and
* return the response
*
* @param string $includefile The file that contains the object definition
* @param string $methodname The name of the method to execute
* @param string $method The full path to the method
* @param string $payload The XML-RPC request payload
* @param string $class The name of the class to instantiate (or false)
*
* @throws mnet_server_exception
*
* @return string The XML-RPC response
*/
function mnet_server_invoke_dangerous_method($includefile, $methodname, $method, $payload) {
if (file_exists($CFG->dirroot . $includefile)) {
require_once $CFG->dirroot . $includefile;
// $callprefix matches the rpc convention
// of not having a leading slash
$callprefix = preg_replace('!^/!', '', $includefile);
} else {
throw new mnet_server_exception(705, "nosuchfile");
}
if ($functionname != clean_param($functionname, PARAM_PATH)) {
throw new mnet_server_exception(7012, "nosuchfunction");
}
if (!function_exists($functionname)) {
throw new mnet_server_exception(7012, "nosuchfunction");
}
$xmlrpcserver = xmlrpc_server_create();
xmlrpc_server_register_method($xmlrpcserver, $method, 'mnet_server_dummy_method');
$response = xmlrpc_server_call_method($xmlrpcserver, $payload, $methodname, array("encoding" => "utf-8"));
xmlrpc_server_destroy($xmlrpcserver);
return $response;
}
/**
* Accepts a public key from a new remote host and returns the public key for
* this host. If 'register all hosts' is turned on, it will bootstrap a record
* for the remote host in the mnet_host table (if it's not already there)
*
* @param string $function XML-RPC requires this but we don't... discard!
* @param array $params Array of parameters
* $params[0] is the remote wwwroot
* $params[1] is the remote public key
* @return string The XML-RPC response
*/
function mnet_keyswap($function, $params) {
global $CFG, $MNET;
$return = array();
if (!empty($CFG->mnet_register_allhosts)) {
$mnet_peer = new mnet_peer();
@list($wwwroot, $pubkey, $application) = each($params);
$keyok = $mnet_peer->bootstrap($wwwroot, $pubkey, $application);
if ($keyok) {
$mnet_peer->commit();
}
}
return $MNET->public_key;
}
/**
* Verify that the requested xmlrpc method can be called
* This just checks the method exists in the rpc table and is enabled.
*
* @param stdclass $rpcrecord the record from mnet_rpc
*
* @throws mnet_server_exception
*/
function mnet_verify_permissions($rpcrecord) {
global $CFG, $MNET_REMOTE_CLIENT, $DB;
$id_list = $MNET_REMOTE_CLIENT->id;
if (!empty($CFG->mnet_all_hosts_id)) {
$id_list .= ', '.$CFG->mnet_all_hosts_id;
}
$sql = "SELECT
r.*, h2s.publish
FROM
{mnet_rpc} r
JOIN {mnet_service2rpc} s2r ON s2r.rpcid = r.id
LEFT JOIN {mnet_host2service} h2s ON h2s.serviceid = s2r.serviceid
WHERE
r.id = ? AND
h2s.hostid in ($id_list)";
$params = array($rpcrecord->id);
if (!$permission = $DB->get_record_sql($sql, $params)) {
throw new mnet_server_exception(7012, "nosuchfunction");
} else if (!$permission->publish || !$permission->enabled) {
throw new mnet_server_exception(707, "nosuchfunction");
}
}
/**
* Figure out exactly what needs to be called and stashes it in $MNET_REMOTE_CLIENT
* Does some further verification that the method is callable
*
* @param string $method the full xmlrpc method that was called eg auth/mnet/auth.php/user_authorise
* @param array $callstack the exploded callstack
* @param stdclass $rpcrecord the record from mnet_rpc
*
* @throws mnet_server_exception
*/
function mnet_setup_dummy_method($method, $callstack, $rpcrecord) {
global $MNET_REMOTE_CLIENT, $CFG;
// verify that the callpath in the stack matches our records
// callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance');
$path = get_plugin_directory($rpcrecord->plugintype, $rpcrecord->pluginname, false);
array_pop($callstack);
$providedpath = implode('/', $callstack);
if ($providedpath != $path . '/' . $rpcrecord->filename) {
throw new mnet_server_exception(705, "nosuchfile");
}
if (!file_exists($CFG->dirroot . '/' . $providedpath)) {
throw new mnet_server_exception(705, "nosuchfile");
}
require_once($CFG->dirroot . '/' . $providedpath);
if (!empty($rpcrecord->classname)) {
if (!class_exists($rpcrecord->classname)) {
throw new mnet_server_exception(708, 'nosuchclass');
}
if (!$rpcrecord->static) {
try {
$object = new $rpcrecord->classname;
} catch (Exception $e) {
throw new mnet_server_exception(709, "classerror");
}
if (!is_callable(array($object, $rpcrecord->function_name))) {
throw new mnet_server_exception(706, "nosuchfunction");
}
$MNET_REMOTE_CLIENT->object_to_call($object);
} else {
if (!is_callable(array($rpcrecord->classname, $rpcrecord->function_name))) {
throw new mnet_server_exception(706, "nosuchfunction");
}
$MNET_REMOTE_CLIENT->static_location($rpcrecord->classname);
}
}
}
/**
* Dummy function for the XML-RPC dispatcher - use to call a method on an object
* or to call a function
*
* Translate XML-RPC's strange function call syntax into a more straightforward
* PHP-friendly alternative. This dummy function will be called by the
* dispatcher, and can be used to call a method on an object, or just a function
*
* The methodName argument (eg. mnet/testlib/mnet_concatenate_strings)
* is ignored.
*
* @throws mnet_server_exception
*
* @param string $methodname We discard this - see 'functionname'
* @param array $argsarray Each element is an argument to the real
* function
* @param string $functionname The name of the PHP function you want to call
* @return mixed The return value will be that of the real
* function, whatever it may be.
*/
function mnet_server_dummy_method($methodname, $argsarray, $functionname) {
global $MNET_REMOTE_CLIENT;
try {
if (is_object($MNET_REMOTE_CLIENT->object_to_call)) {
return @call_user_func_array(array($MNET_REMOTE_CLIENT->object_to_call,$functionname), $argsarray);
} else if (!empty($MNET_REMOTE_CLIENT->static_location)) {
return @call_user_func_array(array($MNET_REMOTE_CLIENT->static_location, $functionname), $argsarray);
} else {
return @call_user_func_array($functionname, $argsarray);
}
} catch (Exception $e) {
exit(mnet_server_fault($e->getCode(), $e->getMessage()));
}
}
/**
* mnet server exception. extends moodle_exception, but takes slightly different arguments.
* error strings are always in mnet, so $module is not necessary,
* and unlike the rest of moodle, the actual int error code is used.
* this exception should only be used during an xmlrpc server request, ie, not for client requests.
*/
class mnet_server_exception extends moodle_exception {
/**
* @param int $intcode the numerical error associated with this fault. this is <b>not</b> the string errorcode
* @param string $languagekey the key to the language string for the error message, or the text of the message (mnet_server_fault figures it out for you) if you're not using mnet error string file
* @param mixed $a params for get_string
*/
public function __construct($intcode, $languagekey, $a=null) {
parent::__construct($languagekey, 'mnet', '', $a);
$this->code = $intcode;
$this->message = $languagekey; // override this because mnet_server_fault (our handler) uses this directly
}
}

View File

@ -0,0 +1,38 @@
<?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/>.
/**
* This file contains the mnet services for the mahara portfolio plugin
*
* @since 2.0
* @package moodlecore
* @subpackage portfolio
* @copyright 2010 Penny Leach
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$publishes = array(
'pf' => array(
'apiversion' => 1,
'classname' => 'portfolio_plugin_mahara',
'filename' => 'lib.php',
'methods' => array(
'fetch_file'
),
),
);

View File

@ -0,0 +1,39 @@
<?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/>.
/**
* This file contains the mnet services for the remotemoodle repository plugin
*
* @since 2.0
* @package moodlecore
* @subpackage repository
* @copyright 2010 Penny Leach
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$publishes = array(
'remoterep' => array(
'apiversion' => 1,
'classname' => 'repository_remotemoodle',
'filename' => 'repository.class.php',
'methods' => array(
'getFileList',
'retrieveFile',
),
),
);