Security
The Zato settings that match each way a DHIS2 instance authenticates clients and calls other systems.
Each section of this page starts from a way the DHIS2 side has been set up and gives the Zato setting that matches it. Calls from Zato to DHIS2 authenticate with a token or with a username and password. Calls from DHIS2 to Zato arrive as event hooks or through the Route API, and each one reaches a REST channel with a security definition of its own.
A personal access token
A personal access token is created in DHIS2 for the user the integration acts as, and Zato sends it in the Authorization header with the ApiToken prefix. This is a static bearer token definition, created as described in the tutorial, and every REST outgoing connection to the instance uses it.
A token has an expiry date set when it was created, and DHIS2 responds with HTTP 401 to every request made with a token that has expired. A new token replaces the old one in the definition, and the connections that use the definition send the new token from the next request on, with no restarts and no services deployed again:
- In the Dashboard, open Security ▹ Bearer tokens.
- Click Edit in the row of the definition.
- Paste the new token into Token on the Static tokens tab.
- Click OK.
With enmasse, the token is the static_token of the definition, set through an environment variable as the enmasse reference shows. A new token is then a new value of the variable and another import of the same file.
A username and password
A DHIS2 user's username and password authenticate with HTTP basic auth. The matching Zato setting is a basic auth definition, selected as the Security of the REST outgoing connections in place of a bearer token definition:
- In the Dashboard, open Security ▹ Basic Auth.
- Click Create a Basic Auth definition.
- Enter
DHIS2as the Name, and the DHIS2 username and password. - Click OK.
The services do not change, because they invoke the connections by name and the connection decides which credentials go with each request.
Event hooks
An event hook makes DHIS2 send a JSON document to a URL each time metadata is created, updated or deleted, or a scheduled job such as the analytics table update runs. The document carries the fields the hook was set up to select, id and displayName unless it selects others.
The URL is a REST channel in Zato, and the credentials the hook sends decide its security definition:
| The hook sends | The channel's security |
|---|---|
A username and password, the http-basic authentication of the hook | A basic auth definition with the same username and password |
| A header with a key, set among the hook's headers | An API key definition whose Header is the name of that header |
The service receives the document as its request:
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class ReceiveEventHook(Service):
name = 'dhis2.receive-event-hook'
def handle(self) -> 'None':
event = self.request.payload
self.logger.info('DHIS2 event: %s', event)
DHIS2 delivers hooks on a best-effort basis, with no guarantee that each event arrives, so a service that keeps a copy of metadata in step with DHIS2 also reads the full state at an interval.
The Route API
A DHIS2 web app reaches other systems through a route, which DHIS2 runs on the app's behalf so that the app never holds the target's credentials. When the target is Zato, the route points to a REST channel, and the route's authentication decides the channel's security definition:
| The route's authentication | The channel's security |
|---|---|
http-basic | A basic auth definition with the same username and password |
api-headers | An API key definition whose Header is the header the route sends |
DHIS2 adds the X-Forwarded-User header to every request a route sends. It holds the username of the DHIS2 user who made the call from the app, and the service reads it with the other request headers:
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class RouteLookup(Service):
name = 'dhis2.route.lookup'
def handle(self) -> 'None':
# The DHIS2 user who made the call from the app ..
headers = self.request.http.headers
user = headers['x-forwarded-user']
# .. is the one the request is recorded under.
self.logger.info('Route request from DHIS2 user %s', user)
Header names in self.request.http.headers are in lower case. A route on current DHIS2 versions waits five seconds for the channel's response unless it sets a different timeout, and returns an error to the app when the response takes longer.
Case data in the audit log
Tracker requests and responses carry case data - names, identifiers, phone numbers and results. The audit log records the requests a REST channel receives and the requests an outgoing REST connection sends, with their bodies, and keeps them for 30 days by default. Opening a message body in the Dashboard is itself recorded, with who opened it and when.
The audit log is set per connection and per channel:
- Keep the events and the bodies. This is the default. The retention period applies to both.
- Keep the events, expire the bodies sooner.
Zato_Audit_Log_Content_Retention_Days_REST_OUTGOINGandZato_Audit_Log_Content_Retention_Days_REST_CHANNELset how many days bodies are kept, as described in keeping payloads for less time than events. Bodies of failed requests are kept for the full period, because they are what explains the failure. - Record nothing. The Audit log checkbox in the form of the connection or channel turns its audit log off.
The OpenTelemetry export of the audit log sends no bodies unless an object's payload export flag is turned on.
See also
| Feature | What it does |
|---|---|
| Bearer tokens | Static tokens sent in a header of your choice |
| Basic Auth | Usernames and passwords for channels and outgoing connections |
| API keys | Keys in a header, matched before any service code runs |
| Audit log | What is recorded, for how long, and who opened it |