web-services.rst 12 KB

  1. Web services
  2. ############
  3. Build an HTTP API for your site.
  4. Elgg provides a powerful framework for building web services. This
  5. allows developers to expose functionality to other web sites and desktop
  6. applications along with doing integrations with third-party web
  7. applications. While we call the API RESTful, it is actually a REST/RPC
  8. hybrid similar to the APIs provided by sites like Flickr and Twitter.
  9. To create an API for your Elgg site, you need to do 4 things:
  10. - enable the web services plugin
  11. - expose methods
  12. - setup API authentication
  13. - setup user authentication
  14. Additionally, you may want to control what types of authentication are
  15. available on your site. This will also be covered.
  16. .. contents:: Contents
  17. :local:
  18. :depth: 2
  19. Security
  20. --------
  21. It is crucial that the web services are consumed via secure protocols. Do not
  22. enable web services if your site is not served via HTTPs. This is especially
  23. important if you allow API key only authentication.
  24. If you are using third-party tools that expose API methods, make sure to carry
  25. out a thorough security audit. You may want to make sure that API authentication
  26. is required for ALL methods, even if they require user authentication. Methods that
  27. do not require API authentication can be easily abused to spam your site.
  28. Ensure that the validity of API keys is limited and provide mechanisms for your
  29. API clients to renew their keys.
  30. Exposing methods
  31. ----------------
  32. The function to use to expose a method is ``elgg_ws_expose_function()``. As an
  33. example, let's assume you want to expose a function that echos text back
  34. to the calling application. The function could look like this
  35. .. code:: php
  36. function my_echo($string) {
  37. return $string;
  38. }
  39. Since we are providing this function to allow developers to test their
  40. API clients, we will require neither API authentication nor user
  41. authentication. This call registers the function with the web services
  42. API framework:
  43. .. code:: php
  44. elgg_ws_expose_function("test.echo",
  45. "my_echo",
  46. array("string" => array('type' => 'string')),
  47. 'A testing method which echos back a string',
  48. 'GET',
  49. false,
  50. false
  51. );
  52. If you add this code to a plugin and then go to
  53. http://yoursite.com/services/api/rest/xml/?method=system.api.list, you
  54. should now see your test.echo method listed as an API call. Further, to
  55. test the exposed method from a web browser, you could hit the url:
  56. http://yoursite.com/services/api/rest/xml/?method=test.echo&string=testing
  57. and you should see xml data like this:
  58. .. code:: xml
  59. <elgg>
  60. <status>0</status>
  61. <result>testing</result>
  62. </elgg>
  63. Response formats
  64. ~~~~~~~~~~~~~~~~
  65. The web services API framework provides three different response formats
  66. by default: xml, json, and serialized php. You can request the different
  67. formats for substituting “json” or “php” for “xml” in the above URLs.
  68. You can also add additional response formats by defining new viewtypes.
  69. Parameters
  70. ~~~~~~~~~~
  71. Parameters expected by each method should be listed as an associative array, where the key represents the parameter name, and the value contains an array with ``type``, ``default`` and ``required`` fields.
  72. Values submitted with the API request for each parameter should match the declared type. API will throw on exception if validation fails.
  73. Recognized parameter types are:
  74. - ``integer`` (or ``int``)
  75. - ``boolean`` (or ``bool``)
  76. - ``string``
  77. - ``float``
  78. - ``array``
  79. Unrecognized types will throw an API exception.
  80. You can use additional fields to describe your parameter, e.g. ``description``.
  81. .. code:: php
  82. elgg_ws_expose_function('test.greet',
  83. 'my_greeting',
  84. array(
  85. 'name' => array(
  86. 'type' => 'string',
  87. 'required' => true,
  88. 'description' => 'Name of the person to be greeted by the API',
  89. ),
  90. 'greeting' => array(
  91. 'type' => 'string',
  92. 'required' => false,
  93. 'default' => 'Hello',
  94. 'description' => 'Greeting to be used, e.g. "Good day" or "Hi"',
  95. ),
  96. ),
  97. 'A testing method which greets the user with a custom greeting',
  98. 'GET',
  99. false,
  100. false
  101. );
  102. API authentication
  103. ------------------
  104. You may want to control access to some of the functions that you expose.
  105. Perhaps you are exposing functions in order to integrate Elgg with
  106. another open source platform on the same server. In that case, you only
  107. want to allow that other application access to these methods. Another
  108. possibility is that you want to limit what external developers have
  109. access to your API. Or maybe you want to limit how many calls a
  110. developer can make against your API in a single day.
  111. In all of these cases, you can use Elgg's API authentication functions
  112. to control access. Elgg provides two built-in methods to perform API
  113. authentication: key based and HMAC signature based. You can also add
  114. your own authentication methods. The key based approach is very similar
  115. to what Google, Flickr, or Twitter. Developers can request a key (a
  116. random string) and pass that key with all calls that require API
  117. authentication. The keys are stored in the database and if an API call
  118. is made without a key or a bad key, the call is denied and an error
  119. message is returned.
  120. Key-based authentication
  121. ~~~~~~~~~~~~~~~~~~~~~~~~
  122. As an example, let's write a function that returns the number of users
  123. that have viewed the site in the last x minutes.
  124. .. code:: php
  125. function count_active_users($minutes=10) {
  126. $seconds = 60 * $minutes;
  127. $count = count(find_active_users($seconds, 9999));
  128. return $count;
  129. }
  130. Now, let's expose it and make the number of minutes an optional
  131. parameter:
  132. .. code:: php
  133. elgg_ws_expose_function("users.active",
  134. "count_active_users",
  135. array("minutes" => array('type' => 'int',
  136. 'required' => false)),
  137. 'Number of users who have used the site in the past x minutes',
  138. 'GET',
  139. true,
  140. false
  141. );
  142. This function is now available and if you check ``system.api.list``, you
  143. will see that it requires API authentication. If you hit the method with
  144. a web browser, it will return an error message about failing the API
  145. authentication. To test this method, you need an API key. Fortunately,
  146. there is a plugin called apiadmin that creates keys for you. It is
  147. available in the Elgg plugin repository. It will return a public and
  148. private key and you will use the public key for this kind of API
  149. authentication. Grab a key and then do a GET request with your browser
  150. on this API method passing in the key string as the parameter
  151. ``api_key``. It might look something like this:
  152. http://yoursite.com/services/api/rest/xml/?method=users.active&api_key=1140321cb56c71710c38feefdf72bc462938f59f.
  153. Signature-based authentication
  154. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  155. The :doc:`web-services/hmac` is similar to what is used with OAuth or
  156. Amazon's S3 service. This involves both the public and private key. If
  157. you want to be very sure that the API calls are coming from the
  158. developer you think they are coming from and you want to make sure the
  159. data is not being tampered with during transmission, you would use this
  160. authentication method. Be aware that it is much more involved and could
  161. turn off developers when there are other sites out there with key-based
  162. authentication.
  163. OAuth
  164. ~~~~~
  165. With the addition of the OAuth plugin, Elgg also fully supports the
  166. OAuth 1.0a authorization standard. Clients can then use standard OAuth
  167. libraries to make any API calls to the site.
  168. User authentication
  169. -------------------
  170. So far you have been allowing developers to pull data out of your Elgg
  171. site. Now we'll move on to pushing data into Elgg. In this case, it is
  172. going to be done by a user. Maybe you have created a desktop application
  173. that allows your Users to post to the wire without going to the site.
  174. You need to expose a method for posting to the wire and you need to make
  175. sure that a user cannot post using someone else's account. Elgg provides
  176. a token-based approach for user authentication. It allows a user to
  177. submit their username and password in exchange for a token using the
  178. method ``auth.gettoken``. This token can then be used for some amount of
  179. time to authenticate all calls to the API before it expires by passing
  180. it as the parameter ``auth_token``. If you do not want to have your
  181. users trusting their passwords to 3rd-party applications, you can also
  182. extend the current capability to use an approach like OAuth.
  183. Let's write our wire posting function:
  184. .. code:: php
  185. function my_post_to_wire($text) {
  186. $text = substr($text, 0, 140);
  187. $access = ACCESS_PUBLIC;
  188. // returns guid of wire post
  189. return thewire_save_post($text, $access, "api");
  190. }
  191. Exposing this function is the same as the previous except we require
  192. user authentication and we're going to make this use POST rather than
  193. GET HTTP requests.
  194. .. code:: php
  195. elgg_ws_expose_function("thewire.post",
  196. "my_post_to_wire",
  197. array("text" => array('type' => 'string')),
  198. 'Post to the wire. 140 characters or less',
  199. 'POST',
  200. true,
  201. true
  202. );
  203. Please note that you will not be able to test this using a web browser
  204. as you did with the other methods. You need to write some client code to
  205. do this. There is some example client code in ``/engine/lib/api.php``.
  206. Take a look at `send\_api\_post\_call()`_. You can also do a search for
  207. clients that have been written for the APIs of Flickr or Twitter or any
  208. other similar API. You will find a wide variety written in almost any
  209. language you can think of.
  210. Building out your API
  211. ---------------------
  212. As soon as you feel comfortable with Elgg's web services API framework,
  213. you will want to step back and design your API. What sort of data are
  214. you trying to expose? Who or what will be API users? How do you want
  215. them to get access to authentication keys? How are you going to document
  216. your API? Be sure to take a look at the APIs created by popular Web 2.0
  217. sites for inspiration. If you are looking for 3rd party developers to
  218. build applications using your API, you will probably want to provide one
  219. or more language-specific clients.
  220. .. _send\_api\_post\_call(): http://reference.elgg.org/lib_2api_8php.html#ee7382c2cbf1ad49ac6892556d3eaff2
  221. Determining the authentication available
  222. ----------------------------------------
  223. Elgg's web services API uses a type of `pluggable authentication module
  224. (PAM)`_ architecture to manage how users and developers are
  225. authenticated. This provides you the flexibility to add and remove
  226. authentication modules. Do you want to not use the default user
  227. authentication PAM but would prefer using OAuth? You can do this.
  228. The first step is registering a callback function for the *rest, init*
  229. plugin hook:
  230. .. code:: php
  231. register_plugin_hook('rest', 'init', 'rest_plugin_setup_pams');
  232. Then in the callback function, you register the PAMs that you want to
  233. use:
  234. .. code:: php
  235. function rest_plugin_setup_pams() {
  236. // user token can also be used for user authentication
  237. register_pam_handler('pam_auth_usertoken');
  238. // simple API key check
  239. register_pam_handler('api_auth_key', "sufficient", "api");
  240. // override the default pams
  241. return true;
  242. }
  243. When testing, you may find it useful to register the
  244. ``pam_auth_session`` PAM so that you can easily test your methods from
  245. the browser. Be careful not to use this PAM on a production site because
  246. it could open up your users to a `CSRF attack`_.
  247. Right now, the only other PAMs publicly available besides those provided
  248. by the Elgg core are the OAuth PAMs. See `Justin Richer's OAuth plugin`_
  249. for more detail.
  250. .. _pluggable authentication module (PAM): http://en.wikipedia.org/wiki/Pluggable_Authentication_Modules
  251. .. _CSRF attack: http://en.wikipedia.org/wiki/Csrf
  252. .. _Justin Richer's OAuth plugin: http://community.elgg.org/pg/plugins/jricher/read/385119/oauth
  253. Related
  254. -------
  255. .. toctree::
  256. :maxdepth: 1
  257. web-services/hmac