Servicios web

Construya una API HTTP para su sitio.

Elgg ofrece una infraestructura perfecta para construir servicios web. Esto permite a los desarrolladores exponer funcionalidad del sitio a otros sitios web y aplicaciones, así como a sitios web y aplicaciones de terceros. Aunque definimos la API como RESTful, en realidad es un híbrido entre REST y RPC, similar a las API de sitios como Flickr o Twitter.

To create an API for your Elgg site, you need to do 4 things:

  • enable the web services plugin
  • Exponer métodos.
  • Configurar la autenticación de la API.
  • Configurar la autenticación de usuarios.

Además, puede que desee controlar los tipos de autenticación disponibles en el sitio. Esto, también, se explica a continuación.

Seguridad

It is crucial that the web services are consumed via secure protocols. Do not enable web services if your site is not served via HTTPs. This is especially important if you allow API key only authentication.

If you are using third-party tools that expose API methods, make sure to carry out a thorough security audit. You may want to make sure that API authentication is required for ALL methods, even if they require user authentication. Methods that do not require API authentication can be easily abused to spam your site.

Ensure that the validity of API keys is limited and provide mechanisms for your API clients to renew their keys.

Exponer métodos

The function to use to expose a method is elgg_ws_expose_function(). As an example, let’s assume you want to expose a function that echos text back to the calling application. The function could look like this

function my_echo($string) {
    return $string;
}

Dado que ofrecemos esta función para permitir a los desarrolladores probar sus clientes para la API, no necesitaremos ni autenticación de la API ni autenticación de usuarios. La siguiente llamada registra la función en la infraestructura de la API para servicios web:

elgg_ws_expose_function(
        "test.echo",
        "my_echo",
        [
                "string" => [
                        'type' => 'string',
                ]
        ],
        'A testing method which echos back a string',
        'GET',
        false,
        false
);

If you add this code to a plugin and then go to http://yoursite.com/services/api/rest/json/?method=system.api.list, you should now see your test.echo method listed as an API call. Further, to test the exposed method from a web browser, you could hit the url: http://yoursite.com/services/api/rest/json/?method=test.echo&string=testing and you should see JSON data like this:

{"status":0,"result":"testing"}

Plugins can filter the output of individual API methods by registering a handler for 'rest:output',$method plugin hook.

Formatos de respuesta

JSON is the default format, however XML and serialized PHP can be fetched by enabling the data_views plugin and substituting xml or php in place of json in the above URLs.

You can also add additional response formats by defining new viewtypes.

Parameters

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.

Values submitted with the API request for each parameter should match the declared type. API will throw on exception if validation fails.

Recognized parameter types are:

  • integer (or int)
  • boolean (or bool)
  • string
  • float
  • array

Unrecognized types will throw an API exception.

You can use additional fields to describe your parameter, e.g. description.

elgg_ws_expose_function(
        'test.greet',
        'my_greeting',
        [
                'name' => [
                        'type' => 'string',
                        'required' => true,
                        'description' => 'Name of the person to be greeted by the API',
                ],
                'greeting' => [
                        'type' => 'string',
                        'required' => false,
                        'default' => 'Hello',
                        'description' => 'Greeting to be used, e.g. "Good day" or "Hi"',
                ],
        ],
        'A testing method which greets the user with a custom greeting',
        'GET',
        false,
        false
);

Nota

If a missing parameter has no default value, the argument will be null. Before Elgg v2.1, a bug caused later arguments to be shifted left in this case.

Receive parameters as associative array

If you have a large number of method parameters, you can force the execution script to invoke the callback function with a single argument that contains an associative array of parameter => input pairs (instead of each parameter being a separate argument). To do that, set $assoc to true in elgg_ws_expose_function().

function greet_me($values) {
        $name = elgg_extract('name', $values);
        $greeting = elgg_extract('greeting', $values, 'Hello');
        return "$greeting, $name";
}

elgg_ws_expose_function(
        "test.greet",
        "greet_me",
        [
                "name" => [
                        'type' => 'string',
                ],
                "greeting" => [
                        'type' => 'string',
                        'default' => 'Hello',
                        'required' => false,
                ],
        ],
        'A testing method which echos a greeting',
        'GET',
        false,
        false,
        true // $assoc makes the callback receive an associative array
);

Nota

If a missing parameter has no default value, null will be used.

Autenticación de la API

Puede que le interese controlar el acceso a algunas de la funciones que expone. Puede que esté exponiendo funciones para poder integrar Elgg con otra plataforma libre en el mismo servidor. En este caso, sólo quiere permitir que esa otra aplicación pueda acceder a estos métodos. Otra posibilidad es que limite qué desarrolladores externos tienen acceso a la API. O quizá quiere limitar el número de llamadas a la API que los desarrolladores pueden hacer cada día.

En todos estos casos, puede usar las funciones de autenticación de la API para controlar el acceso. Elgg provide dos métodos de serie para realizar autenticaciones contra la API: mediante una clave y mediante una firma HMAC. También puede incluir sus propios métodos de autenticación. El método de autenticación mediante una clave es muy similar al que usan servicios como Google, Flickr o Twitter. Los desarrolladores pueden solicitar una clave (una cadena de texto aleatoria) y incluir la clave en las llamadas a la API que requieren autenticación. Las claves se almacenan en una base de datos y si una llamada a la API no incluye la clave o incluye una clave incorrecta, la llamada a la API se contesta con un mensaje de error.

Autenticación mediante clave

Por ejemplo, escribamos una función que devuelva el número de usuarios que han visitado el sitio durante los últimos x minutos:

function count_active_users($minutes=10) {
    $seconds = 60 * $minutes;
    $count = count(find_active_users($seconds, 9999));
    return $count;
}

Ahora, expongamos la función y convirtamos el número de minutos en un parámetro opcional:

elgg_ws_expose_function(
        "users.active",
        "count_active_users",
        [
                "minutes" => [
                        'type' => 'int',
                        'required' => false,
                ],
        ],
        'Number of users who have used the site in the past x minutes',
        'GET',
        true,
        false
);

La función está ahora disponible, y si consulta system.api.list podrá comprobar que la función requiere autenticación contra la API. Si intenta acceder al método mediante un navegador web, obtendrá un mensaje de error que le informará de que no ha sido posible autenticar la llamada contra la API. Para probar el método, necesita una clave de la API. Por suerte, existe un complemento, «apiadmin», que creará una clave para usted. El complemento está disponible en el repositorio de complementos de Elgg. El complemento ofrece dos claves, una pública y otra privada, y usted podrá utilizar la pública para autenticarse contra la API por este método. Obtenga una clave y realice una solicitud de tipo GET a esta función con su navegador web pasándole a la función la clave como valor del parámetro api_key. Este podría ser su aspecto: http://yoursite.com/services/api/rest/xml/?method=users.active&api_key=1140321cb56c71710c38feefdf72bc462938f59f.

Autenticación mediante firma

The HMAC Authentication is similar to what is used with OAuth or Amazon’s S3 service. This involves both the public and private key. If you want to be very sure that the API calls are coming from the developer you think they are coming from and you want to make sure the data is not being tampered with during transmission, you would use this authentication method. Be aware that it is much more involved and could turn off developers when there are other sites out there with key-based authentication.

Autenticación de usuarios

De momento hemos estado permitiendo a los desarrolladores obtener datos del sitio. Ahora procederemos a permitirles enviar datos. En este caso, los datos se añadirán de parte de un usuario. Imagine que ha creado una aplicación que permite a los usuarios publicar en el Wire y que usted necesita asegurarse de que un usuario no publica nada desde la cuenta de otra persona. Elgg ofrece un método de autenticación de usuarios mediante códigos aleatorios (tokens). Este método permite que un usuario envíe su nombre de usuario y contraseña mediante el método auth.gettoken y a cambio obtenga un código. Durante un cierto período de tiempo, el usuario puede usar el código recibido para autenticar todas las llamadas a la API antes de que el código caduque, pasando el código como valor del parámetro auth_token. Si no quiere que los usuarios les faciliten sus contraseñas a aplicaciones de terceros, también puede extender la funcionalidad actual para utilizar un método como OAuth.

Escribamos la función para publicar en el Wire:

function my_post_to_wire($text) {

    $text = substr($text, 0, 140);

    $access = ACCESS_PUBLIC;

    // returns guid of wire post
    return thewire_save_post($text, $access, "api");
}

Expondremos la función como ya hemos hecho anteriormente, con la diferencia de que esta vez exigiremos autenticación de usuario, y que la solicitud sea de tipo POST en vez de GET.

elgg_ws_expose_function(
        "thewire.post",
        "my_post_to_wire",
        [
                "text" => [
                        'type' => 'string',
                ],
        ],
        'Post to the wire. 140 characters or less',
        'POST',
        true,
        true
);

Please note that you will not be able to test this using a web browser as you did with the other methods. You need to write some client code to do this.

Expandir la API

En cuanto se habitúe a la infraestructura del API para servicios web de Elgg, querrá proceder a diseñar su API: decidir qué datos quiere exponer, quién y en qué consistirán los usuarios de la API, cómo accederán a las claves de autenticación, cómo escribirá la documentación del API, etc. Asegúrese de echarle una ojeada a las API creadas por sitios web 2.0 populares, a modo de inspiración. Si pretende que desarrolladores de terceros construyan aplicaciones utilizando su API, quizá debería plantearse ofrecer uno o varios clientes específicos de algún lenguaje de programación.

Determinar el método de autenticación disponible

La API para servicios web de Elgg usa un tipo de arquitectura de módulo de autenticación conectable («PAM» por sus siglas en inglés) para gestionar la forma en que se autentifican los usuarios y desarrolladores. Esto le ofrece flexibilidad para añadir y eliminar módulos de autenticación. ¿No quiere usar el PAM predeterminado de autenticación de usuarios sino que prefiere usar OAuth? Pues puede hacerlo.

El primer paso consiste en registrar una llamada de retorno (callback) para el gancho de complementos rest, init:

register_plugin_hook('rest', 'init', 'rest_plugin_setup_pams');

A continuación, en la función de llamada de retorno registre los PAM que quiera utilizar:

function rest_plugin_setup_pams() {
    // user token can also be used for user authentication
    register_pam_handler('pam_auth_usertoken');

    // simple API key check
    register_pam_handler('api_auth_key', "sufficient", "api");

    // override the default pams
    return true;
}

A la hora de hacer pruebas, puede que le resulte útil registrar el PAM pam_auth_session para poder probar fácilmente sus métodos desde el navegador web. Pero tenga cuidado de no usar este PAM en un sitio de producción, pues podría exponer a sus usuarios a un ataque de falsificación de peticiones entre sitios distintos.