Exemple #1
0
if (empty($CFG->enablewsdocumentation)) {
    echo get_string('wsdocumentationdisable', 'webservice');
    die;
}
//check that the current user is the token user
$webservice = new webservice();
$token = $webservice->get_token_by_id($tokenid);
if (empty($token) or empty($token->userid) or empty($USER->id) or $token->userid != $USER->id) {
    throw new moodle_exception('docaccessrefused', 'webservice');
}
// get the list of all functions related to the token
$functions = $webservice->get_external_functions(array($token->externalserviceid));
// get all the function descriptions
$functiondescs = array();
foreach ($functions as $function) {
    $functiondescs[$function->name] = external_function_info($function);
}
//get activated protocol
$activatedprotocol = array();
$activatedprotocol['rest'] = webservice_protocol_is_enabled('rest');
$activatedprotocol['xmlrpc'] = webservice_protocol_is_enabled('xmlrpc');
/// Check if we are in printable mode
$printableformat = false;
if (isset($_REQUEST['print'])) {
    $printableformat = $_REQUEST['print'];
}
/// OUTPUT
echo $OUTPUT->header();
$renderer = $PAGE->get_renderer('core', 'webservice');
echo $renderer->documentation_html($functiondescs, $printableformat, $activatedprotocol, array('id' => $tokenid));
/// trigger browser print operation
Exemple #2
0
    /**
     * Display list of function for a given service
     * If the service is build-in do not display remove/add operation (read-only)
     * @param array $functions
     * @param object $service
     * @return string the table html + add operation html
     */
    public function admin_service_function_list($functions, $service) {
        global $CFG;
        if (!empty($functions)) {
            $table = new html_table();
            $table->head = array(get_string('function', 'webservice'),
                get_string('description'), get_string('requiredcaps', 'webservice'));
            $table->align = array('left', 'left', 'left');
            $table->size = array('15%', '40%', '40%');
            $table->width = '100%';
            $table->align[] = 'left';

            //display remove function operation (except for build-in service)
            if (empty($service->component)) {
                $table->head[] = get_string('edit');
                $table->align[] = 'center';
            }

            foreach ($functions as $function) {
                $function = external_function_info($function);
                $requiredcaps = html_writer::tag('div',
                                empty($function->capabilities) ? '' : $function->capabilities,
                                array('class' => 'functiondesc'));
                ;
                $description = html_writer::tag('div', $function->description,
                                array('class' => 'functiondesc'));
                //display remove function operation (except for build-in service)
                if (empty($service->component)) {
                    $removeurl = new moodle_url('/' . $CFG->admin . '/webservice/service_functions.php',
                                    array('sesskey' => sesskey(), 'fid' => $function->id,
                                        'id' => $service->id,
                                        'action' => 'delete'));
                    $removelink = html_writer::tag('a',
                                    get_string('removefunction', 'webservice'),
                                    array('href' => $removeurl));
                    $table->data[] = array($function->name, $description, $requiredcaps, $removelink);
                } else {
                    $table->data[] = array($function->name, $description, $requiredcaps);
                }
            }

            $html = html_writer::table($table);
        } else {
            $html = get_string('nofunctions', 'webservice') . html_writer::empty_tag('br');
        }

        //display add function operation (except for build-in service)
        if (empty($service->component)) {
            $addurl = new moodle_url('/' . $CFG->admin . '/webservice/service_functions.php',
                            array('sesskey' => sesskey(), 'id' => $service->id, 'action' => 'add'));
            $html .= html_writer::tag('a', get_string('addfunctions', 'webservice'), array('href' => $addurl));
        }

        return $html;
    }
Exemple #3
0
 function definition()
 {
     global $CFG;
     $mform = $this->_form;
     $data = $this->_customdata;
     $mform->addElement('header', 'addfunction', get_string('addfunctions', 'webservice'));
     require_once $CFG->dirroot . "/webservice/lib.php";
     $webservicemanager = new webservice();
     $functions = $webservicemanager->get_not_associated_external_functions($data['id']);
     //we add the descriptions to the functions
     foreach ($functions as $functionid => $functionname) {
         //retrieve full function information (including the description)
         $function = external_function_info($functionname);
         $functions[$functionid] = $function->name . ':' . $function->description;
     }
     $mform->addElement('searchableselector', 'fids', get_string('name'), $functions, array('multiple'));
     $mform->addElement('hidden', 'id');
     $mform->setType('id', PARAM_INT);
     $mform->addElement('hidden', 'action');
     $mform->setType('action', PARAM_ACTION);
     $this->add_action_buttons(true, get_string('addfunctions', 'webservice'));
     $this->set_data($data);
 }
Exemple #4
0
    /**
     * Returns a virtual method code for a web service function.
     *
     * NOTE: The implementation of this method has been mostly copied from webservice_zend_server::get_virtual_method_code().
     * @param stdClass $function a record from external_function
     * @return string The PHP code of the virtual method.
     * @throws coding_exception
     * @throws moodle_exception
     */
    protected function get_virtual_method_code($function)
    {
        $function = external_function_info($function);
        // Parameters and their defaults for the method signature.
        $paramanddefaults = array();
        // Parameters for external lib call.
        $params = array();
        $paramdesc = array();
        // The method's input parameters and their respective types.
        $inputparams = array();
        // The method's output parameters and their respective types.
        $outputparams = array();
        foreach ($function->parameters_desc->keys as $name => $keydesc) {
            $param = '$' . $name;
            $paramanddefault = $param;
            if ($keydesc->required == VALUE_OPTIONAL) {
                // It does not make sense to declare a parameter VALUE_OPTIONAL. VALUE_OPTIONAL is used only for array/object key.
                throw new moodle_exception('erroroptionalparamarray', 'webservice', '', $name);
            } else {
                if ($keydesc->required == VALUE_DEFAULT) {
                    // Need to generate the default, if there is any.
                    if ($keydesc instanceof external_value) {
                        if ($keydesc->default === null) {
                            $paramanddefault .= ' = null';
                        } else {
                            switch ($keydesc->type) {
                                case PARAM_BOOL:
                                    $default = (int) $keydesc->default;
                                    break;
                                case PARAM_INT:
                                    $default = $keydesc->default;
                                    break;
                                case PARAM_FLOAT:
                                    $default = $keydesc->default;
                                    break;
                                default:
                                    $default = "'{$keydesc->default}'";
                            }
                            $paramanddefault .= " = {$default}";
                        }
                    } else {
                        // Accept empty array as default.
                        if (isset($keydesc->default) && is_array($keydesc->default) && empty($keydesc->default)) {
                            $paramanddefault .= ' = array()';
                        } else {
                            // For the moment we do not support default for other structure types.
                            throw new moodle_exception('errornotemptydefaultparamarray', 'webservice', '', $name);
                        }
                    }
                }
            }
            $params[] = $param;
            $paramanddefaults[] = $paramanddefault;
            $type = $this->get_phpdoc_type($keydesc);
            $inputparams[$name]['type'] = $type;
            $paramdesc[] = '* @param ' . $type . ' $' . $name . ' ' . $keydesc->desc;
        }
        $paramanddefaults = implode(', ', $paramanddefaults);
        $paramdescstr = implode("\n ", $paramdesc);
        $serviceclassmethodbody = $this->service_class_method_body($function, $params);
        if (empty($function->returns_desc)) {
            $return = '* @return void';
        } else {
            $type = $this->get_phpdoc_type($function->returns_desc);
            $outputparams['return']['type'] = $type;
            $return = '* @return ' . $type . ' ' . $function->returns_desc->desc;
        }
        // Now create the virtual method that calls the ext implementation.
        $code = <<<EOD
/**
 * {$function->description}.
 *
 {$paramdescstr}
 {$return}
 */
public function {$function->name}({$paramanddefaults}) {
{$serviceclassmethodbody}
}
EOD;
        // Prepare the method information.
        $methodinfo = new stdClass();
        $methodinfo->name = $function->name;
        $methodinfo->inputparams = $inputparams;
        $methodinfo->outputparams = $outputparams;
        $methodinfo->description = $function->description;
        // Add the method information into the list of service methods.
        $this->servicemethods[] = $methodinfo;
        return $code;
    }
Exemple #5
0
    $amfclientatag = html_writer::tag('a', get_string('amftestclient', 'webservice'), array('href' => $amfclienturl));
    $descparams->amfatag = $amfclientatag;
    echo get_string('testclientdescription', 'webservice', $descparams);
    echo $OUTPUT->box_end();
    $mform->display();
    echo $OUTPUT->footer();
    die;
}
$class = $function . '_form';
$mform = new $class(null, array('authmethod' => $authmethod));
$mform->set_data(array('function' => $function, 'protocol' => $protocol));
if ($mform->is_cancelled()) {
    redirect('testclient.php');
} else {
    if ($data = $mform->get_data()) {
        $functioninfo = external_function_info($function);
        // first load lib of selected protocol
        require_once "{$CFG->dirroot}/webservice/{$protocol}/locallib.php";
        $testclientclass = "webservice_{$protocol}_test_client";
        if (!class_exists($testclientclass)) {
            throw new coding_exception('Missing WS test class in protocol ' . $protocol);
        }
        $testclient = new $testclientclass();
        $serverurl = "{$CFG->wwwroot}/webservice/{$protocol}/";
        if ($authmethod == 'simple') {
            $serverurl .= 'simpleserver.php';
            $serverurl .= '?wsusername='******'&wspassword='******'token') {
                $serverurl .= 'server.php';
Exemple #6
0
 /**
  * Fetches the function description from database,
  * verifies user is allowed to use this function and
  * loads all paremeters and return descriptions.
  * @return void
  */
 protected function load_function_info()
 {
     global $DB, $USER, $CFG;
     if (empty($this->functionname)) {
         throw new invalid_parameter_exception('Missing function name');
     }
     // function must exist
     $function = external_function_info($this->functionname);
     if ($this->restricted_serviceid) {
         $params = array('sid1' => $this->restricted_serviceid, 'sid2' => $this->restricted_serviceid);
         $wscond1 = 'AND s.id = :sid1';
         $wscond2 = 'AND s.id = :sid2';
     } else {
         $params = array();
         $wscond1 = '';
         $wscond2 = '';
     }
     // now let's verify access control
     // now make sure the function is listed in at least one service user is allowed to use
     // allow access only if:
     //  1/ entry in the external_services_users table if required
     //  2/ validuntil not reached
     //  3/ has capability if specified in service desc
     //  4/ iprestriction
     $sql = "SELECT s.*, NULL AS iprestriction\n                  FROM {external_services} s\n                  JOIN {external_services_functions} sf ON (sf.externalserviceid = s.id AND s.restrictedusers = 0 AND sf.functionname = :name1)\n                 WHERE s.enabled = 1 {$wscond1}\n\n                 UNION\n\n                SELECT s.*, su.iprestriction\n                  FROM {external_services} s\n                  JOIN {external_services_functions} sf ON (sf.externalserviceid = s.id AND s.restrictedusers = 1 AND sf.functionname = :name2)\n                  JOIN {external_services_users} su ON (su.externalserviceid = s.id AND su.userid = :userid)\n                 WHERE s.enabled = 1 AND su.validuntil IS NULL OR su.validuntil < :now {$wscond2}";
     $params = array_merge($params, array('userid' => $USER->id, 'name1' => $function->name, 'name2' => $function->name, 'now' => time()));
     $rs = $DB->get_recordset_sql($sql, $params);
     // now make sure user may access at least one service
     $remoteaddr = getremoteaddr();
     $allowed = false;
     foreach ($rs as $service) {
         if ($service->requiredcapability and !has_capability($service->requiredcapability, $this->restricted_context)) {
             continue;
             // cap required, sorry
         }
         if ($service->iprestriction and !address_in_subnet($remoteaddr, $service->iprestriction)) {
             continue;
             // wrong request source ip, sorry
         }
         $allowed = true;
         break;
         // one service is enough, no need to continue
     }
     $rs->close();
     if (!$allowed) {
         throw new webservice_access_exception('Access to external function not allowed');
     }
     // we have all we need now
     $this->function = $function;
 }
Exemple #7
0
 /**
  * Test init_service_class().
  */
 public function test_init_service_class()
 {
     global $DB, $USER;
     $this->resetAfterTest(true);
     // Set current user.
     $this->setAdminUser();
     // Add a web service.
     $webservice = new stdClass();
     $webservice->name = 'Test web service';
     $webservice->enabled = true;
     $webservice->restrictedusers = false;
     $webservice->component = 'moodle';
     $webservice->timecreated = time();
     $webservice->downloadfiles = true;
     $webservice->uploadfiles = true;
     $externalserviceid = $DB->insert_record('external_services', $webservice);
     // Add token.
     $externaltoken = new stdClass();
     $externaltoken->token = 'testtoken';
     $externaltoken->tokentype = 0;
     $externaltoken->userid = $USER->id;
     $externaltoken->externalserviceid = $externalserviceid;
     $externaltoken->contextid = 1;
     $externaltoken->creatorid = $USER->id;
     $externaltoken->timecreated = time();
     $DB->insert_record('external_tokens', $externaltoken);
     // Add a function to the service.
     $wsmethod = new stdClass();
     $wsmethod->externalserviceid = $externalserviceid;
     $wsmethod->functionname = 'core_course_get_contents';
     $DB->insert_record('external_services_functions', $wsmethod);
     // Initialise the dummy web service.
     $dummy = new webservice_dummy(WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN);
     // Set the token.
     $dummy->set_token($externaltoken->token);
     // Run the web service.
     $dummy->run();
     // Get service methods and structs.
     $servicemethods = $dummy->get_service_methods();
     $servicestructs = $dummy->get_service_structs();
     $this->assertNotEmpty($servicemethods);
     // The function core_course_get_contents should be only the only web service function in the moment.
     $this->assertEquals(1, count($servicemethods));
     // The function core_course_get_contents doesn't have a struct class, so the list of service structs should be empty.
     $this->assertEmpty($servicestructs);
     // Add other functions to the service.
     // The function core_comment_get_comments has one struct class in its output.
     $wsmethod->functionname = 'core_comment_get_comments';
     $DB->insert_record('external_services_functions', $wsmethod);
     // The function core_grades_update_grades has one struct class in its input.
     $wsmethod->functionname = 'core_grades_update_grades';
     $DB->insert_record('external_services_functions', $wsmethod);
     // Run the web service again.
     $dummy->run();
     // Get service methods and structs.
     $servicemethods = $dummy->get_service_methods();
     $servicestructs = $dummy->get_service_structs();
     $this->assertEquals(3, count($servicemethods));
     $this->assertEquals(2, count($servicestructs));
     // Check the contents of service methods.
     foreach ($servicemethods as $method) {
         // Get the external function info.
         $function = external_function_info($method->name);
         // Check input params.
         foreach ($function->parameters_desc->keys as $name => $keydesc) {
             $this->check_params($method->inputparams[$name]['type'], $keydesc, $servicestructs);
         }
         // Check output params.
         $this->check_params($method->outputparams['return']['type'], $function->returns_desc, $servicestructs);
         // Check description.
         $this->assertEquals($function->description, $method->description);
     }
 }
 /**
  * @dataProvider all_external_info_provider
  */
 public function test_all_external_info($f)
 {
     $desc = external_function_info($f);
     $this->assertNotEmpty($desc->name);
     $this->assertNotEmpty($desc->classname);
     $this->assertNotEmpty($desc->methodname);
     $this->assertEquals($desc->component, clean_param($desc->component, PARAM_COMPONENT));
     $this->assertInstanceOf('external_function_parameters', $desc->parameters_desc);
     if ($desc->returns_desc != null) {
         $this->assertInstanceOf('external_description', $desc->returns_desc);
     }
 }
Exemple #9
0
 /**
  * Fetches the function description from database,
  * verifies user is allowed to use this function and
  * loads all paremeters and return descriptions.
  */
 protected function load_function_info()
 {
     global $DB, $USER, $CFG;
     if (empty($this->functionname)) {
         throw new invalid_parameter_exception('Missing function name');
     }
     // function must exist
     $function = external_function_info($this->functionname);
     if ($this->restricted_serviceid) {
         $params = array('sid1' => $this->restricted_serviceid, 'sid2' => $this->restricted_serviceid);
         $wscond1 = 'AND s.id = :sid1';
         $wscond2 = 'AND s.id = :sid2';
     } else {
         $params = array();
         $wscond1 = '';
         $wscond2 = '';
     }
     // now let's verify access control
     // now make sure the function is listed in at least one service user is allowed to use
     // allow access only if:
     //  1/ entry in the external_services_users table if required
     //  2/ validuntil not reached
     //  3/ has capability if specified in service desc
     //  4/ iprestriction
     $sql = "SELECT s.*, NULL AS iprestriction\n                  FROM {external_services} s\n                  JOIN {external_services_functions} sf ON (sf.externalserviceid = s.id AND s.restrictedusers = 0 AND sf.functionname = :name1)\n                 WHERE s.enabled = 1 {$wscond1}\n\n                 UNION\n\n                SELECT s.*, su.iprestriction\n                  FROM {external_services} s\n                  JOIN {external_services_functions} sf ON (sf.externalserviceid = s.id AND s.restrictedusers = 1 AND sf.functionname = :name2)\n                  JOIN {external_services_users} su ON (su.externalserviceid = s.id AND su.userid = :userid)\n                 WHERE s.enabled = 1 AND (su.validuntil IS NULL OR su.validuntil < :now) {$wscond2}";
     $params = array_merge($params, array('userid' => $USER->id, 'name1' => $function->name, 'name2' => $function->name, 'now' => time()));
     $rs = $DB->get_recordset_sql($sql, $params);
     // now make sure user may access at least one service
     $remoteaddr = getremoteaddr();
     $allowed = false;
     foreach ($rs as $service) {
         if ($service->requiredcapability and !has_capability($service->requiredcapability, $this->restricted_context)) {
             continue;
             // cap required, sorry
         }
         if ($service->iprestriction and !address_in_subnet($remoteaddr, $service->iprestriction)) {
             continue;
             // wrong request source ip, sorry
         }
         $allowed = true;
         break;
         // one service is enough, no need to continue
     }
     $rs->close();
     if (!$allowed) {
         throw new webservice_access_exception('Access to the function ' . $this->functionname . '() is not allowed.
                  There could be multiple reasons for this:
                  1. The service linked to the user token does not contain the function.
                  2. The service is user-restricted and the user is not listed.
                  3. The service is IP-restricted and the user IP is not listed.
                  4. The service is time-restricted and the time has expired.
                  5. The token is time-restricted and the time has expired.
                  6. The service requires a specific capability which the user does not have.
                  7. The function is called with username/password (no user token is sent)
                  and none of the services has the function to allow the user.
                  These settings can be found in Administration > Site administration
                  > Plugins > Web services > External services and Manage tokens.');
     }
     // we have all we need now
     $this->function = $function;
 }
Exemple #10
0
require_once $CFG->libdir . '/externallib.php';
require_sesskey();
$rawjson = file_get_contents('php://input');
$requests = json_decode($rawjson, true);
if ($requests === null) {
    $lasterror = json_last_error_msg();
    throw new coding_exception('Invalid json in request: ' . $lasterror);
}
$responses = array();
foreach ($requests as $request) {
    $response = array();
    $methodname = clean_param($request['methodname'], PARAM_ALPHANUMEXT);
    $index = clean_param($request['index'], PARAM_INT);
    $args = $request['args'];
    try {
        $externalfunctioninfo = external_function_info($methodname);
        if (!$externalfunctioninfo->allowed_from_ajax) {
            throw new moodle_exception('servicenotavailable', 'webservice');
        }
        // Do not allow access to write or delete webservices as a public user.
        if ($externalfunctioninfo->loginrequired) {
            if (!isloggedin()) {
                error_log('This external function is not available to public users. Failed to call "' . $methodname . '"');
                throw new moodle_exception('servicenotavailable', 'webservice');
            }
        }
        // Validate params, this also sorts the params properly, we need the correct order in the next part.
        $callable = array($externalfunctioninfo->classname, 'validate_parameters');
        $params = call_user_func($callable, $externalfunctioninfo->parameters_desc, $args);
        // Execute - gulp!
        $callable = array($externalfunctioninfo->classname, $externalfunctioninfo->methodname);
    /**
     * returns virtual method code
     *
     * @param stdClass $function a record from external_function
     * @return string PHP code
     */
    protected function get_virtual_method_code($function)
    {
        global $CFG;
        $function = external_function_info($function);
        //arguments in function declaration line with defaults.
        $paramanddefaults = array();
        //arguments used as parameters for external lib call.
        $params = array();
        $params_desc = array();
        foreach ($function->parameters_desc->keys as $name => $keydesc) {
            $param = '$' . $name;
            $paramanddefault = $param;
            //need to generate the default if there is any
            if ($keydesc instanceof external_value) {
                if ($keydesc->required == VALUE_DEFAULT) {
                    if ($keydesc->default === null) {
                        $paramanddefault .= '=null';
                    } else {
                        switch ($keydesc->type) {
                            case PARAM_BOOL:
                                $paramanddefault .= '=' . $keydesc->default;
                                break;
                            case PARAM_INT:
                                $paramanddefault .= '=' . $keydesc->default;
                                break;
                            case PARAM_FLOAT:
                                $paramanddefault .= '=' . $keydesc->default;
                                break;
                            default:
                                $paramanddefault .= '=\'' . $keydesc->default . '\'';
                        }
                    }
                } else {
                    if ($keydesc->required == VALUE_OPTIONAL) {
                        //it does make sens to declare a parameter VALUE_OPTIONAL
                        //VALUE_OPTIONAL is used only for array/object key
                        throw new moodle_exception('parametercannotbevalueoptional');
                    }
                }
            } else {
                //for the moment we do not support default for other structure types
                if ($keydesc->required == VALUE_DEFAULT) {
                    //accept empty array as default
                    if (isset($keydesc->default) and is_array($keydesc->default) and empty($keydesc->default)) {
                        $paramanddefault .= '=array()';
                    } else {
                        throw new moodle_exception('errornotemptydefaultparamarray', 'webservice', '', $name);
                    }
                }
                if ($keydesc->required == VALUE_OPTIONAL) {
                    throw new moodle_exception('erroroptionalparamarray', 'webservice', '', $name);
                }
            }
            $params[] = $param;
            $paramanddefaults[] = $paramanddefault;
            $type = $this->get_phpdoc_type($keydesc, $function->name . '__' . $name . 'Data');
            $params_desc[] = '     * @param ' . $type . ' $' . $name . ' ' . $keydesc->desc;
        }
        $params = implode(', ', $params);
        $paramanddefaults = implode(', ', $paramanddefaults);
        $params_desc = implode("\n", $params_desc);
        $serviceclassmethodbody = $this->service_class_method_body($function, $params);
        if (is_null($function->returns_desc)) {
            $return = '     * @return void';
        } else {
            $type = $this->get_phpdoc_return_type($function->returns_desc, $function->name . '__returnType');
            $return = '     * @return ' . $type . ' ' . $function->returns_desc->desc;
        }
        // now crate the virtual method that calls the ext implementation
        $code = '
    /**
     * ' . $function->description . '
     *
' . $params_desc . '
' . $return . '
     */
    public function ' . $function->name . '(' . $paramanddefaults . ') {
' . $serviceclassmethodbody . '
    }
';
        return $code;
    }