So Drupal has this amazing services module that I have used and written on before in earlier versions. Now I am upgrading an important site to Drupal 6 which uses an xmlrpc server to receive published articles, and so an upgrade is in order. However, despite copious and varied handbook documentation for this module, I just could not find clear directions on getting this to work in a secure fashion without adopting relatively complex solutions. Others have also stated that they wanted to save people hours of futzing about but they leave out the all important sessionid, for example. Where’s it supposed to go exactly? So here is a working example (attached as text) you can actually use, even from a Drupal 5 site. We will:
- Install and configure the services module on the Drupal 6 server.
- Create an application key on the server.
- Configure services options.
- Create a reusable snippet of code that connects to the server, gets a session id and then uses it together with the application key to get a node.
Install and configure the services module on the Drupal 6 server.
Now to install the new version! We are going with the stable and recommended 6.x-2.2 version of the services module. Install as you would any module (# drush dl services – yeah!).
Which services modules to enable?
I enabled the following at this time, for the purposes of this example:
- Services
- Key authentication
- XMLRPC Server
- Node service
- System service
What permissions to apply to which roles and why?
As explained in all to broad terms (although there is a nice example in Java if you want to use Java) in the Handbook’s Overview of Services, the basic workflow is:
session = system.connect(); user = user.login(session, 'MyName'); node.save(session, mynode);
Now, the most straightforward example for API authentication in Php ( http://drupal.org/node/762092 ) explains that “When using API key authentication, Services runs as the anonymous user, so you will need to modify the anonymous user’s permissions as necessary.” Of course, we can use the user.login service to make use of a special authenticated user in line with the best practices workflow. But let’s get it going as a reasonably secure solution with the session id, nonce (random string used only once) and timestamp solution. Once that’s working we could refactor to add in user login.
So, we need to set the following permissions:
module | permission | role |
node_service | load node data | anonymous |
services | administer services | administrator |
system_service | [none for anonymous for this example] | anonymous |
user_service | [none for anonymous for this example] | anonymous |
Create an application key on the server
First stop: Administer > Site building > Services. Here is a test page you can try out once we finish the configuration. Click on the Keys tab. As yet, of course, no API key has been generated. So click on the Create key tab. I entered the following:
Field | Content | Notes |
Application title | getnode | can be anything of course |
Allowed domain | example.com | type in your client domain, in example.com format (no “http://…” |
Method access | node.get |
Click on the Create key button and of course take note of the key, for example: bf182cf462cfd7d8a43e29e737e9b19a
Configure Services Options
Now hit the Options tab and select “Key authentication” as the Authentication module. Use keys checkbox: selected. Leave 30 seconds as token expiration time for the nonce token. Use sessid: checked. Save options. And we are done.
Create a reusable snippet of code that connects to the server, gets a session id and then uses it together with the application key to view a node.
In another Drupal installation, type the following (attached as text file) into a Devel module Execute Php box:
// connect and get session id $domain = 'example.com'; // the domain you are sending the request from, which is specified also in the api key. $timestamp = (string) time(); $nonce = user_password(); $hash = hash_hmac('sha256', $timestamp .';'.$domain .';'. $nonce .';'.'system.connect', 'da8cdeba9ccb7298a1934b1e779f624f'); // replace last parameter with your api key // replace server.com with the name of the drupal site that has the services module installed and configured as server $xmlrpc_result = xmlrpc('http://server.com/services/xmlrpc', 'system.connect'); //dsm($xmlrpc_result); $sid = $xmlrpc_result['sessid']; $timestamp = (string) time(); $hash = hash_hmac('sha256', $timestamp .';'.$domain .';'. $nonce .';'.'node.get', 'da8cdeba9ccb7298a1934b1e779f624f'); // replace last parameter with your api key // replace server.com with the name of the drupal site that has the services module installed and configured as server $xmlrpc_result = xmlrpc('http://server.com/services/xmlrpc', 'node.get', $hash, $domain, $timestamp, $nonce, $sid, 12345, array()); if ($xmlrpc_result === FALSE) { print '<pre>' . print_r(xmlrpc_error(), TRUE) . '<pre>'; } else { print '<pre>' . print_r($xmlrpc_result, TRUE) . '<pre>'; }
Execute, and you should see something like the following:
<pre>Array ( [nid] => 25606 [type] => article [language] => es [uid] => 2 [status] => 1 [created] => 1281088749 [changed] => 1281088749 [comment] => 2 [promote] => 0 [moderate] => 0 [sticky] => 0 [tnid] => 0 [translate] => 0 [vid] => 33854 [revision_uid] => 2 [title] => Nos movilizamos al Congreso por el 82% móvil [body] => [teaser] => [log] => [revision_timestamp] => 1281088749 [format] => 0 [name] => elviejo [picture] => [data] => a:4:{s:7:"contact";i:1;s:14:"picture_delete";s:0:"";s:14:"picture_upload";s:0:"";s:14:"tinymce_status";s:5:"false";} [field_article_publication_issue] => Array ( [0] => Array ( [nid] => 12345 ) ) .... <rest of the fields> .... [last_comment_timestamp] => 1281088749 [last_comment_name] => [comment_count] => 0 [taxonomy] => Array ( ) [files] => Array ( ) ) <pre>