Backend security - Developers guidelines
As a developer, we have to secure the exposed endpoints (APIs) according the desired permission rules.
openIMIS backend is configured with 2 authorizations layers:
the django default (basic) permissions mechanism
the RBAC ‘django-rules’ extensions
If any of the 2 layer grants the access, the access is given to the user.
openIMIS endpoints can be:
django straight ‘views’ endpoints
django-rest-framework endpoints (recommended)
graphene GrapQL queries and mutations (via scheme definition)
At its very basis, backend modules can rely on:
The django straight ‘views’ endpoints must be explicitly defined by the developers (please refer to https://docs.djangoproject.com/en/2.1/topics/security/ )
The django-rest-framework, however, has been configured to enforce the default (type-based) django permissions mapping: GET on <module>.view_<model>, POST on <module>.add_<module>,...
The graphene 'resolve_xxx' hook to access (query) object graph, and 'async_mutate' resolution for mutation
The django default (basic) permissions mechanism has been extended to dynamically add the (legacy) users rights as 'permission'. So, on top of any configured GET <module>.view_<model> (,...) permission, user receives all coded permissions from the tblRoleRight table (for the roles he belongs to).
To check these roles (in the backend), the recommendation is to configure (via module configuration) the used constant for the check:
Example (claim read access):
in openimis-be-claim_py/claim/app.py: DEFAULT_CFG = { [...] ... and check it via standard django permission in openimis-be-claim_py/claim/schema.py: def resolve_claims(self, info, **kwargs): [...] |
|---|
We recommend (and this is what is in place in reference modules such as claims,...) to enforce the fine-grained security in django model itslef (overriding the 'get_query' method). This ensures that security applies whatever endpoint technology (GraphQL, FHIR, ...) is exposing it.
Example (claim limit by user's registered HF):
in openimis-be-claim_py/claim/models.py: @classmethod |
|---|
For finer (rule-based) grained security (object-level) the django-rules module has been configured and is readily available for use (declaring predicates,...). It is however not already used at the time of writing.
For the RBAC,
The ideal situation will be to have only one rest/graphQL API per entity and manage the access based on the role and the status:
for example, a ‘django-rules’ will say that a HF staff has the right to modify a claim only if the status is “entered“, then another ‘django-rules’ will that that the reviewer modify a claim only if the status is “checked“ BUT we should have a way to restrict the fields available/readable.
Do you implement such aproach for the claims ?
For the restrictions on fields, I was thinking to define the “fields readable” and “fields updatable” per ‘django-rules’ and calculate the fields list and the fields_readonly list based on those ‘django-rules’ fields upon submitting/recieving the data (readable if in one ‘django-rules’ readable fields, readable and updatable if in one ‘django-rules’ updatable fields, if no ‘django-rules’ fields set use default from model ). On the front end level, the fields not recieved should not be displayed and the readonly should be grey out.
Can it work ?