Skip to main content

Concepts

Role-based access control is one of Fire Arrow's core features. This chapter explains the concepts behind Fire Arrow's FHIR based RBAC approach, how to plan role concepts for your specific use case and how to implement them in Fire Arrow's configuration.

Request validation

Fire Arrow uses the sequence below to process an incoming client request and return its corresponding result. Refer to the authentication chapter to understand how Fire Arrow determine's a client's role and understand how the role relates to the authentication token.

For each incoming request, Fire Arrow will look up a corresponding validation rule in its configuration file. The default validation strategy will be used for requests that have no matching validation rule.

It is recommended to use a "deny all" default validation rule and then specify explicit "allow" rules per client role and requested FHIR resource. This strategy ensures that data is hidden by default and each and every legitimate request requires a corresponding rule in the configuration file.

Validators

If a validation rule is found, the validator specified in the validation rule is invoked. A validator inspects the request and does one of three things:

  • It lets the request pass unchanged. Example: a patient client tries to access a resource that belongs to that patient.
  • It denies the request. Example: a patient client tries to access a resource which it is not supposed to access, such as a resource belonging to a different patient.
  • It modifies the request so that the validation rule is satisfied. Example: a practitioner tries to search for all organizations in the database but shall only see its own organization.

The first two cases are simple because the request is either executed or not. The last case is interesting as validators are able to modify the incoming request to satisfy validation constraints, a technique that is usually applied to search requests.

Assuming the following validation rule exists in the configuration file:

{
"client_role": "Patient",
"entity_name": "DiagnosticReport",
"operation": "search",
"validator": "PatientCompartment",
}

This rule specifies that a Patient client can search for DiagnosticReport resources but shall only be allowed access to those DiagnosticReport resources that belong to its own patient compartment. (meaning, only those resources where the patient is also a subject)

The following GraphQL query will search for all DiagnosticResources on the server:

query {
DiagnosticReportList {
id
}
}

Normally, this query would return every resource stored on the server. However, the validation rule above instructs Fire Arrow to only make those DiagnosticReport entities available that belong to the client's PatientCompartment. Fire Arrow will automatically window the corresponding query so that the search will only return DiagnosticReports that the client is allowed to see. DiagnosticReport resources that are outside of the client's PatientCompartment will never become visible.

Configuring default access

The default access level is configured through the setting rbac.default_access and specifies the name of the validator that shall be used if no request-specific rule can be found in the section rbac.validation_rules. It is recommended to set this to Forbidden so that each request that has no matching rule will be denied.

To completely disable role-based access control, set the default access to Allowed and remove all validation rules. This will cause all requests to be passed through unfiltered to the FHIR server. In this mode, Fire Arrow will work as a simple GraphQL API layer on top of an existing FHIR REST service.

config.json
{
// ...
"rbac": {
"default_access": "Forbidden",
"validation_rules": [
// ...
]
}
}

Configuring validation rules

Each validation rule has exactly the structure outlined below:

{
"client_role": "Practitioner",
"entity_name": "Patient",
"operation": "search",
"validator": "LegitimateInterest",
// optional
"required_role_system": "http://hl7.org/fhir/ValueSet/practitioner-role",
// optional
"required_role_code": "ict"
}
  • client_role specifies the client's role for which this rule is applicable (see Authentication). If the client has a different role, this rule will not match. The example above matches clients that have authenticated as a Practitioner.
  • entity_name specifies the FHIR resource that the client is trying to access. The example above matches a client that is trying to access a FHIR Patient resource.
  • operation contains the access type that this rule applies to. Valid options are search, create, read, update, delete.
  • validator designates the validator that shall be used to validate this request.
  • required_role_system and required_role_code are optional values applying to clients with the Practitioner role (explained below).

Each request needs to match the combination of client_role, entity_name and operation. Only a single rule should be specified for this combination (with the exception of required_role_system and required_role_code). If multiple rules are specified, the validation configuration is ambiguous and Fire Arrow logs a warning. Fire Arrow will attempt to use the first matching rule (which is usually the one specified first) but configuration should be adjusted if situation occurs.

The validator parameter specifies the name of the validator that should validate the request. Each validator implements its own request validation strategy.

Choosing the right validator

  • Choose the simplest validator that achieves the desired goal.
  • Compartment validators are preferred due to their simplicity and performance.
  • Use the LegitimateInterest validator only if no other validator achieves the desired effect. It's a Swiss army knife and can do many things, but it is also slower than other validators.

Implementing an RBAC scheme

It is important to realize that validators are unaware of the application context they are being used in. They are also unaware of whether they are being used in a sensible way. For example, it is possible to route requests from clients with the Practitioner role through the PatientCompartment validator. These requests will either lead to errors or undesired behavior.

To design an RBAC scheme, the following rules are a good starting point:

  • Ensure that the default validator is set to Forbidden. This prevents unwanted data leaks.
  • Do not ever use the Allowed validator as default validator because it will make everything public that isn't explicitly configured differently. The only reasonable scenario for this setting is when making a FHIR server completely open to the public.
  • The validator concept implicitly enables multi-tenancy, with the exception of Allowed. Allowed makes every entity public, not just for a specific organization or patient.
  • When planning a multi-tenant setup, make sure that rules don't cross the multi-tenancy separation rules. For example, Practitioner clients could be restricted to only see Organization resources in which they have an active role. But if Patient clients have an Allow access to Organization resources, they could retrieve the entire customer list.
  • For every client role that needs to be supported, make a separate list of entities that should be accessible for the corresponding role. For each entity, write down which rights (search / create / read / update / delete) that role should have for the corresponding entity.
  • For every access type for every entity on each list, think which validator is best suited to validate access. Validators with low complexity should be preferred. For example, clients with the Patient role will usually be served best with the PatientCompartment validator.
  • Create individual configuration entries per acess type, entity and client role. Configuration entries for practitioner clients can optionally be restricted to a specific practitioner role coding. For example:
    • "read" on "Patient" for "Practitioner" with role "nurse"
    • "search" on "Patient" for "Practitioner" with role "nurse"
    • "read" on "Patient" for "Practitioner" with role "doctor"
    • "update" on "Patient" for "Practitioner" with role "doctor"
    • "search" on "Patient" for "Practitioner" with role "doctor"
    • "create" on "Patient" for "Practitioner" with role "ict"
    • The above leads to nurses only being able to search for and read patient entries (but not modify them), admins being able to create patients (but not see them) and doctors to read and modify them (but not create or delete them)
  • Do not create configuration entries for entities that don't need to be accessible.
  • Do not create conflicting configuration entries, for example "read" on "Patient" for "Patient" with "PatientCompartment" and "read on "Patient" for "Patient" with "Forbidden". Fire Arrow will log a warning when encountering this. It will not reject the request but pick the validation rule it found first. Since the order of validation rules is not guaranteed, such a configuration may produce unexpected results.
  • Check if the validator you are applying can handle the expected client role. (for example, the DeviceCompartment validator only makes sense for clients with the Device role)
  • Just because you can, doesn't mean you should. Validators can validate many different access patterns. However, all of these patterns need to be checked against your business case. The LegitimateInterest validator can grant a Patient client access to all objects belonging to an organization. This may be useful to access PlanDefinition objects (for self-service clients looking to instantiate a CarePlan), but it may not be desired for PaymentNotice or PaymentReconciliation, as the Patient client will receive access to all payment notices that are sent by the corresponding organization.