|
|
The API is a complete and **fully independent** app whose goal is to provide a RESTful API (or at least something close) to expose the models of Re2o and let the users automate some information gathering and edition through it. The complete list of available endpoints along with their description is available on the [API's endpoint page](User Documentation/API/Endpoints)
|
|
|
|
|
|
This page describes how to enable and use the API directly, with raw HTTP requests. For an easier usage of the API with python, see the dedicated [API client](User Documentation/API/API client)
|
|
|
|
|
|
Of course, the API is not publicly exposed to everyone. Thus some mechanisms of authentication and permission are set to restrict what a user can see and do through this API. This page will also describes how those mechanisms work.
|
|
|
|
|
|
*:warning: As a security warning: it is highly advised to use the API only with TLS/SSL activated. If not doing so, you are exposing your data to be leaked and/or your database to be vulnerable*
|
|
|
|
|
|
|
|
|
# Enable the API
|
|
|
|
|
|
The first step is to enable the API. Indeed the API is intended to be independent from the rest of Re2o, it is only an interface. The consequence is that it was meant to be an optional app and not a required app. Even though it is highly advised to activate it, you are free to deactivate it at any time without any direct consequences for Re2o (but the services using it might broke because the API endpoints will not be reachable any longer).
|
|
|
|
|
|
To activate the API, simply add the optional app `api` in your [settings file](User Documentation/Settings file):
|
|
|
```python
|
|
|
OPTIONAL_APPS = (
|
|
|
# ...
|
|
|
api,
|
|
|
# ...
|
|
|
)
|
|
|
```
|
|
|
After reloading the web server, you should now be able to access the API root page (`https://<hostname>/api/`)
|
|
|
|
|
|
|
|
|
# Authentication
|
|
|
|
|
|
Except for the API root page (`https://<hostname>/api/`), all pages of the API need you to be authenticated in order to get any information. This behavior is mandatory as we can not expose personal or confidential data to anonymous user. First it may be needed to know who accessed it if needed and it is also needed to be authenticated so, the ACL can be checked to determine if you have the access to such data.
|
|
|
|
|
|
Two way of authenticating are available, even if in theory you can use both in all cases, they are meant to be preferred over the other in some circumstances. If you want to explore the API with your browser and your credentials, the recommended way is to use session cookie. On the contrary, if you want to use a script to access the API with a dedicated user's credentials, prefer the token-based authentication.
|
|
|
|
|
|
|
|
|
## Authentication by session cookie
|
|
|
|
|
|
This authentication method uses a cookie returned upon you connection to Re2o. Indeed it is the exact same session cookie used to navigate through the web interface (handled by django-auth). This means that once you log in the web interface as usual and your browser has saved the cookie, you will also be authenticated on the API interface since your browser will send this cookie automatically. This is why, this authentication method is the recommended way to use the API through a browser:
|
|
|
* Log in via the usual log in page (`https://<hostname>/login/`)
|
|
|
* Explore the API (`https://<hostname>/api/`)
|
|
|
|
|
|
|
|
|
## Authentication by token
|
|
|
|
|
|
This authentication method is easier to use in an automated process but more complex by using a standard web browser. It requires you to add a `Authorization` field in the header of your request with a token given by the server. A token is specific to a user, so to get one, you will need to provide a user's credential but once you have a token, you no longer need the password, the token is your credential. For security concerns, tokens expire after 24 hours, meaning that even in case of a leaked token, it can be used only for at most 24 hours. After that delay, the server will require you to re-provide the credentials in order to renew the token.
|
|
|
|
|
|
1. First you need to request the token: make a **POST** requests to `https://<hostname>/api/token-auth/` with the following data `username: <username>, password: <password>`. If your credential are valid, the server will respond with a 200 (*OK*) response and JSON data with two fields: `token` which contains the string to use as a token and `expiration` which contains the date of expiration of the token using ISO-8601 format. If your credentials are not valid, you will be returned a 401 (*UNAUTHORIZED*) response with possibly the reason for reject in the content.
|
|
|
* Example with **curl**:
|
|
|
|
|
|
```bash
|
|
|
$ curl -X POST --data "username=myuser&password=mypass" https://example.net/api/token-auth/
|
|
|
{"token":"sdglkjlsdnawsdjkfsklsdfwe","expiration":"2018-06-11T13:25:35Z"}
|
|
|
```
|
|
|
* Example with **python** and **requests**:
|
|
|
|
|
|
```python
|
|
|
>>> import requests
|
|
|
>>> response = requests.post('https://example.net/api/token-auth', data={'username': username, 'password': password})
|
|
|
>>> response.raise_for_status()
|
|
|
>>> response.json()
|
|
|
{
|
|
|
'token': 'sdglkjlsdnawsdjkfsklsdfwe',
|
|
|
'expiration': '2018-06-11T13:25:35Z'
|
|
|
}
|
|
|
```
|
|
|
|
|
|
2. Once you have your token, you will need to send it along with every requests that need authentication. For that add the `Authorization: Token <token>` in your HTTP header of the requests. If everything is fine, you will receive the appropriate response, else you will get a 400 (*BAD REQUEST*) or a 401 (*UNAUTHORIZED*) response from the server
|
|
|
* Example with **curl**:
|
|
|
|
|
|
```bash
|
|
|
$ curl -X GET -H 'Authorization: Token sdglkjlsdnawsdjkfsklsdfwe' https://example.net/api/users/users/
|
|
|
{....}
|
|
|
```
|
|
|
* Example with **python** and **requests**:
|
|
|
|
|
|
```python
|
|
|
>>> import requests
|
|
|
>>> response = requests.get('https://example.net/api/users/users/', headers={'Authorization': 'Token sdglkjlsdnawsdjkfsklsdfwe'})
|
|
|
>>> response.raise_for_status()
|
|
|
>>> response.json()
|
|
|
{
|
|
|
...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
# Permissions
|
|
|
|
|
|
Being authenticated will not do everything you will need also need to have the right to access the data you are asking for. Most of the endpoints requires you to have specific [ACL](User Documentation/ACL) to access them. For the exact list of ACL required, see the [endpoints' details page](User Documentation/API/Endpoints). In addition, you will need to have the *API Permission* to access any API page (except the API root page).
|
|
|
|
|
|
|
|
|
## The API permission
|
|
|
|
|
|
Every user that wants to use the API (for any endpoint) need to be granted the *API Permission* labeled `api | api | can see the api` in the groups' rights list in Re2o's web interface. It is mandatory and intended by design. Even if, in theory and with a lot of effort, one (with the right ACL) could parse the HTML of the web interface and get the information requested without the *API Permission*, having this specific permission allow to quickly identify which user can access the API and easily restrict someone to non automated process.
|
|
|
|
|
|
|
|
|
## Standard ACL
|
|
|
|
|
|
In addition to that permission, a user accessing an endpoint, will need specific ACL (depending on the models affected by the request and the method used) for the requests to be accepted. In general, the ACL required are the following but some exceptions exists and all endpoint do not fit in this general rule, check the [endpoints' details page](User Documentation/API/Endpoints) for more precise details.
|
|
|
|
|
|
| Method | On a model | On an instance |
|
|
|
| ------ | -------------- | -------------- |
|
|
|
| GET | `can_view_all` | `can_view` |
|
|
|
| OPTION | `can_view_all` | `can_view` |
|
|
|
| HEAD | `can_view_all` | `can_view` |
|
|
|
| POST | `can_create` | not authorized |
|
|
|
| PUT | not authorized | `can_edit` |
|
|
|
| PATCH | not authorized | `can_edit` |
|
|
|
| DELETE | not authorized | `can_delete` |
|
|
|
|
|
|
|
|
|
# Pagination
|
|
|
|
|
|
By exploring the ACL one will probably figure out that the model-based endpoints (those reflecting the instances of a model) and some other endpoints are using a pagination system. The results are split in sub-lists and you need to query the next page for the next part of the results. This is to avoid accidentally requests that overload the server by asking it to dump thousands of database entry and to avoid responses of several MB because of a huge list returned.
|
|
|
|
|
|
The pagination is actually quite simple: instead of returning the results directly, every paginated JSON response follow this architecture:
|
|
|
```
|
|
|
// https://example.net/api/users/users/?page=42
|
|
|
{
|
|
|
"count": 1234, // an integer indicating the total number of results (~= nb_page*page_size)
|
|
|
"next": "https://example.net/api/users/users/?page=43", // The URL to query to get the next page of results
|
|
|
"previous": "https://example.net/api/users/users/?page=41", // The URL to query to get the previous page of results
|
|
|
"results": [ // An array of the result in that page
|
|
|
// ... 100 elements ...
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Two query parameters can be used to control the pagination:
|
|
|
* `page`: An integer to specify which page is requested
|
|
|
* If nothing specified, the first page is selected
|
|
|
* If values `last`, the last page is selected
|
|
|
* If not an integer, the first page is selected
|
|
|
* If a zero or negative integer, the first page is selected
|
|
|
* If too big integer, the last page is selected
|
|
|
* `page_size`: An integer to specify the size of the page (i.e. the number of results per page)
|
|
|
* If nothing specified, the default (100) is selected
|
|
|
* If values `all`, only one page with all the results are returned (*:warning: This option is to use carefully and has been enabled for specific optimization cases only*)
|
|
|
* If not an integer, the default (100) is selected
|
|
|
* If zero or negative integer, the default (100) is selected
|
|
|
* If over-the-limit integer, the limit (10000) is selected
|
|
|
|
|
|
|
|
|
# HTTP status codes
|
|
|
|
|
|
Here is a complete list of the HTTP status code that can be returned while interacting with the API and their meanings. Of course, one can also encounter other HTTP status code due to errors independent from the API (e.g. 404 (*NOT FOUND*) or 503 (*SERVICE UNAVAILABLE*)):
|
|
|
|
|
|
* 200 *OK*:
|
|
|
* The resource can be accessed and its details were returned in the content of the response
|
|
|
* The action asked has been performed successfully. Often the details of the targeted object (if modification or creation) were returned in the content of the response
|
|
|
* 400 *BAD REQUEST*:
|
|
|
* The request was misformatted, some explanation may be found in the content of the response
|
|
|
* The authentication header was misformatted, some explanation may be found in the content of the response
|
|
|
* 401 *UNAUTHORIZED*:
|
|
|
* No authentication mechanism was provided along with the requests and the page requested requires authentication, a WWW-authenticate header inform you on the realm to use for authentication (`WWW-Authenticate: Token`)
|
|
|
* The provided credentials is not valid
|
|
|
* The provided authentication mechanism is not valid or expired
|
|
|
* 403 *FORBIDDEN*:
|
|
|
* The resource requested is not accessible for this user
|
|
|
|
|
|
|
|
|
# RESTful status
|
|
|
|
|
|
This API is intended to be the RESTful possible, but it is not entirely. The only feature missing is the versioning of the API which highly help maintaining the compatibility with old services using the API. Unfortunately, versioning the API also means maintaining the exact same behavior of old endpoints, which is a complicated task in the context of Re2o where the models' definition change quite often and maintaining old endpoint that may not reflect the project anymore will mainly be a burden. Thus it was decided not to version the API at the risk of breaking some service compatibility. This risk is considered to be bearable in the current context were Re2o's instances are installed: the production installations are quite well-known and the teams managing it are highly available in case of a problem. |
|
|
\ No newline at end of file |