Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Mnet Web services API: Difference between revisions

From MoodleDocs
mNo edit summary
 
(22 intermediate revisions by 8 users not shown)
Line 1: Line 1:
== Overview ==
{{Moodle_1.9}}


This page describes internals of how to add a new web service to Moodle 1.9. It's mainly about MNet.<br> 
If you want to know about web services into Moodle 2.0, then go to the [[Web_services]] page and help us build up a [[Web_services_API_-_Function_List|standard set of functions]].


== Overview ==
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.


Line 20: Line 23:


==A word on Encryption==
==A word on Encryption==
There's really no voodoo going on in the Moodle encryption and signing process. We've tried to stick to the W3C's XML-ENC and XML-DSIG standards, and because our implementation of them is fairly simple and naive, getting your application to mimic that behaviour is not (as we say) rocket surgery. The Mahara implementation of XML-ENC/XML-DSIG uses some PHP5 features that make things even easier, so that might be a useful starting point for adventurous hackers.
There's really no voodoo going on in the Moodle encryption and signing process. We've tried to stick to the W3C's XML-ENC and XML-DSIG standards, and because our implementation of them is fairly simple and naive, getting your application to mimic that behaviour is not (as we say) rocket science. The Mahara implementation of XML-ENC/XML-DSIG uses some PHP5 features that make things even easier, so that might be a useful starting point for adventurous hackers.


==But if I'm not using Encryption?==
==But if I'm not using Encryption?==
Line 42: Line 45:


==Tell us!==
==Tell us!==
Oh alright then. In your config table, the row with the 'name' mnet_dispatcher_mode, should have a 'value' of 'promiscuous'. Please don't do that. There are other ways to enable whatever it is you want to enable, so please exhaust those first, however, if you do enable this and your site does got pwned please tell me so I can laugh and high five my co-workers.
Oh alright then. In your config table, the row with the 'name' mnet_dispatcher_mode, should have a 'value' of 'dangerous'. Please don't do that. There are other ways to enable whatever it is you want to enable, so please exhaust those first, however, if you do enable this and your site does got pwned please tell me so I can laugh and high five my co-workers.


==So - back to those four categories of function we were talking about==
==So - back to those four categories of function we were talking about==
The system functions return information about the services and methods that your API is making available to remote servers. These functions are:
The system functions return information about the services and methods that your API is making available to remote servers. These functions are:
* system.listMethods
* system/listMethods
* system.methodSignature
* system/methodSignature
* system.methodHelp
* system/methodHelp
* system.listServices
* system/listServices
The dot-notation is not an XML-RPC standard, but it does seem to be widely deployed, so we figured it would be friendly to implement that. You can call all of these methods with a slash (like system/listMethods) if that's important to you. Missing from this list is a non-standard method, that we implemented to help with key-exchange and key-rotation:
Missing from this list is a non-standard method, that we implemented to help with key-exchange and key-rotation:
* system.keyswap
* system/keyswap
This method returns the public key that your Moodle and Mahara peers will use for message encryption and signing. It is always possible to call this method from any host without signing or encryption, otherwise Moodles would never be able to exchange keys, and for that reason, it might be really useful to use as a test function. If you get a result back from this function, you have a foot in the door to setting up plaintext XML-RPC between your application and Moodle.
This method returns the public key that your Moodle and Mahara peers will use for message encryption and signing. It is always possible to call this method from any host without signing or encryption, otherwise Moodles would never be able to exchange keys, and for that reason, it might be really useful to use as a test function. If you get a result back from this function, you have a foot in the door to setting up plaintext XML-RPC between your application and Moodle.


Line 57: Line 60:
Let's say you have an enrolment class called 'enrol_plugin_bribeme' with the method "buy_me_beer" and you want to be able to call it via XML-RPC. First, your class' constructor must not require arguments. The despatcher should be able to just ask for your enrolment object and get it... end of story.
Let's say you have an enrolment class called 'enrol_plugin_bribeme' with the method "buy_me_beer" and you want to be able to call it via XML-RPC. First, your class' constructor must not require arguments. The despatcher should be able to just ask for your enrolment object and get it... end of story.


Second - enrol_plugin_bribeme must have a method called mnet_publishes which returns information about the "buy_me_beer" method. The method should return an associative array with the following structure:
Second - enrol_plugin_bribeme must have a method called mnet_publishes which returns information about the "buy_me_beer" method. The method should return an array of associative arrays with the following structure:
   $yummy_beer = array();
   $yummy_beer = array();
   $yummy_beer['name']        = 'yummy_beer'; // Name ('Yummy Beer!') goes in lang file
   $yummy_beer['name']        = 'yummy_beer'; // Name ('Yummy Beer!') goes in lang file
   $yummy_beer['apiversion']  = 1;
   $yummy_beer['apiversion']  = 1;
   $yummy_beer['methods']    = array('buy_me_beer');
   $yummy_beer['methods']    = array('buy_me_beer');
  return array($yummy_beer);
The $yummy_beer['methods'] array can contain any number of method names. The $yummy_beer['name'] element is the name of this service, and this will appear in the admin interface to allow the service to be enabled and disabled for different hosts. You enter an identifier here, and the actual name goes in the appropriate lang file - in this case lang/en_utf8/enrol_bribeme.php. Note that the _name and _description suffixes are required:
$string['yummy_beer_name']                = 'Yummy Beer';
$string['yummy_beer_description']        = 'A service to bribe your sysadmin with promises of gourmet beer.';


The $yummy_beer['methods'] array can contain any number of method names. The $yummy_beer['name'] element is the name of this service, and this will appear in the admin interface to allow the service to be enabled and disabled for different hosts. You enter an identifier here, and the actual name goes in the appropriate lang file - in this case lang/en_utf8/enrol_bribeme.php.
When you're writing your method, it's good practice to add a docblock with @param and @return values, because Moodle will read that information and provide it on to remote servers who call the introspection methods (e.g. system.methodSignature). This information is updated every time you hit /admin/index.php, so if you update the docblock for your function, you need to visit that page to have Moodle's introspection recognise your edits.


So - once you've implemented your method, you're ready to call it from your application. Your XML-RPC client needs to pass a function name to Moodle's XML-RPC server, that allows the server to identify the file and function to execute. The name you pass is a concatenation of the path to the file, and the name of your function, e.g.
So - once you've implemented your method, you're ready to call it from your application. Your XML-RPC client needs to pass a function name to Moodle's XML-RPC server, that allows the server to identify the file and function to execute. The name you pass is a concatenation of the path to the file, and the name of your function, e.g.


  "/auth/bribeme/auth.php/buymebeer"
  "auth/bribeme/auth.php/buymebeer"
 
==Functions in Modules==
The mechanics of XML-RPC are such that there are few functions that it would be useful to call directly from a remote server; you'll almost always need some kind of wrapper function to establish a security context or identify the server that's making the request. Partly for this reason, and partly so that it would be really obvious which functions are available to remote servers (i.e. really difficult for someone to call some arbitrary function in Moodle), we mandated that all XML-RPC-callable functions should be implemented in a special file called rpclib.php, so if you want to call a function in mod/forum/lib.php, you'll need to create a file called mod/forum/rpclib.php, and implement a wrapper function in that file that calls the function in mod/forum/lib.php.
 
For rpclib.php files, it is still also necessary to create an mnet_publishes function, but it must be named with a prefix of the module name, e.g. forum_mnet_publishes. This function must return an array in the same format as the one detailed above for auth and enrol plugins. Let's look at that in a little more detail by reviewing the method from the auth_plugin_mnet class.
 
For the benefit of administrators, your functions must be grouped into "Services". Some of the services that we've already implemented include sso_idp; the 'Single-Sign-On Identity Provider' service, and sso_sp; the 'Single-Sign-On Service Provider' service. You can see that the mnet_publishes method for that plugin returns an array with two elements. These elements provide information on the sso_idp and sso_sp services.
 
    /**
    * Provides the allowed RPC services from this class as an array.
    * @return array  Allowed RPC services.
    */
    function mnet_publishes() {
       
        $sso_idp = array();
        $sso_idp['name']        = 'sso_idp'; // Name & Description go in lang file
        $sso_idp['apiversion']  = 1;
        $sso_idp['methods']    = array('user_authorise','keepalive_server', 'kill_children',
                                        'refresh_log', 'fetch_user_image', 'fetch_theme_info',
                                        'update_enrolments');
       
        $sso_sp = array();
        $sso_sp['name']        = 'sso_sp'; // Name & Description go in lang file
        $sso_sp['apiversion']  = 1;
        $sso_sp['methods']      = array('keepalive_client','kill_child');
       
        return array($sso_idp, $sso_sp);
    }
 
Remember that if you're writing such a function for a module, the function name must be prefixed with your module name, e.g. modulename_mnet_publishes.
 
The name that you give your service is entirely up to you, but you need to create a language file in (for example) lang/en_utf8/mod_forum.php to store your service name and description, for example:
 
  $string['sso_idp_name']                = 'SSO  (Identity Provider)';
  $string['sso_idp_description']        = 'A description of the SSO IDP service that allows an admin to know if he needs it or not.';
 
Note that your service name (in this example 'sso_idp') is suffixed with _name and _description.


Turning back to the mnet_publishes function, your associative array has an 'apiversion' key, that allows you to provide more than one version of your API. This is really provided for future use, and isn't implemented in the code yet. We anticipate that eventually APIs will have to change, and this should allow newer Moodles to still be able to communicate with older Moodles. While this is unavoidable, it's something that we want to put of for as long as possible.


Finally, you provide a list of method or function names that constitute the service. When an admin enables this service for a host, he's really permitting the host to remotely call the methods or functions that you list here.


==The server script==
Calling a function remotely will require your XML-RPC client to pass something like this to the Moodle XML-RPC server as the function name:
* allow trusted sites to access web services by configuring '''admin/mnet/trustedhosts.php'''
  "mod/forum/rpclib.php/somefunction"
* start by pointing your browser to '''mnet/xmlrpc/server.php''' - this should show an XML error message
* use POST data in this format:
  // $method is something like: "mod/forum/lib/forum_add_instance"
// $params is an array of parameters. A parameter might itself be an array.
// use only 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
* use '''mnet/xmlrpc/client.php''' to make remote xmlrpc calls


==Things you can do==
==Other Implementations==
XML-RPC won't work for everyone. If you're planning a REST or SOAP service, please consider putting your work into mnet/soap or mnet/rest, so that it's alongside mnet/xmlrpc. The work we've done in the mnet directory might be useful to you as well, so rather than re-invent the wheel, have a look at our code to see if it meets any of your needs.


==See also==
==See also==


* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]   
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]   
* If you are searching for Web Services that manage user or courses data take a look at [https://docs.moodle.org/dev/Web_Services:OK_Tech_Web_Services Ok Tech Web Services]


[[Category:Web services API]]
[[Category:Web Services]]
[[Category:Administrator]]
[[Category:MNet]]

Latest revision as of 09:41, 10 January 2012

Moodle1.9


This page describes internals of how to add a new web service to Moodle 1.9. It's mainly about MNet.
If you want to know about web services into Moodle 2.0, then go to the Web_services page and help us build up a standard set of functions.

Overview

The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.

For example,

  1. Manage user data - send and retrieve the information,
  2. Manage course enrolments - add/remove teachers and students,
  3. Course management - create new courses based on templates,
  4. Gradebook info - extract grades information from Moodle.

XML-RPC background

The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.

To communicate like this with another Moodle host, you'd normally use the Moodle Network [1] features, but it's also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, bypassing Moodle Network's encryption or signed-message features.

Plaintext networking is only likely to be useful if you have another application which:

  • is not a Moodle
  • is completely under your control (for security reasons)

A word on Encryption

There's really no voodoo going on in the Moodle encryption and signing process. We've tried to stick to the W3C's XML-ENC and XML-DSIG standards, and because our implementation of them is fairly simple and naive, getting your application to mimic that behaviour is not (as we say) rocket science. The Mahara implementation of XML-ENC/XML-DSIG uses some PHP5 features that make things even easier, so that might be a useful starting point for adventurous hackers.

But if I'm not using Encryption?

Then you need to tell Moodle to privilege the machine on which your application is running, and allow it to use the XML-RPC service without having to sign or encrypt its messages. You can do this at /admin/mnet/trustedhosts.php, which you can browse to in the admin panel at Networking:XML-RPC hosts

I recommend that you enter something like this (assuming your application's IP address is 192.168.0.7): 192.168.0.7/32

That's all you need. Moodle knows your server is friendly, and will XML-RPC with it, without requiring it to encrypt or sign anything.

What can Moodle's XML-RPC do?

By default, we've strictly limited the functions that can be remotely called. These fall into four categories:

  • special 'system' functions
  • methods of an authorisation object
  • methods of an enrolment object
  • functions in a module

But what if I have a death wish or something?

Don't worry - we've thought of you too. If you want to do something that's not in that list, there is a special 'shoot-yourself-in-the-foot' switch that you can enable by tweaking a field in your database. This switch - if enabled - would allow the remote application to do ANYTHING to your Moodle that has a corresponding function anywhere in the code, so I'm not telling you where it is.

Tell us!

Oh alright then. In your config table, the row with the 'name' mnet_dispatcher_mode, should have a 'value' of 'dangerous'. Please don't do that. There are other ways to enable whatever it is you want to enable, so please exhaust those first, however, if you do enable this and your site does got pwned please tell me so I can laugh and high five my co-workers.

So - back to those four categories of function we were talking about

The system functions return information about the services and methods that your API is making available to remote servers. These functions are:

  • system/listMethods
  • system/methodSignature
  • system/methodHelp
  • system/listServices

Missing from this list is a non-standard method, that we implemented to help with key-exchange and key-rotation:

  • system/keyswap

This method returns the public key that your Moodle and Mahara peers will use for message encryption and signing. It is always possible to call this method from any host without signing or encryption, otherwise Moodles would never be able to exchange keys, and for that reason, it might be really useful to use as a test function. If you get a result back from this function, you have a foot in the door to setting up plaintext XML-RPC between your application and Moodle.

Methods of auth and enrol objects

Let's say you have an enrolment class called 'enrol_plugin_bribeme' with the method "buy_me_beer" and you want to be able to call it via XML-RPC. First, your class' constructor must not require arguments. The despatcher should be able to just ask for your enrolment object and get it... end of story.

Second - enrol_plugin_bribeme must have a method called mnet_publishes which returns information about the "buy_me_beer" method. The method should return an array of associative arrays with the following structure:

 $yummy_beer = array();
 $yummy_beer['name']        = 'yummy_beer'; // Name ('Yummy Beer!') goes in lang file
 $yummy_beer['apiversion']  = 1;
 $yummy_beer['methods']     = array('buy_me_beer');
 return array($yummy_beer);

The $yummy_beer['methods'] array can contain any number of method names. The $yummy_beer['name'] element is the name of this service, and this will appear in the admin interface to allow the service to be enabled and disabled for different hosts. You enter an identifier here, and the actual name goes in the appropriate lang file - in this case lang/en_utf8/enrol_bribeme.php. Note that the _name and _description suffixes are required:

$string['yummy_beer_name']                = 'Yummy Beer';
$string['yummy_beer_description']         = 'A service to bribe your sysadmin with promises of gourmet beer.';

When you're writing your method, it's good practice to add a docblock with @param and @return values, because Moodle will read that information and provide it on to remote servers who call the introspection methods (e.g. system.methodSignature). This information is updated every time you hit /admin/index.php, so if you update the docblock for your function, you need to visit that page to have Moodle's introspection recognise your edits.

So - once you've implemented your method, you're ready to call it from your application. Your XML-RPC client needs to pass a function name to Moodle's XML-RPC server, that allows the server to identify the file and function to execute. The name you pass is a concatenation of the path to the file, and the name of your function, e.g.

"auth/bribeme/auth.php/buymebeer"

Functions in Modules

The mechanics of XML-RPC are such that there are few functions that it would be useful to call directly from a remote server; you'll almost always need some kind of wrapper function to establish a security context or identify the server that's making the request. Partly for this reason, and partly so that it would be really obvious which functions are available to remote servers (i.e. really difficult for someone to call some arbitrary function in Moodle), we mandated that all XML-RPC-callable functions should be implemented in a special file called rpclib.php, so if you want to call a function in mod/forum/lib.php, you'll need to create a file called mod/forum/rpclib.php, and implement a wrapper function in that file that calls the function in mod/forum/lib.php.

For rpclib.php files, it is still also necessary to create an mnet_publishes function, but it must be named with a prefix of the module name, e.g. forum_mnet_publishes. This function must return an array in the same format as the one detailed above for auth and enrol plugins. Let's look at that in a little more detail by reviewing the method from the auth_plugin_mnet class.

For the benefit of administrators, your functions must be grouped into "Services". Some of the services that we've already implemented include sso_idp; the 'Single-Sign-On Identity Provider' service, and sso_sp; the 'Single-Sign-On Service Provider' service. You can see that the mnet_publishes method for that plugin returns an array with two elements. These elements provide information on the sso_idp and sso_sp services.

   /**
    * Provides the allowed RPC services from this class as an array.
    * @return array  Allowed RPC services.
    */
   function mnet_publishes() {
       
       $sso_idp = array();
       $sso_idp['name']        = 'sso_idp'; // Name & Description go in lang file
       $sso_idp['apiversion']  = 1;
       $sso_idp['methods']     = array('user_authorise','keepalive_server', 'kill_children',
                                       'refresh_log', 'fetch_user_image', 'fetch_theme_info',
                                       'update_enrolments');
       
       $sso_sp = array();
       $sso_sp['name']         = 'sso_sp'; // Name & Description go in lang file
       $sso_sp['apiversion']   = 1;
       $sso_sp['methods']      = array('keepalive_client','kill_child');
       
       return array($sso_idp, $sso_sp);
   }

Remember that if you're writing such a function for a module, the function name must be prefixed with your module name, e.g. modulename_mnet_publishes.

The name that you give your service is entirely up to you, but you need to create a language file in (for example) lang/en_utf8/mod_forum.php to store your service name and description, for example:

 $string['sso_idp_name']                = 'SSO  (Identity Provider)';
 $string['sso_idp_description']         = 'A description of the SSO IDP service that allows an admin to know if he needs it or not.';

Note that your service name (in this example 'sso_idp') is suffixed with _name and _description.

Turning back to the mnet_publishes function, your associative array has an 'apiversion' key, that allows you to provide more than one version of your API. This is really provided for future use, and isn't implemented in the code yet. We anticipate that eventually APIs will have to change, and this should allow newer Moodles to still be able to communicate with older Moodles. While this is unavoidable, it's something that we want to put of for as long as possible.

Finally, you provide a list of method or function names that constitute the service. When an admin enables this service for a host, he's really permitting the host to remotely call the methods or functions that you list here.

Calling a function remotely will require your XML-RPC client to pass something like this to the Moodle XML-RPC server as the function name:

"mod/forum/rpclib.php/somefunction"

Other Implementations

XML-RPC won't work for everyone. If you're planning a REST or SOAP service, please consider putting your work into mnet/soap or mnet/rest, so that it's alongside mnet/xmlrpc. The work we've done in the mnet directory might be useful to you as well, so rather than re-invent the wheel, have a look at our code to see if it meets any of your needs.

See also