From 5593d2dc360fddf023bc6d6eba43cc932b9b1bf7 Mon Sep 17 00:00:00 2001 From: skodak Date: Tue, 20 Oct 2009 22:30:18 +0000 Subject: [PATCH] MDL-12886 refactored function return full external function info and improved reporting of access control problems --- lang/en_utf8/webservice.php | 1 + lib/externallib.php | 68 +++++++++++++++ webservice/lib.php | 129 ++++++++--------------------- webservice/xmlrpc/locallib.php | 2 - webservice/xmlrpc/simpleserver.php | 4 - 5 files changed, 103 insertions(+), 101 deletions(-) diff --git a/lang/en_utf8/webservice.php b/lang/en_utf8/webservice.php index 6f3264a67a..028ef711ad 100644 --- a/lang/en_utf8/webservice.php +++ b/lang/en_utf8/webservice.php @@ -1,5 +1,6 @@ get_record('external_functions', array('name'=>$function), '*', $strictness)) { + return false; + } + } + + //first find and include the ext implementation class + $function->classpath = empty($function->classpath) ? get_component_directory($function->component).'/externallib.php' : $CFG->dirroot.'/'.$function->classpath; + if (!file_exists($function->classpath)) { + throw new coding_exception('Can not find file with external function implementation'); + } + require_once($function->classpath); + + $function->parameters_method = $function->methodname.'_parameters'; + $function->returns_method = $function->methodname.'_returns'; + + // make sure the implementaion class is ok + if (!method_exists($function->classname, $function->methodname)) { + throw new coding_exception('Missing implementation method'); + } + if (!method_exists($function->classname, $function->parameters_method)) { + throw new coding_exception('Missing parameters description'); + } + if (!method_exists($function->classname, $function->returns_method)) { + throw new coding_exception('Missing returned values description'); + } + + // fetch the parameters description + $function->parameters_desc = call_user_func(array($function->classname, $function->parameters_method)); + if (!($function->parameters_desc instanceof external_function_parameters)) { + throw new coding_exception('Invalid parameters description'); + } + + // fetch the return values description + $function->returns_desc = call_user_func(array($function->classname, $function->returns_method)); + // null means void result or result is ignored + if (!is_null($function->returns_desc) and !($function->returns_desc instanceof external_description)) { + throw new coding_exception('Invalid return description'); + } + + //now get the function description + //TODO: use localised lang pack descriptions, it would be nice to have + // easy to understand descriptiosn in admin UI, + // on the other hand this is still a bit in a flux and we need to find some new naming + // conventions for these descriptions in lang packs + $function->description = null; + $servicesfile = get_component_directory($function->component).'/db/services.php'; + if (file_exists($servicesfile)) { + $functions = null; + include($servicesfile); + if (isset($functions[$function->name]['description'])) { + $function->description = $functions[$function->name]['description']; + } + } + + return $function; +} + /** * Exception indicating user is not allowed to use external function in * the current context. diff --git a/webservice/lib.php b/webservice/lib.php index 09faa37199..9e8ac4d08c 100644 --- a/webservice/lib.php +++ b/webservice/lib.php @@ -25,6 +25,18 @@ require_once($CFG->libdir.'/externallib.php'); +/** + * Exception indicating access control problem in web service call + */ +class webservice_access_exception extends moodle_exception { + /** + * Constructor + */ + function __construct($debuginfo) { + parent::__construct('accessexception', 'exception', '', null, $debuginfo); + } +} + function webservice_protocol_is_enabled($protocol) { global $CFG; @@ -118,10 +130,11 @@ abstract class webservice_zend_server implements webservice_server { // start the server $this->zend_server->setClass($this->service_class); $response = $this->zend_server->handle(); - - //$grrr = ob_get_clean(); - //error_log($grrr); - +/* + $grrr = ob_get_clean(); + error_log($grrr); + error_log($response); +*/ // session cleanup $this->session_cleanup(); @@ -204,6 +217,7 @@ abstract class webservice_zend_server implements webservice_server { $methods .= $this->get_virtual_method_code($function); } + // let's use unique class name, there might be problem in unit tests $classname = 'webservices_virtual_class_000000'; while(class_exists($classname)) { $classname++; @@ -230,39 +244,7 @@ class '.$classname.' { protected function get_virtual_method_code($function) { global $CFG; - //first find and include the ext implementation class - $function->classpath = empty($function->classpath) ? get_component_directory($function->component).'/externallib.php' : $CFG->dirroot.'/'.$function->classpath; - if (!file_exists($function->classpath)) { - throw new coding_exception('Can not find file with external function implementation'); - } - require_once($function->classpath); - - $function->parameters_method = $function->methodname.'_parameters'; - $function->returns_method = $function->methodname.'_returns'; - - // make sure the implementaion class is ok - if (!method_exists($function->classname, $function->methodname)) { - throw new coding_exception('Missing implementation method'); - } - if (!method_exists($function->classname, $function->parameters_method)) { - throw new coding_exception('Missing parameters description'); - } - if (!method_exists($function->classname, $function->returns_method)) { - throw new coding_exception('Missing returned values description'); - } - - // fetch the parameters description - $function->parameters_desc = call_user_func(array($function->classname, $function->parameters_method)); - if (!($function->parameters_desc instanceof external_function_parameters)) { - throw new coding_exception('Invalid parameters description'); - } - - // fetch the return values description - $function->returns_desc = call_user_func(array($function->classname, $function->returns_method)); - // null means void result or result is ignored - if (!is_null($function->returns_desc) and !($function->returns_desc instanceof external_description)) { - throw new coding_exception('Invalid return description'); - } + $function = external_function_info($function); $params = array(); $params_desc = array(); @@ -316,8 +298,8 @@ class '.$classname.' { $code = ' /** - * External function: '.$function->name.' - * TODO: add function description + * '.$function->description.' + * '.$params_desc.' '.$return.' */ @@ -370,27 +352,25 @@ class '.$classname.' { $this->restricted_context = get_context_instance(CONTEXT_SYSTEM); if (!is_enabled_auth('webservice')) { - error_log('WS auth not enabled'); - die('WS auth not enabled'); + throw new webservice_access_exception('WS auth not enabled'); } if (!$auth = get_auth_plugin('webservice')) { - error_log('WS auth missing'); - die('WS auth missing'); + throw new webservice_access_exception('WS auth missing'); } // the username is hardcoded as URL parameter because we can not easily parse the request data :-( if (!$username = optional_param('wsusername', '', PARAM_RAW)) { - throw new invalid_parameter_exception('Missing username'); + throw new webservice_access_exception('Missing username'); } // the password is hardcoded as URL parameter because we can not easily parse the request data :-( if (!$password = optional_param('wspassword', '', PARAM_RAW)) { - throw new invalid_parameter_exception('Missing password'); + throw new webservice_access_exception('Missing password'); } if (!$auth->user_login_webservice($username, $password)) { - throw new invalid_parameter_exception('Wrong username or password'); + throw new webservice_access_exception('Wrong username or password'); } $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0), '*', MUST_EXIST); @@ -406,7 +386,7 @@ class '.$classname.' { } if (!has_capability("webservice/$this->wsname:use", $this->restricted_context)) { - throw new invalid_parameter_exception('Access to web service not allowed'); + throw new webservice_access_exception('Access to web service not allowed'); } external_api::set_context_restriction($this->restricted_context); @@ -630,23 +610,23 @@ abstract class webservice_base_server implements webservice_server { $this->restricted_context = get_context_instance(CONTEXT_SYSTEM); if (!is_enabled_auth('webservice')) { - die('WS auth not enabled'); + throw new webservice_access_exception('WS auth not enabled'); } if (!$auth = get_auth_plugin('webservice')) { - die('WS auth missing'); + throw new webservice_access_exception('WS auth missing'); } if (!$this->username) { - throw new invalid_parameter_exception('Missing username'); + throw new webservice_access_exception('Missing username'); } if (!$this->password) { - throw new invalid_parameter_exception('Missing password'); + throw new webservice_access_exception('Missing password'); } if (!$auth->user_login_webservice($this->username, $this->password)) { - throw new invalid_parameter_exception('Wrong username or password'); + throw new webservice_access_exception('Wrong username or password'); } $user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0), '*', MUST_EXIST); @@ -661,7 +641,7 @@ abstract class webservice_base_server implements webservice_server { } if (!has_capability("webservice/$this->wsname:use", $this->restricted_context)) { - throw new invalid_parameter_exception('Access to web service not allowed'); + throw new webservice_access_exception('Access to web service not allowed'); } external_api::set_context_restriction($this->restricted_context); @@ -681,8 +661,7 @@ abstract class webservice_base_server implements webservice_server { } // function must exist - $function = $DB->get_record('external_functions', array('name'=>$this->functionname), '*', MUST_EXIST); - + $function = external_function_info($this->functionname); // now let's verify access control if ($this->simple) { @@ -730,46 +709,6 @@ abstract class webservice_base_server implements webservice_server { if (!$allowed) { throw new invalid_parameter_exception('Access to external function not allowed'); } - // now we finally know the user may execute this function, - // the last step is to set context restriction - in this simple case - // we use system context because each external system has different user account - // and we can manage everything through normal permissions. - - // get the params and return descriptions of the function - unset($function->id); // we want to prevent any accidental db updates ;-) - - $function->classpath = empty($function->classpath) ? get_component_directory($function->component).'/externallib.php' : $CFG->dirroot.'/'.$function->classpath; - if (!file_exists($function->classpath)) { - throw new coding_exception('Can not find file with external function implementation'); - } - require_once($function->classpath); - - $function->parameters_method = $function->methodname.'_parameters'; - $function->returns_method = $function->methodname.'_returns'; - - // make sure the implementaion class is ok - if (!method_exists($function->classname, $function->methodname)) { - throw new coding_exception('Missing implementation method'); - } - if (!method_exists($function->classname, $function->parameters_method)) { - throw new coding_exception('Missing parameters description'); - } - if (!method_exists($function->classname, $function->returns_method)) { - throw new coding_exception('Missing returned values description'); - } - - // fetch the parameters description - $function->parameters_desc = call_user_func(array($function->classname, $function->parameters_method)); - if (!($function->parameters_desc instanceof external_function_parameters)) { - throw new coding_exception('Invalid parameters description'); - } - - // fetch the return values description - $function->returns_desc = call_user_func(array($function->classname, $function->returns_method)); - // null means void result or result is ignored - if (!is_null($function->returns_desc) and !($function->returns_desc instanceof external_description)) { - throw new coding_exception('Invalid return description'); - } // we have all we need now $this->function = $function; diff --git a/webservice/xmlrpc/locallib.php b/webservice/xmlrpc/locallib.php index 38afe3bcb0..75f8c9e76a 100644 --- a/webservice/xmlrpc/locallib.php +++ b/webservice/xmlrpc/locallib.php @@ -37,7 +37,5 @@ class webservice_xmlrpc_server extends webservice_zend_server { parent::__construct('Zend_XmlRpc_Server'); $this->wsname = 'xmlrpc'; } - - } diff --git a/webservice/xmlrpc/simpleserver.php b/webservice/xmlrpc/simpleserver.php index 2020162867..c7a1338b35 100644 --- a/webservice/xmlrpc/simpleserver.php +++ b/webservice/xmlrpc/simpleserver.php @@ -33,12 +33,8 @@ require_once("$CFG->dirroot/webservice/xmlrpc/locallib.php"); //TODO: for now disable all mess in xml ini_set('display_errors', '0'); ini_set('log_errors', '1'); -$CFG->debug = 0; $CFG->debugdisplay = false; -//error_log('yy'); -//error_log(var_export($_SERVER, true)); - if (!webservice_protocol_is_enabled('xmlrpc')) { die; } -- 2.39.5