HL7 FHIR Implementation Guide: Data Access Policies, published by HL7 International / Security. This guide is not an authorized publication; it is the continuous build for version 1.0.0-current built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/HL7/data-access-policies/ and changes regularly. See the Directory of published versions
Page standards status: Informative |
REST API scopes allow us to give CRUD rights to resource consumers.
However, we cannot give detailed access to a specific pool of Patients or to only certain elements inside of the allowed resource.
This use-case aims to enable fine-grained filtering using the Permission resource to express which pool of resources are accessible to a specific data collector and which data is allowed whithin these resources.
For now this use-cases only applies to request on Patient resources, but aims to apply to any kind of resource in the futur.
Our security design consists of five layers, each providing additional depth to data protection:
Patient.read
).Layers 4 and 5 leverage the Permission resource for advanced filtering and are the focus of this document.
Our platform's architecture integrates a FHIR server and a middleware that acts as both an Enterprise Application Integration (EAI) layer and an proxy for Patient identity consumers.
This architecture, ensures that data collectors never directly interact with the FHIR server, adding a layer of protection and allowing us to apply changes between the request and the response.
This EAI will interact with the FHIR server when it receives the request and apply Permission-based filters to the request before sending the response to the initial request sender.
GET /Patient?family=Baker
) with its scope and Permission ID within the OAuth2 token.TAG_1
and not patients tagged with VIP
).Before applying Permissions, we must identify and tag resource pools.
For example, if we want to restrict access to patients from a specific department or age group, we use security labels within the meta
element of resources.
{
"resourceType": "Patient",
"id": "1",
"meta": {
"security": [
{
"system": "http://your-fhir-server.com/fhir/ValueSet/local-tags",
"code": "TAG_1"
}
]
}
}
We utilize custom scripts within the middleware to automate the tagging process, based on events triggering PATCH requests, adding or removing security labels on specific patients using their national identifier, ensuring consistency and efficiency.
The Permission resource defines the rules for granting or denying access to specific data pools or elements. It consists of general information and two primary rule types:
TAG_1
).VIP
or sensitive elements like addresses).In this example, the Permission resource allow consumer EXAMPLE
to see exclusively Patient resources with the security label TAG_1
but cannot see Patients with the security label VIP
. They also cannot see the Patient's given birthdate, address and metadata.
{
"resourceType": "Permission",
"id": "EXAMPLE"
"status": "active",
"combining": "permit-unless-deny",
"rule": [
{
"type": "permit",
"data": [
{
"resource": {
"type": "Patient",
},
"security": [
{
"system": "http://your-fhir-server.com/fhir/ValueSet/local-tags",
"code": "TAG_1"
}
]
}
]
},
{
"type": "deny",
"data": [
{
"resource": {
"type": "Patient",
},
"security": [
{
"system": "http://your-fhir-server.com/fhir/ValueSet/local-tags",
"code": "VIP"
}
],
"expression": [
{"language": "text/jsonpath", "expression": "$.address"}
],
"expression": [
{"language": "text/jsonpath", "expression": "$.birthdate"}
],
"expression": [
{"language": "text/jsonpath", "expression": "$.meta"}
]
}
]
}
]
}
Patient 1 :
{
"resourceType": "Patient",
"id": "1",
"name": [
{
"family": "Baker",
"given": [
"William Howard"
],
"text": "William Howard Baker",
"use": "official",
}
],
"meta": {
"security": [
{
"system": "http://your-fhir-server.com/fhir/ValueSet/local-tags",
"code": "VIP"
}
]
}
}
Patient 2 :
{
"resourceType": "Patient",
"id": "2",
"name": [
{
"family": "Baker",
"given": [
"Joséphine"
],
"text": "Joséphine Baker",
"use": "official",
}
],
"address": [
{
"use": "home",
"text": "2127 Lucas Avenue, St. Louis, MO, USA",
"line": ["2127 Lucas Avenue"],
"city": "St. Louis",
"state": "MO",
"postalCode": "63103",
"country": "USA"
}
],
"birthDate": "1906-06-03",
"gender": "female",
"meta": {
"security": [
{
"system": "http://your-fhir-server.com/fhir/ValueSet/local-tags",
"code": "TAG_1"
}
]
}
}
GET /Patient?family=Baker
Scope: Patient.read EXAMPLE
EXAMPLE
TAG_1
VIP
Address
, BirthDate
, Metadata
{
"resourceType": "Patient",
"id": "2",
"name": [
{
"family": "Baker",
"given": [
"Joséphine"
],
"text": "Joséphine Baker",
"use": "official",
}
],
"gender": "female"
}
Patient
resource since it is the only resource we actually have.Identifier
element to retrieve Permissions is not ideal, but for now search parameters are restricted to identifier and status element.
In this new version, we've adressed a few points mentioned previously :
In our first use-case using Permissions, the data collector is an app that wants to retrieve certain Patient informations.
We used a Device resource to represent it.
This device resource has an id
, which we will use to refer to this Device later on.
The List resource seems to suit our needs well as it can store references to other resources such as Patients.
We created a List resource with it's subject
element referencing to the Device resource representing our data collector App and each entry.item
representing a Patient.
Here's an example of a short List :
{
"resourceType": "List",
"status": "current",
"mode": "working",
"subject": [
{
"reference": "Device/1",
}
],
"entry": [
{
"item": {
"reference": "Patient/1",
}
},
{
"item": {
"reference": "Patient/2",
}
}
]
}
The goal here is to create an empty list and then use a script to add patients to the List using PATCH requests when they match specific criterias or triggers.
With these changes, the Permission resource needs some adjustments
Here is a new example of a Permission resource with these changes applied :
{
"resourceType": "Permission",
"status": "active",
"asserter": {
"reference": "Organization/1",
"display": "Hospital of Toulouse"
},
"combining": "permit-overrides",
"rule": [
{
"activity": [
{
"actor": [
{
"reference": "Device/1"
}
],
"action": [
{
"coding": [
{
"system": "http://hl7.org/fhir/ValueSet/consent-action",
"code": "collect",
"display": "Collect"
}
]
}
]
}
],
"type": "permit",
"data": [
{
"resource": [
{
"meaning": "related",
"reference": {
"reference": "List/1",
"display": "Patient List managed by xxxxx"
}
}
],
"expression": {
"description": "Access only patients from the xxxxx-managed list",
"language": "text/fhirpath",
"expression": "entry.item.reference.where(resolve() is Patient)"
}
}
]
},
{
"type": "deny",
"data": [
{
"expression": {
"language": "text/jsonpath",
"expression": "$.id"
}
},
{
"expression": {
"language": "text/jsonpath",
"expression": "$.active"
}
},
{
"expression": {
"language": "text/jsonpath",
"expression": "$.name"
}
}
]
}
],
}
As discussed previously, for now we use data.expression
to refer to jsonpath elements that we want to remove from the Patient resource.
This data.expression
is meant to be used as a data selector, like we use it in our example to use a fhirpath expression selecting Patients inside of our List.
The limit
element is meant for this use, but is a CodeableConcept
which cannot refer to specific elements inside of a FHIR resource.
The Jira ticket n°49031 refers a similar issues, it's solution being a change in the limit
element :
" change the limit element to a backbone element
move the CodeableConcept from limit to control
- 0..* - http://terminology.hl7.org/ValueSet/v3-SecurityControlObservationValue (preferred)
add .tag
- 0..* - Coding – http://terminology.hl7.org/ValueSet/v3-InformationSensitivityPolicy (preferred)
add .element
- 0..* - string - "path of the element in the hierarchy of elements"
"
For now, it is only possible to search Permission resources based on two parameters :
Permission.identifier
: The unique id for a particular PermissionPermission.status : active |
entered-in-error | draft | rejected |
In order to stop using custom ids for our Permissions and use actor.reference
to refer to a device, we need to be able to search a Permission resource based on this element.
In addition to this mandatory change, we've thought of a few search parameters that will be useful when we will have several Permissions in use.
Validity
Allowing the search of expressions based on they Validity.start
element or Validity.end
element could allow us to quickly check on Permissions that will close/open on a specific date which will be useful.Data.resource.reference
This element is used to refer to the List containing each allowed Patient.Limit.element
If we are able to change the Limit element to use element, we will need to be able to call for all permissions that disable Patient's name or Patient's address for example, allowing this search parameter will prevent going through every permission or use solutions such as FHIR SQL Builders to find these permissions.Data.expression.expression
The data.expression.expression element is a fhirpath expression used to specify which element we aim to use inside of the List, for now we only use Patient, but once we use several resources we might need to search for Permissions using a specific fhirpath expression.