As a developer, we have to secure the exposed endpoints (APIs) according the desired permission rules.

openIMIS backend is configured with 2 authorizations layers:

If any of the 2 layer grants the access, the access is given to the user.


openIMIS endpoints can be:


At its very basis, backend modules can rely on:


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 = {
[...]
"gql_query_claims_perms": ["111001"],

[...]
}

... and check it via standard django permission in openimis-be-claim_py/claim/schema.py:

def resolve_claims(self, info, **kwargs):
if not info.context.user.has_perms(ClaimConfig.gql_query_claims_perms):
raise PermissionDenied(_("unauthorized"))

[...]


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:


class Claim(core_models.VersionedModel):
id = models.AutoField(db_column='ClaimID', primary_key=True)
[...]

@classmethod
def get_queryset(cls, queryset, user):
queryset = Claim.filter_queryset(queryset)
# GraphQL calls with an info object while Rest calls with the user itself
if isinstance(user, ResolveInfo):
user = user.context.user
if settings.ROW_SECURITY and user.is_anonymous:
return queryset.filter(id=-1)
if settings.ROW_SECURITY:
# TechnicalUsers don't have health_facility_id attribute
if hasattr(user._u, 'health_facility_id') and user._u.health_facility_id:
return queryset.filter(
health_facility_id=user._u.health_facility_id
)
else:
dist = UserDistrict.get_user_districts(user._u)
return queryset.filter(
health_facility__location_id__in=[l.location_id for l in dist]
)
return queryset


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.