Using a custom CompartmentDefinition for HAPI FHIR Store Authorization

Hi all,

I’m currently looking into implementing authorization checks onto our HAPI FHIR store. Our store is accessed by applications (not users) as SMART Backend Services. Every application has their own client_id and their own Device instance.

I’ve written an interceptor, that adds an extension (resource-origin: Reference(Device)) to every DomainResource that gets persisted. This way we can track who originally created and “owns” a resource.

The next step is to implement an Authorization Interceptor and a Search Narrowing Interceptor. As they both use compartments, the logical step seemed to be to create a CompartmentDefinition that groups any DomainResource created by an application to its own “application compartment” via the newly created resource-origin extension. So the following CompartmentDefinition is created:

PUT {{baseUrl}}/CompartmentDefinition/ResourceOrigin
{
  "resourceType": "CompartmentDefinition",
  ...
  "id": "ResourceOrigin",
  "name": "ResourceOrigin",
  "search": true,
  "code": "Device",
  "resource": [
    {"code": "ActivityDefinition", "param": ["resource-origin"]},
    {"code": "CareTeam", "param": ["resource-origin"]},
    ...
  ]
}

I can successfully create the CompartmentDefinition, but I’m unsure on how to use this in combination with the interceptor. It felt like the following should work:

@Override
public List<IAuthRule> buildRuleList(RequestDetails requestDetails) {
	
	Device device = ResourceOriginUtil.getDevice(requestDetails, deviceDao)
		.orElseThrow(() -> new IllegalStateException("Device not present"));
	
	return new RuleBuilder()
		.allow().read().allResources().inCompartment("ResourceOrigin", device.getIdElement())
		.andThen().denyAll().build();
}

When the interceptor is being executed, I always get the following response:

{
  "resourceType": "OperationOutcome",
  "text": {
    "status": "generated",
    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><h1>Operation Outcome</h1><table border=\"0\"><tr><td style=\"font-weight: bold;\">ERROR</td><td>[]</td><td><pre>Access denied by rule: (unnamed rule)</pre></td>\n\t\t\t</tr>\n\t\t</table>\n\t</div>"
  },
  "issue": [ {
    "severity": "error",
    "code": "processing",
    "diagnostics": "Access denied by rule: (unnamed rule)"
  } ]
}

HL7 does mention they are only allowed to create CompartmentDefinitions. I am unsure if this counts for HAPI as it’s not mentioned in the HAPI documentation and they use compartments as the core of the authorization & search narrowing interceptors.

So my questions are:

  1. Is the above a good approach to handling authorizations in HAPI?
  2. If not, what would be a “best practice” alternative?
  3. If it is, how should I refer to a compartment for a specific Device? So for example: allow to read all resources that have the resource-origin extension with value ABC.
  4. Does the RuleBuilder.inCompartment(String theCompartmentName, IIdType theOwner) find the compartment based on the CompartmentDefinition.name property?
  5. I’ve also seen an approach where they seem to overwrite the CompartmentDefinition. Would this be an option for HAPI as well if the above is not possible?

Thanks in advance,
Joris

Hi all,

A small update:

I’ve decided to write a custom AuthorizationInterceptor and SearchNarrowingInterceptor. A Compartment seemed nice, but I doubt that HAPI supports custom compartments.

As well, in our case, every DomainResouce is mapped to the Device by the exact same Extension. This meant the Compartment might be an overkill.

For the search narrowing, I simply add my custom SearchParameter to the parameter map of incoming search requests. For the Create/Update/Delete I simply look at who is trying to do what based on the resource-origin and the Bearer token.

Cheers,
Joris